Przeglądaj źródła

Merge branch 'sit-eight-categories' into sit

# Conflicts:
#	alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/OrderReviewServiceImpl.java
#	alien-store/src/main/java/shop/alien/store/service/StoreInfoService.java
penghao 1 dzień temu
rodzic
commit
49523fcef1
100 zmienionych plików z 12626 dodań i 344 usunięć
  1. 18 0
      Dockerfile
  2. 48 0
      alien-entity/src/main/java/shop/alien/entity/result/CommonEnum.java
  3. 139 0
      alien-entity/src/main/java/shop/alien/entity/store/BarPerformance.java
  4. 94 0
      alien-entity/src/main/java/shop/alien/entity/store/FitnessEquipmentInfo.java
  5. 1 1
      alien-entity/src/main/java/shop/alien/entity/store/LifeLikeRecord.java
  6. 15 3
      alien-entity/src/main/java/shop/alien/entity/store/SportsEquipmentFacility.java
  7. 67 0
      alien-entity/src/main/java/shop/alien/entity/store/SportsFacilityArea.java
  8. 102 0
      alien-entity/src/main/java/shop/alien/entity/store/StoreProductBar.java
  9. 91 0
      alien-entity/src/main/java/shop/alien/entity/store/StoreProductDelicacies.java
  10. 106 0
      alien-entity/src/main/java/shop/alien/entity/store/StoreProductGym.java
  11. 111 0
      alien-entity/src/main/java/shop/alien/entity/store/StoreProductItem.java
  12. 54 2
      alien-entity/src/main/java/shop/alien/entity/store/StoreStaffConfig.java
  13. 82 0
      alien-entity/src/main/java/shop/alien/entity/store/StoreStaffFitnessBase.java
  14. 69 0
      alien-entity/src/main/java/shop/alien/entity/store/StoreStaffFitnessCertification.java
  15. 83 0
      alien-entity/src/main/java/shop/alien/entity/store/StoreStaffFitnessCourse.java
  16. 76 0
      alien-entity/src/main/java/shop/alien/entity/store/StoreStaffFitnessExperience.java
  17. 33 0
      alien-entity/src/main/java/shop/alien/entity/store/dto/BarPerformanceOnlineStatusDto.java
  18. 64 0
      alien-entity/src/main/java/shop/alien/entity/store/dto/StoreProductItemDto.java
  19. 38 0
      alien-entity/src/main/java/shop/alien/entity/store/dto/StoreStaffConfigListQueryDto.java
  20. 36 0
      alien-entity/src/main/java/shop/alien/entity/store/dto/StoreStaffFitnessCourseGroup.java
  21. 37 0
      alien-entity/src/main/java/shop/alien/entity/store/dto/StoreStaffFitnessCourseItem.java
  22. 201 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/BarPerformanceDetailVo.java
  23. 29 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/CreateAreaRequestVo.java
  24. 26 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/DeleteAreaRequestVo.java
  25. 46 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/FitnessEquipmentCategoryDetailVo.java
  26. 37 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/FitnessEquipmentTypeSummaryVo.java
  27. 3 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/LawyerConsultationOrderVO.java
  28. 47 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/PerformerVo.java
  29. 43 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/SportsEquipmentFacilityCategorySummaryVo.java
  30. 73 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StoreProductDelicaciesVo.java
  31. 146 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StoreProductItemDelicaciesVo.java
  32. 150 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StoreProductItemGymVo.java
  33. 35 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StoreStaffDetailVo.java
  34. 46 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StoreStaffFitnessDetailVo.java
  35. 27 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StoreStaffPositionCountVo.java
  36. 15 0
      alien-entity/src/main/java/shop/alien/mapper/BarPerformanceMapper.java
  37. 16 0
      alien-entity/src/main/java/shop/alien/mapper/FitnessEquipmentInfoMapper.java
  38. 17 0
      alien-entity/src/main/java/shop/alien/mapper/SportsFacilityAreaMapper.java
  39. 19 0
      alien-entity/src/main/java/shop/alien/mapper/StoreProductBarMapper.java
  40. 17 0
      alien-entity/src/main/java/shop/alien/mapper/StoreProductDelicaciesMapper.java
  41. 19 0
      alien-entity/src/main/java/shop/alien/mapper/StoreProductGymMapper.java
  42. 92 0
      alien-entity/src/main/java/shop/alien/mapper/StoreProductItemMapper.java
  43. 19 0
      alien-entity/src/main/java/shop/alien/mapper/StoreStaffFitnessBaseMapper.java
  44. 19 0
      alien-entity/src/main/java/shop/alien/mapper/StoreStaffFitnessCertificationMapper.java
  45. 19 0
      alien-entity/src/main/java/shop/alien/mapper/StoreStaffFitnessCourseMapper.java
  46. 19 0
      alien-entity/src/main/java/shop/alien/mapper/StoreStaffFitnessExperienceMapper.java
  47. 431 0
      alien-entity/src/main/resources/mapper/StoreProductItemMapper.xml
  48. 19 32
      alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/OrderReviewServiceImpl.java
  49. 48 0
      alien-store/src/main/java/shop/alien/store/config/AsyncConfig.java
  50. 64 64
      alien-store/src/main/java/shop/alien/store/config/MyBatisFieldHandler.java
  51. 202 0
      alien-store/src/main/java/shop/alien/store/controller/BarPerformanceController.java
  52. 363 0
      alien-store/src/main/java/shop/alien/store/controller/FitnessEquipmentInfoController.java
  53. 88 19
      alien-store/src/main/java/shop/alien/store/controller/LifeCommentController.java
  54. 17 0
      alien-store/src/main/java/shop/alien/store/controller/LifeUserDynamicsController.java
  55. 77 5
      alien-store/src/main/java/shop/alien/store/controller/SportsEquipmentFacilityController.java
  56. 218 0
      alien-store/src/main/java/shop/alien/store/controller/SportsFacilityAreaController.java
  57. 78 0
      alien-store/src/main/java/shop/alien/store/controller/StoreInfoController.java
  58. 180 0
      alien-store/src/main/java/shop/alien/store/controller/StoreProductBarController.java
  59. 204 0
      alien-store/src/main/java/shop/alien/store/controller/StoreProductDelicaciesController.java
  60. 170 0
      alien-store/src/main/java/shop/alien/store/controller/StoreProductGymController.java
  61. 167 0
      alien-store/src/main/java/shop/alien/store/controller/StoreProductItemController.java
  62. 287 22
      alien-store/src/main/java/shop/alien/store/controller/StoreStaffConfigController.java
  63. 143 0
      alien-store/src/main/java/shop/alien/store/controller/StoreStaffFitnessBaseController.java
  64. 162 0
      alien-store/src/main/java/shop/alien/store/controller/StoreStaffFitnessCertificationController.java
  65. 145 0
      alien-store/src/main/java/shop/alien/store/controller/StoreStaffFitnessCourseController.java
  66. 142 0
      alien-store/src/main/java/shop/alien/store/controller/StoreStaffFitnessExperienceController.java
  67. 23 0
      alien-store/src/main/java/shop/alien/store/service/BarPerformanceAuditService.java
  68. 81 0
      alien-store/src/main/java/shop/alien/store/service/BarPerformanceService.java
  69. 88 0
      alien-store/src/main/java/shop/alien/store/service/FitnessEquipmentInfoService.java
  70. 262 88
      alien-store/src/main/java/shop/alien/store/service/LifeCommentService.java
  71. 33 0
      alien-store/src/main/java/shop/alien/store/service/LifeUserDynamicsService.java
  72. 20 3
      alien-store/src/main/java/shop/alien/store/service/SportsEquipmentFacilityService.java
  73. 63 0
      alien-store/src/main/java/shop/alien/store/service/SportsFacilityAreaService.java
  74. 19 0
      alien-store/src/main/java/shop/alien/store/service/StoreInfoService.java
  75. 99 0
      alien-store/src/main/java/shop/alien/store/service/StoreProductBarService.java
  76. 106 0
      alien-store/src/main/java/shop/alien/store/service/StoreProductDelicaciesService.java
  77. 89 0
      alien-store/src/main/java/shop/alien/store/service/StoreProductGymService.java
  78. 117 0
      alien-store/src/main/java/shop/alien/store/service/StoreProductItemService.java
  79. 100 12
      alien-store/src/main/java/shop/alien/store/service/StoreStaffConfigService.java
  80. 77 0
      alien-store/src/main/java/shop/alien/store/service/StoreStaffFitnessBaseService.java
  81. 86 0
      alien-store/src/main/java/shop/alien/store/service/StoreStaffFitnessCertificationService.java
  82. 78 0
      alien-store/src/main/java/shop/alien/store/service/StoreStaffFitnessCourseService.java
  83. 78 0
      alien-store/src/main/java/shop/alien/store/service/StoreStaffFitnessExperienceService.java
  84. 108 0
      alien-store/src/main/java/shop/alien/store/service/impl/BarPerformanceAuditServiceImpl.java
  85. 489 0
      alien-store/src/main/java/shop/alien/store/service/impl/BarPerformanceServiceImpl.java
  86. 391 0
      alien-store/src/main/java/shop/alien/store/service/impl/FitnessEquipmentInfoServiceImpl.java
  87. 1025 39
      alien-store/src/main/java/shop/alien/store/service/impl/SportsEquipmentFacilityServiceImpl.java
  88. 279 0
      alien-store/src/main/java/shop/alien/store/service/impl/SportsFacilityAreaServiceImpl.java
  89. 464 0
      alien-store/src/main/java/shop/alien/store/service/impl/StoreInfoServiceImpl.java
  90. 183 0
      alien-store/src/main/java/shop/alien/store/service/impl/StoreProductBarServiceImpl.java
  91. 180 0
      alien-store/src/main/java/shop/alien/store/service/impl/StoreProductDelicaciesServiceImpl.java
  92. 169 0
      alien-store/src/main/java/shop/alien/store/service/impl/StoreProductGymServiceImpl.java
  93. 461 0
      alien-store/src/main/java/shop/alien/store/service/impl/StoreProductItemServiceImpl.java
  94. 907 54
      alien-store/src/main/java/shop/alien/store/service/impl/StoreStaffConfigServiceImpl.java
  95. 139 0
      alien-store/src/main/java/shop/alien/store/service/impl/StoreStaffFitnessBaseServiceImpl.java
  96. 151 0
      alien-store/src/main/java/shop/alien/store/service/impl/StoreStaffFitnessCertificationServiceImpl.java
  97. 138 0
      alien-store/src/main/java/shop/alien/store/service/impl/StoreStaffFitnessCourseServiceImpl.java
  98. 138 0
      alien-store/src/main/java/shop/alien/store/service/impl/StoreStaffFitnessExperienceServiceImpl.java
  99. 31 0
      alien-store/src/main/java/shop/alien/store/util/CommonConstant.java
  100. 205 0
      alien-store/src/main/java/shop/alien/store/util/ai/AiContentModerationUtil.java

+ 18 - 0
Dockerfile

@@ -0,0 +1,18 @@
+# 第一阶段:构建 jar(可选,如果你想在 Docker 里编译)
+# 也可以直接用 Jenkins 外面 mvn package,这里用两阶段构建做参考
+
+FROM maven:3.9.6-eclipse-temurin-17 AS build
+WORKDIR /app
+COPY pom.xml .
+COPY src ./src
+RUN mvn clean package -DskipTests
+
+# 第二阶段:运行 jar
+FROM eclipse-temurin:17-jdk
+WORKDIR /app
+
+# 从上一阶段复制打包好的 jar
+COPY --from=build /app/target/*.jar app.jar
+
+EXPOSE 8080
+ENTRYPOINT ["java","-jar","/app/app.jar"]

+ 48 - 0
alien-entity/src/main/java/shop/alien/entity/result/CommonEnum.java

@@ -0,0 +1,48 @@
+package shop.alien.entity.result;
+
+
+public interface CommonEnum {
+
+    /**
+     * 商品所属模块
+     */
+    enum ModelType {
+        BAR(1, "酒吧"),
+        DELICACY(2, "美食"),
+        GYM(3, "运动健身");
+
+        private final int code;
+        private final String desc;
+
+        ModelType(int code, String desc) {
+            this.code = code;
+            this.desc = desc;
+        }
+
+        public int getCode() { return code; }
+        public String getDesc() { return desc; }
+    }
+
+    /**
+     * 商品所属模块,细化
+     */
+    enum StoreProductItemProdType {
+        BAR_DRINK(1, "酒吧-酒水"),
+        BAR_FOOD(2, "酒吧-餐食"),
+        BAR_COMBO(3, "酒吧-套餐"),
+        DELICACY_FOOD(4, "美食-餐食"),
+        GYM_SINGLE(5, "运动健身-单次"),
+        GYM_MULTI(6, "运动健身-多次");
+
+        private final int code;
+        private final String desc;
+
+        StoreProductItemProdType(int code, String desc) {
+            this.code = code;
+            this.desc = desc;
+        }
+
+        public int getCode() { return code; }
+        public String getDesc() { return desc; }
+    }
+}

+ 139 - 0
alien-entity/src/main/java/shop/alien/entity/store/BarPerformance.java

@@ -0,0 +1,139 @@
+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;
+
+/**
+ * 演出信息实体类
+ * 对应表: bar_performance
+ *
+ * @author assistant
+ * @since 2025-12-17
+ */
+@Data
+@JsonInclude
+@TableName("bar_performance")
+@ApiModel(value = "BarPerformance对象", description = "演出信息")
+public class BarPerformance {
+
+    @ApiModelProperty(value = "演出ID(主键)")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "父演出ID(关联本表id,null为顶级演出)")
+    @TableField("parent_id")
+    private Integer parentId;
+
+    @ApiModelProperty(value = "关联门店ID(关联store_info.id)")
+    @TableField("store_id")
+    private Integer storeId;
+
+    @ApiModelProperty(value = "表演嘉宾ids,逗号分隔")
+    @TableField("staff_config_ids")
+    private String staffConfigIds;
+
+    @ApiModelProperty(value = "演出名称")
+    @TableField("performance_name")
+    private String performanceName;
+
+    @ApiModelProperty(value = "演出海报路径")
+    @TableField("performance_poster")
+    private String performancePoster;
+
+    @ApiModelProperty(value = "演出风格(字典表proficient_tag逗号分隔)")
+    @TableField("performance_style")
+    private String performanceStyle;
+
+    @ApiModelProperty(value = "演出类型(单选:0-特邀演出,1-常规演出)")
+    @TableField("performance_type")
+    private Integer performanceType;
+
+    @ApiModelProperty(value = "演出频次:0-单次 1-每天定时 2-每周定时")
+    @TableField("performance_frequency")
+    private String performanceFrequency;
+
+    @ApiModelProperty(value = "演出日期(0-周一,1-周二,2-周三,3-周四,4-周五,5-周六,6-周日,复选逗号分隔)")
+    @TableField("performance_week")
+    private String performanceWeek;
+
+    @ApiModelProperty(value = "演出日期开始时间")
+    @TableField("single_start_datetime")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date singleStartDatetime;
+
+    @ApiModelProperty(value = "演出日期结束时间")
+    @TableField("single_end_datetime")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date singleEndDatetime;
+
+    @ApiModelProperty(value = "演出时间开始日期")
+    @TableField("daily_start_date")
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    private Date dailyStartDate;
+
+    @ApiModelProperty(value = "演出时间结束日期")
+    @TableField("daily_end_date")
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    private Date dailyEndDate;
+
+    @ApiModelProperty(value = "图片")
+    @TableField("performance_detail")
+    private String performanceDetail;
+
+    @ApiModelProperty(value = "演出完整详情")
+    @TableField("performance_content")
+    private String performanceContent;
+
+    @ApiModelProperty(value = "演出须知(限300字)")
+    @TableField("performance_notice")
+    private String performanceNotice;
+
+    @ApiModelProperty(value = "审核状态:0-待审核 1-审核通过 2-审核拒绝")
+    @TableField("review_status")
+    private Integer reviewStatus;
+
+    @ApiModelProperty(value = "审核拒绝原因")
+    @TableField("reject_reason")
+    private String rejectReason;
+
+    @ApiModelProperty(value = "删除标记:0-未删除,1-已删除")
+    @TableField("delete_flag")
+    @TableLogic
+    private Integer deleteFlag;
+
+    @ApiModelProperty(value = "隐藏标记:0-不隐藏,1-隐藏")
+    @TableField("hidden")
+    private Integer hidden;
+
+    @ApiModelProperty(value = "上线状态:0-下线,1-上线")
+    @TableField("online_status")
+    private Integer onlineStatus;
+
+    @ApiModelProperty(value = "状态:0禁用,1启用")
+    @TableField("status")
+    private Integer status;
+
+    @ApiModelProperty(value = "创建人")
+    @TableField("created_user_id")
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "更新人")
+    @TableField("updated_user_id")
+    private Integer updatedUserId;
+
+    @ApiModelProperty(value = "创建时间")
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "更新时间")
+    @TableField(value = "updated_time", fill = FieldFill.UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+}

+ 94 - 0
alien-entity/src/main/java/shop/alien/entity/store/FitnessEquipmentInfo.java

@@ -0,0 +1,94 @@
+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(JsonInclude.Include.NON_NULL)
+@TableName("fitness_equipment_info")
+@ApiModel(value = "FitnessEquipmentInfo对象", description = "健身设备信息")
+public class FitnessEquipmentInfo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "主键ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "设备名称", required = true)
+    @TableField("equipment_name")
+    private String equipmentName;
+
+    @ApiModelProperty(value = "设备编码")
+    @TableField("equipment_code")
+    private String equipmentCode;
+
+    @ApiModelProperty(value = "设备类型(1:心肺训练, 2:核心训练, 3:臀腿训练, 4:上肢训练, 5:全身训练, 6:其他)", required = true)
+    @TableField("equipment_type")
+    private Integer equipmentType;
+
+    @ApiModelProperty(value = "设备图标URL")
+    @TableField("equipment_icon")
+    private String equipmentIcon;
+
+    @ApiModelProperty(value = "设备图片URL")
+    @TableField("equipment_image")
+    private String equipmentImage;
+
+    @ApiModelProperty(value = "设备描述")
+    @TableField("description")
+    private String description;
+
+    @ApiModelProperty(value = "使用方法")
+    @TableField("usage_method")
+    private String usageMethod;
+
+    @ApiModelProperty(value = "注意事项")
+    @TableField("precautions")
+    private String precautions;
+
+    @ApiModelProperty(value = "排序号")
+    @TableField("sort_order")
+    private Integer sortOrder;
+
+    @ApiModelProperty(value = "状态(0:禁用, 1:启用)")
+    @TableField("status")
+    private Integer status;
+
+    @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.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;
+}
+

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

@@ -23,7 +23,7 @@ public class LifeLikeRecord {
 
     private String huifuId;
 
-    @ApiModelProperty(value = "1-评论 2-社区动态 3-活动 4-推荐菜 5-店铺打卡 6-二手商品")
+    @ApiModelProperty(value = "1-评论 2-社区动态 3-活动 4-推荐菜 5-店铺打卡 6-二手商品 7-律师评分 8-点赞员工")
     private String type;
 
     private Integer dianzanCount;

+ 15 - 3
alien-entity/src/main/java/shop/alien/entity/store/SportsEquipmentFacility.java

@@ -11,7 +11,7 @@ import java.io.Serializable;
 import java.util.Date;
 
 /**
- * 运动器材设施表
+ * 店铺运动器材设施表
  *
  * @author system
  * @since 2025-01-XX
@@ -19,7 +19,7 @@ import java.util.Date;
 @Data
 @JsonInclude
 @TableName("sports_equipment_facility")
-@ApiModel(value = "SportsEquipmentFacility对象", description = "运动器材设施表")
+@ApiModel(value = "SportsEquipmentFacility对象", description = "店铺运动器材设施表")
 public class SportsEquipmentFacility implements Serializable {
 
     private static final long serialVersionUID = 1L;
@@ -32,10 +32,14 @@ public class SportsEquipmentFacility implements Serializable {
     @TableField("store_id")
     private Integer storeId;
 
-    @ApiModelProperty(value = "设施分类(1:有氧区, 2:力量区, 3:单功能机械区)")
+    @ApiModelProperty(value = "设施分类(1:有氧区, 2:力量区, 3:单功能机械区) - 已废弃,请使用facilityCategoryName")
     @TableField("facility_category")
     private Integer facilityCategory;
 
+    @ApiModelProperty(value = "设施分类名称(商户自定义)")
+    @TableField("facility_category_name")
+    private String facilityCategoryName;
+
     @ApiModelProperty(value = "设施名称")
     @TableField("facility_name")
     private String facilityName;
@@ -78,5 +82,13 @@ public class SportsEquipmentFacility implements Serializable {
     @ApiModelProperty(value = "修改人ID")
     @TableField(value = "updated_user_id", fill = FieldFill.INSERT_UPDATE)
     private Integer updatedUserId;
+
+    @ApiModelProperty(value = "设备列表(fitness_equipment_info表的id,用逗号分隔)")
+    @TableField("fitness_equipment_ids")
+    private String fitnessEquipmentIds;
+
+    @ApiModelProperty(value = "区域ID(关联sports_facility_area.id)")
+    @TableField("area_id")
+    private Integer areaId;
 }
 

+ 67 - 0
alien-entity/src/main/java/shop/alien/entity/store/SportsFacilityArea.java

@@ -0,0 +1,67 @@
+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;
+
+/**
+ * 运动设施区域表实体类
+ * 对应表: sports_facility_area
+ *
+ * @author assistant
+ * @since 2025-12-19
+ */
+@Data
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@TableName("sports_facility_area")
+@ApiModel(value = "SportsFacilityArea对象", description = "运动设施区域表")
+public class SportsFacilityArea implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "主键ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "门店ID", required = true)
+    @TableField("store_id")
+    private Integer storeId;
+
+    @ApiModelProperty(value = "区域名称(必填,限10字)", required = true)
+    @TableField("area_name")
+    private String areaName;
+
+    @ApiModelProperty(value = "排序号(数字越小越靠前)")
+    @TableField("sort_order")
+    private Integer sortOrder;
+
+    @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;
+}
+

+ 102 - 0
alien-entity/src/main/java/shop/alien/entity/store/StoreProductBar.java

@@ -0,0 +1,102 @@
+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.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 酒吧商品表
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@JsonInclude
+@TableName("store_product_bar")
+@ApiModel(value = "StoreProductBar对象", description = "酒吧商品表")
+public class StoreProductBar implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "主键")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "商品表主键")
+    @TableField("ext_id")
+    private Integer extId;
+
+    @ApiModelProperty(value = "区分套餐下的酒水1,餐食2")
+    @TableField("sub_type")
+    private int sub_type;
+
+    @ApiModelProperty(value = "名称(酒水,餐食)")
+    @TableField("name")
+    private String name;
+
+    @ApiModelProperty(value = "价格(¥)")
+    @TableField("price")
+    private BigDecimal price;
+
+    @ApiModelProperty(value = "成本价(¥)")
+    @TableField("cost_price")
+    private BigDecimal costPrice;
+
+    @ApiModelProperty(value = "单位,如份/瓶/杯")
+    @TableField("unit")
+    private String unit;
+
+    @ApiModelProperty(value = "数量")
+    @TableField("quantity")
+    private Integer quantity;
+
+    @ApiModelProperty(value = "品类")
+    @TableField("category")
+    private String category;
+
+    @ApiModelProperty(value = "酒精度(%vol)")
+    @TableField("alcohol_vol")
+    private BigDecimal alcoholVol;
+
+    @ApiModelProperty(value = "酒水体积(ml)")
+    @TableField("volume_ml")
+    private Integer volumeMl;
+
+    @ApiModelProperty(value = "风味")
+    @TableField("flavor")
+    private String flavor;
+
+    @ApiModelProperty(value = "状态:0禁用,1启用")
+    @TableField("status")
+    private Integer status;
+
+    @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
+    @TableField("delete_flag")
+    @TableLogic
+    private Integer deleteFlag;
+
+    @ApiModelProperty(value = "创建人")
+    @TableField("created_user_id")
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "更新人")
+    @TableField("updated_user_id")
+    private Integer updatedUserId;
+
+    @ApiModelProperty(value = "创建时间")
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "更新时间")
+    @TableField(value = "updated_time", fill = FieldFill.UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+}

+ 91 - 0
alien-entity/src/main/java/shop/alien/entity/store/StoreProductDelicacies.java

@@ -0,0 +1,91 @@
+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.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 美食商品表
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@JsonInclude
+@TableName("store_product_delicacies")
+@ApiModel(value = "StoreProductDelicacies对象", description = "美食商品表")
+public class StoreProductDelicacies implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "主键")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "商品表主键")
+    @TableField("ext_id")
+    private Integer extId;
+
+    @ApiModelProperty(value = "名称")
+    @TableField("name")
+    private String name;
+
+    @ApiModelProperty(value = "价格(¥)")
+    @TableField("price")
+    private BigDecimal price;
+
+    @ApiModelProperty(value = "成本价(¥)")
+    @TableField("cost_price")
+    private BigDecimal costPrice;
+
+    @ApiModelProperty(value = "单位,如份/瓶/杯")
+    @TableField("unit")
+    private String unit;
+
+    @ApiModelProperty(value = "数量")
+    @TableField("quantity")
+    private Integer quantity;
+
+    @ApiModelProperty(value = "类别")
+    @TableField("category")
+    private String category;
+
+    @ApiModelProperty(value = "菜品分组")
+    @TableField("ext_group")
+    private String extGroup;
+
+    @ApiModelProperty(value = "状态:0禁用,1启用")
+    @TableField("status")
+    private Integer status;
+
+    @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
+    @TableField("delete_flag")
+    @TableLogic
+    private Integer deleteFlag;
+
+    @ApiModelProperty(value = "创建人")
+    @TableField("created_user_id")
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "更新人")
+    @TableField("updated_user_id")
+    private Integer updatedUserId;
+
+    @ApiModelProperty(value = "创建时间")
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "更新时间")
+    @TableField(value = "updated_time", fill = FieldFill.UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+}
+

+ 106 - 0
alien-entity/src/main/java/shop/alien/entity/store/StoreProductGym.java

@@ -0,0 +1,106 @@
+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.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 运动健身商品表
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@JsonInclude
+@TableName("store_product_gym")
+@ApiModel(value = "StoreProductGym对象", description = "运动健身商品表")
+public class StoreProductGym implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "主键")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "商品表主键")
+    @TableField("ext_id")
+    private Integer extId;
+
+    @ApiModelProperty(value = "名称")
+    @TableField("name")
+    private String name;
+
+    @ApiModelProperty(value = "可用/剩余次数")
+    @TableField("usage_count")
+    private Integer usageCount;
+
+    @ApiModelProperty(value = "上课形式,如一对一/小班课")
+    @TableField("class_mode")
+    private String classMode;
+
+    @ApiModelProperty(value = "可用周期(天)")
+    @TableField("available_days")
+    private Integer availableDays;
+
+    @ApiModelProperty(value = "总课时数(节)")
+    @TableField("total_sessions")
+    private Integer totalSessions;
+
+    @ApiModelProperty(value = "价格(¥)")
+    @TableField("price")
+    private BigDecimal price;
+
+    @ApiModelProperty(value = "课程功效")
+    @TableField("course_effect")
+    private String courseEffect;
+
+    @ApiModelProperty(value = "适用人群")
+    @TableField("suitable_people")
+    private String suitablePeople;
+
+    @ApiModelProperty(value = "适用会员")
+    @TableField("suitable_membership")
+    private String suitableMembership;
+
+    @ApiModelProperty(value = "淋浴设施")
+    @TableField("shower_facility")
+    private String showerFacility;
+
+    @ApiModelProperty(value = "额外须知")
+    @TableField("extra_notice")
+    private String extraNotice;
+
+    @ApiModelProperty(value = "状态:0禁用,1启用")
+    @TableField("status")
+    private Integer status;
+
+    @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
+    @TableField("delete_flag")
+    @TableLogic
+    private Integer deleteFlag;
+
+    @ApiModelProperty(value = "创建人")
+    @TableField("created_user_id")
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "更新人")
+    @TableField("updated_user_id")
+    private Integer updatedUserId;
+
+    @ApiModelProperty(value = "创建时间")
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "更新时间")
+    @TableField(value = "updated_time", fill = FieldFill.UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+}

+ 111 - 0
alien-entity/src/main/java/shop/alien/entity/store/StoreProductItem.java

@@ -0,0 +1,111 @@
+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.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 商品表
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@JsonInclude
+@TableName("store_product_item")
+@ApiModel(value = "StoreProductItem对象", description = "商品表")
+public class StoreProductItem 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 = "商品名称")
+    @TableField("prod_name")
+    private String prodName;
+
+    @ApiModelProperty(value = "总价")
+    @TableField("total_price")
+    private BigDecimal totalPrice;
+
+    @ApiModelProperty(value = "商品类型,整型枚举:1酒吧-酒水 2酒吧-餐食 3美食-餐食 4运动健身-单次 5运动健身-多次")
+    @TableField("prod_type")
+    private Integer prodType;
+
+    @ApiModelProperty(value = "图片列表,最多 9 张 URL")
+    @TableField("images")
+    private String images;
+
+    @ApiModelProperty(value = "图文详情-图片")
+    @TableField("image_content")
+    private String imageContent;
+
+    @ApiModelProperty(value = "图文详情-文字")
+    @TableField("detail_content")
+    private String detailContent;
+
+    @ApiModelProperty(value = "补充说明")
+    @TableField("extra_note")
+    private String extraNote;
+
+    @ApiModelProperty(value = "是否需要预约:0=否,1=是")
+    @TableField("need_reserve")
+    private Integer needReserve;
+
+    @ApiModelProperty(value = "预约规则")
+    @TableField("reserve_rule")
+    private String reserveRule;
+
+    @ApiModelProperty(value = "适用人数")
+    @TableField("people_limit")
+    private Integer peopleLimit;
+
+    @ApiModelProperty(value = "使用规则")
+    @TableField("usage_rule")
+    private String usageRule;
+
+    @ApiModelProperty(value = "状态:0-待审核 1-审核通过 2-审核拒绝")
+    @TableField("status")
+    private Integer status;
+
+    @ApiModelProperty(value = "拒绝原因")
+    @TableField("rejection_reason")
+    private String rejectionReason;
+
+    @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
+    @TableField("delete_flag")
+    @TableLogic
+    private Integer deleteFlag;
+
+    @ApiModelProperty(value = "创建人")
+    @TableField("created_user_id")
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "更新人")
+    @TableField("updated_user_id")
+    private Integer updatedUserId;
+
+    @ApiModelProperty(value = "创建时间")
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "更新时间")
+    @TableField(value = "updated_time", fill = FieldFill.UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+}
+

+ 54 - 2
alien-entity/src/main/java/shop/alien/entity/store/StoreStaffConfig.java

@@ -8,8 +8,10 @@ import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
+import shop.alien.entity.store.dto.StoreStaffFitnessCourseGroup;
 
 import java.util.Date;
+import java.util.List;
 
 /**
  * @Author: fcw
@@ -33,15 +35,23 @@ public class StoreStaffConfig extends Model<StoreStaffConfig> {
     @TableField("staff_position")
     private String staffPosition;
 
-    @ApiModelProperty(value = "员工名称/昵称")
+    @ApiModelProperty(value = "姓名/昵称")
     @TableField("name")
     private String name;
 
+    @ApiModelProperty(value = "背景图片/视频(存储URL逗号分隔)")
+    @TableField("background_url")
+    private String backgroundUrl;
+
     @ApiModelProperty(value = "头像")
     @TableField("staff_image")
     private String staffImage;
 
-    @ApiModelProperty(value = "擅长项目")
+    @ApiModelProperty(value = "擅长标签id(字典表proficient_tag的dict_id逗号分隔)")
+    @TableField("proficient_id")
+    private String proficientId;
+
+    @ApiModelProperty(value = "擅长标签名称(逗号分隔)")
     @TableField("proficient_projects")
     private String proficientProjects;
 
@@ -53,6 +63,10 @@ public class StoreStaffConfig extends Model<StoreStaffConfig> {
     @TableField("personal_introduction")
     private String personalIntroduction;
 
+    @ApiModelProperty(value = "电话")
+    @TableField("phone")
+    private String phone;
+
     @ApiModelProperty(value = "人员状态0-待审核 1-审核通过 2-审核拒绝")
     @TableField("status")
     private String status;
@@ -100,4 +114,42 @@ public class StoreStaffConfig extends Model<StoreStaffConfig> {
     @TableField("top_time")
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     private Date topTime;
+
+    @ApiModelProperty(value = "上线状态(0-上线 1-下线)")
+    @TableField("online_status")
+    private Integer onlineStatus;
+
+    @ApiModelProperty(value = "经营板块id(词典表 键为 business_section)")
+    @TableField("business_section")
+    private Integer businessSection;
+
+    // ===== 运动健身业务扩展信息(当 businessSection 对应“运动健身”时生效) =====
+
+    @ApiModelProperty(value = "运动健身-课程信息列表")
+    @TableField(exist = false)
+    private List<StoreStaffFitnessCourse> fitnessCourseList;
+
+    @ApiModelProperty(value = "运动健身-课程分组(前端推荐使用:课程类型下多项目)")
+    @TableField(exist = false)
+    private List<StoreStaffFitnessCourseGroup> fitnessCourseGroupList;
+
+    @ApiModelProperty(value = "运动健身-职业认证列表")
+    @TableField(exist = false)
+    private List<StoreStaffFitnessCertification> fitnessCertificationList;
+
+    @ApiModelProperty(value = "运动健身-荣誉奖项列表")
+    @TableField(exist = false)
+    private List<StoreStaffFitnessCertification> fitnessHonorList;
+
+    @ApiModelProperty(value = "运动健身-从业经历列表")
+    @TableField(exist = false)
+    private List<StoreStaffFitnessExperience> fitnessExperienceList;
+
+    @ApiModelProperty(value = "运动健身-基本信息")
+    @TableField(exist = false)
+    private StoreStaffFitnessBase fitnessBase;
+
+    @ApiModelProperty(value = "点赞数量")
+    @TableField("like_count")
+    private Integer likeCount;
 }

+ 82 - 0
alien-entity/src/main/java/shop/alien/entity/store/StoreStaffFitnessBase.java

@@ -0,0 +1,82 @@
+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("store_staff_fitness_base")
+@ApiModel(value = "StoreStaffFitnessBase对象", description = "运动健身员工基本信息表")
+public class StoreStaffFitnessBase implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "主键")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "员工id")
+    @TableField("staff_id")
+    private Integer staffId;
+
+    @ApiModelProperty(value = "民族")
+    @TableField("nation")
+    private String nation;
+
+    @ApiModelProperty(value = "星座")
+    @TableField("constellation")
+    private String constellation;
+
+    @ApiModelProperty(value = "身高(cm)")
+    @TableField("height")
+    private String height;
+
+    @ApiModelProperty(value = "体重(kg)")
+    @TableField("weight")
+    private String weight;
+
+    @ApiModelProperty(value = "毕业院校")
+    @TableField("Educational_background")
+    private String educationalBackground;
+
+    @ApiModelProperty(value = "学历")
+    @TableField("education")
+    private String education;
+
+    @ApiModelProperty(value = "删除状态")
+    @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 = "更新时间")
+    @TableField(value = "updated_time", fill = FieldFill.UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+
+    @ApiModelProperty(value = "创建人ID")
+    @TableField("created_user_id")
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "修改人ID")
+    @TableField("updated_user_id")
+    private Integer updatedUserId;
+}
+

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

@@ -0,0 +1,69 @@
+package shop.alien.entity.store;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 运动健身员工认证/荣誉表
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@JsonInclude
+@TableName("store_staff_fitness_certification")
+@ApiModel(value = "StoreStaffFitnessCertification对象", description = "运动健身员工认证/荣誉表")
+public class StoreStaffFitnessCertification implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "主键")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "员工id")
+    @TableField("staff_id")
+    private Integer staffId;
+
+    @ApiModelProperty(value = "类型(1-认证 2-荣誉)")
+    @TableField("type")
+    private Integer type;
+
+    @ApiModelProperty(value = "名称(认证名称/荣誉名称)")
+    @TableField("name")
+    private String name;
+
+    @ApiModelProperty(value = "图片URL(最多9张,逗号隔开)")
+    @TableField("img_urls")
+    private String imgUrls;
+
+    @ApiModelProperty(value = "删除状态")
+    @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 = "更新时间")
+    @TableField(value = "updated_time", fill = FieldFill.UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+
+    @ApiModelProperty(value = "创建人ID")
+    @TableField("created_user_id")
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "修改人ID")
+    @TableField("updated_user_id")
+    private Integer updatedUserId;
+}

+ 83 - 0
alien-entity/src/main/java/shop/alien/entity/store/StoreStaffFitnessCourse.java

@@ -0,0 +1,83 @@
+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.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 运动健身员工课程信息表
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@JsonInclude
+@TableName("store_staff_fitness_course")
+@ApiModel(value = "StoreStaffFitnessCourse对象", description = "运动健身员工课程信息表")
+public class StoreStaffFitnessCourse implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "主键")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "员工id")
+    @TableField("staff_id")
+    private Integer staffId;
+
+    @ApiModelProperty(value = "课程类型")
+    @TableField("course_type")
+    private String courseType;
+
+    @ApiModelProperty(value = "项目名称")
+    @TableField("course_name")
+    private String courseName;
+
+    @ApiModelProperty(value = "价格类型(0-固定价 1-价格区间)")
+    @TableField("course_price_type")
+    private Integer coursePriceType;
+
+    @ApiModelProperty(value = "课程价格")
+    @TableField("course_price")
+    private BigDecimal coursePrice;
+
+    @ApiModelProperty(value = "课程最低价格")
+    @TableField("course_min_price")
+    private BigDecimal courseMinPrice;
+
+    @ApiModelProperty(value = "课程最高价格")
+    @TableField("course_max_price")
+    private BigDecimal courseMaxPrice;
+
+    @ApiModelProperty(value = "删除状态")
+    @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 = "更新时间")
+    @TableField(value = "updated_time", fill = FieldFill.UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+
+    @ApiModelProperty(value = "创建人ID")
+    @TableField("created_user_id")
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "修改人ID")
+    @TableField("updated_user_id")
+    private Integer updatedUserId;
+}
+

+ 76 - 0
alien-entity/src/main/java/shop/alien/entity/store/StoreStaffFitnessExperience.java

@@ -0,0 +1,76 @@
+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("store_staff_fitness_experience")
+@ApiModel(value = "StoreStaffFitnessExperience对象", description = "运动健身员工从业经历表")
+public class StoreStaffFitnessExperience implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "主键")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "员工id")
+    @TableField("staff_id")
+    private Integer staffId;
+
+    @ApiModelProperty(value = "开始时间")
+    @TableField("start_time")
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    private Date startTime;
+
+    @ApiModelProperty(value = "结束时间")
+    @TableField("end_time")
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    private Date endTime;
+
+    @ApiModelProperty(value = "工作单位")
+    @TableField("work_unit")
+    private String workUnit;
+
+    @ApiModelProperty(value = "职位")
+    @TableField("work_position")
+    private String workPosition;
+
+    @ApiModelProperty(value = "删除状态")
+    @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 = "更新时间")
+    @TableField(value = "updated_time", fill = FieldFill.UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+
+    @ApiModelProperty(value = "创建人ID")
+    @TableField("created_user_id")
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "修改人ID")
+    @TableField("updated_user_id")
+    private Integer updatedUserId;
+}
+

+ 33 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/BarPerformanceOnlineStatusDto.java

@@ -0,0 +1,33 @@
+package shop.alien.entity.store.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 酒吧演出上线状态设置DTO
+ * 用于接收设置演出上线状态的请求参数
+ *
+ * @author assistant
+ * @since 2025-12-19
+ */
+@Data
+@ApiModel(value = "BarPerformanceOnlineStatusDto对象", description = "酒吧演出上线状态设置DTO")
+public class BarPerformanceOnlineStatusDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 演出ID
+     */
+    @ApiModelProperty(value = "演出ID", required = true)
+    private Integer id;
+
+    /**
+     * 上线状态(0-下线,1-上线)
+     */
+    @ApiModelProperty(value = "上线状态(0-下线,1-上线)", required = true)
+    private Integer onlineStatus;
+}
+

+ 64 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/StoreProductItemDto.java

@@ -0,0 +1,64 @@
+package shop.alien.entity.store.dto;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.List;
+
+@Data
+@ApiModel(value = "StoreProductItemDto", description = "商品表 DTO")
+public class StoreProductItemDto implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty("区分模块:1酒吧 2美食 3运动健身")
+    private Integer modelType;
+
+    @ApiModelProperty("主键")
+    private Integer id;
+
+    @ApiModelProperty("门店id")
+    private Integer storeId;
+
+    @ApiModelProperty("商品名称")
+    private String prodName;
+
+    @ApiModelProperty(value = "总价")
+    @TableField("total_price")
+    private BigDecimal totalPrice;
+
+    @ApiModelProperty("商品类型,整型枚举:1酒吧-酒水 2酒吧-餐食 3酒吧-套餐  4美食-餐食 5运动健身-单次 6运动健身-多次")
+    private Integer prodType;
+
+    @ApiModelProperty("图片列表,最多 9 张 URL")
+    private String images;
+
+    @ApiModelProperty("图文详情-图片")
+    private String imageContent;
+
+    @ApiModelProperty("图文详情-文字")
+    private String detailContent;
+
+    @ApiModelProperty("补充说明")
+    private String extraNote;
+
+    @ApiModelProperty("是否需要预约:0=否,1=是")
+    private Integer needReserve;
+
+    @ApiModelProperty("预约规则")
+    private String reserveRule;
+
+    @ApiModelProperty("适用人数")
+    private Integer peopleLimit;
+
+    @ApiModelProperty("使用规则")
+    private String usageRule;
+
+    @ApiModelProperty("子项列表(后端按类型转换)")
+    private List<?> subList;
+
+}

+ 38 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/StoreStaffConfigListQueryDto.java

@@ -0,0 +1,38 @@
+package shop.alien.entity.store.dto;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 商家端-员工列表查询 DTO(QueryString 绑定)
+ */
+@Data
+@JsonInclude
+@ApiModel(value = "StoreStaffConfigListQueryDto对象", description = "商家端-员工列表查询DTO")
+public class StoreStaffConfigListQueryDto {
+
+    @ApiModelProperty(value = "店铺ID")
+    private Integer storeId;
+
+    @ApiModelProperty(value = "分页页数", example = "1")
+    private Integer page;
+
+    @ApiModelProperty(value = "分页条数", example = "10")
+    private Integer size;
+
+    @ApiModelProperty(value = "员工状态(0-待审核 1-审核通过 2-审核拒绝)", example = "1")
+    private String status;
+
+    @ApiModelProperty(value = "经营板块id(词典表 键为 business_section)")
+    private Integer businessSection;
+
+    @ApiModelProperty(value = "上线状态(0-上线 1-下线)")
+    private Integer onlineStatus;
+
+    @ApiModelProperty(value = "职位")
+    private String staffPosition;
+}
+
+

+ 36 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/StoreStaffFitnessCourseGroup.java

@@ -0,0 +1,36 @@
+package shop.alien.entity.store.dto;
+
+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;
+
+/**
+ * 运动健身-课程类型分组(用于前端“课程类型下多项目”的绑定;不直接映射数据库表)
+ */
+@Data
+@JsonInclude
+@ApiModel(value = "StoreStaffFitnessCourseGroup对象", description = "运动健身-课程类型分组(分组结构用)")
+public class StoreStaffFitnessCourseGroup implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 课程类型编码:
+     * - 推荐传字典 dictId(更稳定)
+     * - 兼容传字典 dictDetail(名称)
+     */
+    @ApiModelProperty(value = "课程类型(推荐 dictId;兼容 dictDetail)")
+    private String courseType;
+
+    @ApiModelProperty(value = "课程类型名称(可选,便于回填展示)")
+    private String courseTypeName;
+
+    @ApiModelProperty(value = "项目列表")
+    private List<StoreStaffFitnessCourseItem> items;
+}
+
+

+ 37 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/StoreStaffFitnessCourseItem.java

@@ -0,0 +1,37 @@
+package shop.alien.entity.store.dto;
+
+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.math.BigDecimal;
+
+/**
+ * 运动健身-课程项目(用于前端分组绑定;不直接映射数据库表)
+ */
+@Data
+@JsonInclude
+@ApiModel(value = "StoreStaffFitnessCourseItem对象", description = "运动健身-课程项目(分组结构用)")
+public class StoreStaffFitnessCourseItem implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "项目名称")
+    private String courseName;
+
+    @ApiModelProperty(value = "价格类型(0-固定价 1-价格区间)")
+    private Integer coursePriceType;
+
+    @ApiModelProperty(value = "固定价(coursePriceType=0)")
+    private BigDecimal coursePrice;
+
+    @ApiModelProperty(value = "最低价(coursePriceType=1)")
+    private BigDecimal courseMinPrice;
+
+    @ApiModelProperty(value = "最高价(coursePriceType=1)")
+    private BigDecimal courseMaxPrice;
+}
+
+

+ 201 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/BarPerformanceDetailVo.java

@@ -0,0 +1,201 @@
+package shop.alien.entity.store.vo;
+
+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 shop.alien.entity.store.BarPerformance;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 酒吧演出详情VO
+ * 包含演出基本信息和表演嘉宾列表
+ *
+ * @author assistant
+ * @since 2025-12-19
+ */
+@Data
+@JsonInclude
+@ApiModel(value = "BarPerformanceDetailVo对象", description = "酒吧演出详情")
+public class BarPerformanceDetailVo implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 演出ID(主键)
+     */
+    @ApiModelProperty(value = "演出ID(主键)")
+    private Integer id;
+
+    /**
+     * 父演出ID(关联本表id,null为顶级演出)
+     */
+    @ApiModelProperty(value = "父演出ID(关联本表id,null为顶级演出)")
+    private Integer parentId;
+
+    /**
+     * 关联门店ID(关联store_info.id)
+     */
+    @ApiModelProperty(value = "关联门店ID(关联store_info.id)")
+    private Integer storeId;
+
+    /**
+     * 表演嘉宾ids,逗号分隔
+     */
+    @ApiModelProperty(value = "表演嘉宾ids,逗号分隔")
+    private String staffConfigIds;
+
+    /**
+     * 演出名称
+     */
+    @ApiModelProperty(value = "演出名称")
+    private String performanceName;
+
+    /**
+     * 演出海报路径
+     */
+    @ApiModelProperty(value = "演出海报路径")
+    private String performancePoster;
+
+    /**
+     * 演出风格(字典表proficient_tag逗号分隔)
+     */
+    @ApiModelProperty(value = "演出风格(字典表proficient_tag逗号分隔)")
+    private String performanceStyle;
+
+    /**
+     * 演出类型(单选:0-特邀演出,1-常规演出)
+     */
+    @ApiModelProperty(value = "演出类型(单选:0-特邀演出,1-常规演出)")
+    private Integer performanceType;
+
+    /**
+     * 演出频次:0-单次 1-每天定时 2-每周定时
+     */
+    @ApiModelProperty(value = "演出频次:0-单次 1-每天定时 2-每周定时")
+    private String performanceFrequency;
+
+    /**
+     * 演出日期(0-周一,1-周二,2-周三,3-周四,4-周五,5-周六,6-周日,复选逗号分隔)
+     */
+    @ApiModelProperty(value = "演出日期(0-周一,1-周二,2-周三,3-周四,4-周五,5-周六,6-周日,复选逗号分隔)")
+    private String performanceWeek;
+
+    /**
+     * 演出日期开始时间
+     */
+    @ApiModelProperty(value = "演出日期开始时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date singleStartDatetime;
+
+    /**
+     * 演出日期结束时间
+     */
+    @ApiModelProperty(value = "演出日期结束时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date singleEndDatetime;
+
+    /**
+     * 演出时间开始日期
+     */
+    @ApiModelProperty(value = "演出时间开始日期")
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    private Date dailyStartDate;
+
+    /**
+     * 演出时间结束日期
+     */
+    @ApiModelProperty(value = "演出时间结束日期")
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    private Date dailyEndDate;
+
+    /**
+     * 图片
+     */
+    @ApiModelProperty(value = "图片")
+    private String performanceDetail;
+
+    /**
+     * 演出完整详情
+     */
+    @ApiModelProperty(value = "演出完整详情")
+    private String performanceContent;
+
+    /**
+     * 演出须知(限300字)
+     */
+    @ApiModelProperty(value = "演出须知(限300字)")
+    private String performanceNotice;
+
+    /**
+     * 审核状态:0-待审核 1-审核通过 2-审核拒绝
+     */
+    @ApiModelProperty(value = "审核状态:0-待审核 1-审核通过 2-审核拒绝")
+    private Integer reviewStatus;
+
+    /**
+     * 审核拒绝原因
+     */
+    @ApiModelProperty(value = "审核拒绝原因")
+    private String rejectReason;
+
+    /**
+     * 删除标记:0-未删除,1-已删除
+     */
+    @ApiModelProperty(value = "删除标记:0-未删除,1-已删除")
+    private Integer deleteFlag;
+
+    /**
+     * 隐藏标记:0-不隐藏,1-隐藏
+     */
+    @ApiModelProperty(value = "隐藏标记:0-不隐藏,1-隐藏")
+    private Integer hidden;
+
+    /**
+     * 上线状态:0-下线,1-上线
+     */
+    @ApiModelProperty(value = "上线状态:0-下线,1-上线")
+    private Integer onlineStatus;
+
+    /**
+     * 状态:0禁用,1启用
+     */
+    @ApiModelProperty(value = "状态:0禁用,1启用")
+    private Integer status;
+
+    /**
+     * 创建人
+     */
+    @ApiModelProperty(value = "创建人")
+    private Integer createdUserId;
+
+    /**
+     * 更新人
+     */
+    @ApiModelProperty(value = "更新人")
+    private Integer updatedUserId;
+
+    /**
+     * 创建时间
+     */
+    @ApiModelProperty(value = "创建时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    /**
+     * 更新时间
+     */
+    @ApiModelProperty(value = "更新时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+
+    /**
+     * 表演嘉宾列表
+     */
+    @ApiModelProperty(value = "表演嘉宾列表")
+    private List<PerformerVo> performers;
+}
+

+ 29 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/CreateAreaRequestVo.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 java.io.Serializable;
+
+/**
+ * 新建区域请求VO
+ *
+ * @author assistant
+ * @since 2025-12-19
+ */
+@Data
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@ApiModel(value = "CreateAreaRequestVo对象", description = "新建区域请求视图对象")
+public class CreateAreaRequestVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "门店ID", required = true, example = "1")
+    private Integer storeId;
+
+    @ApiModelProperty(value = "区域名称(必填,限10字)", required = true, example = "有氧区")
+    private String facilityCategoryName;
+}
+

+ 26 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/DeleteAreaRequestVo.java

@@ -0,0 +1,26 @@
+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;
+
+/**
+ * 删除区域请求VO
+ *
+ * @author assistant
+ * @since 2025-12-19
+ */
+@Data
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@ApiModel(value = "DeleteAreaRequestVo对象", description = "删除区域请求视图对象")
+public class DeleteAreaRequestVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "区域ID", required = true, example = "1")
+    private Integer areaId;
+}
+

+ 46 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/FitnessEquipmentCategoryDetailVo.java

@@ -0,0 +1,46 @@
+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.FitnessEquipmentInfo;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 健身设备分类详情视图对象(用于用户端查询)
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@ApiModel(value = "FitnessEquipmentCategoryDetailVo对象", description = "健身设备分类详情视图对象(用于用户端查询)")
+public class FitnessEquipmentCategoryDetailVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "门店ID")
+    private Integer storeId;
+
+    @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<FitnessEquipmentTypeSummaryVo> equipmentTypeList;
+
+    @ApiModelProperty(value = "设备列表(已废弃,设备列表已放入equipmentTypeList中)")
+    private List<FitnessEquipmentInfo> equipmentList;
+}
+

+ 37 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/FitnessEquipmentTypeSummaryVo.java

@@ -0,0 +1,37 @@
+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.FitnessEquipmentInfo;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 健身设备类型汇总视图对象
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@ApiModel(value = "FitnessEquipmentTypeSummaryVo对象", description = "健身设备类型汇总视图对象")
+public class FitnessEquipmentTypeSummaryVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "设备类型(1:心肺训练, 2:核心训练, 3:臀腿训练, 4:上肢训练, 5:全身训练, 6:其他)")
+    private Integer equipmentType;
+
+    @ApiModelProperty(value = "设备类型名称")
+    private String equipmentTypeName;
+
+    @ApiModelProperty(value = "该类型下的设备数量")
+    private Integer equipmentCount;
+
+    @ApiModelProperty(value = "该类型下的设备列表")
+    private List<FitnessEquipmentInfo> equipmentList;
+}
+

+ 3 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/LawyerConsultationOrderVO.java

@@ -252,6 +252,9 @@ public class LawyerConsultationOrderVO implements Serializable {
     @ApiModelProperty(value = "评价状态 1:  没有评价,去评价 2:  有评价了,没有被举报或者举报驳回了,就是查看评价 3:  有评价了,被举报成功了,什么都不显示")
     private String commonStatus;
 
+    @ApiModelProperty(value = "再次举报状态,0-不可以再次举报,1-可以再次举报")
+    private  String repeatViolationStatus;
+
     /**
      * 获取执业年限(根据执业开始日期自动计算)
      * 返回当前时间减去执业开始时间的年数

+ 47 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/PerformerVo.java

@@ -0,0 +1,47 @@
+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;
+
+/**
+ * 表演嘉宾VO
+ * 用于返回表演嘉宾的基本信息
+ *
+ * @author assistant
+ * @since 2025-12-19
+ */
+@Data
+@JsonInclude
+@ApiModel(value = "PerformerVo对象", description = "表演嘉宾信息")
+public class PerformerVo implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 表演嘉宾ID
+     */
+    @ApiModelProperty(value = "表演嘉宾ID")
+    private Integer id;
+
+    /**
+     * 头像
+     */
+    @ApiModelProperty(value = "头像URL")
+    private String avatar;
+
+    /**
+     * 名称
+     */
+    @ApiModelProperty(value = "姓名/昵称")
+    private String name;
+
+    /**
+     * 职位
+     */
+    @ApiModelProperty(value = "职位")
+    private String style;
+}
+

+ 43 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/SportsEquipmentFacilityCategorySummaryVo.java

@@ -0,0 +1,43 @@
+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.FitnessEquipmentInfo;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 运动器材设施分类汇总视图对象(用于categorySummary接口)
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@ApiModel(value = "SportsEquipmentFacilityCategorySummaryVo对象", description = "运动器材设施分类汇总视图对象(用于categorySummary接口)")
+public class SportsEquipmentFacilityCategorySummaryVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "门店ID")
+    private Integer storeId;
+
+    @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 = "该分类下的设备列表(FitnessEquipmentInfo)")
+    private List<FitnessEquipmentInfo> facilityList;
+}
+

+ 73 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/StoreProductDelicaciesVo.java

@@ -0,0 +1,73 @@
+package shop.alien.entity.store.vo;
+
+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.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 美食商品表VO(用于子表列表)
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@JsonInclude
+@ApiModel(value = "StoreProductDelicaciesVo对象", description = "美食商品表VO")
+public class StoreProductDelicaciesVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "美食商品表主键")
+    private Integer id;
+
+    @ApiModelProperty(value = "商品表主键(关联字段)")
+    private Integer extId;
+
+    @ApiModelProperty(value = "名称")
+    private String name;
+
+    @ApiModelProperty(value = "价格(¥)")
+    private BigDecimal price;
+
+    @ApiModelProperty(value = "成本价(¥)")
+    private BigDecimal costPrice;
+
+    @ApiModelProperty(value = "单位,如份/瓶/杯")
+    private String unit;
+
+    @ApiModelProperty(value = "数量")
+    private Integer quantity;
+
+    @ApiModelProperty(value = "类别")
+    private String category;
+
+    @ApiModelProperty(value = "菜品分组")
+    private String extGroup;
+
+    @ApiModelProperty(value = "状态:0禁用,1启用")
+    private Integer status;
+
+    @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
+    private Integer deleteFlag;
+
+    @ApiModelProperty(value = "创建人")
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "更新人")
+    private Integer updatedUserId;
+
+    @ApiModelProperty(value = "创建时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "更新时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+}
+

+ 146 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/StoreProductItemDelicaciesVo.java

@@ -0,0 +1,146 @@
+package shop.alien.entity.store.vo;
+
+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.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 商品表与美食商品表关联查询VO
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@JsonInclude
+@ApiModel(value = "StoreProductItemDelicaciesVo对象", description = "商品表与美食商品表关联查询VO")
+public class StoreProductItemDelicaciesVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    // ========== store_product_item 表字段 ==========
+    @ApiModelProperty(value = "商品表主键")
+    private Integer id;
+
+    @ApiModelProperty(value = "门店id")
+    private Integer storeId;
+
+    @ApiModelProperty(value = "商品名称")
+    private String prodName;
+
+    @ApiModelProperty(value = "总价")
+    private BigDecimal totalPrice;
+
+    @ApiModelProperty(value = "商品类型,整型枚举:1酒吧-酒水 2酒吧-餐食 3美食-餐食 4运动健身-单次 5运动健身-多次")
+    private Integer prodType;
+
+    @ApiModelProperty(value = "图片列表,最多 9 张 URL")
+    private String images;
+
+    @ApiModelProperty(value = "图文详情-图片")
+    private String imageContent;
+
+    @ApiModelProperty(value = "图文详情-文字")
+    private String detailContent;
+
+    @ApiModelProperty(value = "补充说明")
+    private String extraNote;
+
+    @ApiModelProperty(value = "是否需要预约:0=否,1=是")
+    private Integer needReserve;
+
+    @ApiModelProperty(value = "预约规则")
+    private String reserveRule;
+
+    @ApiModelProperty(value = "适用人数")
+    private Integer peopleLimit;
+
+    @ApiModelProperty(value = "使用规则")
+    private String usageRule;
+
+    @ApiModelProperty(value = "状态:0-待审核 1-审核通过 2-审核拒绝")
+    private Integer status;
+
+    @ApiModelProperty(value = "拒绝原因")
+    private String rejectionReason;
+
+    @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
+    private Integer deleteFlag;
+
+    @ApiModelProperty(value = "创建人")
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "更新人")
+    private Integer updatedUserId;
+
+    @ApiModelProperty(value = "创建时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "更新时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+
+    @ApiModelProperty(value = "美食商品子表列表(一对多关系)")
+    private List<StoreProductDelicaciesVo> delicaciesList;
+
+
+    // ========== store_product_delicacies 表字段 ==========
+    @ApiModelProperty(value = "美食商品表主键")
+    private Integer delicaciesId;
+
+    @ApiModelProperty(value = "商品表主键(关联字段)")
+    private Integer extId;
+
+    @ApiModelProperty(value = "名称")
+    private String name;
+
+    @ApiModelProperty(value = "价格(¥)")
+    private BigDecimal price;
+
+    @ApiModelProperty(value = "成本价(¥)")
+    private BigDecimal costPrice;
+
+    @ApiModelProperty(value = "单位,如份/瓶/杯")
+    private String unit;
+
+    @ApiModelProperty(value = "数量")
+    private Integer quantity;
+
+    @ApiModelProperty(value = "类别")
+    private String category;
+
+    @ApiModelProperty(value = "菜品分组")
+    private String extGroup;
+
+    @ApiModelProperty(value = "美食商品状态:0禁用,1启用")
+    private Integer delicaciesStatus;
+
+    @ApiModelProperty(value = "美食商品删除标记, 0:未删除, 1:已删除")
+    private Integer delicaciesDeleteFlag;
+
+    @ApiModelProperty(value = "美食商品创建人")
+    private Integer delicaciesCreatedUserId;
+
+    @ApiModelProperty(value = "美食商品更新人")
+    private Integer delicaciesUpdatedUserId;
+
+    @ApiModelProperty(value = "美食商品创建时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date delicaciesCreatedTime;
+
+    @ApiModelProperty(value = "美食商品更新时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date delicaciesUpdatedTime;
+
+    // ========== 辅助字段 ==========
+    @ApiModelProperty(value = "数据类型:1-菜品(来自store_product_delicacies),2-套餐/商品(来自store_product_item)")
+    private Integer dataType;
+}
+

+ 150 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/StoreProductItemGymVo.java

@@ -0,0 +1,150 @@
+package shop.alien.entity.store.vo;
+
+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.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 商品表与运动健身商品表关联查询VO
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@JsonInclude
+@ApiModel(value = "StoreProductItemGymVo对象", description = "商品表与运动健身商品表关联查询VO")
+public class StoreProductItemGymVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    // ========== store_product_item 表字段 ==========
+    @ApiModelProperty(value = "商品表主键")
+    private Long id;
+
+    @ApiModelProperty(value = "父id")
+    private Long pid;
+
+    @ApiModelProperty(value = "门店id")
+    private Integer storeId;
+
+    @ApiModelProperty(value = "商品名称")
+    private String prodName;
+
+    @ApiModelProperty(value = "商品类型,整型枚举:1酒吧-酒水 2酒吧-餐食 3美食-餐食 4运动健身-单次 5运动健身-多次")
+    private Integer prodType;
+
+    @ApiModelProperty(value = "图片列表,最多 9 张 URL")
+    private String images;
+
+    @ApiModelProperty(value = "图文详情-图片")
+    private String imageContent;
+
+    @ApiModelProperty(value = "图文详情-文字")
+    private String detailContent;
+
+    @ApiModelProperty(value = "补充说明")
+    private String extraNote;
+
+    @ApiModelProperty(value = "是否需要预约:0=否,1=是")
+    private Integer needReserve;
+
+    @ApiModelProperty(value = "预约规则")
+    private String reserveRule;
+
+    @ApiModelProperty(value = "适用人数")
+    private Integer peopleLimit;
+
+    @ApiModelProperty(value = "使用规则")
+    private String usageRule;
+
+    @ApiModelProperty(value = "状态:0禁用,1启用")
+    private Integer status;
+
+    @ApiModelProperty(value = "拒绝原因")
+    private String rejectionReason;
+
+    @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
+    private Integer deleteFlag;
+
+    @ApiModelProperty(value = "创建人")
+    private Long createdUserId;
+
+    @ApiModelProperty(value = "更新人")
+    private Long updatedUserId;
+
+    @ApiModelProperty(value = "创建时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "更新时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+
+    // ========== store_product_gym 表字段 ==========
+    @ApiModelProperty(value = "运动健身商品表主键")
+    private Long gymId;
+
+    @ApiModelProperty(value = "商品表主键(关联字段)")
+    private Long extId;
+
+    @ApiModelProperty(value = "名称")
+    private String name;
+
+    @ApiModelProperty(value = "上课形式,如一对一/小班课")
+    private String classMode;
+
+    @ApiModelProperty(value = "可用周期(天)")
+    private Integer availableDays;
+
+    @ApiModelProperty(value = "总课时数(节)")
+    private Integer totalSessions;
+
+    @ApiModelProperty(value = "价格(¥)")
+    private BigDecimal price;
+
+    @ApiModelProperty(value = "课程功效")
+    private String courseEffect;
+
+    @ApiModelProperty(value = "适用人群")
+    private String suitablePeople;
+
+    @ApiModelProperty(value = "适用会员")
+    private String suitableMembership;
+
+    @ApiModelProperty(value = "淋浴设施")
+    private String showerFacility;
+
+    @ApiModelProperty(value = "额外须知")
+    private String extraNotice;
+
+    @ApiModelProperty(value = "运动健身商品状态:0禁用,1启用")
+    private Integer gymStatus;
+
+    @ApiModelProperty(value = "运动健身商品删除标记, 0:未删除, 1:已删除")
+    private Integer gymDeleteFlag;
+
+    @ApiModelProperty(value = "运动健身商品创建人")
+    private Long gymCreatedUserId;
+
+    @ApiModelProperty(value = "运动健身商品更新人")
+    private Long gymUpdatedUserId;
+
+    @ApiModelProperty(value = "运动健身商品创建时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date gymCreatedTime;
+
+    @ApiModelProperty(value = "运动健身商品更新时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date gymUpdatedTime;
+
+    // ========== store_info 表字段 ==========
+    @ApiModelProperty(value = "门店电话")
+    private String storeTel;
+}
+

+ 35 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/StoreStaffDetailVo.java

@@ -0,0 +1,35 @@
+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.StoreStaffConfig;
+import shop.alien.entity.store.StoreStaffFitnessCourse;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 员工详情视图对象(包含员工信息和课程列表)
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@ApiModel(value = "StoreStaffDetailVo对象", description = "员工详情视图对象(包含员工信息和课程列表)")
+public class StoreStaffDetailVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "员工基本信息")
+    private StoreStaffConfig staffInfo;
+
+    @ApiModelProperty(value = "运动健身员工课程信息列表")
+    private List<StoreStaffFitnessCourse> courseList;
+
+    @ApiModelProperty(value = "店铺名称")
+    private String storeName;
+}
+

+ 46 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/StoreStaffFitnessDetailVo.java

@@ -0,0 +1,46 @@
+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.StoreStaffConfig;
+import shop.alien.entity.store.StoreStaffFitnessBase;
+import shop.alien.entity.store.StoreStaffFitnessCertification;
+import shop.alien.entity.store.StoreStaffFitnessExperience;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 健身教练详情视图对象(包含员工信息、基本信息、认证/荣誉列表和从业经历)
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@ApiModel(value = "StoreStaffFitnessDetailVo对象", description = "健身教练详情视图对象(包含员工信息、基本信息、认证/荣誉列表和从业经历)")
+public class StoreStaffFitnessDetailVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "员工基本信息")
+    private StoreStaffConfig staffInfo;
+
+    @ApiModelProperty(value = "员工基本信息(运动健身)")
+    private StoreStaffFitnessBase baseInfo;
+
+    @ApiModelProperty(value = "认证列表(type=1)")
+    private List<StoreStaffFitnessCertification> certificationList;
+
+    @ApiModelProperty(value = "荣誉列表(type=2)")
+    private List<StoreStaffFitnessCertification> honorList;
+
+    @ApiModelProperty(value = "从业经历列表")
+    private List<StoreStaffFitnessExperience> experienceList;
+
+    @ApiModelProperty(value = "店铺名称")
+    private String storeName;
+}
+

+ 27 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/StoreStaffPositionCountVo.java

@@ -0,0 +1,27 @@
+package shop.alien.entity.store.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 员工职位统计VO
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@ApiModel(value = "StoreStaffPositionCountVo对象", description = "员工职位统计")
+public class StoreStaffPositionCountVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "员工职位")
+    private String staffPosition;
+
+    @ApiModelProperty(value = "该职位对应的员工数量")
+    private Long count;
+}
+

+ 15 - 0
alien-entity/src/main/java/shop/alien/mapper/BarPerformanceMapper.java

@@ -0,0 +1,15 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import shop.alien.entity.store.BarPerformance;
+
+/**
+ * BarPerformanceMapper接口
+ * 对应数据库表:bar_performance
+ * 用于操作演出信息表
+ * 
+ * @author AI Assistant
+ * @since 2025-12-17
+ */
+public interface BarPerformanceMapper extends BaseMapper<BarPerformance> {
+}

+ 16 - 0
alien-entity/src/main/java/shop/alien/mapper/FitnessEquipmentInfoMapper.java

@@ -0,0 +1,16 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.entity.store.FitnessEquipmentInfo;
+
+/**
+ * 健身设备信息Mapper接口
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Mapper
+public interface FitnessEquipmentInfoMapper extends BaseMapper<FitnessEquipmentInfo> {
+}
+

+ 17 - 0
alien-entity/src/main/java/shop/alien/mapper/SportsFacilityAreaMapper.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.SportsFacilityArea;
+
+/**
+ * 运动设施区域 Mapper 接口
+ *
+ * @author assistant
+ * @since 2025-12-19
+ */
+@Mapper
+public interface SportsFacilityAreaMapper extends BaseMapper<SportsFacilityArea> {
+
+}
+

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

@@ -0,0 +1,19 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.entity.store.StoreProductBar;
+
+/**
+ * <p>
+ * 酒吧商品表 Mapper 接口
+ * </p>
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Mapper
+public interface StoreProductBarMapper extends BaseMapper<StoreProductBar> {
+
+}
+

+ 17 - 0
alien-entity/src/main/java/shop/alien/mapper/StoreProductDelicaciesMapper.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.StoreProductDelicacies;
+
+/**
+ * 美食商品表 Mapper 接口
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Mapper
+public interface StoreProductDelicaciesMapper extends BaseMapper<StoreProductDelicacies> {
+
+}
+

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

@@ -0,0 +1,19 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.entity.store.StoreProductGym;
+
+/**
+ * <p>
+ * 运动健身商品表 Mapper 接口
+ * </p>
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Mapper
+public interface StoreProductGymMapper extends BaseMapper<StoreProductGym> {
+
+}
+

+ 92 - 0
alien-entity/src/main/java/shop/alien/mapper/StoreProductItemMapper.java

@@ -0,0 +1,92 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import shop.alien.entity.store.StoreProductItem;
+import shop.alien.entity.store.dto.StoreProductItemDto;
+
+import java.util.ArrayList;
+import java.util.List;
+import shop.alien.entity.store.vo.StoreProductItemGymVo;
+import shop.alien.entity.store.vo.StoreProductItemDelicaciesVo;
+
+/**
+ * <p>
+ * 商品表 Mapper 接口
+ * </p>
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Mapper
+public interface StoreProductItemMapper extends BaseMapper<StoreProductItem> {
+
+    ArrayList<StoreProductItemDto> getByPId(Integer id);
+    /**
+     * 分页查询商品表与运动健身商品表关联数据
+     *
+     * @param page 分页对象
+     * @param storeId 门店ID
+     * @param prodName 商品名称(模糊查询)
+     * @param prodType 商品类型
+     * @return IPage<StoreProductItemGymVo>
+     */
+    IPage<StoreProductItemGymVo> getPageWithGym(Page<StoreProductItemGymVo> page,
+                                                 @Param("storeId") Integer storeId,
+                                                 @Param("prodName") String prodName,
+                                                 @Param("prodType") Integer prodType);
+
+    /**
+     * 根据ID查询商品表与运动健身商品表关联详情
+     *
+     * @param id 商品表主键ID
+     * @return StoreProductItemGymVo
+     */
+    StoreProductItemGymVo getDetailWithGym(@Param("id") Long id);
+
+    /**
+     * 查询store_product_delicacies的所有菜品(用于合并查询)
+     *
+     * @param storeId 门店ID
+     * @param name 名称(模糊查询)
+     * @param category 类别
+     * @param extGroup 菜品分组
+     * @param status 状态(0:禁用,1:启用)
+     * @return List<StoreProductItemDelicaciesVo>
+     */
+    List<StoreProductItemDelicaciesVo> getDelicaciesList(@Param("storeId") Integer storeId,
+                                                          @Param("name") String name,
+                                                          @Param("category") String category,
+                                                          @Param("extGroup") String extGroup,
+                                                          @Param("status") Integer status);
+
+    /**
+     * 查询store_product_item的所有美食相关的套餐和商品(prodType = 3)(用于合并查询)
+     *
+     * @param storeId 门店ID
+     * @param name 名称(模糊查询)
+     * @param status 状态(0:禁用,1:启用)
+     * @param packageType 套餐类型(预留,用于根据类型查询套餐)
+     * @return List<StoreProductItemDelicaciesVo>
+     */
+    List<StoreProductItemDelicaciesVo> getFoodPackageList(@Param("storeId") Integer storeId,
+                                                            @Param("name") String name,
+                                                            @Param("status") Integer status,
+                                                            @Param("packageType") Integer packageType);
+
+
+
+
+
+    List<StoreProductItemDelicaciesVo> getAllDelicacies(@Param("storeId") Integer storeId,
+                                                          @Param("name") String name,
+                                                          @Param("category") String category,
+                                                          @Param("extGroup") String extGroup,
+                                                          @Param("status") Integer status,
+                                                        @Param("packageType") Integer packageType
+    );
+}
+

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

@@ -0,0 +1,19 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.entity.store.StoreStaffFitnessBase;
+
+/**
+ * <p>
+ * 运动健身员工基本信息表 Mapper 接口
+ * </p>
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Mapper
+public interface StoreStaffFitnessBaseMapper extends BaseMapper<StoreStaffFitnessBase> {
+
+}
+

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

@@ -0,0 +1,19 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.entity.store.StoreStaffFitnessCertification;
+
+/**
+ * <p>
+ * 运动健身员工认证表 Mapper 接口
+ * </p>
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Mapper
+public interface StoreStaffFitnessCertificationMapper extends BaseMapper<StoreStaffFitnessCertification> {
+
+}
+

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

@@ -0,0 +1,19 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.entity.store.StoreStaffFitnessCourse;
+
+/**
+ * <p>
+ * 运动健身员工课程信息表 Mapper 接口
+ * </p>
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Mapper
+public interface StoreStaffFitnessCourseMapper extends BaseMapper<StoreStaffFitnessCourse> {
+
+}
+

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

@@ -0,0 +1,19 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.entity.store.StoreStaffFitnessExperience;
+
+/**
+ * <p>
+ * 运动健身员工从业经历表 Mapper 接口
+ * </p>
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Mapper
+public interface StoreStaffFitnessExperienceMapper extends BaseMapper<StoreStaffFitnessExperience> {
+
+}
+

+ 431 - 0
alien-entity/src/main/resources/mapper/StoreProductItemMapper.xml

@@ -0,0 +1,431 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="shop.alien.mapper.StoreProductItemMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="shop.alien.entity.store.StoreProductItem">
+        <id column="id" property="id" />
+        <result column="store_id" property="storeId" />
+        <result column="prod_name" property="prodName" />
+        <result column="prod_type" property="prodType" />
+        <result column="images" property="images" />
+        <result column="image_content" property="imageContent" />
+        <result column="detail_content" property="detailContent" />
+        <result column="extra_note" property="extraNote" />
+        <result column="need_reserve" property="needReserve" />
+        <result column="reserve_rule" property="reserveRule" />
+        <result column="people_limit" property="peopleLimit" />
+        <result column="usage_rule" property="usageRule" />
+        <result column="status" property="status" />
+        <result column="rejection_reason" property="rejectionReason" />
+        <result column="delete_flag" property="deleteFlag" />
+        <result column="created_user_id" property="createdUserId" />
+        <result column="updated_user_id" property="updatedUserId" />
+        <result column="created_time" property="createdTime" />
+        <result column="updated_time" property="updatedTime" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, store_id, prod_name, prod_type, images, image_content, detail_content,
+        extra_note, need_reserve, reserve_rule, people_limit, usage_rule, status, 
+        rejection_reason, delete_flag, created_user_id, updated_user_id, created_time, updated_time
+    </sql>
+    <!-- 酒吧商品模块 -->
+    <resultMap id="StoreProductItemDtoMap" type="shop.alien.entity.store.dto.StoreProductItemDto">
+        <id column="id" property="id"/>
+        <result column="store_id" property="storeId"/>
+        <result column="prod_name" property="prodName"/>
+        <result column="prod_type" property="prodType"/>
+        <result column="images" property="images"/>
+        <result column="image_content" property="imageContent"/>
+        <result column="detail_content" property="detailContent"/>
+        <result column="extra_note" property="extraNote"/>
+        <result column="need_reserve" property="needReserve"/>
+        <result column="reserve_rule" property="reserveRule"/>
+        <result column="people_limit" property="peopleLimit"/>
+        <result column="usage_rule" property="usageRule"/>
+
+        <collection property="subList" ofType="shop.alien.entity.store.StoreProductBar">
+            <id column="bar_id" property="id"/>
+            <result column="bar_ext_id" property="extId"/>
+            <result column="sub_type" property="sub_type"/>
+            <result column="name" property="name"/>
+            <result column="price" property="price"/>
+            <result column="cost_price" property="costPrice"/>
+            <result column="unit" property="unit"/>
+            <result column="quantity" property="quantity"/>
+            <result column="category" property="category"/>
+            <result column="alcohol_vol" property="alcoholVol"/>
+            <result column="volume_ml" property="volumeMl"/>
+            <result column="flavor" property="flavor"/>
+            <result column="bar_status" property="status"/>
+            <result column="bar_delete_flag" property="deleteFlag"/>
+            <result column="created_user_id" property="createdUserId" />
+            <result column="updated_user_id" property="updatedUserId" />
+            <result column="bar_created_time" property="createdTime"/>
+            <result column="bar_updated_time" property="updatedTime"/>
+        </collection>
+    </resultMap>
+
+    <select id="getByExtId" resultMap="StoreProductItemDtoMap">
+        SELECT
+            spi.id,
+            spi.store_id,
+            spi.prod_name,
+            spi.prod_type,
+            spi.images,
+            spi.image_content,
+            spi.detail_content,
+            spi.extra_note,
+            spi.need_reserve,
+            spi.reserve_rule,
+            spi.people_limit,
+            spi.usage_rule,
+            spb.id           AS bar_id,
+            spb.ext_id       AS bar_ext_id,
+            spb.sub_type,
+            spb.name,
+            spb.price,
+            spb.cost_price,
+            spb.unit,
+            spb.quantity,
+            spb.category,
+            spb.alcohol_vol,
+            spb.volume_ml,
+            spb.flavor,
+            spb.status       AS bar_status,
+            spb.delete_flag  AS bar_delete_flag,
+            spb.created_user_id AS bar_created_user_id,
+            spb.updated_user_id AS bar_updated_user_id,
+            spb.created_time AS bar_created_time,
+            spb.updated_time AS bar_updated_time
+        FROM store_product_item spi
+                 LEFT JOIN store_product_bar spb
+                           ON spb.ext_id = spi.id
+                               AND spb.delete_flag = 0
+        WHERE spi.delete_flag = 0
+    </select>
+
+    <!-- 商品表与运动健身商品表关联查询结果映射 -->
+    <resultMap id="StoreProductItemGymVoResultMap" type="shop.alien.entity.store.vo.StoreProductItemGymVo">
+        <!-- store_product_item 表字段 -->
+        <id column="id" property="id" />
+        <result column="pid" property="pid" />
+        <result column="store_id" property="storeId" />
+        <result column="prod_name" property="prodName" />
+        <result column="prod_type" property="prodType" />
+        <result column="images" property="images" />
+        <result column="image_content" property="imageContent" />
+        <result column="detail_content" property="detailContent" />
+        <result column="extra_note" property="extraNote" />
+        <result column="need_reserve" property="needReserve" />
+        <result column="reserve_rule" property="reserveRule" />
+        <result column="people_limit" property="peopleLimit" />
+        <result column="usage_rule" property="usageRule" />
+        <result column="status" property="status" />
+        <result column="rejection_reason" property="rejectionReason" />
+        <result column="delete_flag" property="deleteFlag" />
+        <result column="created_user_id" property="createdUserId" />
+        <result column="updated_user_id" property="updatedUserId" />
+        <result column="created_time" property="createdTime" />
+        <result column="updated_time" property="updatedTime" />
+        <!-- store_product_gym 表字段 -->
+        <result column="gym_id" property="gymId" />
+        <result column="ext_id" property="extId" />
+        <result column="gym_name" property="name" />
+        <result column="class_mode" property="classMode" />
+        <result column="available_days" property="availableDays" />
+        <result column="total_sessions" property="totalSessions" />
+        <result column="price" property="price" />
+        <result column="course_effect" property="courseEffect" />
+        <result column="suitable_people" property="suitablePeople" />
+        <result column="suitable_membership" property="suitableMembership" />
+        <result column="shower_facility" property="showerFacility" />
+        <result column="extra_notice" property="extraNotice" />
+        <result column="gym_status" property="gymStatus" />
+        <result column="gym_delete_flag" property="gymDeleteFlag" />
+        <result column="gym_created_user_id" property="gymCreatedUserId" />
+        <result column="gym_updated_user_id" property="gymUpdatedUserId" />
+        <result column="gym_created_time" property="gymCreatedTime" />
+        <result column="gym_updated_time" property="gymUpdatedTime" />
+        <!-- store_info 表字段 -->
+        <result column="store_tel" property="storeTel" />
+    </resultMap>
+
+    <!-- 分页查询商品表与运动健身商品表关联数据 -->
+    <select id="getPageWithGym" resultMap="StoreProductItemGymVoResultMap">
+        SELECT
+            -- store_product_item 表字段
+            item.id,
+            item.store_id,
+            item.prod_name,
+            item.prod_type,
+            item.images,
+            item.image_content,
+            item.detail_content,
+            item.extra_note,
+            item.need_reserve,
+            item.reserve_rule,
+            item.people_limit,
+            item.usage_rule,
+            item.status,
+            item.rejection_reason,
+            item.delete_flag,
+            item.created_user_id,
+            item.updated_user_id,
+            item.created_time,
+            item.updated_time,
+            -- store_product_gym 表字段
+            gym.id AS gym_id,
+            gym.ext_id,
+            gym.name AS gym_name,
+            gym.class_mode,
+            gym.available_days,
+            gym.total_sessions,
+            gym.price,
+            gym.course_effect,
+            gym.suitable_people,
+            gym.suitable_membership,
+            gym.shower_facility,
+            gym.extra_notice,
+            gym.status AS gym_status,
+            gym.delete_flag AS gym_delete_flag,
+            gym.created_user_id AS gym_created_user_id,
+            gym.updated_user_id AS gym_updated_user_id,
+            gym.created_time AS gym_created_time,
+            gym.updated_time AS gym_updated_time
+        FROM store_product_item item
+        LEFT JOIN store_product_gym gym ON item.id = gym.ext_id AND gym.delete_flag = 0
+        WHERE item.delete_flag = 0
+        <if test="storeId != null">
+            AND item.store_id = #{storeId}
+        </if>
+        <if test="prodName != null and prodName != ''">
+            AND item.prod_name LIKE CONCAT('%', #{prodName}, '%')
+        </if>
+        <if test="prodType != null">
+            AND item.prod_type = #{prodType}
+        </if>
+        ORDER BY item.created_time DESC
+    </select>
+
+    <!-- 根据ID查询商品表与运动健身商品表关联详情 -->
+    <select id="getDetailWithGym" resultMap="StoreProductItemGymVoResultMap">
+        SELECT
+            -- store_product_item 表字段
+            item.id,
+            item.store_id,
+            item.prod_name,
+            item.prod_type,
+            item.images,
+            item.image_content,
+            item.detail_content,
+            item.extra_note,
+            item.need_reserve,
+            item.reserve_rule,
+            item.people_limit,
+            item.usage_rule,
+            item.status,
+            item.rejection_reason,
+            item.delete_flag,
+            item.created_user_id,
+            item.updated_user_id,
+            item.created_time,
+            item.updated_time,
+            -- store_product_gym 表字段
+            gym.id AS gym_id,
+            gym.ext_id,
+            gym.name AS gym_name,
+            gym.class_mode,
+            gym.available_days,
+            gym.total_sessions,
+            gym.price,
+            gym.course_effect,
+            gym.suitable_people,
+            gym.suitable_membership,
+            gym.shower_facility,
+            gym.extra_notice,
+            gym.status AS gym_status,
+            gym.delete_flag AS gym_delete_flag,
+            gym.created_user_id AS gym_created_user_id,
+            gym.updated_user_id AS gym_updated_user_id,
+            gym.created_time AS gym_created_time,
+            gym.updated_time AS gym_updated_time,
+            -- store_info 表字段
+            store.store_tel
+        FROM store_product_item item
+        LEFT JOIN store_product_gym gym ON item.id = gym.ext_id AND gym.delete_flag = 0
+        LEFT JOIN store_info store ON item.store_id = store.id AND store.delete_flag = 0
+        WHERE item.id = #{id} AND item.delete_flag = 0
+    </select>
+
+    <!-- 商品表与美食商品表关联查询结果映射 -->
+    <resultMap id="StoreProductItemDelicaciesVoResultMap" type="shop.alien.entity.store.vo.StoreProductItemDelicaciesVo">
+        <!-- store_product_item 表字段 -->
+        <id column="id" property="id" />
+        <result column="store_id" property="storeId" />
+        <result column="prod_name" property="prodName" />
+        <result column="total_price" property="totalPrice" />
+        <result column="prod_type" property="prodType" />
+        <result column="images" property="images" />
+        <result column="image_content" property="imageContent" />
+        <result column="detail_content" property="detailContent" />
+        <result column="extra_note" property="extraNote" />
+        <result column="need_reserve" property="needReserve" />
+        <result column="reserve_rule" property="reserveRule" />
+        <result column="people_limit" property="peopleLimit" />
+        <result column="usage_rule" property="usageRule" />
+        <result column="item_status" property="status" />
+        <result column="rejection_reason" property="rejectionReason" />
+        <result column="item_delete_flag" property="deleteFlag" />
+        <result column="item_created_user_id" property="createdUserId" />
+        <result column="item_updated_user_id" property="updatedUserId" />
+        <result column="item_created_time" property="createdTime" />
+        <result column="item_updated_time" property="updatedTime" />
+        <!-- store_product_delicacies 表字段 -->
+        <result column="delicacies_id" property="delicaciesId" />
+        <result column="ext_id" property="extId" />
+        <result column="name" property="name" />
+        <result column="price" property="price" />
+        <result column="cost_price" property="costPrice" />
+        <result column="unit" property="unit" />
+        <result column="quantity" property="quantity" />
+        <result column="category" property="category" />
+        <result column="ext_group" property="extGroup" />
+        <result column="delicacies_status" property="delicaciesStatus" />
+        <result column="delicacies_delete_flag" property="delicaciesDeleteFlag" />
+        <result column="delicacies_created_user_id" property="delicaciesCreatedUserId" />
+        <result column="delicacies_updated_user_id" property="delicaciesUpdatedUserId" />
+        <result column="delicacies_created_time" property="delicaciesCreatedTime" />
+        <result column="delicacies_updated_time" property="delicaciesUpdatedTime" />
+        <!-- 辅助字段 -->
+        <result column="data_type" property="dataType" />
+    </resultMap>
+
+    <!-- 查询store_product_delicacies的所有菜品 -->
+    <select id="getDelicaciesList" resultMap="StoreProductItemDelicaciesVoResultMap">
+        SELECT
+            -- store_product_item 表字段
+            item.id,
+            item.store_id,
+            item.prod_name,
+            item.total_price,
+            item.prod_type,
+            item.images,
+            item.image_content,
+            item.detail_content,
+            item.extra_note,
+            item.need_reserve,
+            item.reserve_rule,
+            item.people_limit,
+            item.usage_rule,
+            item.status AS item_status,
+            item.rejection_reason,
+            item.delete_flag AS item_delete_flag,
+            item.created_user_id AS item_created_user_id,
+            item.updated_user_id AS item_updated_user_id,
+            item.created_time AS item_created_time,
+            item.updated_time AS item_updated_time,
+            -- store_product_delicacies 表字段
+            delicacies.id AS delicacies_id,
+            delicacies.ext_id,
+            delicacies.name,
+            delicacies.price,
+            delicacies.cost_price,
+            delicacies.unit,
+            delicacies.quantity,
+            delicacies.category,
+            delicacies.ext_group,
+            delicacies.status AS delicacies_status,
+            delicacies.delete_flag AS delicacies_delete_flag,
+            delicacies.created_user_id AS delicacies_created_user_id,
+            delicacies.updated_user_id AS delicacies_updated_user_id,
+            delicacies.created_time AS delicacies_created_time,
+            delicacies.updated_time AS delicacies_updated_time,
+            -- 数据类型:1-菜品
+            1 AS data_type
+        FROM store_product_delicacies delicacies
+        LEFT JOIN store_product_item item ON delicacies.ext_id = item.id AND item.delete_flag = 0
+        WHERE delicacies.delete_flag = 0
+        <if test="storeId != null">
+            AND (item.store_id = #{storeId} OR item.store_id IS NULL)
+        </if>
+        <if test="name != null and name != ''">
+            AND delicacies.name LIKE CONCAT('%', #{name}, '%')
+        </if>
+        <if test="category != null and category != ''">
+            AND delicacies.category = #{category}
+        </if>
+        <if test="extGroup != null and extGroup != ''">
+            AND delicacies.ext_group = #{extGroup}
+        </if>
+        <if test="status != null">
+            AND delicacies.status = #{status}
+        </if>
+        ORDER BY delicacies.created_time DESC
+    </select>
+
+    <!-- 查询store_product_item的所有美食相关的套餐和商品(prodType = 3) -->
+    <select id="getFoodPackageList" resultMap="StoreProductItemDelicaciesVoResultMap">
+        SELECT
+            -- store_product_item 表字段
+            item.id,
+            item.store_id,
+            item.prod_name,
+            item.total_price,
+            item.prod_type,
+            item.images,
+            item.image_content,
+            item.detail_content,
+            item.extra_note,
+            item.need_reserve,
+            item.reserve_rule,
+            item.people_limit,
+            item.usage_rule,
+            item.status AS item_status,
+            item.rejection_reason,
+            item.delete_flag AS item_delete_flag,
+            item.created_user_id AS item_created_user_id,
+            item.updated_user_id AS item_updated_user_id,
+            item.created_time AS item_created_time,
+            item.updated_time AS item_updated_time,
+            -- store_product_delicacies 表字段(可能为空,因为是套餐)
+            NULL AS delicacies_id,
+            NULL AS ext_id,
+            NULL AS name,
+            NULL AS price,
+            NULL AS cost_price,
+            NULL AS unit,
+            NULL AS quantity,
+            NULL AS category,
+            NULL AS ext_group,
+            NULL AS delicacies_status,
+            NULL AS delicacies_delete_flag,
+            NULL AS delicacies_created_user_id,
+            NULL AS delicacies_updated_user_id,
+            NULL AS delicacies_created_time,
+            NULL AS delicacies_updated_time,
+            -- 数据类型:2-套餐/商品
+            2 AS data_type
+        FROM store_product_item item
+        WHERE item.delete_flag = 0
+        AND item.prod_type = 3
+        <if test="storeId != null">
+            AND item.store_id = #{storeId}
+        </if>
+        <if test="name != null and name != ''">
+            AND item.prod_name LIKE CONCAT('%', #{name}, '%')
+        </if>
+        <if test="status != null">
+            AND item.status = #{status}
+        </if>
+        <if test="packageType != null">
+            -- 预留:根据类型查询套餐的口子,可以根据需要扩展
+            -- 例如:可以根据某个字段来区分不同类型的套餐
+        </if>
+        ORDER BY item.created_time DESC
+    </select>
+
+</mapper>
+

+ 19 - 32
alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/OrderReviewServiceImpl.java

@@ -2,6 +2,7 @@ package shop.alien.lawyer.service.impl;
 
 import com.alibaba.fastjson2.JSON;
 import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
@@ -9,16 +10,18 @@ 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.apache.commons.lang3.StringUtils;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.CollectionUtils;
 import shop.alien.entity.result.R;
 import shop.alien.entity.store.LawyerConsultationOrder;
+import shop.alien.entity.store.LawyerUser;
 import shop.alien.entity.store.LifeLikeRecord;
+import shop.alien.entity.store.LifeNotice;
 import shop.alien.entity.store.OrderReview;
 import shop.alien.entity.store.ReviewComment;
 import shop.alien.entity.store.dto.OrderReviewDto;
-import shop.alien.entity.store.LifeNotice;
 import shop.alien.entity.store.vo.LawyerReviewStatisticsVo;
 import shop.alien.entity.store.vo.OrderReviewDetailVo;
 import shop.alien.entity.store.vo.OrderReviewVo;
@@ -27,13 +30,10 @@ import shop.alien.entity.store.vo.WebSocketVo;
 import shop.alien.lawyer.config.WebSocketProcess;
 import shop.alien.lawyer.service.OrderReviewService;
 import shop.alien.lawyer.service.ReviewCommentService;
-import shop.alien.entity.store.LawyerUser;
-import shop.alien.entity.store.LifeUser;
 import shop.alien.mapper.LawyerConsultationOrderMapper;
 import shop.alien.mapper.LawyerUserMapper;
 import shop.alien.mapper.LifeLikeRecordMapper;
 import shop.alien.mapper.LifeNoticeMapper;
-import shop.alien.mapper.LifeUserMapper;
 import shop.alien.mapper.OrderReviewMapper;
 import shop.alien.mapper.ReviewCommentMapper;
 
@@ -59,7 +59,6 @@ public class OrderReviewServiceImpl extends ServiceImpl<OrderReviewMapper, Order
     private final ReviewCommentMapper reviewCommentMapper;
     private final LifeLikeRecordMapper lifeLikeRecordMapper;
     private final LawyerUserMapper lawyerUserMapper;
-    private final LifeUserMapper lifeUserMapper;
     private final LifeNoticeMapper lifeNoticeMapper;
     private final WebSocketProcess webSocketProcess;
 
@@ -69,10 +68,9 @@ public class OrderReviewServiceImpl extends ServiceImpl<OrderReviewMapper, Order
     private static final String SYSTEM_SENDER_ID = "system";
 
     /**
-     * 差评评分阈值
-     * 评分规则:0-2.5分=差评
+     * 差评阈值(0-2.5分=差评)
      */
-    private static final Double BAD_REVIEW_THRESHOLD = 3.0;
+    private static final double BAD_REVIEW_THRESHOLD = 3.0;
 
     /**
      * 律师接收者ID前缀
@@ -85,7 +83,7 @@ public class OrderReviewServiceImpl extends ServiceImpl<OrderReviewMapper, Order
     private static final Integer NOTICE_TYPE_SYSTEM = 1;
 
     /**
-     * 业务类型:律师相关
+     * 业务类型:律师模块通知
      */
     private static final Integer BUSINESS_TYPE_LAWYER = 1;
 
@@ -320,12 +318,6 @@ public class OrderReviewServiceImpl extends ServiceImpl<OrderReviewMapper, Order
             totalCommentCount = 0;
         }
         
-        // 查询用户详细信息
-        LifeUser userDetail = null;
-        if (reviewVo.getUserId() != null) {
-            userDetail = lifeUserMapper.selectById(reviewVo.getUserId());
-        }
-
         // 构建返回结果
         OrderReviewDetailVo detailVo = new OrderReviewDetailVo();
         detailVo.setReview(reviewVo);
@@ -334,8 +326,6 @@ public class OrderReviewServiceImpl extends ServiceImpl<OrderReviewMapper, Order
         // 设置评价的点赞数和是否已点赞(从 reviewVo 中获取)
         detailVo.setLikeCount(reviewVo.getLikeCount() != null ? reviewVo.getLikeCount() : 0);
         detailVo.setIsLiked(reviewVo.getIsLiked() != null ? reviewVo.getIsLiked() : 0);
-        // 设置用户详细信息
-        detailVo.setUserDetail(userDetail);
 
         return R.data(detailVo);
     }
@@ -763,13 +753,13 @@ public class OrderReviewServiceImpl extends ServiceImpl<OrderReviewMapper, Order
      * @param order  订单对象
      */
     private void sendBadReviewNoticeToLawyer(OrderReview review, LawyerConsultationOrder order) {
-        log.info("发送差评通知给律师,评价ID={},订单ID={},律师ID={}",
+        log.info("发送差评通知给律师,评价ID={},订单ID={},律师ID={}", 
                 review.getId(), review.getOrderId(), review.getLawyerUserId());
 
         try {
             // 获取律师信息
             LawyerUser lawyerUser = lawyerUserMapper.selectById(review.getLawyerUserId());
-            if (lawyerUser == null || org.apache.commons.lang3.StringUtils.isBlank(lawyerUser.getPhone())) {
+            if (lawyerUser == null || StringUtils.isBlank(lawyerUser.getPhone())) {
                 log.warn("发送差评通知失败,律师不存在或手机号为空,律师ID={}", review.getLawyerUserId());
                 return;
             }
@@ -779,10 +769,10 @@ public class OrderReviewServiceImpl extends ServiceImpl<OrderReviewMapper, Order
 
             // 获取订单编号
             String orderNumber = review.getOrderNumber();
-            if (org.apache.commons.lang3.StringUtils.isBlank(orderNumber) && order != null) {
+            if (StringUtils.isBlank(orderNumber) && order != null) {
                 orderNumber = order.getOrderNumber();
             }
-            if (org.apache.commons.lang3.StringUtils.isBlank(orderNumber)) {
+            if (StringUtils.isBlank(orderNumber)) {
                 orderNumber = "未知";
             }
 
@@ -795,17 +785,17 @@ public class OrderReviewServiceImpl extends ServiceImpl<OrderReviewMapper, Order
             // 保存通知
             int insertResult = lifeNoticeMapper.insert(lifeNotice);
             if (insertResult > 0) {
-                log.info("发送差评通知成功,评价ID={},订单编号={},接收人ID={}",
+                log.info("发送差评通知成功,评价ID={},订单编号={},接收人ID={}", 
                         review.getId(), orderNumber, receiverId);
-
+                
                 // 发送WebSocket消息
                 sendWebSocketMessage(receiverId, lifeNotice);
             } else {
-                log.warn("发送差评通知失败:保存通知失败,评价ID={},订单编号={}",
+                log.warn("发送差评通知失败:保存通知失败,评价ID={},订单编号={}", 
                         review.getId(), orderNumber);
             }
         } catch (Exception e) {
-            log.error("发送差评通知异常,评价ID={},订单ID={},异常信息:{}",
+            log.error("发送差评通知异常,评价ID={},订单ID={},异常信息:{}", 
                     review.getId(), review.getOrderId(), e.getMessage(), e);
         }
     }
@@ -828,13 +818,11 @@ public class OrderReviewServiceImpl extends ServiceImpl<OrderReviewMapper, Order
         lifeNotice.setNoticeType(NOTICE_TYPE_SYSTEM);
         lifeNotice.setIsRead(IS_READ_UNREAD);
         lifeNotice.setBusinessType(BUSINESS_TYPE_LAWYER);
-        lifeNotice.setDeleteFlag(0);
-        lifeNotice.setCreatedTime(new Date());
 
         // 构建通知内容JSON
-        com.alibaba.fastjson2.JSONObject jsonObject = new com.alibaba.fastjson2.JSONObject();
-        jsonObject.put("message", message);
+        JSONObject jsonObject = new JSONObject();
         jsonObject.put("title", "差评通知");
+        jsonObject.put("message", message);
         lifeNotice.setContext(jsonObject.toJSONString());
 
         return lifeNotice;
@@ -854,9 +842,8 @@ public class OrderReviewServiceImpl extends ServiceImpl<OrderReviewMapper, Order
             webSocketVo.setCategory("notice");
             webSocketVo.setNoticeType("1");
             webSocketVo.setIsRead(IS_READ_UNREAD);
-            webSocketVo.setText(com.alibaba.fastjson2.JSONObject.from(lifeNotice).toJSONString());
-
-            webSocketProcess.sendMessage(receiverId, com.alibaba.fastjson2.JSONObject.from(webSocketVo).toJSONString());
+            webSocketVo.setText(JSON.toJSONString(lifeNotice));
+            webSocketProcess.sendMessage(receiverId, JSON.toJSONString(webSocketVo));
             log.debug("发送WebSocket消息成功,接收人ID={}", receiverId);
         } catch (Exception e) {
             log.error("发送WebSocket消息异常,接收人ID={}, 异常信息={}", receiverId, e.getMessage(), e);

+ 48 - 0
alien-store/src/main/java/shop/alien/store/config/AsyncConfig.java

@@ -0,0 +1,48 @@
+package shop.alien.store.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.Executor;
+
+/**
+ * 异步任务配置类
+ * 用于配置异步任务的线程池
+ *
+ * @author assistant
+ * @since 2025-12-19
+ */
+@Configuration
+@EnableAsync
+public class AsyncConfig {
+
+    /**
+     * 配置异步任务执行器
+     * 用于执行AI审核等异步任务
+     *
+     * @return 异步任务执行器
+     */
+    @Bean(name = "taskExecutor")
+    public Executor taskExecutor() {
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        // 核心线程数
+        executor.setCorePoolSize(5);
+        // 最大线程数
+        executor.setMaxPoolSize(10);
+        // 队列容量
+        executor.setQueueCapacity(100);
+        // 线程名前缀
+        executor.setThreadNamePrefix("async-task-");
+        // 拒绝策略:调用者运行策略
+        executor.setRejectedExecutionHandler(new java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy());
+        // 等待所有任务结束后再关闭线程池
+        executor.setWaitForTasksToCompleteOnShutdown(true);
+        // 等待时间
+        executor.setAwaitTerminationSeconds(60);
+        executor.initialize();
+        return executor;
+    }
+}
+

+ 64 - 64
alien-store/src/main/java/shop/alien/store/config/MyBatisFieldHandler.java

@@ -1,64 +1,64 @@
-package shop.alien.store.config;
-
-import com.alibaba.fastjson.JSONObject;
-import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
-import com.baomidou.mybatisplus.core.toolkit.StringUtils;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.ibatis.reflection.MetaObject;
-import org.springframework.stereotype.Component;
-import org.springframework.web.context.request.RequestContextHolder;
-import org.springframework.web.context.request.ServletRequestAttributes;
-import shop.alien.util.common.JwtUtil;
-
-import java.util.Date;
-import java.util.Objects;
-
-/**
- * Mybatis日期填充
- *
- * @author ssk
- * @version 1.0
- * @date 2024/12/6 10:19
- */
-@Component
-@Slf4j
-public class MyBatisFieldHandler implements MetaObjectHandler {
-
-    /**
-     * insert操作时填充方法
-     *
-     * @param metaObject 元对象
-     */
-    @Override
-    public void insertFill(MetaObject metaObject) {
-        log.info("=================================insertFill=========================================");
-        System.out.println(metaObject.getOriginalObject());
-        //字段为实体类名, 不是表字段名
-        this.setFieldValByName("createdTime", new Date(), metaObject);
-        this.setFieldValByName("updatedTime", new Date(), metaObject);
-        if (JwtUtil.hasToken()) {
-            this.setFieldValByName("createdUserId", Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getInteger("userId"), metaObject);
-            this.setFieldValByName("updatedUserId", Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getInteger("userId"), metaObject);
-        } else {
-            this.setFieldValByName("createdUserId", 0, metaObject);
-            this.setFieldValByName("updatedUserId", 0, metaObject);
-        }
-    }
-
-    /**
-     * update操作时填充方法
-     *
-     * @param metaObject 元对象
-     */
-    @Override
-    public void updateFill(MetaObject metaObject) {
-        log.info("=================================updateFill=========================================");
-        //字段为实体类名, 不是表字段名
-        this.setFieldValByName("updatedTime", new Date(), metaObject);
-        if (JwtUtil.hasToken()) {
-            this.setFieldValByName("updatedUserId", Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getInteger("userId"), metaObject);
-        } else {
-            this.setFieldValByName("updatedUserId", 0, metaObject);
-        }
-    }
-}
+package shop.alien.store.config;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.ibatis.reflection.MetaObject;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+import shop.alien.util.common.JwtUtil;
+
+import java.util.Date;
+import java.util.Objects;
+
+/**
+ * Mybatis日期填充
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2024/12/6 10:19
+ */
+@Component
+@Slf4j
+public class MyBatisFieldHandler implements MetaObjectHandler {
+
+    /**
+     * insert操作时填充方法
+     *
+     * @param metaObject 元对象
+     */
+    @Override
+    public void insertFill(MetaObject metaObject) {
+        log.info("=================================insertFill=========================================");
+        System.out.println(metaObject.getOriginalObject());
+        //字段为实体类名, 不是表字段名
+        this.setFieldValByName("createdTime", new Date(), metaObject);
+        this.setFieldValByName("updatedTime", new Date(), metaObject);
+        if (JwtUtil.hasToken()) {
+            this.setFieldValByName("createdUserId", Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getInteger("userId"), metaObject);
+            this.setFieldValByName("updatedUserId", Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getInteger("userId"), metaObject);
+        } else {
+            this.setFieldValByName("createdUserId", 0, metaObject);
+            this.setFieldValByName("updatedUserId", 0, metaObject);
+        }
+    }
+
+    /**
+     * update操作时填充方法
+     *
+     * @param metaObject 元对象
+     */
+    @Override
+    public void updateFill(MetaObject metaObject) {
+        log.info("=================================updateFill=========================================");
+        //字段为实体类名, 不是表字段名
+        this.setFieldValByName("updatedTime", new Date(), metaObject);
+        if (JwtUtil.hasToken()) {
+            this.setFieldValByName("updatedUserId", Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getInteger("userId"), metaObject);
+        } else {
+            this.setFieldValByName("updatedUserId", 0, metaObject);
+        }
+    }
+}

+ 202 - 0
alien-store/src/main/java/shop/alien/store/controller/BarPerformanceController.java

@@ -0,0 +1,202 @@
+package shop.alien.store.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+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.BarPerformance;
+import shop.alien.store.service.BarPerformanceService;
+
+/**
+ * 酒吧演出Controller
+ * 提供酒吧演出的CRUD、审核、上下线等RESTful API接口
+ *
+ * @author assistant
+ * @since 2025-12-17
+ */
+@Slf4j
+@Api(tags = {"商户端-酒吧演出"})
+@CrossOrigin
+@RestController
+@RequestMapping("/store/bar/performance")
+@RequiredArgsConstructor
+public class BarPerformanceController {
+
+    private final BarPerformanceService barPerformanceService;
+
+    /**
+     * 获取酒吧演出列表(分页)
+     *
+     * @param page    分页页数
+     * @param size    分页条数
+     * @param status  演出状态
+     * @return 演出列表
+     */
+    @ApiOperation("获取酒吧演出列表")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "分页页数", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "size", value = "分页条数", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "status", value = "演出状态", dataType = "String", paramType = "query", required = false)
+    })
+    @GetMapping("/list")
+    public R<IPage<BarPerformance>> getBarPerformanceList(
+            @RequestParam int page,
+            @RequestParam int size,
+            @RequestParam(required = false) String status) {
+        log.info("BarPerformanceController.getBarPerformanceList?page={}, size={}, status={}", page, size, status);
+        try {
+            IPage<BarPerformance> performanceList = barPerformanceService.getBarPerformanceList(page, size, status);
+            return R.data(performanceList);
+        } catch (Exception e) {
+            log.error("获取酒吧演出列表失败", e);
+            return R.fail("获取酒吧演出列表失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 新增或更新酒吧演出
+     *
+     * @param barPerformance 演出信息
+     * @return 操作结果
+     */
+    @ApiOperation("新增或更新酒吧演出")
+    @PostMapping("/saveOrUpdate")
+    public R<Integer> addOrUpdateBarPerformance(@RequestBody BarPerformance barPerformance) {
+        log.info("BarPerformanceController.addOrUpdateBarPerformance?barPerformance={}", barPerformance);
+        try {
+            int result = barPerformanceService.addOrUpdateBarPerformance(barPerformance);
+            if (result > 0) {
+                return R.success("操作成功");
+            } else {
+                return R.fail("操作失败");
+            }
+        } catch (Exception e) {
+            log.error("新增或更新酒吧演出失败", e);
+            return R.fail("新增或更新酒吧演出失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 获取酒吧演出详情
+     *
+     * @param id 演出ID
+     * @return 演出详情
+     */
+    @ApiOperation("获取酒吧演出详情")
+    @ApiImplicitParam(name = "id", value = "演出ID", dataType = "Integer", paramType = "query", required = true)
+    @GetMapping("/detail")
+    public R<BarPerformance> getBarPerformanceDetail(@RequestParam Integer id) {
+        log.info("BarPerformanceController.getBarPerformanceDetail?id={}", id);
+        try {
+            BarPerformance performance = barPerformanceService.getBarPerformanceDetail(id);
+            if (performance != null) {
+                return R.data(performance);
+            } else {
+                return R.fail("演出不存在");
+            }
+        } catch (Exception e) {
+            log.error("获取酒吧演出详情失败", e);
+            return R.fail("获取酒吧演出详情失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 删除酒吧演出
+     *
+     * @param id 演出ID
+     * @return 操作结果
+     */
+    @ApiOperation("删除酒吧演出")
+    @ApiImplicitParam(name = "id", value = "演出ID", dataType = "Integer", paramType = "query", required = true)
+    @DeleteMapping("/delete")
+    public R<Integer> deleteBarPerformance(@RequestParam Integer id) {
+        log.info("BarPerformanceController.deleteBarPerformance?id={}", id);
+        try {
+            Integer result = barPerformanceService.deleteBarPerformance(id);
+            if (result > 0) {
+                return R.success("删除成功");
+            } else {
+                return R.fail("删除失败");
+            }
+        } catch (Exception e) {
+            log.error("删除酒吧演出失败", e);
+            return R.fail("删除酒吧演出失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 设置演出上线下线状态
+     *
+     * @param id          演出ID
+     * @param onlineStatus 上线状态(0-下线,1-上线)
+     * @return 操作结果
+     */
+    @ApiOperation("设置演出上线状态")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "演出ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "onlineStatus", value = "上线状态(0-下线,1-上线)", dataType = "Integer", paramType = "query", required = true)
+    })
+    @PostMapping("/setOnlineStatus")
+    public R<Integer> setOnlineStatus(
+            @RequestParam Integer id,
+            @RequestParam Integer onlineStatus) {
+        log.info("BarPerformanceController.setOnlineStatus?id={}, onlineStatus={}", id, onlineStatus);
+        try {
+            Integer result = barPerformanceService.setOnlineStatus(id, onlineStatus);
+            if (result > 0) {
+                return R.success("设置成功");
+            } else {
+                return R.fail("设置失败");
+            }
+        } catch (Exception e) {
+            log.error("设置演出上线状态失败", e);
+            return R.fail("设置演出上线状态失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 根据门店ID查询演出列表
+     *
+     * @param page    分页页数
+     * @param size    分页条数
+     * @param storeId 门店ID
+     * @param statusReview 审核状态(0-待审核 1-审核通过 2-审核拒绝)
+     * @param category 演出分类(all-全部, not_started-未开始, in_progress-进行中, ended-已结束)
+     * @param performanceName 演出名称(可选,支持模糊搜索)
+     * @return 演出列表
+     */
+    @ApiOperation("根据门店ID查询演出列表(支持按审核状态、分类和名称搜索)")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "分页页数", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "size", value = "分页条数", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "statusReview", value = "审核状态(0-待审核 1-审核通过 2-审核拒绝)", dataType = "Integer", paramType = "query", required = false),
+            @ApiImplicitParam(name = "category", value = "演出分类(all-全部, not_started-未开始, in_progress-进行中, ended-已结束)", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "performanceName", value = "演出名称(支持模糊搜索)", dataType = "String", paramType = "query", required = false)
+    })
+    @GetMapping("/listByStoreId")
+    public R<IPage<BarPerformance>> queryPerformanceListByStoreId(
+            @RequestParam Integer page,
+            @RequestParam Integer size,
+            @RequestParam Integer storeId,
+            @RequestParam(required = false) Integer statusReview,
+            @RequestParam(required = false) String category,
+            @RequestParam(required = false) String performanceName) {
+        log.info("BarPerformanceController.queryPerformanceListByStoreId?page={}, size={}, storeId={}, statusReview={}, category={}, performanceName={}", 
+                page, size, storeId, statusReview, category, performanceName);
+        try {
+            IPage<BarPerformance> performanceList = barPerformanceService.queryPerformanceListByStoreIdAndCategory(
+                    page, size, storeId, statusReview, category != null ? category : "all", performanceName);
+            return R.data(performanceList);
+        } catch (Exception e) {
+            log.error("根据门店ID查询演出列表失败", e);
+            return R.fail("根据门店ID查询演出列表失败:" + e.getMessage());
+        }
+    }
+
+}

+ 363 - 0
alien-store/src/main/java/shop/alien/store/controller/FitnessEquipmentInfoController.java

@@ -0,0 +1,363 @@
+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.FitnessEquipmentInfo;
+import shop.alien.store.service.FitnessEquipmentInfoService;
+
+import java.util.List;
+
+/**
+ * 健身设备信息控制器
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"健身设备信息管理"})
+@ApiSort(1)
+@CrossOrigin
+@RestController
+@RequestMapping("/fitness/equipment/info")
+@RequiredArgsConstructor
+public class FitnessEquipmentInfoController {
+
+    /**
+     * 操作成功消息
+     */
+    private static final String OPERATION_SUCCESS_MSG = "操作成功";
+
+    /**
+     * 操作失败消息
+     */
+    private static final String OPERATION_FAIL_MSG = "操作失败";
+
+    /**
+     * 参数为空消息
+     */
+    private static final String PARAM_NULL_MSG = "参数不能为空";
+
+    /**
+     * 查询失败消息
+     */
+    private static final String QUERY_FAIL_MSG = "查询失败";
+
+    /**
+     * 新增成功消息
+     */
+    private static final String ADD_SUCCESS_MSG = "新增成功";
+
+    /**
+     * 修改成功消息
+     */
+    private static final String MODIFY_SUCCESS_MSG = "修改成功";
+
+    /**
+     * 删除成功消息
+     */
+    private static final String DELETE_SUCCESS_MSG = "删除成功";
+
+    /**
+     * 状态:启用
+     */
+    private static final int STATUS_ENABLED = 1;
+
+    /**
+     * 状态:禁用
+     */
+    private static final int STATUS_DISABLED = 0;
+
+    /**
+     * 健身设备信息服务
+     */
+    private final FitnessEquipmentInfoService fitnessEquipmentInfoService;
+
+    /**
+     * 分页查询健身设备信息列表
+     *
+     * @param pageNum       页码,默认1
+     * @param pageSize      页大小,默认10
+     * @param equipmentType 设备类型(1:心肺训练, 2:核心训练, 3:臀腿训练, 4:上肢训练, 5:全身训练, 6:其他),可选
+     * @param equipmentName 设备名称(模糊查询),可选
+     * @param status        状态(0:禁用, 1:启用),可选
+     * @return 健身设备信息分页列表
+     */
+    @ApiOperation("分页查询健身设备信息列表")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "pageNum", value = "页码", dataType = "Integer", paramType = "query", required = false),
+            @ApiImplicitParam(name = "pageSize", value = "页大小", dataType = "Integer", paramType = "query", required = false),
+            @ApiImplicitParam(name = "equipmentType", value = "设备类型(1:心肺训练, 2:核心训练, 3:臀腿训练, 4:上肢训练, 5:全身训练, 6:其他)", dataType = "Integer", paramType = "query", required = false),
+            @ApiImplicitParam(name = "equipmentName", value = "设备名称(模糊查询)", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "status", value = "状态(0:禁用, 1:启用)", dataType = "Integer", paramType = "query", required = false)
+    })
+    @GetMapping("/page")
+    public R<IPage<FitnessEquipmentInfo>> getPageList(
+            @RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
+            @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize,
+            @RequestParam(required = false) Integer equipmentType,
+            @RequestParam(required = false) String equipmentName,
+            @RequestParam(required = false) Integer status) {
+        log.info("分页查询健身设备信息列表,pageNum={}, pageSize={}, equipmentType={}, equipmentName={}, status={}", 
+                pageNum, pageSize, equipmentType, equipmentName, status);
+
+        try {
+            IPage<FitnessEquipmentInfo> result = fitnessEquipmentInfoService.getPageList(
+                    pageNum, pageSize, equipmentType, equipmentName, status);
+            log.info("分页查询健身设备信息列表成功,查询到{}条记录", result.getTotal());
+            return R.data(result);
+        } catch (Exception e) {
+            log.error("分页查询健身设备信息列表异常,异常信息:{}", e.getMessage(), e);
+            return R.fail(QUERY_FAIL_MSG + ":" + e.getMessage());
+        }
+    }
+
+    /**
+     * 查询健身设备信息列表
+     *
+     * @param equipmentType 设备类型(1:心肺训练, 2:核心训练, 3:臀腿训练, 4:上肢训练, 5:全身训练, 6:其他),可选
+     * @param status        状态(0:禁用, 1:启用),可选
+     * @return 健身设备信息列表
+     */
+    @ApiOperation("查询健身设备信息列表")
+    @ApiOperationSupport(order = 2)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "equipmentType", value = "设备类型(1:心肺训练, 2:核心训练, 3:臀腿训练, 4:上肢训练, 5:全身训练, 6:其他)", dataType = "Integer", paramType = "query", required = false),
+            @ApiImplicitParam(name = "status", value = "状态(0:禁用, 1:启用)", dataType = "Integer", paramType = "query", required = false)
+    })
+    @GetMapping("/list")
+    public R<List<FitnessEquipmentInfo>> getList(
+            @RequestParam(required = false) Integer equipmentType,
+            @RequestParam(required = false) Integer status) {
+        log.info("查询健身设备信息列表,equipmentType={}, status={}", equipmentType, status);
+
+        try {
+            List<FitnessEquipmentInfo> result = fitnessEquipmentInfoService.getList(equipmentType, status);
+            log.info("查询健身设备信息列表成功,查询到{}条记录", result.size());
+            return R.data(result);
+        } catch (Exception e) {
+            log.error("查询健身设备信息列表异常,异常信息:{}", e.getMessage(), e);
+            return R.fail(QUERY_FAIL_MSG + ":" + e.getMessage());
+        }
+    }
+
+    /**
+     * 根据设备类型查询设备列表(用于前端分类展示)
+     *
+     * @param equipmentType 设备类型(1:心肺训练, 2:核心训练, 3:臀腿训练, 4:上肢训练, 5:全身训练, 6:其他),必传
+     * @return 健身设备信息列表
+     */
+    @ApiOperation("根据设备类型查询设备列表(用于前端分类展示)")
+    @ApiOperationSupport(order = 3)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "equipmentType", value = "设备类型(1:心肺训练, 2:核心训练, 3:臀腿训练, 4:上肢训练, 5:全身训练, 6:其他)", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/listByType")
+    public R<List<FitnessEquipmentInfo>> getListByType(@RequestParam("equipmentType") Integer equipmentType) {
+        log.info("根据设备类型查询设备列表,equipmentType={}", equipmentType);
+
+        try {
+            // 参数验证
+            if (equipmentType == null || equipmentType <= 0) {
+                log.warn("根据设备类型查询设备列表失败,设备类型无效:{}", equipmentType);
+                return R.fail("设备类型不能为空且必须大于0");
+            }
+
+            List<FitnessEquipmentInfo> result = fitnessEquipmentInfoService.getListByType(equipmentType);
+            log.info("根据设备类型查询设备列表成功,equipmentType={},查询到{}条记录", equipmentType, result.size());
+            return R.data(result);
+        } catch (Exception e) {
+            log.error("根据设备类型查询设备列表异常,equipmentType={},异常信息:{}", equipmentType, e.getMessage(), e);
+            return R.fail(QUERY_FAIL_MSG + ":" + e.getMessage());
+        }
+    }
+
+    /**
+     * 根据ID查询健身设备信息详情
+     *
+     * @param id 主键ID,不能为空
+     * @return 健身设备信息
+     */
+    @ApiOperation("根据ID查询健身设备信息详情")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/detail")
+    public R<FitnessEquipmentInfo> getDetailById(@RequestParam("id") Integer id) {
+        log.info("根据ID查询健身设备信息详情,id={}", id);
+
+        try {
+            // 参数验证
+            if (id == null || id <= 0) {
+                log.warn("根据ID查询健身设备信息详情失败,ID无效:{}", id);
+                return R.fail("ID不能为空且必须大于0");
+            }
+
+            FitnessEquipmentInfo result = fitnessEquipmentInfoService.getDetailById(id);
+            if (result == null) {
+                log.warn("根据ID查询健身设备信息详情失败,设备不存在,id={}", id);
+                return R.fail("设备不存在");
+            }
+
+            log.info("根据ID查询健身设备信息详情成功,id={}", id);
+            return R.data(result);
+        } catch (Exception e) {
+            log.error("根据ID查询健身设备信息详情异常,id={},异常信息:{}", id, e.getMessage(), e);
+            return R.fail(QUERY_FAIL_MSG + ":" + e.getMessage());
+        }
+    }
+
+    /**
+     * 新增健身设备信息
+     *
+     * @param fitnessEquipmentInfo 健身设备信息对象,不能为空
+     * @return 操作结果
+     */
+    @ApiOperation("新增健身设备信息")
+    @ApiOperationSupport(order = 5)
+    @PostMapping("/save")
+    public R<Boolean> saveEquipment(@RequestBody FitnessEquipmentInfo fitnessEquipmentInfo) {
+        log.info("新增健身设备信息,请求参数:{}", fitnessEquipmentInfo);
+
+        try {
+            // 参数验证
+            if (fitnessEquipmentInfo == null) {
+                log.warn("新增健身设备信息失败,设备信息对象为空");
+                return R.fail(PARAM_NULL_MSG);
+            }
+
+            boolean result = fitnessEquipmentInfoService.saveEquipment(fitnessEquipmentInfo);
+            if (result) {
+                log.info("新增健身设备信息成功");
+                return R.success(ADD_SUCCESS_MSG);
+            } else {
+                log.warn("新增健身设备信息失败");
+                return R.fail(OPERATION_FAIL_MSG);
+            }
+        } catch (Exception e) {
+            log.error("新增健身设备信息异常,异常信息:{}", e.getMessage(), e);
+            return R.fail(OPERATION_FAIL_MSG + ":" + e.getMessage());
+        }
+    }
+
+    /**
+     * 修改健身设备信息
+     *
+     * @param fitnessEquipmentInfo 健身设备信息对象,不能为空
+     * @return 操作结果
+     */
+    @ApiOperation("修改健身设备信息")
+    @ApiOperationSupport(order = 6)
+    @PostMapping("/update")
+    public R<Boolean> updateEquipment(@RequestBody FitnessEquipmentInfo fitnessEquipmentInfo) {
+        log.info("修改健身设备信息,请求参数:{}", fitnessEquipmentInfo);
+
+        try {
+            // 参数验证
+            if (fitnessEquipmentInfo == null || fitnessEquipmentInfo.getId() == null) {
+                log.warn("修改健身设备信息失败,设备信息对象为空或ID为空");
+                return R.fail(PARAM_NULL_MSG);
+            }
+            if (fitnessEquipmentInfo.getId() <= 0) {
+                log.warn("修改健身设备信息失败,ID无效:{}", fitnessEquipmentInfo.getId());
+                return R.fail("ID不能为空且必须大于0");
+            }
+
+            boolean result = fitnessEquipmentInfoService.updateEquipment(fitnessEquipmentInfo);
+            if (result) {
+                log.info("修改健身设备信息成功,id={}", fitnessEquipmentInfo.getId());
+                return R.success(MODIFY_SUCCESS_MSG);
+            } else {
+                log.warn("修改健身设备信息失败,id={}", fitnessEquipmentInfo.getId());
+                return R.fail(OPERATION_FAIL_MSG);
+            }
+        } catch (Exception e) {
+            log.error("修改健身设备信息异常,异常信息:{}", e.getMessage(), e);
+            return R.fail(OPERATION_FAIL_MSG + ":" + e.getMessage());
+        }
+    }
+
+    /**
+     * 删除健身设备信息(逻辑删除)
+     *
+     * @param id 主键ID,不能为空
+     * @return 操作结果
+     */
+    @ApiOperation("删除健身设备信息")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @DeleteMapping("/delete")
+    public R<Boolean> deleteEquipment(@RequestParam("id") Integer id) {
+        log.info("删除健身设备信息,id={}", id);
+
+        try {
+            // 参数验证
+            if (id == null || id <= 0) {
+                log.warn("删除健身设备信息失败,ID无效:{}", id);
+                return R.fail("ID不能为空且必须大于0");
+            }
+
+            boolean result = fitnessEquipmentInfoService.deleteEquipment(id);
+            if (result) {
+                log.info("删除健身设备信息成功,id={}", id);
+                return R.success(DELETE_SUCCESS_MSG);
+            } else {
+                log.warn("删除健身设备信息失败,id={}", id);
+                return R.fail(OPERATION_FAIL_MSG);
+            }
+        } catch (Exception e) {
+            log.error("删除健身设备信息异常,id={},异常信息:{}", id, e.getMessage(), e);
+            return R.fail(OPERATION_FAIL_MSG + ":" + e.getMessage());
+        }
+    }
+
+    /**
+     * 启用/禁用健身设备信息
+     *
+     * @param id     主键ID,不能为空
+     * @param status 状态(0:禁用, 1:启用),不能为空
+     * @return 操作结果
+     */
+    @ApiOperation("启用/禁用健身设备信息")
+    @ApiOperationSupport(order = 8)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "status", value = "状态(0:禁用, 1:启用)", dataType = "Integer", paramType = "query", required = true)
+    })
+    @PostMapping("/updateStatus")
+    public R<Boolean> updateStatus(@RequestParam("id") Integer id, @RequestParam("status") Integer status) {
+        log.info("启用/禁用健身设备信息,id={}, status={}", id, status);
+
+        try {
+            // 参数验证
+            if (id == null || id <= 0) {
+                log.warn("启用/禁用健身设备信息失败,ID无效:{}", id);
+                return R.fail("ID不能为空且必须大于0");
+            }
+            if (status == null || (status != STATUS_ENABLED && status != STATUS_DISABLED)) {
+                log.warn("启用/禁用健身设备信息失败,状态值无效:{}", status);
+                return R.fail("状态值必须为0(禁用)或1(启用)");
+            }
+
+            boolean result = fitnessEquipmentInfoService.updateStatus(id, status);
+            if (result) {
+                log.info("启用/禁用健身设备信息成功,id={}, status={}", id, status);
+                return R.success(OPERATION_SUCCESS_MSG);
+            } else {
+                log.warn("启用/禁用健身设备信息失败,id={}, status={}", id, status);
+                return R.fail(OPERATION_FAIL_MSG);
+            }
+        } catch (Exception e) {
+            log.error("启用/禁用健身设备信息异常,id={}, status={},异常信息:{}", id, status, e.getMessage(), e);
+            return R.fail(OPERATION_FAIL_MSG + ":" + e.getMessage());
+        }
+    }
+}
+

+ 88 - 19
alien-store/src/main/java/shop/alien/store/controller/LifeCommentController.java

@@ -34,34 +34,104 @@ public class LifeCommentController {
 
     private final FileUpload fileUpload;
 
+    /**
+     * 点赞接口
+     *
+     * @param userId  用户ID
+     * @param huifuId 被点赞对象ID(评论ID/动态ID/活动ID等)
+     * @param type    点赞类型(1-评论 2-社区动态 3-活动 4-推荐菜 5-店铺打卡 6-二手商品 7-律师评分 8-点赞员工)
+     * @return 点赞结果
+     */
     @ApiOperation("点赞")
     @ApiOperationSupport(order = 1)
-    @ApiImplicitParams({@ApiImplicitParam(name = "userId", value = "用户id", dataType = "String", paramType = "query"),
-            @ApiImplicitParam(name = "huifuId", value = "huifuId", dataType = "String", paramType = "query"),
-            @ApiImplicitParam(name = "type", value = "type", dataType = "String", paramType = "query")})
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "用户ID", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "huifuId", value = "被点赞对象ID(评论ID/动态ID/活动ID等)", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "type", value = "点赞类型(1-评论 2-社区动态 3-活动 4-推荐菜 5-店铺打卡 6-二手商品 7-律师评分 8-点赞员工)", dataType = "String", paramType = "query", required = true)
+    })
     @PostMapping("/like")
-    public R<Boolean> dianzan(String userId, String huifuId, String type) {
-        log.info("LifeCommentController.like?userId={}, huifuId={}, type={}", userId, huifuId, type);
-        int num = lifeCommentService.like(userId, huifuId, type);
-        if (num == 0) {
-            return R.fail("点赞失败");
+    public R<Boolean> like(@RequestParam("userId") String userId,
+                           @RequestParam("huifuId") String huifuId,
+                           @RequestParam("type") String type) {
+        log.info("点赞请求,userId={},huifuId={},type={}", userId, huifuId, type);
+        
+        try {
+            // 参数校验
+            if (!StringUtils.hasText(userId)) {
+                log.warn("点赞失败,用户ID为空");
+                return R.fail("用户ID不能为空");
+            }
+            if (!StringUtils.hasText(huifuId)) {
+                log.warn("点赞失败,被点赞对象ID为空");
+                return R.fail("被点赞对象ID不能为空");
+            }
+            if (!StringUtils.hasText(type)) {
+                log.warn("点赞失败,点赞类型为空");
+                return R.fail("点赞类型不能为空");
+            }
+            
+            int result = lifeCommentService.like(userId, huifuId, type);
+            if (result > 0) {
+                log.info("点赞成功,userId={},huifuId={},type={}", userId, huifuId, type);
+                return R.success("点赞成功");
+            } else {
+                log.warn("点赞失败,可能已点赞或操作失败,userId={},huifuId={},type={}", userId, huifuId, type);
+                return R.fail("点赞失败,可能已点赞或操作失败");
+            }
+        } catch (Exception e) {
+            log.error("点赞异常,userId={},huifuId={},type={},异常信息:{}", userId, huifuId, type, e.getMessage(), e);
+            return R.fail("点赞失败:" + e.getMessage());
         }
-        return R.success("点赞成功");
     }
 
+    /**
+     * 取消点赞接口
+     *
+     * @param userId  用户ID
+     * @param huifuId 被取消点赞对象ID(评论ID/动态ID/活动ID等)
+     * @param type    点赞类型(1-评论 2-社区动态 3-活动 4-推荐菜 5-店铺打卡 6-二手商品 7-律师评分 8-点赞员工)
+     * @return 取消点赞结果
+     */
     @ApiOperation("取消点赞")
     @ApiOperationSupport(order = 2)
-    @ApiImplicitParams({@ApiImplicitParam(name = "userId", value = "用户id", dataType = "String", paramType = "query"),
-            @ApiImplicitParam(name = "huifuId", value = "huifuId", dataType = "String", paramType = "query"),
-            @ApiImplicitParam(name = "type", value = "type", dataType = "String", paramType = "query")})
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "userId", value = "用户ID", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "huifuId", value = "被取消点赞对象ID(评论ID/动态ID/活动ID等)", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "type", value = "点赞类型(1-评论 2-社区动态 3-活动 4-推荐菜 5-店铺打卡 6-二手商品 7-律师评分 8-点赞员工)", dataType = "String", paramType = "query", required = true)
+    })
     @PostMapping("/cancelLike")
-    public R<Boolean> cancelLike(String userId, String huifuId, String type) {
-        log.info("LifeCommentController.cancelLike?userId={}, huifuId={}, type={}", userId, huifuId, type);
-        int num = lifeCommentService.cancelLike(userId, huifuId, type);
-        if (num == 0) {
-            return R.fail("取消点赞失败");
+    public R<Boolean> cancelLike(@RequestParam("userId") String userId,
+                                  @RequestParam("huifuId") String huifuId,
+                                  @RequestParam("type") String type) {
+        log.info("取消点赞请求,userId={},huifuId={},type={}", userId, huifuId, type);
+        
+        try {
+            // 参数校验
+            if (!StringUtils.hasText(userId)) {
+                log.warn("取消点赞失败,用户ID为空");
+                return R.fail("用户ID不能为空");
+            }
+            if (!StringUtils.hasText(huifuId)) {
+                log.warn("取消点赞失败,被取消点赞对象ID为空");
+                return R.fail("被取消点赞对象ID不能为空");
+            }
+            if (!StringUtils.hasText(type)) {
+                log.warn("取消点赞失败,点赞类型为空");
+                return R.fail("点赞类型不能为空");
+            }
+            
+            int result = lifeCommentService.cancelLike(userId, huifuId, type);
+            if (result > 0) {
+                log.info("取消点赞成功,userId={},huifuId={},type={}", userId, huifuId, type);
+                return R.success("取消点赞成功");
+            } else {
+                log.warn("取消点赞失败,可能未点赞或操作失败,userId={},huifuId={},type={}", userId, huifuId, type);
+                return R.fail("取消点赞失败,可能未点赞或操作失败");
+            }
+        } catch (Exception e) {
+            log.error("取消点赞异常,userId={},huifuId={},type={},异常信息:{}", userId, huifuId, type, e.getMessage(), e);
+            return R.fail("取消点赞失败:" + e.getMessage());
         }
-        return R.success("取消点赞成功");
     }
 
     @ApiOperation("回复评论")
@@ -73,7 +143,6 @@ public class LifeCommentController {
         log.info("LifeCommentController.addOrUpdateComment?store={}&image1={}&image2={}", pingun.toString(),
                 image1 == null ? null : image1.getOriginalFilename(),
                 image2 == null ? null : image2.getOriginalFilename());
-        Map<String, Object> response = new HashMap<>();
         try {
             int num = lifeCommentService.addOrUpdateStore(pingun);
             String path = "";

+ 17 - 0
alien-store/src/main/java/shop/alien/store/controller/LifeUserDynamicsController.java

@@ -205,6 +205,23 @@ public class LifeUserDynamicsController {
         return R.data(ListToPage.setPage(stores, page, size));
     }
 
+    @ApiOperation("门店动态列表")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storePhone", value = "门店账号", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "userType", value = "user-用户 store-商户", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "loginPhone", value = "当前登录人", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getStoreDynamicsList")
+    public R<List<LifeUserDynamicsVo>> getStoreDynamicsList(
+                                                        @RequestParam(required = false) String storePhone,
+                                                        @RequestParam(required = false) String userType,
+                                                        @RequestParam(required = false) String loginPhone) {
+        log.info("LifeUserDynamicsController.getStoreDynamicsList?storePhone={}&userType={}&loginPhone={}", storePhone, userType, loginPhone);
+        List<LifeUserDynamicsVo> stores = lifeUserDynamicsService.getStoreUserDynamics(storePhone, userType, loginPhone);
+        return R.data(stores);
+    }
+
 
     @ApiOperation("置顶/取消功能")
     @ApiOperationSupport(order = 2)

+ 77 - 5
alien-store/src/main/java/shop/alien/store/controller/SportsEquipmentFacilityController.java

@@ -7,6 +7,8 @@ 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.FitnessEquipmentCategoryDetailVo;
+import shop.alien.entity.store.vo.SportsEquipmentFacilityCategorySummaryVo;
 import shop.alien.entity.store.vo.SportsEquipmentFacilityCategoryVo;
 import shop.alien.entity.store.vo.SportsEquipmentFacilityVo;
 import shop.alien.store.service.SportsEquipmentFacilityService;
@@ -158,9 +160,20 @@ public class SportsEquipmentFacilityController {
             @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));
+    public R<List<SportsEquipmentFacilityCategorySummaryVo>> getCategorySummary(@RequestParam Integer storeId) {
+        log.info("SportsEquipmentFacilityController.getCategorySummary,storeId={}", storeId);
+        try {
+            if (storeId == null || storeId <= 0) {
+                log.warn("查询分类汇总失败,门店ID无效:{}", storeId);
+                return R.fail("门店ID不能为空且必须大于0");
+            }
+            List<SportsEquipmentFacilityCategorySummaryVo> result = facilityService.getCategorySummary(storeId);
+            log.info("查询分类汇总成功,storeId={},返回分类数量:{}", storeId, result.size());
+            return R.data(result);
+        } catch (Exception e) {
+            log.error("查询分类汇总异常,storeId={},异常信息:{}", storeId, e.getMessage(), e);
+            return R.fail("查询失败:" + e.getMessage());
+        }
     }
 
 
@@ -186,8 +199,8 @@ public class SportsEquipmentFacilityController {
             @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);
+    public R<List<SportsEquipmentFacilityCategoryVo>> getStoreCategorySummary(@RequestParam Integer storeId) {
+        log.info("SportsEquipmentFacilityController.getStoreCategorySummary?storeId={}", storeId);
         return R.data(facilityService.getstoreCategorySummary(storeId));
     }
 
@@ -208,5 +221,64 @@ public class SportsEquipmentFacilityController {
             return R.fail(e.getMessage());
         }
     }
+
+    /**
+     * 查询失败消息
+     */
+    private static final String QUERY_FAIL_MSG = "查询失败";
+    
+    /**
+     * 门店ID无效消息
+     */
+    private static final String INVALID_STORE_ID_MSG = "门店ID不能为空且必须大于0";
+    
+    /**
+     * 最小有效值
+     */
+    private static final int MIN_VALID_VALUE = 1;
+    
+    @ApiOperation("查询分类的设备详情(用户端)- 包含图片列表、各子分类的设备数量汇总、设备列表。facilityCategoryName为空时返回所有分类,equipmentType为空时返回所有类型")
+    @ApiOperationSupport(order = 11)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "facilityCategoryName", value = "设施分类名称(商户自定义),可选,为空时返回所有分类", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "equipmentType", value = "设备类型(1:心肺训练, 2:核心训练, 3:臀腿训练, 4:上肢训练, 5:全身训练, 6:其他),可选,为空时返回所有类型", dataType = "Integer", paramType = "query", required = false)
+    })
+    @GetMapping("/categoryDetail")
+    public R<List<FitnessEquipmentCategoryDetailVo>> getCategoryDetail(
+            @RequestParam("storeId") Integer storeId,
+            @RequestParam(required = false) String facilityCategoryName,
+            @RequestParam(required = false) Integer equipmentType) {
+        log.info("查询分类的设备详情(用户端),storeId={},facilityCategoryName={},equipmentType={}", 
+                storeId, facilityCategoryName, equipmentType);
+        try {
+            // 参数验证
+            if (storeId == null || storeId < MIN_VALID_VALUE) {
+                log.warn("查询分类详情失败,门店ID无效:{}", storeId);
+                return R.fail(INVALID_STORE_ID_MSG);
+            }
+            
+            List<FitnessEquipmentCategoryDetailVo> resultList = facilityService.getCategoryDetail(storeId, facilityCategoryName, equipmentType);
+            
+            // 统计总设备数量(从equipmentTypeList中统计)
+            int totalEquipmentCount = 0;
+            if (resultList != null) {
+                for (FitnessEquipmentCategoryDetailVo vo : resultList) {
+                    if (vo.getEquipmentTypeList() != null) {
+                        totalEquipmentCount += vo.getEquipmentTypeList().stream()
+                                .mapToInt(typeVo -> typeVo.getEquipmentList() != null ? typeVo.getEquipmentList().size() : 0)
+                                .sum();
+                    }
+                }
+            }
+            log.info("查询分类详情成功,storeId={},facilityCategoryName={},equipmentType={},返回分类数量:{},总设备数量:{}", 
+                    storeId, facilityCategoryName, equipmentType, resultList != null ? resultList.size() : 0, totalEquipmentCount);
+            return R.data(resultList);
+        } catch (Exception e) {
+            log.error("查询分类详情异常,storeId={},facilityCategoryName={},equipmentType={},异常信息:{}", 
+                    storeId, facilityCategoryName, equipmentType, e.getMessage(), e);
+            return R.fail(QUERY_FAIL_MSG + ":" + e.getMessage());
+        }
+    }
 }
 

+ 218 - 0
alien-store/src/main/java/shop/alien/store/controller/SportsFacilityAreaController.java

@@ -0,0 +1,218 @@
+package shop.alien.store.controller;
+
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.SportsFacilityArea;
+import shop.alien.entity.store.vo.CreateAreaRequestVo;
+import shop.alien.entity.store.vo.DeleteAreaRequestVo;
+import shop.alien.store.service.SportsFacilityAreaService;
+
+import java.util.List;
+
+/**
+ * 运动设施区域Controller
+ *
+ * @author assistant
+ * @since 2025-12-19
+ */
+@Slf4j
+@Api(tags = {"商户端-运动设施区域管理"})
+@ApiSort(2)
+@CrossOrigin
+@RestController
+@RequestMapping("/sports/facility/area")
+@RequiredArgsConstructor
+public class SportsFacilityAreaController {
+
+    private final SportsFacilityAreaService areaService;
+
+    /**
+     * 最小有效值
+     */
+    private static final int MIN_VALID_VALUE = 1;
+
+    /**
+     * 门店ID无效消息
+     */
+    private static final String INVALID_STORE_ID_MSG = "门店ID不能为空且必须大于0";
+
+    @ApiOperation("新建区域(创建新的设施区域)")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "request", value = "新建区域请求参数", dataType = "CreateAreaRequestVo", paramType = "body", required = true)
+    })
+    @PostMapping("/create")
+    public R<Integer> createArea(@RequestBody CreateAreaRequestVo request) {
+        log.info("新建区域,request={}", request);
+        try {
+            // 参数验证
+            if (request == null) {
+                log.warn("新建区域失败,请求参数为空");
+                return R.fail("请求参数不能为空");
+            }
+
+            if (request.getStoreId() == null || request.getStoreId() < MIN_VALID_VALUE) {
+                log.warn("新建区域失败,门店ID无效:{}", request.getStoreId());
+                return R.fail(INVALID_STORE_ID_MSG);
+            }
+
+            if (StringUtils.isBlank(request.getFacilityCategoryName())) {
+                log.warn("新建区域失败,区域名称为空");
+                return R.fail("区域名称不能为空");
+            }
+
+            // 调用Service创建区域
+            Integer areaId = areaService.createArea(request.getStoreId(), request.getFacilityCategoryName());
+            log.info("新建区域成功,storeId={},areaName={},区域ID={}", 
+                    request.getStoreId(), request.getFacilityCategoryName(), areaId);
+            return R.data(areaId, "新建区域成功");
+        } catch (IllegalArgumentException e) {
+            log.warn("新建区域失败,参数验证失败:{}", e.getMessage());
+            return R.fail(e.getMessage());
+        } catch (Exception e) {
+            log.error("新建区域异常,request={},异常信息:{}", request, e.getMessage(), e);
+            return R.fail("新建区域失败:" + e.getMessage());
+        }
+    }
+
+    @ApiOperation("查询指定门店下的所有区域列表")
+    @ApiOperationSupport(order = 2)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/list")
+    public R<List<SportsFacilityArea>> listAreas(@RequestParam("storeId") Integer storeId) {
+        log.info("查询区域列表,storeId={}", storeId);
+        try {
+            // 参数验证
+            if (storeId == null || storeId < MIN_VALID_VALUE) {
+                log.warn("查询区域列表失败,门店ID无效:{}", storeId);
+                return R.fail(INVALID_STORE_ID_MSG);
+            }
+
+            List<SportsFacilityArea> areaList = areaService.listAreas(storeId);
+            log.info("查询区域列表成功,storeId={},区域数量:{}", storeId, areaList.size());
+            return R.data(areaList);
+        } catch (IllegalArgumentException e) {
+            log.warn("查询区域列表失败,参数验证失败:{}", e.getMessage());
+            return R.fail(e.getMessage());
+        } catch (Exception e) {
+            log.error("查询区域列表异常,storeId={},异常信息:{}", storeId, e.getMessage(), e);
+            return R.fail("查询区域列表失败:" + e.getMessage());
+        }
+    }
+
+    @ApiOperation("更新区域信息")
+    @ApiOperationSupport(order = 3)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "areaId", value = "区域ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "areaName", value = "区域名称(可选)", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "sortOrder", value = "排序号(可选)", dataType = "Integer", paramType = "query", required = false)
+    })
+    @PostMapping("/update")
+    public R<Boolean> updateArea(
+            @RequestParam("areaId") Integer areaId,
+            @RequestParam(required = false) String areaName,
+            @RequestParam(required = false) Integer sortOrder) {
+        log.info("更新区域,areaId={},areaName={},sortOrder={}", areaId, areaName, sortOrder);
+        try {
+            // 参数验证
+            if (areaId == null || areaId < MIN_VALID_VALUE) {
+                log.warn("更新区域失败,区域ID无效:{}", areaId);
+                return R.fail("区域ID不能为空且必须大于0");
+            }
+
+            boolean result = areaService.updateArea(areaId, areaName, sortOrder);
+            if (result) {
+                log.info("更新区域成功,areaId={}", areaId);
+                return R.success("更新区域成功");
+            }
+            return R.fail("更新区域失败");
+        } catch (IllegalArgumentException e) {
+            log.warn("更新区域失败,参数验证失败:{}", e.getMessage());
+            return R.fail(e.getMessage());
+        } catch (Exception e) {
+            log.error("更新区域异常,areaId={},异常信息:{}", areaId, e.getMessage(), e);
+            return R.fail("更新区域失败:" + e.getMessage());
+        }
+    }
+
+    @ApiOperation("删除区域(逻辑删除,同时删除该区域下的所有设施)")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "request", value = "删除区域请求参数", dataType = "DeleteAreaRequestVo", paramType = "body", required = true)
+    })
+    @PostMapping("/delete")
+    public R<Boolean> deleteArea(@RequestBody DeleteAreaRequestVo request) {
+        log.info("删除区域,request={}", request);
+        try {
+            // 参数验证
+            if (request == null) {
+                log.warn("删除区域失败,请求参数为空");
+                return R.fail("请求参数不能为空");
+            }
+
+            if (request.getAreaId() == null || request.getAreaId() < MIN_VALID_VALUE) {
+                log.warn("删除区域失败,区域ID无效:{}", request.getAreaId());
+                return R.fail("区域ID不能为空且必须大于0");
+            }
+
+            boolean result = areaService.deleteArea(request.getAreaId());
+            if (result) {
+                log.info("删除区域成功,areaId={}", request.getAreaId());
+                return R.success("删除区域成功");
+            }
+            return R.fail("删除区域失败");
+        } catch (IllegalArgumentException e) {
+            log.warn("删除区域失败,参数验证失败:{}", e.getMessage());
+            return R.fail(e.getMessage());
+        } catch (Exception e) {
+            log.error("删除区域异常,request={},异常信息:{}", request, e.getMessage(), e);
+            return R.fail("删除区域失败:" + e.getMessage());
+        }
+    }
+
+    @ApiOperation("批量删除区域(逻辑删除,同时删除这些区域下的所有设施)")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "areaIds", value = "区域ID列表", dataType = "List<Integer>", paramType = "body", required = true)
+    })
+    @PostMapping("/batchDelete")
+    public R<Boolean> batchDeleteAreas(
+            @RequestParam("storeId") Integer storeId,
+            @RequestBody List<Integer> areaIds) {
+        log.info("批量删除区域,storeId={},areaIds={}", storeId, areaIds);
+        try {
+            // 参数验证
+            if (storeId == null || storeId < MIN_VALID_VALUE) {
+                log.warn("批量删除区域失败,门店ID无效:{}", storeId);
+                return R.fail(INVALID_STORE_ID_MSG);
+            }
+
+            if (areaIds == null || areaIds.isEmpty()) {
+                log.warn("批量删除区域失败,区域ID列表为空");
+                return R.fail("区域ID列表不能为空");
+            }
+
+            boolean result = areaService.batchDeleteAreas(storeId, areaIds);
+            if (result) {
+                log.info("批量删除区域成功,storeId={},区域数量:{}", storeId, areaIds.size());
+                return R.success("批量删除区域成功");
+            }
+            return R.fail("批量删除区域失败");
+        } catch (IllegalArgumentException e) {
+            log.warn("批量删除区域失败,参数验证失败:{}", e.getMessage());
+            return R.fail(e.getMessage());
+        } catch (Exception e) {
+            log.error("批量删除区域异常,storeId={},areaIds={},异常信息:{}", 
+                    storeId, areaIds, e.getMessage(), e);
+            return R.fail("批量删除区域失败:" + e.getMessage());
+        }
+    }
+}
+

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

@@ -1235,4 +1235,82 @@ public class StoreInfoController {
     }
 
 
+
+    /**
+     * 查询七种类型店铺(酒吧、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 = "storeName", value = "店铺名称(模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "storeType", value = "美食3 休闲娱乐1  健身2 ", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "pageNum", value = "页码", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "pageSize", value = "页容", dataType = "int", paramType = "query", required = true)
+    })
+    @GetMapping("/getThreeCategoriesStoresByDistance")
+    public R<IPage<StoreInfoVo>> getThreeCategoriesStoresByDistance(
+            @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(value = "storeName", required = false) String storeName,
+            @RequestParam(value = "storeType", required = false) Integer storeType,
+            @RequestParam(defaultValue = "1") int pageNum,
+            @RequestParam(defaultValue = "10") int pageSize) {
+        log.info("StoreInfoController.getSpecialTypeStoresByDistance?lon={},lat={},distance={},sortType={},businessType={},categoryId={},storeName={},storeType{},pageNum={},pageSize={}",
+                lon, lat, distance, sortType, businessType, categoryId, storeName,storeType, 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.getThreeCategoriesStoresByDistance(lon, lat, distance, sortType, businessType, categoryId, storeName,storeType, 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("筛选失败,请稍后重试");
+        }
+    }
+
+
 }

+ 180 - 0
alien-store/src/main/java/shop/alien/store/controller/StoreProductBarController.java

@@ -0,0 +1,180 @@
+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.StoreProductBar;
+import shop.alien.store.service.StoreProductBarService;
+
+import java.util.List;
+
+/**
+ * 酒吧商品表 Controller
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"酒吧商品管理"})
+@CrossOrigin
+@RestController
+@RequestMapping("/store/product/bar")
+@RequiredArgsConstructor
+public class StoreProductBarController {
+
+    private final StoreProductBarService storeProductBarService;
+
+    @ApiOperation("新增酒吧商品")
+    @ApiOperationSupport(order = 1)
+    @PostMapping
+    public R<String> saveBar(@RequestBody StoreProductBar bar) {
+        log.info("StoreProductBarController.saveBar?bar={}", bar);
+        // 参数校验
+        if (bar.getName() == null || bar.getName().trim().isEmpty()) {
+            return R.fail("名称不能为空");
+        }
+        if (bar.getPrice() == null) {
+            return R.fail("价格不能为空");
+        }
+
+
+        R<StoreProductBar> result = storeProductBarService.addStoreProductBar(bar);
+        if (result.getCode() == 200) {
+            return R.success("新增成功");
+        }
+        return R.fail(result.getMsg());
+    }
+
+    @ApiOperation("修改酒吧商品")
+    @ApiOperationSupport(order = 2)
+    @PutMapping
+    public R<String> updateBar(@RequestBody StoreProductBar bar) {
+        log.info("StoreProductBarController.updateBar?bar={}", bar);
+        if (bar.getId() == null) {
+            return R.fail("ID不能为空");
+        }
+
+        R<StoreProductBar> result = storeProductBarService.editStoreProductBar(bar);
+        if (result.getCode() == 200) {
+            return R.success("修改成功");
+        }
+        return R.fail(result.getMsg());
+    }
+
+    @ApiOperation("删除酒吧商品")
+    @ApiOperationSupport(order = 3)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "Integer", paramType = "path", required = true)
+    })
+    @DeleteMapping("/{id}")
+    public R<String> deleteBar(@PathVariable("id") Integer id) {
+        log.info("StoreProductBarController.deleteBar?id={}", id);
+        R<Boolean> result = storeProductBarService.deleteStoreProductBar(id);
+        if (result.getCode() == 200) {
+            return R.success("删除成功");
+        }
+        return R.fail(result.getMsg());
+    }
+
+    @ApiOperation("根据ID查询酒吧商品详情")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "Integer", paramType = "path", required = true)
+    })
+    @GetMapping("/{id}")
+    public R<StoreProductBar> getById(@PathVariable("id") Integer id) {
+        log.info("StoreProductBarController.getById?id={}", id);
+        R<StoreProductBar> result = storeProductBarService.getStoreProductBarById(id);
+        if (result.getCode() == 200 && result.getData() != null) {
+            return result;
+        }
+        return R.fail("未找到该酒吧商品信息");
+    }
+
+    @ApiOperation("分页查询酒吧商品列表")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "pageNum", value = "页码", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "pageSize", value = "页容", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "name", value = "名称(模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "category", value = "品类", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "status", value = "状态(0:禁用,1:启用)", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "extId", value = "商品表主键", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/page")
+    public R<IPage<StoreProductBar>> getPage(
+            @RequestParam(defaultValue = "1") int pageNum,
+            @RequestParam(defaultValue = "10") int pageSize,
+            @RequestParam(required = false) String name,
+            @RequestParam(required = false) String category,
+            @RequestParam(required = false) Integer status,
+            @RequestParam(required = false) Integer extId) {
+        log.info("StoreProductBarController.getPage?pageNum={}, pageSize={}, name={}, category={}, status={}, extId={}",
+                pageNum, pageSize, name, category, status, extId);
+        IPage<StoreProductBar> page = storeProductBarService.getPage(pageNum, pageSize, name, category, status, extId);
+        return R.data(page);
+    }
+
+    @ApiOperation("批量删除酒吧商品")
+    @ApiOperationSupport(order = 6)
+    @PostMapping("/batchDelete")
+    public R<String> deleteBatch(@RequestBody List<Integer> ids) {
+        log.info("StoreProductBarController.deleteBatch?ids={}", ids);
+        if (ids == null || ids.isEmpty()) {
+            return R.fail("ID列表不能为空");
+        }
+        boolean result = storeProductBarService.deleteBatch(ids);
+        if (result) {
+            return R.success("批量删除成功");
+        }
+        return R.fail("批量删除失败");
+    }
+
+    @ApiOperation("更新酒吧商品状态")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "Integer", paramType = "path", required = true),
+            @ApiImplicitParam(name = "status", value = "状态(0:禁用,1:启用)", dataType = "Integer", paramType = "query", required = true)
+    })
+    @PutMapping("/{id}/status")
+    public R<String> updateStatus(
+            @PathVariable("id") Integer id,
+            @RequestParam("status") Integer status) {
+        log.info("StoreProductBarController.updateStatus?id={}, status={}", id, status);
+        if (status == null || (status != 0 && status != 1)) {
+            return R.fail("状态值无效,只能为0或1");
+        }
+        boolean result = storeProductBarService.updateStatus(id, status);
+        if (result) {
+            return R.success("状态更新成功");
+        }
+        return R.fail("状态更新失败");
+    }
+
+    @ApiOperation("根据商品表主键查询酒吧商品列表")
+    @ApiOperationSupport(order = 8)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "extId", value = "商品表主键", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/listByExtId")
+    public R<List<StoreProductBar>> getListByExtId(@RequestParam("extId") Integer extId) {
+        log.info("StoreProductBarController.getListByExtId?extId={}", extId);
+        List<StoreProductBar> list = storeProductBarService.getListByExtId(extId);
+        return R.data(list);
+    }
+
+    @ApiOperation("根据品类查询酒吧商品列表")
+    @ApiOperationSupport(order = 9)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "category", value = "品类", dataType = "String", paramType = "query", required = true)
+    })
+    @GetMapping("/listByCategory")
+    public R<List<StoreProductBar>> getListByCategory(@RequestParam("category") String category) {
+        log.info("StoreProductBarController.getListByCategory?category={}", category);
+        List<StoreProductBar> list = storeProductBarService.getListByCategory(category);
+        return R.data(list);
+    }
+}

+ 204 - 0
alien-store/src/main/java/shop/alien/store/controller/StoreProductDelicaciesController.java

@@ -0,0 +1,204 @@
+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.StoreProductDelicacies;
+import shop.alien.store.service.StoreProductDelicaciesService;
+
+import java.util.List;
+
+/**
+ * 美食商品表 Controller
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"美食商品管理"})
+@ApiSort(1)
+@CrossOrigin
+@RestController
+@RequestMapping("/store/product/delicacies")
+@RequiredArgsConstructor
+public class StoreProductDelicaciesController {
+
+    private final StoreProductDelicaciesService storeProductDelicaciesService;
+
+    @ApiOperation("分页查询美食商品列表")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "pageNum", value = "页码", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "pageSize", value = "页容", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "name", value = "名称(模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "category", value = "类别", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "extGroup", value = "菜品分组", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "status", value = "状态(0:禁用,1:启用)", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "extId", value = "商品表主键", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/page")
+    public R<IPage<StoreProductDelicacies>> getPage(
+            @RequestParam(defaultValue = "1") int pageNum,
+            @RequestParam(defaultValue = "10") int pageSize,
+            @RequestParam(required = false) String name,
+            @RequestParam(required = false) String category,
+            @RequestParam(required = false) String extGroup,
+            @RequestParam(required = false) Integer status,
+            @RequestParam(required = false) Integer extId) {
+        log.info("StoreProductDelicaciesController.getPage?pageNum={}, pageSize={}, name={}, category={}, extGroup={}, status={}, extId={}",
+                pageNum, pageSize, name, category, extGroup, status, extId);
+        IPage<StoreProductDelicacies> page = storeProductDelicaciesService.getPage(pageNum, pageSize, name, category, extGroup, status, extId);
+        return R.data(page);
+    }
+
+    @ApiOperation("根据ID查询美食商品详情")
+    @ApiOperationSupport(order = 2)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "Integer", paramType = "path", required = true)
+    })
+    @GetMapping("/{id}")
+    public R<StoreProductDelicacies> getById(@PathVariable("id") Integer id) {
+        log.info("StoreProductDelicaciesController.getById?id={}", id);
+        StoreProductDelicacies delicacies = storeProductDelicaciesService.getById(id);
+        if (delicacies == null) {
+            return R.fail("未找到该美食商品信息");
+        }
+        return R.data(delicacies);
+    }
+
+    @ApiOperation("新增美食商品")
+    @ApiOperationSupport(order = 3)
+    @PostMapping
+    public R<String> saveDelicacies(@RequestBody StoreProductDelicacies delicacies) {
+        log.info("StoreProductDelicaciesController.saveDelicacies?delicacies={}", delicacies);
+        // 参数校验
+        if (delicacies.getName() == null || delicacies.getName().trim().isEmpty()) {
+            return R.fail("名称不能为空");
+        }
+        if (delicacies.getPrice() == null) {
+            return R.fail("价格不能为空");
+        }
+        if (delicacies.getCostPrice() == null) {
+            return R.fail("成本价不能为空");
+        }
+        if (delicacies.getCategory() == null || delicacies.getCategory().trim().isEmpty()) {
+            return R.fail("类别不能为空");
+        }
+        if (delicacies.getExtGroup() == null || delicacies.getExtGroup().trim().isEmpty()) {
+            return R.fail("菜品分组不能为空");
+        }
+
+        boolean result = storeProductDelicaciesService.saveDelicacies(delicacies);
+        if (result) {
+            return R.success("新增成功");
+        }
+        return R.fail("新增失败");
+    }
+
+    @ApiOperation("修改美食商品")
+    @ApiOperationSupport(order = 4)
+    @PutMapping
+    public R<String> updateDelicacies(@RequestBody StoreProductDelicacies delicacies) {
+        log.info("StoreProductDelicaciesController.updateDelicacies?delicacies={}", delicacies);
+        if (delicacies.getId() == null) {
+            return R.fail("ID不能为空");
+        }
+
+        boolean result = storeProductDelicaciesService.updateDelicacies(delicacies);
+        if (result) {
+            return R.success("修改成功");
+        }
+        return R.fail("修改失败");
+    }
+
+    @ApiOperation("删除美食商品")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "Long", paramType = "path", required = true)
+    })
+    @DeleteMapping("/{id}")
+    public R<String> deleteDelicacies(@PathVariable("id") Integer id) {
+        log.info("StoreProductDelicaciesController.deleteDelicacies?id={}", id);
+        boolean result = storeProductDelicaciesService.deleteDelicacies(id);
+        if (result) {
+            return R.success("删除成功");
+        }
+        return R.fail("删除失败");
+    }
+
+    @ApiOperation("批量删除美食商品")
+    @ApiOperationSupport(order = 6)
+    @PostMapping("/batchDelete")
+    public R<String> deleteBatch(@RequestBody List<Integer> ids) {
+        log.info("StoreProductDelicaciesController.deleteBatch?ids={}", ids);
+        if (ids == null || ids.isEmpty()) {
+            return R.fail("ID列表不能为空");
+        }
+        boolean result = storeProductDelicaciesService.deleteBatch(ids);
+        if (result) {
+            return R.success("批量删除成功");
+        }
+        return R.fail("批量删除失败");
+    }
+
+    @ApiOperation("更新美食商品状态")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "Integer", paramType = "path", required = true),
+            @ApiImplicitParam(name = "status", value = "状态(0:禁用,1:启用)", dataType = "Integer", paramType = "query", required = true)
+    })
+    @PutMapping("/{id}/status")
+    public R<String> updateStatus(
+            @PathVariable("id") Integer id,
+            @RequestParam("status") Integer status) {
+        log.info("StoreProductDelicaciesController.updateStatus?id={}, status={}", id, status);
+        if (status == null || (status != 0 && status != 1)) {
+            return R.fail("状态值无效,只能为0或1");
+        }
+        boolean result = storeProductDelicaciesService.updateStatus(id, status);
+        if (result) {
+            return R.success("状态更新成功");
+        }
+        return R.fail("状态更新失败");
+    }
+
+    @ApiOperation("根据商品表主键查询美食商品列表")
+    @ApiOperationSupport(order = 8)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "extId", value = "商品表主键", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/listByExtId")
+    public R<List<StoreProductDelicacies>> getListByExtId(@RequestParam("extId") Integer extId) {
+        log.info("StoreProductDelicaciesController.getListByExtId?extId={}", extId);
+        List<StoreProductDelicacies> list = storeProductDelicaciesService.getListByExtId(extId);
+        return R.data(list);
+    }
+
+    @ApiOperation("根据类别查询美食商品列表")
+    @ApiOperationSupport(order = 9)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "category", value = "类别", dataType = "String", paramType = "query", required = true)
+    })
+    @GetMapping("/listByCategory")
+    public R<List<StoreProductDelicacies>> getListByCategory(@RequestParam("category") String category) {
+        log.info("StoreProductDelicaciesController.getListByCategory?category={}", category);
+        List<StoreProductDelicacies> list = storeProductDelicaciesService.getListByCategory(category);
+        return R.data(list);
+    }
+
+    @ApiOperation("根据菜品分组查询美食商品列表")
+    @ApiOperationSupport(order = 10)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "extGroup", value = "菜品分组", dataType = "String", paramType = "query", required = true)
+    })
+    @GetMapping("/listByExtGroup")
+    public R<List<StoreProductDelicacies>> getListByExtGroup(@RequestParam("extGroup") String extGroup) {
+        log.info("StoreProductDelicaciesController.getListByExtGroup?extGroup={}", extGroup);
+        List<StoreProductDelicacies> list = storeProductDelicaciesService.getListByExtGroup(extGroup);
+        return R.data(list);
+    }
+}
+

+ 170 - 0
alien-store/src/main/java/shop/alien/store/controller/StoreProductGymController.java

@@ -0,0 +1,170 @@
+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.StoreProductGym;
+import shop.alien.store.service.StoreProductGymService;
+
+import java.util.List;
+
+/**
+ * 运动健身商品表 Controller
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"运动健身商品管理"})
+@CrossOrigin
+@RestController
+@RequestMapping("/store/product/gym")
+@RequiredArgsConstructor
+public class StoreProductGymController {
+
+    private final StoreProductGymService storeProductGymService;
+
+    @ApiOperation("新增运动健身商品")
+    @ApiOperationSupport(order = 1)
+    @PostMapping
+    public R<String> saveGym(@RequestBody StoreProductGym gym) {
+        log.info("StoreProductGymController.saveGym?gym={}", gym);
+        // 参数校验
+        if (gym.getName() == null || gym.getName().trim().isEmpty()) {
+            return R.fail("名称不能为空");
+        }
+        if (gym.getClassMode() == null || gym.getClassMode().trim().isEmpty()) {
+            return R.fail("上课形式不能为空");
+        }
+        if (gym.getPrice() == null) {
+            return R.fail("价格不能为空");
+        }
+
+        R<StoreProductGym> result = storeProductGymService.addStoreProductGym(gym);
+        if (result.getCode() == 200) {
+            return R.success("新增成功");
+        }
+        return R.fail(result.getMsg());
+    }
+
+    @ApiOperation("修改运动健身商品")
+    @ApiOperationSupport(order = 2)
+    @PutMapping
+    public R<String> updateGym(@RequestBody StoreProductGym gym) {
+        log.info("StoreProductGymController.updateGym?gym={}", gym);
+        if (gym.getId() == null) {
+            return R.fail("ID不能为空");
+        }
+
+        R<StoreProductGym> result = storeProductGymService.editStoreProductGym(gym);
+        if (result.getCode() == 200) {
+            return R.success("修改成功");
+        }
+        return R.fail(result.getMsg());
+    }
+
+    @ApiOperation("删除运动健身商品")
+    @ApiOperationSupport(order = 3)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "Integer", paramType = "path", required = true)
+    })
+    @DeleteMapping("/{id}")
+    public R<String> deleteGym(@PathVariable("id") Integer id) {
+        log.info("StoreProductGymController.deleteGym?id={}", id);
+        R<Boolean> result = storeProductGymService.deleteStoreProductGym(id);
+        if (result.getCode() == 200) {
+            return R.success("删除成功");
+        }
+        return R.fail(result.getMsg());
+    }
+
+    @ApiOperation("根据ID查询运动健身商品详情")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "Integer", paramType = "path", required = true)
+    })
+    @GetMapping("/{id}")
+    public R<StoreProductGym> getById(@PathVariable("id") Integer id) {
+        log.info("StoreProductGymController.getById?id={}", id);
+        R<StoreProductGym> result = storeProductGymService.getStoreProductGymById(id);
+        if (result.getCode() == 200 && result.getData() != null) {
+            return result;
+        }
+        return R.fail("未找到该运动健身商品信息");
+    }
+
+    @ApiOperation("分页查询运动健身商品列表")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "pageNum", value = "页码", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "pageSize", value = "页容", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "name", value = "名称(模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "classMode", value = "上课形式", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "status", value = "状态(0:禁用,1:启用)", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "extId", value = "商品表主键", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/page")
+    public R<IPage<StoreProductGym>> getPage(
+            @RequestParam(defaultValue = "1") int pageNum,
+            @RequestParam(defaultValue = "10") int pageSize,
+            @RequestParam(required = false) String name,
+            @RequestParam(required = false) String classMode,
+            @RequestParam(required = false) Integer status,
+            @RequestParam(required = false) Integer extId) {
+        log.info("StoreProductGymController.getPage?pageNum={}, pageSize={}, name={}, classMode={}, status={}, extId={}",
+                pageNum, pageSize, name, classMode, status, extId);
+        IPage<StoreProductGym> page = storeProductGymService.getPage(pageNum, pageSize, name, classMode, status, extId);
+        return R.data(page);
+    }
+
+    @ApiOperation("批量删除运动健身商品")
+    @ApiOperationSupport(order = 6)
+    @PostMapping("/batchDelete")
+    public R<String> deleteBatch(@RequestBody List<Integer> ids) {
+        log.info("StoreProductGymController.deleteBatch?ids={}", ids);
+        if (ids == null || ids.isEmpty()) {
+            return R.fail("ID列表不能为空");
+        }
+        boolean result = storeProductGymService.deleteBatch(ids);
+        if (result) {
+            return R.success("批量删除成功");
+        }
+        return R.fail("批量删除失败");
+    }
+
+    @ApiOperation("更新运动健身商品状态")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "Integer", paramType = "path", required = true),
+            @ApiImplicitParam(name = "status", value = "状态(0:禁用,1:启用)", dataType = "Integer", paramType = "query", required = true)
+    })
+    @PutMapping("/{id}/status")
+    public R<String> updateStatus(
+            @PathVariable("id") Integer id,
+            @RequestParam("status") Integer status) {
+        log.info("StoreProductGymController.updateStatus?id={}, status={}", id, status);
+        if (status == null || (status != 0 && status != 1)) {
+            return R.fail("状态值无效,只能为0或1");
+        }
+        boolean result = storeProductGymService.updateStatus(id, status);
+        if (result) {
+            return R.success("状态更新成功");
+        }
+        return R.fail("状态更新失败");
+    }
+
+    @ApiOperation("根据商品表主键查询运动健身商品列表")
+    @ApiOperationSupport(order = 8)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "extId", value = "商品表主键", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/listByExtId")
+    public R<List<StoreProductGym>> getListByExtId(@RequestParam("extId") Integer extId) {
+        log.info("StoreProductGymController.getListByExtId?extId={}", extId);
+        List<StoreProductGym> list = storeProductGymService.getListByExtId(extId);
+        return R.data(list);
+    }
+}

+ 167 - 0
alien-store/src/main/java/shop/alien/store/controller/StoreProductItemController.java

@@ -0,0 +1,167 @@
+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.StoreProductItem;
+import shop.alien.entity.store.dto.StoreProductItemDto;
+import shop.alien.entity.store.vo.StoreProductItemGymVo;
+import shop.alien.entity.store.vo.StoreProductItemDelicaciesVo;
+import shop.alien.store.service.StoreProductItemService;
+
+import java.util.List;
+
+/**
+ * 商品表 前端控制器
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"商品管理"})
+@CrossOrigin
+@RestController
+@RequestMapping("/storeProductItem")
+@RequiredArgsConstructor
+public class StoreProductItemController {
+
+    private final StoreProductItemService storeProductItemService;
+
+    @ApiOperation("新增商品")
+    @ApiOperationSupport(order = 1)
+    @PostMapping("/add")
+    public R<StoreProductItem> addStoreProductItem(@RequestBody StoreProductItemDto storeProductItemDto) {
+        log.info("StoreProductItemController.addStoreProductItem?storeProductItem={}", storeProductItemDto);
+        return storeProductItemService.addStoreProductItemDto(storeProductItemDto);
+    }
+
+    @ApiOperation("编辑商品")
+    @ApiOperationSupport(order = 2)
+    @PostMapping("/edit")
+    public R<StoreProductItem> editStoreProductItem(@RequestBody StoreProductItemDto storeProductItemDto) {
+        log.info("StoreProductItemController.editStoreProductItem?storeProductItem={}", storeProductItemDto);
+        return storeProductItemService.editStoreProductItem(storeProductItemDto);
+    }
+
+    @ApiOperation("删除商品")
+    @ApiOperationSupport(order = 3)
+    @DeleteMapping("/delete")
+    public R<Boolean> deleteStoreProductItem(@RequestParam(value = "id") Integer id) {
+        log.info("StoreProductItemController.deleteStoreProductItem?id={}", id);
+        return storeProductItemService.deleteStoreProductItem(id);
+    }
+
+    @ApiOperation("根据ID查询商品")
+    @ApiOperationSupport(order = 4)
+    @GetMapping("/getById")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "modelType", value = "1酒吧2美食3运动", dataType = "int", paramType = "query"),
+    })
+    public R<List<StoreProductItemDto>> getStoreProductItemById(@RequestParam(value = "id") Integer id,@RequestParam(value = "modelType")Integer modelType) {
+        log.info("StoreProductItemController.getStoreProductItemById?id={}", id);
+        return storeProductItemService.getStoreProductItemById(id, modelType);
+    }
+
+    @ApiOperation("按商品类型查询列表")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "prodType", value = "商品类型:1酒吧-酒水 2酒吧-餐食 3美食-餐食 4运动健身-单次 5运动健身-多次", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/listByProdType")
+    public R<List<StoreProductItem>> listByProdType(@RequestParam("prodType") Integer prodType) {
+        log.info("StoreProductItemController.listByProdType?prodType={}", prodType);
+        return storeProductItemService.listByProdType(prodType);
+    }
+
+    @ApiOperation("分页查询(按门店/名称/类型/状态/预约筛选)")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "pageNum", value = "页码(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "pageSize", value = "页容(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "prodName", value = "商品名称(模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "prodType", value = "商品类型", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "status", value = "状态:0-待审核 1-审核通过 2-审核拒绝", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "needReserve", value = "是否需要预约:0-否 1-是", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/pageList")
+    public R<IPage<StoreProductItem>> pageList(@RequestParam(defaultValue = "1") int pageNum,
+                                               @RequestParam(defaultValue = "10") int pageSize,
+                                               @RequestParam(required = false) Integer storeId,
+                                               @RequestParam(required = false) String prodName,
+                                               @RequestParam(required = false) Integer prodType,
+                                               @RequestParam(required = false) Integer status,
+                                               @RequestParam(required = false) Integer needReserve) {
+        IPage<StoreProductItem> pageResult = storeProductItemService.pageStoreProductItems(
+                pageNum > 0 ? pageNum : 1,
+                pageSize > 0 ? pageSize : 10,
+                storeId, prodName, prodType, status, needReserve);
+        return R.data(pageResult);
+    }
+
+    @ApiOperation("用户端 运动健身列表查询")
+    @ApiOperationSupport(order = 8)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "pageNum", value = "页码(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "pageSize", value = "页容(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "prodName", value = "商品名称(模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "prodType", value = "商品类型(1酒吧-酒水 2酒吧-餐食 3美食-餐食 4运动健身-单次 5运动健身-多次)", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/getPageWithGym")
+    public R<IPage<StoreProductItemGymVo>> getPageWithGym(
+            @RequestParam int pageNum,
+            @RequestParam int pageSize,
+            @RequestParam(required = false) Integer storeId,
+            @RequestParam(required = false) String prodName,
+            @RequestParam(required = false) Integer prodType) {
+        log.info("StoreProductItemController.getPageWithGym?pageNum={}, pageSize={}, storeId={}, prodName={}, prodType={}",
+                pageNum, pageSize, storeId, prodName, prodType);
+        IPage<StoreProductItemGymVo> pageResult = storeProductItemService.getPageWithGym(pageNum, pageSize, storeId, prodName, prodType);
+        return R.data(pageResult);
+    }
+
+    @ApiOperation("用户端 运动健身详情查询")
+    @ApiOperationSupport(order = 9)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "商品表主键ID", dataType = "Long", paramType = "query", required = true)
+    })
+    @GetMapping("/getDetailWithGym")
+    public R<StoreProductItemGymVo> getDetailWithGym(@RequestParam("id") Long id) {
+        log.info("StoreProductItemController.getDetailWithGym?id={}", id);
+        return storeProductItemService.getDetailWithGym(id);
+    }
+
+    @ApiOperation("用户端查询美食商品列表(包含菜品和套餐)")
+    @ApiOperationSupport(order = 10)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "pageNum", value = "页码", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "pageSize", value = "页容", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "name", value = "名称(模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "category", value = "类别", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "extGroup", value = "菜品分组", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "status", value = "状态(0:禁用,1:启用)", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "packageType", value = "套餐类型(预留,用于根据类型查询套餐)", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/getFoodAndPackagePage")
+    public R<IPage<StoreProductItemDelicaciesVo>> getFoodAndPackagePage(
+            @RequestParam(defaultValue = "1") int pageNum,
+            @RequestParam(defaultValue = "10") int pageSize,
+            @RequestParam(required = false) Integer storeId,
+            @RequestParam(required = false) String name,
+            @RequestParam(required = false) String category,
+            @RequestParam(required = false) String extGroup,
+            @RequestParam(required = false) Integer status,
+            @RequestParam(required = false) Integer packageType) {
+        log.info("StoreProductItemController.getFoodAndPackagePage?pageNum={}, pageSize={}, storeId={}, name={}, category={}, extGroup={}, status={}, packageType={}",
+                pageNum, pageSize, storeId, name, category, extGroup, status, packageType);
+        IPage<StoreProductItemDelicaciesVo> page = storeProductItemService.getPageWithDelicacies(
+                pageNum, pageSize, storeId, name, category, extGroup, status, packageType);
+        return R.data(page);
+    }
+}
+

+ 287 - 22
alien-store/src/main/java/shop/alien/store/controller/StoreStaffConfigController.java

@@ -1,15 +1,26 @@
 package shop.alien.store.controller;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 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.StoreDictionary;
 import shop.alien.entity.store.StoreStaffConfig;
+import shop.alien.entity.store.dto.StoreStaffConfigListQueryDto;
+import shop.alien.entity.store.vo.StoreStaffDetailVo;
+import shop.alien.entity.store.vo.StoreStaffFitnessDetailVo;
+import shop.alien.entity.store.vo.StoreStaffPositionCountVo;
+import shop.alien.mapper.StoreDictionaryMapper;
 import shop.alien.store.service.StoreStaffConfigService;
 
 import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
 /**
  * @Author: fcw
@@ -26,20 +37,33 @@ public class StoreStaffConfigController {
 
     private final StoreStaffConfigService storeStaffConfigService;
 
-    @ApiOperation("员工列表")
+    private final StoreDictionaryMapper storeDictionaryMapper;
+
+    @ApiOperation("员工列表(商家端)")
     @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "店铺ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "page", value = "分页页数", dataType = "Integer", paramType = "query", required = false),
+            @ApiImplicitParam(name = "size", value = "分页条数", dataType = "Integer", paramType = "query", required = false),
+            @ApiImplicitParam(name = "status", value = "员工状态(0-待审核 1-审核通过 2-审核拒绝)", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "businessSection", value = "经营板块id(词典表 键为 business_section)", dataType = "Integer", paramType = "query", required = false),
+            @ApiImplicitParam(name = "onlineStatus", value = "上线状态(0-上线 1-下线)", dataType = "Integer", paramType = "query", required = false),
+            @ApiImplicitParam(name = "staffPosition", value = "职位", dataType = "String", paramType = "query", required = false)
+    })
     @GetMapping("/getStaffConfigList")
-    public R<IPage<StoreStaffConfig>> getStaffConfigList(@RequestParam(value = "page", defaultValue = "1") int page,
-                                                         @RequestParam(value = "size", defaultValue = "10") int size,
-                                                         @RequestParam(value = "status", required = false) String status) {
-        log.info("StoreStaffConfigController.getStaffConfigList?status={}", status);
-        return R.data(storeStaffConfigService.getStaffConfigList(page, size, status));
+    public R<IPage<StoreStaffConfig>> getStaffConfigList(StoreStaffConfigListQueryDto query) {
+        Integer storeId = query == null ? null : query.getStoreId();
+        log.info("StoreStaffConfigController.getStaffConfigList?storeId={},query={}", storeId, query);
+        if (storeId == null) {
+            return R.fail("storeId未传入,请检查参数");
+        }
+        return R.data(storeStaffConfigService.getStaffConfigList(query));
     }
 
-    @ApiOperation("新增修改员工")
+    @ApiOperation("新增修改员工(商家端)")
     @ApiOperationSupport(order = 2)
     @PostMapping("/addOrUpdateStaffConfig")
-    public R<String> addOrUpdateStaffConfig(@RequestBody StoreStaffConfig storeStaffConfig) throws IOException, InterruptedException {
+    public R<String> addOrUpdateStaffConfig(@RequestBody StoreStaffConfig storeStaffConfig) {
         log.info("StoreStaffConfigController.addOrUpdateStaffConfig?multipartRequest={}, storeStaffConfig={}", storeStaffConfig);
         int num = storeStaffConfigService.addOrUpdateStaffConfig(storeStaffConfig);
         if (0 == num) {
@@ -60,7 +84,7 @@ public class StoreStaffConfigController {
         return R.data(storeStaffConfigService.audit(id, status, rejectionReason));
     }
 
-    @ApiOperation("员工详情")
+    @ApiOperation("员工详情(商家端)")
     @ApiOperationSupport(order = 2)
     @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "主键id", dataType = "Integer", paramType = "query", required = true)})
     @GetMapping("/getStaffConfigDeatail")
@@ -72,13 +96,13 @@ public class StoreStaffConfigController {
     @ApiOperation("员工导出")
     @ApiOperationSupport(order = 11)
     @GetMapping("/staffConfigExport")
-    public R staffConfigExport(@RequestParam(required = false) String status) throws IOException {
+    public R<String> staffConfigExport(@RequestParam(required = false) String status) throws IOException {
         log.info("PlatformStoreCouponController.staffConfigExport");
         String s = storeStaffConfigService.staffConfigExport(status);
         return R.data(s);
     }
 
-    @ApiOperation("删除员工")
+    @ApiOperation("删除员工(商家端)")
     @ApiOperationSupport(order = 4)
     @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "主键id", dataType = "Integer", paramType = "query", required = true)})
     @GetMapping("/deleteStaffConfig")
@@ -87,7 +111,7 @@ public class StoreStaffConfigController {
         return R.data(storeStaffConfigService.deleteStaffConfig(id));
     }
 
-    @ApiOperation("置顶员工")
+    @ApiOperation("置顶员工(商家端)")
     @ApiOperationSupport(order = 5)
     @ApiImplicitParams({
             @ApiImplicitParam(name = "id", value = "主键id", dataType = "Integer", paramType = "query", required = true),
@@ -98,13 +122,27 @@ public class StoreStaffConfigController {
         log.info("StoreStaffConfigController.setTopStatus?id={},topStatus={}", id, topStatus);
         return R.data(storeStaffConfigService.setTopStatus(id, topStatus));
     }
+
+    @ApiOperation("设置员工上线/下线状态(商家端)")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键id", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "onlineStatus", value = "上线状态 0-上线, 1-下线", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/setOnlineStatus")
+    public R<Integer> setOnlineStatus(Integer id, Integer onlineStatus) {
+        log.info("StoreStaffConfigController.setOnlineStatus?id={},onlineStatus={}", id, onlineStatus);
+        return R.data(storeStaffConfigService.setOnlineStatus(id, onlineStatus));
+    }
+
+
     /**
      * 员工列表查询接口(用户端)
      *
-     * @param page    分页页数
-     * @param size    分页条数
-     * @param storeId 店铺ID
-     * @param status  员工状态
+     * @param page    分页页数,默认1
+     * @param size    分页条数,默认10
+     * @param storeId 店铺ID,必填
+     * @param status  员工状态,可选(0-待审核 1-审核通过 2-审核拒绝)
      * @return 员工列表
      */
     @ApiOperation("员工列表查询(用户端)")
@@ -113,16 +151,36 @@ public class StoreStaffConfigController {
             @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)
+            @ApiImplicitParam(name = "status", value = "员工状态(0-待审核 1-审核通过 2-审核拒绝)", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "staffPosition", value = "员工职位", 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);
+            @RequestParam(value = "size", defaultValue = "10") Integer size,
+            @RequestParam(value = "storeId") Integer storeId,
+            @RequestParam(value = "status", required = false) String status,
+            @RequestParam(value = "staffPosition", required = false) String staffPosition) {
+        log.info("查询员工列表,参数:page={}, size={}, storeId={}, status={}, staffPosition={}", page, size, storeId, status, staffPosition);
+
+        // 参数校验
+        if (storeId == null || storeId <= 0) {
+            log.warn("查询员工列表失败,店铺ID无效:storeId={}", storeId);
+            return R.fail("店铺ID不能为空且必须大于0");
+        }
+        if (page == null || page < 1) {
+            page = 1;
+        }
+        if (size == null || size < 1) {
+            size = 10;
+        }
+        // 限制分页大小,防止查询过多数据
+        if (size > 100) {
+            size = 100;
+        }
+
+        IPage<StoreStaffConfig> result = storeStaffConfigService.queryStaffList(page, size, storeId, status, staffPosition);
+        log.info("查询员工列表成功,共{}条记录", result.getTotal());
         return R.data(result);
     }
 
@@ -146,4 +204,211 @@ public class StoreStaffConfigController {
         }
         return R.data(result);
     }
+
+    /**
+     * 查询擅长类型和标签(用于人员配置)
+     * 返回格式:{types: [{id, dictId, dictDetail, typeDetail, tags: [...]}]}
+     *
+     * @param businessSection 经营板块id(词典表 键为 business_section),可选
+     * @return 擅长类型和标签的树形结构
+     */
+    @ApiOperation("查询擅长类型和标签(商家端)")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "businessSection", value = "经营板块id(词典表 键为 business_section)", dataType = "Integer", paramType = "query", required = false)
+    })
+    @GetMapping("/getProficientTypesAndTags")
+    public R<Map<String, Object>> getProficientTypesAndTags(
+            @RequestParam(value = "businessSection", required = false) Integer businessSection) {
+        log.info("StoreStaffConfigController.getProficientTypesAndTags?businessSection={}", businessSection);
+
+        // 查询经营板块(business_section)
+        LambdaQueryWrapper<StoreDictionary> typeWrapper = new LambdaQueryWrapper<>();
+        typeWrapper.eq(StoreDictionary::getTypeName, "business_section");
+        typeWrapper.eq(StoreDictionary::getDeleteFlag, 0);
+        typeWrapper.isNull(StoreDictionary::getParentId);
+
+        // 如果指定了经营板块,根据 businessSection(dict_id) 过滤
+        if (businessSection != null) {
+            typeWrapper.eq(StoreDictionary::getDictId, String.valueOf(businessSection));
+        }
+
+        List<StoreDictionary> types = storeDictionaryMapper.selectList(typeWrapper);
+
+        // 如果指定了经营板块,只查询对应板块的标签
+        List<Integer> typeIds = types.stream().map(StoreDictionary::getId).collect(Collectors.toList());
+
+        // 查询擅长标签(proficient_tag),其 parent_id 指向 business_section 的主键id
+        LambdaQueryWrapper<StoreDictionary> tagWrapper = new LambdaQueryWrapper<>();
+        tagWrapper.eq(StoreDictionary::getTypeName, "proficient_tag");
+        tagWrapper.eq(StoreDictionary::getDeleteFlag, 0);
+        if (!typeIds.isEmpty()) {
+            tagWrapper.in(StoreDictionary::getParentId, typeIds);
+        }
+        List<StoreDictionary> allTags = storeDictionaryMapper.selectList(tagWrapper);
+
+        // 构建结果:按 parent_id 分组标签
+        Map<Integer, List<StoreDictionary>> tagsByParent = allTags.stream()
+                .filter(tag -> tag.getParentId() != null)
+                .collect(Collectors.groupingBy(StoreDictionary::getParentId));
+
+        // 构建返回结果
+        List<Map<String, Object>> resultList = types.stream().map(type -> {
+            Map<String, Object> typeMap = new HashMap<>();
+            typeMap.put("id", type.getId());
+            typeMap.put("dictId", type.getDictId());
+            typeMap.put("dictDetail", type.getDictDetail());
+            typeMap.put("typeDetail", type.getTypeDetail());
+
+            // 获取该类型下的标签
+            List<StoreDictionary> tags = tagsByParent.getOrDefault(type.getId(), new java.util.ArrayList<>());
+            List<Map<String, Object>> tagList = tags.stream().map(tag -> {
+                Map<String, Object> tagMap = new HashMap<>();
+                tagMap.put("id", tag.getId());
+                tagMap.put("dictId", tag.getDictId());
+                tagMap.put("dictDetail", tag.getDictDetail());
+                return tagMap;
+            }).collect(Collectors.toList());
+            typeMap.put("tags", tagList);
+
+            return typeMap;
+        }).collect(Collectors.toList());
+
+        Map<String, Object> result = new HashMap<>();
+        result.put("types", resultList);
+
+        return R.data(result);
+    }
+
+
+    @ApiOperation("获取美食员工列表")
+    @ApiOperationSupport(order = 7)
+    @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 = "staffPosition", value = "员工职位", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "status", value = "员工状态", dataType = "String", paramType = "query", required = false)
+    })
+    @GetMapping("/getFoodStaffConfigList")
+    public R<IPage<StoreStaffConfig>> getFoodStaffConfigList(@RequestParam(value = "page", defaultValue = "1") int page,
+                                                         @RequestParam(value = "size", defaultValue = "10") int size,
+                                                         @RequestParam(value = "storeId", required = true) Integer storeId,
+                                                         @RequestParam(value = "staffPosition", required = false) String staffPosition,
+                                                         @RequestParam(value = "status", required = false) String status) {
+        log.info("StoreStaffConfigController.getFoodStaffConfigList?page={}&size={}&storeId={}&staffPosition={}&status={}", page, size, storeId, staffPosition, status);
+        return R.data(storeStaffConfigService.getFoodStaffConfigList(page, size, storeId, staffPosition, status));
+    }
+
+    /**
+     * 根据ID查询员工详情(包含员工信息和课程列表)
+     *
+     * @param id 员工主键ID
+     * @return 员工详情(包含员工信息和课程列表)
+     */
+    @ApiOperation("根据ID查询员工详情(包含员工信息和课程列表)用户端")
+    @ApiOperationSupport(order = 8)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "员工主键ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getStaffDetailWithCourse")
+    public R<StoreStaffDetailVo> getStaffDetailWithCourse(
+            @RequestParam(value = "id") Integer id) {
+        log.info("查询员工详情(包含课程信息),id={}", id);
+
+        try {
+            // 参数校验
+            if (id == null || id <= 0) {
+                log.warn("查询员工详情失败,员工ID无效:{}", id);
+                return R.fail("员工ID不能为空且必须大于0");
+            }
+
+            StoreStaffDetailVo result = storeStaffConfigService.getStaffDetailWithCourse(id);
+
+            if (result == null) {
+                log.warn("查询员工详情失败,员工不存在:id={}", id);
+                return R.fail("员工不存在");
+            }
+
+            log.info("查询员工详情成功,id={},课程数量:{}", id,
+                    result.getCourseList() != null ? result.getCourseList().size() : 0);
+            return R.data(result);
+        } catch (Exception e) {
+            log.error("查询员工详情异常,id={},异常信息:{}", id, e.getMessage(), e);
+            return R.fail("查询失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 查询健身教练详情(包含员工信息、基本信息、认证/荣誉列表和从业经历)
+     *
+     * @param id 员工主键ID
+     * @return 健身教练详情(包含员工信息、基本信息、认证/荣誉列表和从业经历)
+     */
+    @ApiOperation("查询健身教练详情(包含员工信息、基本信息、认证/荣誉列表和从业经历)用户端")
+    @ApiOperationSupport(order = 9)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "员工主键ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getFitnessCoachDetail")
+    public R<StoreStaffFitnessDetailVo> getFitnessCoachDetail(
+            @RequestParam(value = "id") Integer id) {
+        log.info("查询健身教练详情,id={}", id);
+
+        try {
+            // 参数校验
+            if (id == null || id <= 0) {
+                log.warn("查询健身教练详情失败,员工ID无效:{}", id);
+                return R.fail("员工ID不能为空且必须大于0");
+            }
+            
+            StoreStaffFitnessDetailVo result = storeStaffConfigService.getFitnessCoachDetail(id);
+            
+            if (result == null) {
+                log.warn("查询健身教练详情失败,员工不存在:id={}", id);
+                return R.fail("员工不存在");
+            }
+
+            return R.data(result);
+        } catch (Exception e) {
+            log.error("查询健身教练详情异常,id={},异常信息:{}", id, e.getMessage(), e);
+            return R.fail("查询失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 查询指定店铺的员工职位统计信息
+     *
+     * @param storeId 店铺ID,必填
+     * @return 员工职位统计列表,每个元素包含职位名称和对应员工数量
+     */
+    @ApiOperation("查询员工职位统计(用户端)")
+    @ApiOperationSupport(order = 10)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "店铺ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getStaffPositionCount")
+    public R<List<StoreStaffPositionCountVo>> getStaffPositionCount(
+            @RequestParam(value = "storeId") Integer storeId) {
+        log.info("查询员工职位统计,参数:storeId={}", storeId);
+
+        try {
+            // 参数校验
+            if (storeId == null || storeId <= 0) {
+                log.warn("查询员工职位统计失败,店铺ID无效:storeId={}", storeId);
+                return R.fail("店铺ID不能为空且必须大于0");
+            }
+
+            List<StoreStaffPositionCountVo> result = storeStaffConfigService.getStaffPositionCount(storeId);
+            log.info("查询员工职位统计成功,storeId={},职位数量:{}", storeId, result.size());
+            return R.data(result);
+        } catch (IllegalArgumentException e) {
+            log.warn("查询员工职位统计失败,参数错误:{}", e.getMessage());
+            return R.fail(e.getMessage());
+        } catch (Exception e) {
+            log.error("查询员工职位统计异常,storeId={},异常信息:{}", storeId, e.getMessage(), e);
+            return R.fail("查询失败:" + e.getMessage());
+        }
+    }
+
 }

+ 143 - 0
alien-store/src/main/java/shop/alien/store/controller/StoreStaffFitnessBaseController.java

@@ -0,0 +1,143 @@
+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.StoreStaffFitnessBase;
+import shop.alien.store.service.StoreStaffFitnessBaseService;
+
+import java.util.List;
+
+/**
+ * 运动健身员工基本信息表 Controller
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"运动健身员工基本信息管理"})
+@CrossOrigin
+@RestController
+@RequestMapping("/store/staff/fitness/base")
+@RequiredArgsConstructor
+public class StoreStaffFitnessBaseController {
+
+    private final StoreStaffFitnessBaseService storeStaffFitnessBaseService;
+
+    @ApiOperation("新增运动健身员工基本信息")
+    @ApiOperationSupport(order = 1)
+    @PostMapping
+    public R<String> saveBase(@RequestBody StoreStaffFitnessBase base) {
+        log.info("StoreStaffFitnessBaseController.saveBase?base={}", base);
+        // 参数校验
+        if (base.getStaffId() == null) {
+            return R.fail("员工ID不能为空");
+        }
+
+        R<StoreStaffFitnessBase> result = storeStaffFitnessBaseService.addStoreStaffFitnessBase(base);
+        if (result.getCode() == 200) {
+            return R.success("新增成功");
+        }
+        return R.fail(result.getMsg());
+    }
+
+    @ApiOperation("修改运动健身员工基本信息")
+    @ApiOperationSupport(order = 2)
+    @PutMapping
+    public R<String> updateBase(@RequestBody StoreStaffFitnessBase base) {
+        log.info("StoreStaffFitnessBaseController.updateBase?base={}", base);
+        if (base.getId() == null) {
+            return R.fail("ID不能为空");
+        }
+
+        R<StoreStaffFitnessBase> result = storeStaffFitnessBaseService.editStoreStaffFitnessBase(base);
+        if (result.getCode() == 200) {
+            return R.success("修改成功");
+        }
+        return R.fail(result.getMsg());
+    }
+
+    @ApiOperation("删除运动健身员工基本信息")
+    @ApiOperationSupport(order = 3)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "Integer", paramType = "path", required = true)
+    })
+    @DeleteMapping("/{id}")
+    public R<String> deleteBase(@PathVariable("id") Integer id) {
+        log.info("StoreStaffFitnessBaseController.deleteBase?id={}", id);
+        R<Boolean> result = storeStaffFitnessBaseService.deleteStoreStaffFitnessBase(id);
+        if (result.getCode() == 200) {
+            return R.success("删除成功");
+        }
+        return R.fail(result.getMsg());
+    }
+
+    @ApiOperation("根据ID查询运动健身员工基本信息详情")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "Integer", paramType = "path", required = true)
+    })
+    @GetMapping("/{id}")
+    public R<StoreStaffFitnessBase> getById(@PathVariable("id") Integer id) {
+        log.info("StoreStaffFitnessBaseController.getById?id={}", id);
+        R<StoreStaffFitnessBase> result = storeStaffFitnessBaseService.getStoreStaffFitnessBaseById(id);
+        if (result.getCode() == 200 && result.getData() != null) {
+            return result;
+        }
+        return R.fail("未找到该基本信息");
+    }
+
+    @ApiOperation("根据员工ID查询基本信息")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "staffId", value = "员工ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getByStaffId")
+    public R<StoreStaffFitnessBase> getByStaffId(@RequestParam("staffId") Integer staffId) {
+        log.info("StoreStaffFitnessBaseController.getByStaffId?staffId={}", staffId);
+        R<StoreStaffFitnessBase> result = storeStaffFitnessBaseService.getByStaffId(staffId);
+        if (result.getCode() == 200 && result.getData() != null) {
+            return result;
+        }
+        return R.fail("未找到该员工的基本信息");
+    }
+
+    @ApiOperation("分页查询运动健身员工基本信息列表")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "pageNum", value = "页码", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "pageSize", value = "页容", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "staffId", value = "员工ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "education", value = "学历", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/page")
+    public R<IPage<StoreStaffFitnessBase>> getPage(
+            @RequestParam(defaultValue = "1") int pageNum,
+            @RequestParam(defaultValue = "10") int pageSize,
+            @RequestParam(required = false) Integer staffId,
+            @RequestParam(required = false) String education) {
+        log.info("StoreStaffFitnessBaseController.getPage?pageNum={}, pageSize={}, staffId={}, education={}",
+                pageNum, pageSize, staffId, education);
+        IPage<StoreStaffFitnessBase> page = storeStaffFitnessBaseService.getPage(pageNum, pageSize, staffId, education);
+        return R.data(page);
+    }
+
+    @ApiOperation("批量删除运动健身员工基本信息")
+    @ApiOperationSupport(order = 7)
+    @PostMapping("/batchDelete")
+    public R<String> deleteBatch(@RequestBody List<Integer> ids) {
+        log.info("StoreStaffFitnessBaseController.deleteBatch?ids={}", ids);
+        if (ids == null || ids.isEmpty()) {
+            return R.fail("ID列表不能为空");
+        }
+        boolean result = storeStaffFitnessBaseService.deleteBatch(ids);
+        if (result) {
+            return R.success("批量删除成功");
+        }
+        return R.fail("批量删除失败");
+    }
+}
+

+ 162 - 0
alien-store/src/main/java/shop/alien/store/controller/StoreStaffFitnessCertificationController.java

@@ -0,0 +1,162 @@
+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.StoreStaffFitnessCertification;
+import shop.alien.store.service.StoreStaffFitnessCertificationService;
+
+import java.util.List;
+
+/**
+ * 运动健身员工认证/荣誉表 Controller
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"运动健身员工认证/荣誉管理"})
+@CrossOrigin
+@RestController
+@RequestMapping("/store/staff/fitness/certification")
+@RequiredArgsConstructor
+public class StoreStaffFitnessCertificationController {
+
+    private final StoreStaffFitnessCertificationService storeStaffFitnessCertificationService;
+
+    @ApiOperation("新增运动健身员工认证/荣誉")
+    @ApiOperationSupport(order = 1)
+    @PostMapping
+    public R<String> saveCertification(@RequestBody StoreStaffFitnessCertification certification) {
+        log.info("StoreStaffFitnessCertificationController.saveCertification?certification={}", certification);
+        // 参数校验
+        if (certification.getStaffId() == null) {
+            return R.fail("员工ID不能为空");
+        }
+        if (certification.getType() == null) {
+            return R.fail("类型不能为空(1-认证 2-荣誉)");
+        }
+        if (certification.getType() != 1 && certification.getType() != 2) {
+            return R.fail("类型值无效,只能为1(认证)或2(荣誉)");
+        }
+
+        R<StoreStaffFitnessCertification> result = storeStaffFitnessCertificationService.addStoreStaffFitnessCertification(certification);
+        if (result.getCode() == 200) {
+            return R.success("新增成功");
+        }
+        return R.fail(result.getMsg());
+    }
+
+    @ApiOperation("修改运动健身员工认证/荣誉")
+    @ApiOperationSupport(order = 2)
+    @PutMapping
+    public R<String> updateCertification(@RequestBody StoreStaffFitnessCertification certification) {
+        log.info("StoreStaffFitnessCertificationController.updateCertification?certification={}", certification);
+        if (certification.getId() == null) {
+            return R.fail("ID不能为空");
+        }
+
+        R<StoreStaffFitnessCertification> result = storeStaffFitnessCertificationService.editStoreStaffFitnessCertification(certification);
+        if (result.getCode() == 200) {
+            return R.success("修改成功");
+        }
+        return R.fail(result.getMsg());
+    }
+
+    @ApiOperation("删除运动健身员工认证/荣誉")
+    @ApiOperationSupport(order = 3)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "Integer", paramType = "path", required = true)
+    })
+    @DeleteMapping("/{id}")
+    public R<String> deleteCertification(@PathVariable("id") Integer id) {
+        log.info("StoreStaffFitnessCertificationController.deleteCertification?id={}", id);
+        R<Boolean> result = storeStaffFitnessCertificationService.deleteStoreStaffFitnessCertification(id);
+        if (result.getCode() == 200) {
+            return R.success("删除成功");
+        }
+        return R.fail(result.getMsg());
+    }
+
+    @ApiOperation("根据ID查询运动健身员工认证/荣誉详情")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "Integer", paramType = "path", required = true)
+    })
+    @GetMapping("/{id}")
+    public R<StoreStaffFitnessCertification> getById(@PathVariable("id") Integer id) {
+        log.info("StoreStaffFitnessCertificationController.getById?id={}", id);
+        R<StoreStaffFitnessCertification> result = storeStaffFitnessCertificationService.getStoreStaffFitnessCertificationById(id);
+        if (result.getCode() == 200 && result.getData() != null) {
+            return result;
+        }
+        return R.fail("未找到该认证/荣誉信息");
+    }
+
+    @ApiOperation("分页查询运动健身员工认证/荣誉列表")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "pageNum", value = "页码", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "pageSize", value = "页容", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "staffId", value = "员工ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "type", value = "类型(1-认证 2-荣誉)", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "name", value = "名称(模糊查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/page")
+    public R<IPage<StoreStaffFitnessCertification>> getPage(
+            @RequestParam(defaultValue = "1") int pageNum,
+            @RequestParam(defaultValue = "10") int pageSize,
+            @RequestParam(required = false) Integer staffId,
+            @RequestParam(required = false) Integer type,
+            @RequestParam(required = false) String name) {
+        log.info("StoreStaffFitnessCertificationController.getPage?pageNum={}, pageSize={}, staffId={}, type={}, name={}",
+                pageNum, pageSize, staffId, type, name);
+        IPage<StoreStaffFitnessCertification> page = storeStaffFitnessCertificationService.getPage(pageNum, pageSize, staffId, type, name);
+        return R.data(page);
+    }
+
+    @ApiOperation("批量删除运动健身员工认证/荣誉")
+    @ApiOperationSupport(order = 6)
+    @PostMapping("/batchDelete")
+    public R<String> deleteBatch(@RequestBody List<Integer> ids) {
+        log.info("StoreStaffFitnessCertificationController.deleteBatch?ids={}", ids);
+        if (ids == null || ids.isEmpty()) {
+            return R.fail("ID列表不能为空");
+        }
+        boolean result = storeStaffFitnessCertificationService.deleteBatch(ids);
+        if (result) {
+            return R.success("批量删除成功");
+        }
+        return R.fail("批量删除失败");
+    }
+
+    @ApiOperation("根据员工ID查询认证/荣誉列表")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "staffId", value = "员工ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/listByStaffId")
+    public R<List<StoreStaffFitnessCertification>> getListByStaffId(@RequestParam("staffId") Integer staffId) {
+        log.info("StoreStaffFitnessCertificationController.getListByStaffId?staffId={}", staffId);
+        List<StoreStaffFitnessCertification> list = storeStaffFitnessCertificationService.getListByStaffId(staffId);
+        return R.data(list);
+    }
+
+    @ApiOperation("根据员工ID和类型查询认证/荣誉列表")
+    @ApiOperationSupport(order = 8)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "staffId", value = "员工ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "type", value = "类型(1-认证 2-荣誉)", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/listByStaffIdAndType")
+    public R<List<StoreStaffFitnessCertification>> getListByStaffIdAndType(
+            @RequestParam("staffId") Integer staffId,
+            @RequestParam(required = false) Integer type) {
+        log.info("StoreStaffFitnessCertificationController.getListByStaffIdAndType?staffId={}, type={}", staffId, type);
+        List<StoreStaffFitnessCertification> list = storeStaffFitnessCertificationService.getListByStaffIdAndType(staffId, type);
+        return R.data(list);
+    }
+}

+ 145 - 0
alien-store/src/main/java/shop/alien/store/controller/StoreStaffFitnessCourseController.java

@@ -0,0 +1,145 @@
+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.StoreStaffFitnessCourse;
+import shop.alien.store.service.StoreStaffFitnessCourseService;
+
+import java.util.List;
+
+/**
+ * 运动健身员工课程信息表 Controller
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"运动健身员工课程管理"})
+@CrossOrigin
+@RestController
+@RequestMapping("/store/staff/fitness/course")
+@RequiredArgsConstructor
+public class StoreStaffFitnessCourseController {
+
+    private final StoreStaffFitnessCourseService storeStaffFitnessCourseService;
+
+    @ApiOperation("新增运动健身员工课程")
+    @ApiOperationSupport(order = 1)
+    @PostMapping
+    public R<String> saveCourse(@RequestBody StoreStaffFitnessCourse course) {
+        log.info("StoreStaffFitnessCourseController.saveCourse?course={}", course);
+        // 参数校验
+        if (course.getStaffId() == null) {
+            return R.fail("员工ID不能为空");
+        }
+        if (course.getCourseName() == null || course.getCourseName().trim().isEmpty()) {
+            return R.fail("项目名称不能为空");
+        }
+
+        R<StoreStaffFitnessCourse> result = storeStaffFitnessCourseService.addStoreStaffFitnessCourse(course);
+        if (result.getCode() == 200) {
+            return R.success("新增成功");
+        }
+        return R.fail(result.getMsg());
+    }
+
+    @ApiOperation("修改运动健身员工课程")
+    @ApiOperationSupport(order = 2)
+    @PutMapping
+    public R<String> updateCourse(@RequestBody StoreStaffFitnessCourse course) {
+        log.info("StoreStaffFitnessCourseController.updateCourse?course={}", course);
+        if (course.getId() == null) {
+            return R.fail("ID不能为空");
+        }
+
+        R<StoreStaffFitnessCourse> result = storeStaffFitnessCourseService.editStoreStaffFitnessCourse(course);
+        if (result.getCode() == 200) {
+            return R.success("修改成功");
+        }
+        return R.fail(result.getMsg());
+    }
+
+    @ApiOperation("删除运动健身员工课程")
+    @ApiOperationSupport(order = 3)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "Integer", paramType = "path", required = true)
+    })
+    @DeleteMapping("/{id}")
+    public R<String> deleteCourse(@PathVariable("id") Integer id) {
+        log.info("StoreStaffFitnessCourseController.deleteCourse?id={}", id);
+        R<Boolean> result = storeStaffFitnessCourseService.deleteStoreStaffFitnessCourse(id);
+        if (result.getCode() == 200) {
+            return R.success("删除成功");
+        }
+        return R.fail(result.getMsg());
+    }
+
+    @ApiOperation("根据ID查询运动健身员工课程详情")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "Integer", paramType = "path", required = true)
+    })
+    @GetMapping("/{id}")
+    public R<StoreStaffFitnessCourse> getById(@PathVariable("id") Integer id) {
+        log.info("StoreStaffFitnessCourseController.getById?id={}", id);
+        R<StoreStaffFitnessCourse> result = storeStaffFitnessCourseService.getStoreStaffFitnessCourseById(id);
+        if (result.getCode() == 200 && result.getData() != null) {
+            return result;
+        }
+        return R.fail("未找到该课程信息");
+    }
+
+    @ApiOperation("分页查询运动健身员工课程列表")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "pageNum", value = "页码", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "pageSize", value = "页容", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "staffId", value = "员工ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "courseType", value = "课程类型", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "courseName", value = "项目名称(模糊查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/page")
+    public R<IPage<StoreStaffFitnessCourse>> getPage(
+            @RequestParam(defaultValue = "1") int pageNum,
+            @RequestParam(defaultValue = "10") int pageSize,
+            @RequestParam(required = false) Integer staffId,
+            @RequestParam(required = false) String courseType,
+            @RequestParam(required = false) String courseName) {
+        log.info("StoreStaffFitnessCourseController.getPage?pageNum={}, pageSize={}, staffId={}, courseType={}, courseName={}",
+                pageNum, pageSize, staffId, courseType, courseName);
+        IPage<StoreStaffFitnessCourse> page = storeStaffFitnessCourseService.getPage(pageNum, pageSize, staffId, courseType, courseName);
+        return R.data(page);
+    }
+
+    @ApiOperation("批量删除运动健身员工课程")
+    @ApiOperationSupport(order = 6)
+    @PostMapping("/batchDelete")
+    public R<String> deleteBatch(@RequestBody List<Integer> ids) {
+        log.info("StoreStaffFitnessCourseController.deleteBatch?ids={}", ids);
+        if (ids == null || ids.isEmpty()) {
+            return R.fail("ID列表不能为空");
+        }
+        boolean result = storeStaffFitnessCourseService.deleteBatch(ids);
+        if (result) {
+            return R.success("批量删除成功");
+        }
+        return R.fail("批量删除失败");
+    }
+
+    @ApiOperation("根据员工ID查询课程列表")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "staffId", value = "员工ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/listByStaffId")
+    public R<List<StoreStaffFitnessCourse>> getListByStaffId(@RequestParam("staffId") Integer staffId) {
+        log.info("StoreStaffFitnessCourseController.getListByStaffId?staffId={}", staffId);
+        List<StoreStaffFitnessCourse> list = storeStaffFitnessCourseService.getListByStaffId(staffId);
+        return R.data(list);
+    }
+}
+

+ 142 - 0
alien-store/src/main/java/shop/alien/store/controller/StoreStaffFitnessExperienceController.java

@@ -0,0 +1,142 @@
+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.StoreStaffFitnessExperience;
+import shop.alien.store.service.StoreStaffFitnessExperienceService;
+
+import java.util.List;
+
+/**
+ * 运动健身员工从业经历表 Controller
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"运动健身员工从业经历管理"})
+@CrossOrigin
+@RestController
+@RequestMapping("/store/staff/fitness/experience")
+@RequiredArgsConstructor
+public class StoreStaffFitnessExperienceController {
+
+    private final StoreStaffFitnessExperienceService storeStaffFitnessExperienceService;
+
+    @ApiOperation("新增运动健身员工从业经历")
+    @ApiOperationSupport(order = 1)
+    @PostMapping
+    public R<String> saveExperience(@RequestBody StoreStaffFitnessExperience experience) {
+        log.info("StoreStaffFitnessExperienceController.saveExperience?experience={}", experience);
+        // 参数校验
+        if (experience.getStaffId() == null) {
+            return R.fail("员工ID不能为空");
+        }
+
+        R<StoreStaffFitnessExperience> result = storeStaffFitnessExperienceService.addStoreStaffFitnessExperience(experience);
+        if (result.getCode() == 200) {
+            return R.success("新增成功");
+        }
+        return R.fail(result.getMsg());
+    }
+
+    @ApiOperation("修改运动健身员工从业经历")
+    @ApiOperationSupport(order = 2)
+    @PutMapping
+    public R<String> updateExperience(@RequestBody StoreStaffFitnessExperience experience) {
+        log.info("StoreStaffFitnessExperienceController.updateExperience?experience={}", experience);
+        if (experience.getId() == null) {
+            return R.fail("ID不能为空");
+        }
+
+        R<StoreStaffFitnessExperience> result = storeStaffFitnessExperienceService.editStoreStaffFitnessExperience(experience);
+        if (result.getCode() == 200) {
+            return R.success("修改成功");
+        }
+        return R.fail(result.getMsg());
+    }
+
+    @ApiOperation("删除运动健身员工从业经历")
+    @ApiOperationSupport(order = 3)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "Integer", paramType = "path", required = true)
+    })
+    @DeleteMapping("/{id}")
+    public R<String> deleteExperience(@PathVariable("id") Integer id) {
+        log.info("StoreStaffFitnessExperienceController.deleteExperience?id={}", id);
+        R<Boolean> result = storeStaffFitnessExperienceService.deleteStoreStaffFitnessExperience(id);
+        if (result.getCode() == 200) {
+            return R.success("删除成功");
+        }
+        return R.fail(result.getMsg());
+    }
+
+    @ApiOperation("根据ID查询运动健身员工从业经历详情")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "Integer", paramType = "path", required = true)
+    })
+    @GetMapping("/{id}")
+    public R<StoreStaffFitnessExperience> getById(@PathVariable("id") Integer id) {
+        log.info("StoreStaffFitnessExperienceController.getById?id={}", id);
+        R<StoreStaffFitnessExperience> result = storeStaffFitnessExperienceService.getStoreStaffFitnessExperienceById(id);
+        if (result.getCode() == 200 && result.getData() != null) {
+            return result;
+        }
+        return R.fail("未找到该从业经历信息");
+    }
+
+    @ApiOperation("分页查询运动健身员工从业经历列表")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "pageNum", value = "页码", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "pageSize", value = "页容", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "staffId", value = "员工ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "workUnit", value = "工作单位(模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "workPosition", value = "职位(模糊查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/page")
+    public R<IPage<StoreStaffFitnessExperience>> getPage(
+            @RequestParam(defaultValue = "1") int pageNum,
+            @RequestParam(defaultValue = "10") int pageSize,
+            @RequestParam(required = false) Integer staffId,
+            @RequestParam(required = false) String workUnit,
+            @RequestParam(required = false) String workPosition) {
+        log.info("StoreStaffFitnessExperienceController.getPage?pageNum={}, pageSize={}, staffId={}, workUnit={}, workPosition={}",
+                pageNum, pageSize, staffId, workUnit, workPosition);
+        IPage<StoreStaffFitnessExperience> page = storeStaffFitnessExperienceService.getPage(pageNum, pageSize, staffId, workUnit, workPosition);
+        return R.data(page);
+    }
+
+    @ApiOperation("批量删除运动健身员工从业经历")
+    @ApiOperationSupport(order = 6)
+    @PostMapping("/batchDelete")
+    public R<String> deleteBatch(@RequestBody List<Integer> ids) {
+        log.info("StoreStaffFitnessExperienceController.deleteBatch?ids={}", ids);
+        if (ids == null || ids.isEmpty()) {
+            return R.fail("ID列表不能为空");
+        }
+        boolean result = storeStaffFitnessExperienceService.deleteBatch(ids);
+        if (result) {
+            return R.success("批量删除成功");
+        }
+        return R.fail("批量删除失败");
+    }
+
+    @ApiOperation("根据员工ID查询从业经历列表")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "staffId", value = "员工ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/listByStaffId")
+    public R<List<StoreStaffFitnessExperience>> getListByStaffId(@RequestParam("staffId") Integer staffId) {
+        log.info("StoreStaffFitnessExperienceController.getListByStaffId?staffId={}", staffId);
+        List<StoreStaffFitnessExperience> list = storeStaffFitnessExperienceService.getListByStaffId(staffId);
+        return R.data(list);
+    }
+}
+

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

@@ -0,0 +1,23 @@
+package shop.alien.store.service;
+
+import shop.alien.entity.store.BarPerformance;
+
+/**
+ * 酒吧演出审核服务接口
+ * 用于异步执行AI内容审核
+ *
+ * @author assistant
+ * @since 2025-12-19
+ */
+public interface BarPerformanceAuditService {
+
+    /**
+     * 异步执行AI内容审核
+     * 审核演出名称、演出详情、演出须知等文本内容以及演出海报、详情图片
+     *
+     * @param performanceId 演出ID
+     * @param barPerformance 演出信息
+     */
+    void asyncAuditBarPerformance(Integer performanceId, BarPerformance barPerformance);
+}
+

+ 81 - 0
alien-store/src/main/java/shop/alien/store/service/BarPerformanceService.java

@@ -0,0 +1,81 @@
+package shop.alien.store.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import shop.alien.entity.store.BarPerformance;
+
+/**
+ * 酒吧演出服务接口
+ * 提供酒吧演出的CRUD、审核、上下线等核心业务功能
+ *
+ * @author assistant
+ * @since 2025-12-17
+ */
+public interface BarPerformanceService {
+
+    /**
+     * 获取酒吧演出列表(分页)
+     *
+     * @param page    分页页数
+     * @param size    分页条数
+     * @param status  演出状态
+     * @return 演出列表
+     */
+    IPage<BarPerformance> getBarPerformanceList(int page, int size, String status);
+
+    /**
+     * 新增或更新酒吧演出
+     *
+     * @param barPerformance 演出信息
+     * @return 操作结果
+     */
+    int addOrUpdateBarPerformance(BarPerformance barPerformance);
+
+    /**
+     * 获取酒吧演出详情
+     *
+     * @param id 演出ID
+     * @return 演出详情
+     */
+    BarPerformance getBarPerformanceDetail(Integer id);
+
+    /**
+     * 删除酒吧演出
+     *
+     * @param id 演出ID
+     * @return 操作结果
+     */
+    Integer deleteBarPerformance(Integer id);
+
+    /**
+     * 设置演出上线状态
+     *
+     * @param id          演出ID
+     * @param onlineStatus 上线状态(0-下线,1-上线)
+     * @return 操作结果
+     */
+    Integer setOnlineStatus(Integer id, Integer onlineStatus);
+
+    /**
+     * 根据门店ID查询演出列表
+     *
+     * @param page    分页页数
+     * @param size    分页条数
+     * @param storeId 门店ID
+     * @param status  演出状态
+     * @return 演出列表
+     */
+    IPage<BarPerformance> queryPerformanceListByStoreId(Integer page, Integer size, Integer storeId, String status);
+
+    /**
+     * 根据门店ID和分类查询演出列表
+     *
+     * @param page    分页页数
+     * @param size    分页条数
+     * @param storeId 门店ID
+     * @param statusReview 审核状态(0-待审核 1-审核通过 2-审核拒绝)
+     * @param category 演出分类(all-全部, not_started-未开始, in_progress-进行中, ended-已结束)
+     * @param performanceName 演出名称(可选,支持模糊搜索)
+     * @return 演出列表
+     */
+    IPage<BarPerformance> queryPerformanceListByStoreIdAndCategory(Integer page, Integer size, Integer storeId, Integer statusReview, String category, String performanceName);
+}

+ 88 - 0
alien-store/src/main/java/shop/alien/store/service/FitnessEquipmentInfoService.java

@@ -0,0 +1,88 @@
+package shop.alien.store.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.store.FitnessEquipmentInfo;
+
+import java.util.List;
+
+/**
+ * 健身设备信息服务接口
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface FitnessEquipmentInfoService extends IService<FitnessEquipmentInfo> {
+
+    /**
+     * 分页查询健身设备信息列表
+     *
+     * @param pageNum       页码
+     * @param pageSize      页大小
+     * @param equipmentType 设备类型(1:心肺训练, 2:核心训练, 3:臀腿训练, 4:上肢训练, 5:全身训练, 6:其他),可选
+     * @param equipmentName 设备名称(模糊查询),可选
+     * @param status        状态(0:禁用, 1:启用),可选
+     * @return 健身设备信息分页列表
+     */
+    IPage<FitnessEquipmentInfo> getPageList(Integer pageNum, Integer pageSize, 
+                                            Integer equipmentType, String equipmentName, Integer status);
+
+    /**
+     * 查询健身设备信息列表
+     *
+     * @param equipmentType 设备类型(1:心肺训练, 2:核心训练, 3:臀腿训练, 4:上肢训练, 5:全身训练, 6:其他),可选
+     * @param status        状态(0:禁用, 1:启用),可选
+     * @return 健身设备信息列表
+     */
+    List<FitnessEquipmentInfo> getList(Integer equipmentType, Integer status);
+
+    /**
+     * 根据设备类型查询设备列表(用于前端分类展示)
+     *
+     * @param equipmentType 设备类型(1:心肺训练, 2:核心训练, 3:臀腿训练, 4:上肢训练, 5:全身训练, 6:其他)
+     * @return 健身设备信息列表
+     */
+    List<FitnessEquipmentInfo> getListByType(Integer equipmentType);
+
+    /**
+     * 根据ID查询健身设备信息详情
+     *
+     * @param id 主键ID
+     * @return 健身设备信息
+     */
+    FitnessEquipmentInfo getDetailById(Integer id);
+
+    /**
+     * 新增健身设备信息
+     *
+     * @param fitnessEquipmentInfo 健身设备信息对象
+     * @return true表示新增成功,false表示新增失败
+     */
+    boolean saveEquipment(FitnessEquipmentInfo fitnessEquipmentInfo);
+
+    /**
+     * 修改健身设备信息
+     *
+     * @param fitnessEquipmentInfo 健身设备信息对象
+     * @return true表示修改成功,false表示修改失败
+     */
+    boolean updateEquipment(FitnessEquipmentInfo fitnessEquipmentInfo);
+
+    /**
+     * 删除健身设备信息(逻辑删除)
+     *
+     * @param id 主键ID
+     * @return true表示删除成功,false表示删除失败
+     */
+    boolean deleteEquipment(Integer id);
+
+    /**
+     * 启用/禁用健身设备信息
+     *
+     * @param id     主键ID
+     * @param status 状态(0:禁用, 1:启用)
+     * @return true表示操作成功,false表示操作失败
+     */
+    boolean updateStatus(Integer id, Integer status);
+}
+

+ 262 - 88
alien-store/src/main/java/shop/alien/store/service/LifeCommentService.java

@@ -5,11 +5,13 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.BeanUtils;
 import org.springframework.stereotype.Service;
 import org.springframework.util.CollectionUtils;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.StringUtils;
+import shop.alien.store.util.CommonConstant;
 import shop.alien.entity.second.SecondGoods;
 import shop.alien.entity.store.*;
 import shop.alien.entity.store.vo.LifePinglunVo;
@@ -23,8 +25,12 @@ import java.util.*;
 import java.util.stream.Collectors;
 
 /**
- * 评论
+ * 评论服务类
+ *
+ * @author system
+ * @since 2025-01-XX
  */
+@Slf4j
 @Service
 @RequiredArgsConstructor
 public class LifeCommentService {
@@ -49,56 +55,139 @@ public class LifeCommentService {
 
     private final SecondGoodsMapper secondGoodsMapper;
 
+    private final StoreStaffConfigMapper storeStaffConfigMapper;
+
+    /**
+     * 点赞操作
+     * <p>
+     * 检查是否已点赞,如果未点赞则插入点赞记录并更新对应表的点赞数
+     * </p>
+     *
+     * @param userId  用户ID
+     * @param huifuId 被点赞对象ID(评论ID/动态ID/活动ID等)
+     * @param type    点赞类型(1-评论 2-社区动态 3-活动 4-推荐菜 5-店铺打卡 6-二手商品 7-律师评分 8-点赞员工)
+     * @return 更新影响的行数,0表示已点赞或操作失败
+     */
     public int like(String userId, String huifuId, String type) {
-        LambdaUpdateWrapper<LifeLikeRecord> updateWrapper = new LambdaUpdateWrapper<>();
-        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)) {
+        log.info("执行点赞操作,userId={},huifuId={},type={}", userId, huifuId, type);
+        
+        // 参数校验
+        if (!StringUtils.hasText(userId)) {
+            log.warn("点赞失败,用户ID为空");
+            return 0;
+        }
+        if (!StringUtils.hasText(huifuId)) {
+            log.warn("点赞失败,被点赞对象ID为空");
+            return 0;
+        }
+        if (!StringUtils.hasText(type)) {
+            log.warn("点赞失败,点赞类型为空");
+            return 0;
+        }
+        
+        try {
+            // 查询是否已有点赞记录(使用 LambdaQueryWrapper 而不是 LambdaUpdateWrapper)
+            LambdaQueryWrapper<LifeLikeRecord> queryWrapper = new LambdaQueryWrapper<>();
+            queryWrapper.eq(LifeLikeRecord::getType, type)
+                    .eq(LifeLikeRecord::getDianzanId, userId)
+                    .eq(LifeLikeRecord::getHuifuId, huifuId)
+                    .eq(LifeLikeRecord::getDeleteFlag, CommonConstant.DELETE_FLAG_UNDELETE);
+            List<LifeLikeRecord> recordList = lifeLikeRecordMapper.selectList(queryWrapper);
+            
+            // 如果已有点赞记录,直接返回0(表示已点赞)
+            if (!CollectionUtils.isEmpty(recordList)) {
+                log.info("用户已点赞,userId={},huifuId={},type={}", userId, huifuId, type);
+                return 0;
+            }
+            
+            // 插入点赞记录
             LifeLikeRecord lifeLikeRecord = new LifeLikeRecord();
             lifeLikeRecord.setCreatedTime(new Date());
             lifeLikeRecord.setHuifuId(huifuId);
             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);
+            int insertResult = lifeLikeRecordMapper.insert(lifeLikeRecord);
+            
+            if (insertResult <= 0) {
+                log.warn("插入点赞记录失败,userId={},huifuId={},type={}", userId, huifuId, type);
+                return 0;
+            }
+            
+            // 根据类型更新对应表的点赞数
+            int updateResult = updateLikeCountByType(huifuId, type);
+            
+            // 如果是动态类型,发送通知
+            if (updateResult > 0 && CommonConstant.LIKE_TYPE_DYNAMICS.equals(type)) {
+                insertNotice(userId, huifuId, type);
+            }
+            
+            log.info("点赞操作完成,userId={},huifuId={},type={},更新结果={}", userId, huifuId, type, updateResult);
+            return updateResult;
+        } catch (Exception e) {
+            log.error("点赞操作异常,userId={},huifuId={},type={},异常信息:{}", userId, huifuId, type, e.getMessage(), e);
+            return 0;
+        }
+    }
+    
+    /**
+     * 根据类型更新对应表的点赞数
+     *
+     * @param huifuId 被点赞对象ID
+     * @param type    点赞类型
+     * @return 更新影响的行数
+     */
+    private int updateLikeCountByType(String huifuId, String type) {
+        try {
+            if (CommonConstant.LIKE_TYPE_COMMENT.equals(type)) {
+                // 类型1:评论
+                LambdaUpdateWrapper<StoreComment> updateWrapper = new LambdaUpdateWrapper<>();
+                updateWrapper.eq(StoreComment::getId, huifuId)
+                        .setSql("like_count = like_count + 1");
+                return storeCommentMapper.update(null, updateWrapper);
+            } else if (CommonConstant.LIKE_TYPE_DYNAMICS.equals(type)) {
+                // 类型2:社区动态
+                LambdaUpdateWrapper<LifeUserDynamics> updateWrapper = new LambdaUpdateWrapper<>();
+                updateWrapper.eq(LifeUserDynamics::getId, huifuId)
+                        .setSql("dianzan_count = dianzan_count + 1");
+                return lifeUserDynamicsMapper.update(null, updateWrapper);
+            } else if (CommonConstant.LIKE_TYPE_ACTIVITY.equals(type)) {
+                // 类型3:活动
+                LambdaUpdateWrapper<LifeActivity> updateWrapper = new LambdaUpdateWrapper<>();
+                updateWrapper.eq(LifeActivity::getId, huifuId)
+                        .setSql("dianzan_count = dianzan_count + 1");
+                return lifeActivityMapper.update(null, updateWrapper);
+            } else if (CommonConstant.LIKE_TYPE_MENU.equals(type)) {
+                // 类型4:推荐菜
+                LambdaUpdateWrapper<StoreMenu> updateWrapper = new LambdaUpdateWrapper<>();
+                updateWrapper.eq(StoreMenu::getId, huifuId)
+                        .setSql("like_count = like_count + 1");
+                return storeRecommendMapper.update(null, updateWrapper);
+            } else if (CommonConstant.LIKE_TYPE_CLOCK_IN.equals(type)) {
+                // 类型5:店铺打卡
+                LambdaUpdateWrapper<StoreClockIn> updateWrapper = new LambdaUpdateWrapper<>();
+                updateWrapper.eq(StoreClockIn::getId, huifuId)
+                        .setSql("like_count = like_count + 1");
+                return storeClockInMapper.update(null, updateWrapper);
+            } else if (CommonConstant.LIKE_TYPE_SECOND_GOODS.equals(type)) {
+                // 类型6:二手商品
+                LambdaUpdateWrapper<SecondGoods> updateWrapper = new LambdaUpdateWrapper<>();
+                updateWrapper.eq(SecondGoods::getId, huifuId)
+                        .setSql("like_count = like_count + 1");
+                return secondGoodsMapper.update(null, updateWrapper);
+            } else if (CommonConstant.LIKE_TYPE_STAFF.equals(type)) {
+                // 类型8:点赞员工
+                LambdaUpdateWrapper<StoreStaffConfig> updateWrapper = new LambdaUpdateWrapper<>();
+                updateWrapper.eq(StoreStaffConfig::getId, huifuId)
+                        .setSql("like_count = like_count + 1");
+                return storeStaffConfigMapper.update(null, updateWrapper);
+            } else {
+                log.warn("未知的点赞类型,type={},huifuId={}", type, huifuId);
+                return 0;
             }
+        } catch (Exception e) {
+            log.error("更新点赞数异常,type={},huifuId={},异常信息:{}", type, huifuId, e.getMessage(), e);
+            return 0;
         }
-        return 0;
     }
 
     /**
@@ -118,54 +207,139 @@ public class LifeCommentService {
         }
     }
 
+    /**
+     * 取消点赞操作
+     * <p>
+     * 检查是否已点赞,如果已点赞则删除点赞记录并更新对应表的点赞数
+     * </p>
+     *
+     * @param userId  用户ID
+     * @param huifuId 被取消点赞对象ID(评论ID/动态ID/活动ID等)
+     * @param type    点赞类型(1-评论 2-社区动态 3-活动 4-推荐菜 5-店铺打卡 6-二手商品 7-律师评分 8-点赞员工)
+     * @return 更新影响的行数,0表示未点赞或操作失败
+     */
     public int cancelLike(String userId, String huifuId, String type) {
-        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> 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);
-            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).gt(LifeUserDynamics::getDianzanCount, 0);
-            lambdaUpdateWrapper.setSql("dianzan_count = dianzan_count - 1");
-            return lifeUserDynamicsMapper.update(null, lambdaUpdateWrapper);
-        } else if ("3".equals(type)) {
-            LambdaUpdateWrapper<LifeActivity> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
-            lambdaUpdateWrapper.eq(LifeActivity::getId, huifuId).gt(LifeActivity::getDianzanCount, 0);
-            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).gt(StoreMenu::getLikeCount, 0);
-            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).gt(StoreClockIn::getLikeCount, 0);
-            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).gt(SecondGoods::getLikeCount, 0);
-            lambdaUpdateWrapper.setSql("like_count = like_count - 1");
-            return secondGoodsMapper.update(null, lambdaUpdateWrapper);
+        log.info("执行取消点赞操作,userId={},huifuId={},type={}", userId, huifuId, type);
+        
+        // 参数校验
+        if (!StringUtils.hasText(userId)) {
+            log.warn("取消点赞失败,用户ID为空");
+            return 0;
+        }
+        if (!StringUtils.hasText(huifuId)) {
+            log.warn("取消点赞失败,被取消点赞对象ID为空");
+            return 0;
+        }
+        if (!StringUtils.hasText(type)) {
+            log.warn("取消点赞失败,点赞类型为空");
+            return 0;
+        }
+        
+        try {
+            // 查询是否已有点赞记录
+            LambdaQueryWrapper<LifeLikeRecord> queryWrapper = new LambdaQueryWrapper<>();
+            queryWrapper.eq(LifeLikeRecord::getDianzanId, userId)
+                    .eq(LifeLikeRecord::getHuifuId, huifuId)
+                    .eq(LifeLikeRecord::getType, type)
+                    .eq(LifeLikeRecord::getDeleteFlag, CommonConstant.DELETE_FLAG_UNDELETE);
+            List<LifeLikeRecord> recordList = lifeLikeRecordMapper.selectList(queryWrapper);
+            
+            // 如果没有点赞记录,直接返回0(表示未点赞)
+            if (CollectionUtils.isEmpty(recordList)) {
+                log.info("用户未点赞,无需取消,userId={},huifuId={},type={}", userId, huifuId, type);
+                return 0;
+            }
+            
+            // 删除点赞记录(逻辑删除)
+            LambdaUpdateWrapper<LifeLikeRecord> updateWrapper = new LambdaUpdateWrapper<>();
+            updateWrapper.eq(LifeLikeRecord::getHuifuId, huifuId)
+                    .eq(LifeLikeRecord::getDianzanId, userId)
+                    .eq(LifeLikeRecord::getType, type)
+                    .eq(LifeLikeRecord::getDeleteFlag, CommonConstant.DELETE_FLAG_UNDELETE);
+            int deleteResult = lifeLikeRecordMapper.delete(updateWrapper);
+            
+            if (deleteResult <= 0) {
+                log.warn("删除点赞记录失败,userId={},huifuId={},type={}", userId, huifuId, type);
+                return 0;
+            }
+            
+            // 根据类型更新对应表的点赞数
+            int updateResult = decreaseLikeCountByType(huifuId, type);
+            
+            log.info("取消点赞操作完成,userId={},huifuId={},type={},更新结果={}", userId, huifuId, type, updateResult);
+            return updateResult;
+        } catch (Exception e) {
+            log.error("取消点赞操作异常,userId={},huifuId={},type={},异常信息:{}", userId, huifuId, type, e.getMessage(), e);
+            return 0;
+        }
+    }
+    
+    /**
+     * 根据类型减少对应表的点赞数
+     *
+     * @param huifuId 被取消点赞对象ID
+     * @param type    点赞类型
+     * @return 更新影响的行数
+     */
+    private int decreaseLikeCountByType(String huifuId, String type) {
+        try {
+            if (CommonConstant.LIKE_TYPE_COMMENT.equals(type)) {
+                // 类型1:评论
+                LambdaUpdateWrapper<StoreComment> updateWrapper = new LambdaUpdateWrapper<>();
+                updateWrapper.eq(StoreComment::getId, huifuId)
+                        .gt(StoreComment::getLikeCount, 0)
+                        .setSql("like_count = like_count - 1");
+                return storeCommentMapper.update(null, updateWrapper);
+            } else if (CommonConstant.LIKE_TYPE_DYNAMICS.equals(type)) {
+                // 类型2:社区动态
+                LambdaUpdateWrapper<LifeUserDynamics> updateWrapper = new LambdaUpdateWrapper<>();
+                updateWrapper.eq(LifeUserDynamics::getId, huifuId)
+                        .gt(LifeUserDynamics::getDianzanCount, 0)
+                        .setSql("dianzan_count = dianzan_count - 1");
+                return lifeUserDynamicsMapper.update(null, updateWrapper);
+            } else if (CommonConstant.LIKE_TYPE_ACTIVITY.equals(type)) {
+                // 类型3:活动
+                LambdaUpdateWrapper<LifeActivity> updateWrapper = new LambdaUpdateWrapper<>();
+                updateWrapper.eq(LifeActivity::getId, huifuId)
+                        .gt(LifeActivity::getDianzanCount, 0)
+                        .setSql("dianzan_count = dianzan_count - 1");
+                return lifeActivityMapper.update(null, updateWrapper);
+            } else if (CommonConstant.LIKE_TYPE_MENU.equals(type)) {
+                // 类型4:推荐菜
+                LambdaUpdateWrapper<StoreMenu> updateWrapper = new LambdaUpdateWrapper<>();
+                updateWrapper.eq(StoreMenu::getId, huifuId)
+                        .gt(StoreMenu::getLikeCount, 0)
+                        .setSql("like_count = like_count - 1");
+                return storeRecommendMapper.update(null, updateWrapper);
+            } else if (CommonConstant.LIKE_TYPE_CLOCK_IN.equals(type)) {
+                // 类型5:店铺打卡
+                LambdaUpdateWrapper<StoreClockIn> updateWrapper = new LambdaUpdateWrapper<>();
+                updateWrapper.eq(StoreClockIn::getId, huifuId)
+                        .gt(StoreClockIn::getLikeCount, 0)
+                        .setSql("like_count = like_count - 1");
+                return storeClockInMapper.update(null, updateWrapper);
+            } else if (CommonConstant.LIKE_TYPE_SECOND_GOODS.equals(type)) {
+                // 类型6:二手商品
+                LambdaUpdateWrapper<SecondGoods> updateWrapper = new LambdaUpdateWrapper<>();
+                updateWrapper.eq(SecondGoods::getId, huifuId)
+                        .gt(SecondGoods::getLikeCount, 0)
+                        .setSql("like_count = like_count - 1");
+                return secondGoodsMapper.update(null, updateWrapper);
+            } else if (CommonConstant.LIKE_TYPE_STAFF.equals(type)) {
+                // 类型8:点赞员工
+                LambdaUpdateWrapper<StoreStaffConfig> updateWrapper = new LambdaUpdateWrapper<>();
+                updateWrapper.eq(StoreStaffConfig::getId, Integer.parseInt(huifuId))
+                        .setSql("like_count = IF(like_count > 0, like_count - 1, 0)");
+                return storeStaffConfigMapper.update(null, updateWrapper);
+            } else {
+                log.warn("未知的点赞类型,type={},huifuId={}", type, huifuId);
+                return 0;
+            }
+        } catch (Exception e) {
+            log.error("减少点赞数异常,type={},huifuId={},异常信息:{}", type, huifuId, e.getMessage(), e);
+            return 0;
         }
     }
-        return 0;
-}
 
     public int addOrUpdateStore(LifeComment store) {
         if (StringUtils.isEmpty(store.getId())) {

+ 33 - 0
alien-store/src/main/java/shop/alien/store/service/LifeUserDynamicsService.java

@@ -286,6 +286,39 @@ public class LifeUserDynamicsService extends ServiceImpl<LifeUserDynamicsMapper,
         return lifeUserDynamicsVoList;
     }
 
+    public List<LifeUserDynamicsVo> getStoreUserDynamics(String storePhone, String userType, String loginPhone) {
+            LambdaQueryWrapper<LifeUserDynamics> lambdaQueryWrapper = new LambdaQueryWrapper<>();
+            lambdaQueryWrapper.eq(LifeUserDynamics :: getPhoneId, storePhone);
+            List<LifeUserDynamics> lifeUserDynamics = lifeUserDynamicsMapper.selectList(lambdaQueryWrapper);
+            List<LifeUserDynamicsVo> lifeUserDynamicsVos = lifeUserDynamics.stream()
+                .map(dynamics -> {
+                    LifeUserDynamicsVo vo = new LifeUserDynamicsVo();
+                    BeanUtils.copyProperties(dynamics, vo);
+                    // 特殊字段处理
+                    return vo;
+                })
+                .collect(Collectors.toList());
+
+            // 查询我的点赞记录,构建点赞ID列表
+            LambdaQueryWrapper<LifeLikeRecord> likeWrapper = new LambdaQueryWrapper<>();
+            likeWrapper.eq(LifeLikeRecord::getType, "2");
+            if(userType.equals("1")){
+                likeWrapper.eq(LifeLikeRecord::getDianzanId, "user_"+loginPhone);
+            }else{
+                likeWrapper.eq(LifeLikeRecord::getDianzanId, "store_"+loginPhone);
+            }
+            List<LifeLikeRecord> lifeLikeList = lifeLikeRecordMapper.selectList(likeWrapper);
+            List<String> likeList = lifeLikeList.stream().map(LifeLikeRecord::getHuifuId).collect(Collectors.toList());
+         for (LifeUserDynamicsVo vo : lifeUserDynamicsVos) {
+            if (likeList.contains(String.valueOf(vo.getId()))) {
+                vo.setIsLike("1");
+            } else {
+                vo.setIsLike("0");
+            }
+        }
+            return lifeUserDynamicsVos;
+    }
+
     public Map<String, Object> getUserDynamics(String myselfPhoneId, String phoneId, String type, Integer page, Integer size) {
 
         Map<String, Object> resultMap = new HashMap<>();

+ 20 - 3
alien-store/src/main/java/shop/alien/store/service/SportsEquipmentFacilityService.java

@@ -3,6 +3,8 @@ 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.FitnessEquipmentCategoryDetailVo;
+import shop.alien.entity.store.vo.SportsEquipmentFacilityCategorySummaryVo;
 import shop.alien.entity.store.vo.SportsEquipmentFacilityCategoryVo;
 import shop.alien.entity.store.vo.SportsEquipmentFacilityVo;
 
@@ -71,13 +73,14 @@ public interface SportsEquipmentFacilityService extends IService<SportsEquipment
     boolean deleteFacility(Integer id);
 
     /**
-     * 查询指定店铺按分类汇总的设备信息
+     * 查询指定店铺按分类汇总的设备信息(用户端)
      * 包含每个分类的设备数量、图片列表和设备列表
+     * 通过fitnessEquipmentIds关联FitnessEquipmentInfo信息
      *
      * @param storeId 门店ID
-     * @return List<SportsEquipmentFacilityCategoryVo>
+     * @return List<SportsEquipmentFacilityCategorySummaryVo>
      */
-    List<SportsEquipmentFacilityCategoryVo> getCategorySummary(Integer storeId);
+    List<SportsEquipmentFacilityCategorySummaryVo> getCategorySummary(Integer storeId);
 
     SportsEquipmentFacilityVo getStoreDetail(Integer id);
 
@@ -93,5 +96,19 @@ public interface SportsEquipmentFacilityService extends IService<SportsEquipment
      * @return boolean
      */
     boolean saveCategoryImages(Integer storeId, Integer facilityCategory, List<String> imageList);
+
+    /**
+     * 查询分类的设备详情(用户端)
+     * 包含图片列表、各子分类的设备数量汇总、设备列表
+     * 按facility_category_name查询,支持商户自定义分类名称
+     * 如果facilityCategoryName为空,返回所有分类的数据
+     * 如果equipmentType为空,返回该分类下所有类型的数据
+     *
+     * @param storeId            门店ID,不能为空且必须大于0
+     * @param facilityCategoryName 设施分类名称(商户自定义),可选,为空时返回所有分类
+     * @param equipmentType       设备类型(1:心肺训练, 2:核心训练, 3:臀腿训练, 4:上肢训练, 5:全身训练, 6:其他),可选,为空时返回所有类型
+     * @return 分类详情列表,不会返回null
+     */
+    List<FitnessEquipmentCategoryDetailVo> getCategoryDetail(Integer storeId, String facilityCategoryName, Integer equipmentType);
 }
 

+ 63 - 0
alien-store/src/main/java/shop/alien/store/service/SportsFacilityAreaService.java

@@ -0,0 +1,63 @@
+package shop.alien.store.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.store.SportsFacilityArea;
+
+import java.util.List;
+
+/**
+ * 运动设施区域服务接口
+ *
+ * @author assistant
+ * @since 2025-12-19
+ */
+public interface SportsFacilityAreaService extends IService<SportsFacilityArea> {
+
+    /**
+     * 新建区域
+     * 创建新的设施区域
+     *
+     * @param storeId  门店ID,不能为空且必须大于0
+     * @param areaName 区域名称,必填,限10字
+     * @return 创建的区域ID
+     */
+    Integer createArea(Integer storeId, String areaName);
+
+    /**
+     * 查询指定门店下的所有区域列表
+     *
+     * @param storeId 门店ID,不能为空且必须大于0
+     * @return 区域列表,按排序号升序,相同排序号按创建时间降序
+     */
+    List<SportsFacilityArea> listAreas(Integer storeId);
+
+    /**
+     * 更新区域信息
+     *
+     * @param areaId   区域ID
+     * @param areaName 区域名称(可选)
+     * @param sortOrder 排序号(可选)
+     * @return 更新是否成功
+     */
+    boolean updateArea(Integer areaId, String areaName, Integer sortOrder);
+
+    /**
+     * 删除区域(逻辑删除)
+     * 删除区域时,会同时逻辑删除该区域下的所有设施
+     *
+     * @param areaId 区域ID
+     * @return 删除是否成功
+     */
+    boolean deleteArea(Integer areaId);
+
+    /**
+     * 批量删除区域(逻辑删除)
+     * 删除区域时,会同时逻辑删除该区域下的所有设施
+     *
+     * @param storeId 门店ID
+     * @param areaIds 区域ID列表
+     * @return 删除是否成功
+     */
+    boolean batchDeleteAreas(Integer storeId, List<Integer> areaIds);
+}
+

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

@@ -455,6 +455,25 @@ public interface StoreInfoService extends IService<StoreInfo> {
      */
     IPage<StoreInfoVo> getLifeServicesByDistance(Double lon, Double lat, Double distance, Integer sortType, Integer businessType, Integer categoryId, String storeName, int pageNum, int pageSize);
 
+
+
+    /**
+     * 查询七种类型店铺(酒吧、ktv、洗浴汗蒸、按摩足浴 、运动健身、丽人美发 、特色美食)并按距离筛选
+     *
+     * @param lon         经度
+     * @param lat         纬度
+     * @param distance    距离范围(单位:公里)
+     * @param sortType    排序模式(1:智能排序,2:好评优先,3:距离优先)
+     * @param businessType 店铺类型(KTV=3、洗浴汗蒸=4、按摩足浴=5,酒吧需要查询字典表),可选
+     * @param categoryId  分类ID(二级或三级分类的dictId,从getAllBusinessSection接口获取),可选
+     * @param storeName   店铺名称(模糊查询),可选
+     * @param pageNum     页码
+     * @param pageSize    页容
+     * @return IPage<StoreInfoVo> 分页的门店信息列表
+     */
+    IPage<StoreInfoVo> getThreeCategoriesStoresByDistance(Double lon, Double lat, Double distance, Integer sortType, Integer businessType, Integer categoryId, String storeName,Integer storeType, int pageNum, int pageSize);
+
+
     /**
      * 门店证照查询(分页)
      *

+ 99 - 0
alien-store/src/main/java/shop/alien/store/service/StoreProductBarService.java

@@ -0,0 +1,99 @@
+package shop.alien.store.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreProductBar;
+
+import java.util.List;
+
+/**
+ * 酒吧商品表 服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface StoreProductBarService extends IService<StoreProductBar> {
+
+    /**
+     * 新增酒吧商品
+     *
+     * @param storeProductBar 酒吧商品
+     * @return R<StoreProductBar>
+     */
+    R<StoreProductBar> addStoreProductBar(StoreProductBar storeProductBar);
+
+    /**
+     * 编辑酒吧商品
+     *
+     * @param storeProductBar 酒吧商品
+     * @return R<StoreProductBar>
+     */
+    R<StoreProductBar> editStoreProductBar(StoreProductBar storeProductBar);
+
+    /**
+     * 删除酒吧商品
+     *
+     * @param id 主键
+     * @return R<Boolean>
+     */
+    R<Boolean> deleteStoreProductBar(Integer id);
+
+    /**
+     * 根据ID查询酒吧商品
+     *
+     * @param id 主键
+     * @return R<StoreProductBar>
+     */
+    R<StoreProductBar> getStoreProductBarById(Integer id);
+
+    /**
+     * 分页查询酒吧商品列表
+     *
+     * @param pageNum  页码
+     * @param pageSize 页容
+     * @param name     名称(模糊查询)
+     * @param category 品类
+     * @param status   状态(0:禁用,1:启用)
+     * @param extId    商品表主键
+     * @return IPage<StoreProductBar>
+     */
+    IPage<StoreProductBar> getPage(int pageNum, int pageSize, String name, String category, Integer status, Integer extId);
+
+    /**
+     * 根据商品表主键查询酒吧商品列表
+     *
+     * @param extId 商品表主键
+     * @return List<StoreProductBar>
+     */
+    List<StoreProductBar> getListByExtId(Integer extId);
+
+    /**
+     * 根据品类查询酒吧商品列表
+     *
+     * @param category 品类
+     * @return List<StoreProductBar>
+     */
+    List<StoreProductBar> getListByCategory(String category);
+
+    /**
+     * 更新酒吧商品状态
+     *
+     * @param id     主键ID
+     * @param status 状态(0:禁用,1:启用)
+     * @return boolean
+     */
+    boolean updateStatus(Integer id, Integer status);
+
+    /**
+     * 批量删除酒吧商品(逻辑删除)
+     *
+     * @param ids 主键ID列表
+     * @return boolean
+     */
+    boolean deleteBatch(List<Integer> ids);
+
+
+    List<StoreProductBar> getByExtId(Integer id);
+
+}

+ 106 - 0
alien-store/src/main/java/shop/alien/store/service/StoreProductDelicaciesService.java

@@ -0,0 +1,106 @@
+package shop.alien.store.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.store.StoreProductDelicacies;
+
+import java.util.List;
+
+/**
+ * 美食商品表 服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface StoreProductDelicaciesService extends IService<StoreProductDelicacies> {
+
+    /**
+     * 分页查询美食商品列表
+     *
+     * @param pageNum  页码
+     * @param pageSize 页容
+     * @param name     名称(模糊查询)
+     * @param category 类别
+     * @param extGroup 菜品分组
+     * @param status   状态(0:禁用,1:启用)
+     * @param extId    商品表主键
+     * @return IPage<StoreProductDelicacies>
+     */
+    IPage<StoreProductDelicacies> getPage(int pageNum, int pageSize, String name, String category, String extGroup, Integer status, Integer extId);
+
+    /**
+     * 根据ID查询美食商品详情
+     *
+     * @param id 主键ID
+     * @return StoreProductDelicacies
+     */
+    StoreProductDelicacies getById(Integer id);
+
+    /**
+     * 新增美食商品
+     *
+     * @param delicacies 美食商品信息
+     * @return boolean
+     */
+    boolean saveDelicacies(StoreProductDelicacies delicacies);
+
+    /**
+     * 修改美食商品
+     *
+     * @param delicacies 美食商品信息
+     * @return boolean
+     */
+    boolean updateDelicacies(StoreProductDelicacies delicacies);
+
+    /**
+     * 删除美食商品(逻辑删除)
+     *
+     * @param id 主键ID
+     * @return boolean
+     */
+    boolean deleteDelicacies(Integer id);
+
+    /**
+     * 批量删除美食商品(逻辑删除)
+     *
+     * @param ids 主键ID列表
+     * @return boolean
+     */
+    boolean deleteBatch(List<Integer> ids);
+
+    /**
+     * 更新美食商品状态
+     *
+     * @param id     主键ID
+     * @param status 状态(0:禁用,1:启用)
+     * @return boolean
+     */
+    boolean updateStatus(Integer id, Integer status);
+
+    /**
+     * 根据商品表主键查询美食商品列表
+     *
+     * @param extId 商品表主键
+     * @return List<StoreProductDelicacies>
+     */
+    List<StoreProductDelicacies> getListByExtId(Integer extId);
+
+    /**
+     * 根据类别查询美食商品列表
+     *
+     * @param category 类别
+     * @return List<StoreProductDelicacies>
+     */
+    List<StoreProductDelicacies> getListByCategory(String category);
+
+    /**
+     * 根据菜品分组查询美食商品列表
+     *
+     * @param extGroup 菜品分组
+     * @return List<StoreProductDelicacies>
+     */
+    List<StoreProductDelicacies> getListByExtGroup(String extGroup);
+
+    List<StoreProductDelicacies> getByExtId(Integer id);
+}
+

+ 89 - 0
alien-store/src/main/java/shop/alien/store/service/StoreProductGymService.java

@@ -0,0 +1,89 @@
+package shop.alien.store.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreProductGym;
+
+import java.util.List;
+
+/**
+ * 运动健身商品表 服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface StoreProductGymService extends IService<StoreProductGym> {
+
+    /**
+     * 新增运动健身商品
+     *
+     * @param storeProductGym 运动健身商品
+     * @return R<StoreProductGym>
+     */
+    R<StoreProductGym> addStoreProductGym(StoreProductGym storeProductGym);
+
+    /**
+     * 编辑运动健身商品
+     *
+     * @param storeProductGym 运动健身商品
+     * @return R<StoreProductGym>
+     */
+    R<StoreProductGym> editStoreProductGym(StoreProductGym storeProductGym);
+
+    /**
+     * 删除运动健身商品
+     *
+     * @param id 主键
+     * @return R<Boolean>
+     */
+    R<Boolean> deleteStoreProductGym(Integer id);
+
+    /**
+     * 根据ID查询运动健身商品
+     *
+     * @param id 主键
+     * @return R<StoreProductGym>
+     */
+    R<StoreProductGym> getStoreProductGymById(Integer id);
+
+    /**
+     * 分页查询运动健身商品列表
+     *
+     * @param pageNum  页码
+     * @param pageSize 页容
+     * @param name     名称(模糊查询)
+     * @param classMode 上课形式
+     * @param status   状态(0:禁用,1:启用)
+     * @param extId    商品表主键
+     * @return IPage<StoreProductGym>
+     */
+    IPage<StoreProductGym> getPage(int pageNum, int pageSize, String name, String classMode, Integer status, Integer extId);
+
+    /**
+     * 根据商品表主键查询运动健身商品列表
+     *
+     * @param extId 商品表主键
+     * @return List<StoreProductGym>
+     */
+    List<StoreProductGym> getListByExtId(Integer extId);
+
+    /**
+     * 更新运动健身商品状态
+     *
+     * @param id     主键ID
+     * @param status 状态(0:禁用,1:启用)
+     * @return boolean
+     */
+    boolean updateStatus(Integer id, Integer status);
+
+    /**
+     * 批量删除运动健身商品(逻辑删除)
+     *
+     * @param ids 主键ID列表
+     * @return boolean
+     */
+    boolean deleteBatch(List<Integer> ids);
+
+    List<StoreProductGym> getByExtId(Integer id);
+}

+ 117 - 0
alien-store/src/main/java/shop/alien/store/service/StoreProductItemService.java

@@ -0,0 +1,117 @@
+package shop.alien.store.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreProductItem;
+import shop.alien.entity.store.dto.StoreProductItemDto;
+import shop.alien.entity.store.vo.StoreProductItemGymVo;
+import shop.alien.entity.store.vo.StoreProductItemDelicaciesVo;
+
+import java.util.List;
+
+/**
+ * 商品表 服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface StoreProductItemService extends IService<StoreProductItem> {
+
+    /**
+     * 新增商品
+     * @return R<StoreProductItem>
+     */
+    R<StoreProductItem> addStoreProductItemDto(StoreProductItemDto storeProductItemDto);
+
+    /**
+     * 编辑商品
+     *
+     * @param storeProductItem 商品
+     * @return R<StoreProductItem>
+     */
+    R<StoreProductItem> editStoreProductItem(StoreProductItemDto storeProductItem);
+
+    /**
+     * 删除商品
+     *
+     * @param id 主键
+     * @return R<Boolean>
+     */
+    R<Boolean> deleteStoreProductItem(Integer id);
+
+    /**
+     * 根据ID查询商品
+     *
+     * @param id 主键
+     * @return R<StoreProductItem>
+     */
+    R<List<StoreProductItemDto>> getStoreProductItemById(Integer id,Integer modelType);
+
+
+    /**
+     * 按商品类型查询列表
+     *
+     * @param prodType 商品类型:1酒吧-酒水 2酒吧-餐食 3美食-餐食 4运动健身-单次 5运动健身-多次
+     * @return R<List<StoreProductItem>>
+     */
+    R<List<StoreProductItem>> listByProdType(Integer prodType);
+
+
+    /**
+     * 分页查询商品表与运动健身商品表关联数据
+     *
+     * @param pageNum  页码
+     * @param pageSize 页容
+     * @param storeId  门店ID
+     * @param prodName 商品名称(模糊查询)
+     * @param prodType 商品类型
+     * @return IPage<StoreProductItemGymVo>
+     */
+    IPage<StoreProductItemGymVo> getPageWithGym(int pageNum, int pageSize, Integer storeId, String prodName, Integer prodType);
+
+    /**
+     * 根据ID查询商品表与运动健身商品表关联详情
+     *
+     * @param id 商品表主键ID
+     * @return R<StoreProductItemGymVo>
+     */
+    R<StoreProductItemGymVo> getDetailWithGym(Long id);
+
+    /**
+     * 通用分页查询(支持门店、名称、类型、状态、是否预约筛选)
+     *
+     * @param pageNum     页码
+     * @param pageSize    页容
+     * @param storeId     门店ID
+     * @param prodName    商品名称(模糊)
+     * @param prodType    商品类型
+     * @param status      状态:0-待审核 1-审核通过 2-审核拒绝
+     * @param needReserve 是否需要预约:0-否 1-是
+     * @return IPage<StoreProductItem>
+     */
+    IPage<StoreProductItem> pageStoreProductItems(int pageNum, int pageSize,
+                                                  Integer storeId, String prodName, Integer prodType,
+                                                  Integer status, Integer needReserve);
+
+    /**
+     * 分页查询商品表与美食商品表关联数据
+     * 包含:1. store_product_delicacies的所有菜品
+     *      2. store_product_item的所有美食相关的套餐和商品(prodType = 3)
+     *
+     * @param pageNum     页码
+     * @param pageSize    页容
+     * @param storeId     门店ID
+     * @param name        名称(模糊查询)
+     * @param category    类别
+     * @param extGroup    菜品分组
+     * @param status      状态(0:禁用,1:启用)
+     * @param packageType 套餐类型(预留,用于根据类型查询套餐)
+     * @return IPage<StoreProductItemDelicaciesVo>
+     */
+    IPage<StoreProductItemDelicaciesVo> getPageWithDelicacies(int pageNum, int pageSize,
+                                                              Integer storeId, String name,
+                                                              String category, String extGroup,
+                                                              Integer status, Integer packageType);
+}
+

+ 100 - 12
alien-store/src/main/java/shop/alien/store/service/StoreStaffConfigService.java

@@ -2,24 +2,52 @@ package shop.alien.store.service;
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import shop.alien.entity.store.StoreStaffConfig;
+import shop.alien.entity.store.dto.StoreStaffConfigListQueryDto;
+import shop.alien.entity.store.vo.StoreStaffPositionCountVo;
 
 import java.io.IOException;
+import java.util.List;
 
 public interface StoreStaffConfigService {
 
-    IPage<StoreStaffConfig> getStaffConfigList(int page, int size, String status);
+    /**
+     * 员工列表查询(商家端)
+     *
+     * @param query 查询条件DTO
+     * @return 员工列表
+     */
+    IPage<StoreStaffConfig> getStaffConfigList(StoreStaffConfigListQueryDto query);
 
+    /**
+     * 新增或更新员工(商家端)
+     *
+     * @param storeStaffConfig 员工信息
+     * @return 新增或更新结果
+     */
     int addOrUpdateStaffConfig(StoreStaffConfig storeStaffConfig);
 
-
+    /**
+     * 审核员工
+     *
+     * @param id 员工ID
+     * @param status 审核状态 0-待审核, 1-审核通过, 2-审核拒绝
+     * @param rejectionReason 拒绝原因(status=2时必填)
+     * @return
+     */
     Integer audit(Integer id, Integer status, String rejectionReason);
 
+    /**
+     * 员工详情查询(商家端)
+     *
+     * @param id 员工ID
+     * @return 员工详情
+     */
     StoreStaffConfig getStaffConfigDeatail(Integer id);
 
     String staffConfigExport(String status) throws IOException;
 
     /**
-     * 删除员工
+     * 删除员工(商家端)
      *
      * @param id
      * @return
@@ -27,7 +55,7 @@ public interface StoreStaffConfigService {
     Integer deleteStaffConfig(Integer id);
 
     /**
-     * 置顶员工
+     * 置顶员工(商家端)
      *
      * @param id 员工ID
      * @param topStatus 置顶状态 0-未置顶, 1-置顶
@@ -35,23 +63,83 @@ public interface StoreStaffConfigService {
      */
     Integer setTopStatus(Integer id, Integer topStatus);
 
+    /**
+     * 设置员工上线/下线状态(商家端)
+     *
+     * @param id 员工ID
+     * @param onlineStatus 上线状态 0-上线, 1-下线
+     * @return
+     */
+    Integer setOnlineStatus(Integer id, Integer onlineStatus);
+
 
     /**
-     * 员工列表查询
+     * 员工列表查询(用户端)
+     * <p>
+     * 查询指定店铺的员工列表,支持按状态和职位筛选
+     * 排序规则:先按置顶状态降序,再按置顶时间降序,最后按创建时间降序
+     * </p>
      *
-     * @param page    分页页数
-     * @param size    分页条数
-     * @param storeId 店铺ID
-     * @param status  员工状态
-     * @return 员工列表
+     * @param page          分页页数,必须大于0
+     * @param size          分页条数,必须大于0
+     * @param storeId       店铺ID,必须大于0
+     * @param status        员工状态,可选(0-待审核 1-审核通过 2-审核拒绝),为空时查询所有状态
+     * @param staffPosition 员工职位,可选,为空时查询所有职位
+     * @return 员工列表分页结果
      */
-    IPage<StoreStaffConfig> queryStaffList(Integer page, Integer size, Integer storeId, String status);
+    IPage<StoreStaffConfig> queryStaffList(Integer page, Integer size, Integer storeId, String status, String staffPosition);
 
     /**
-     * 员工详情查询
+     * 员工详情查询(用户端)
      *
      * @param id 员工主键id
      * @return 员工详情
      */
     StoreStaffConfig queryStaffDetail(Integer id);
+
+    /**
+     * 获取美食员工列表
+     *
+     * @param page          分页页数
+     * @param size          分页条数
+     * @param storeId       店铺ID
+     * @param staffPosition 员工职位
+     * @param status        员工状态
+     * @return 员工列表
+     */
+    IPage<StoreStaffConfig> getFoodStaffConfigList(int page, int size, Integer storeId, String staffPosition, String status);
+
+    /**
+     * 根据ID查询员工详情(包含员工信息和课程列表)
+     * <p>
+     * 查询员工基本信息以及关联的运动健身员工课程信息
+     * </p>
+     *
+     * @param id 员工主键ID,必须大于0
+     * @return 员工详情(包含员工信息和课程列表),如果员工不存在则返回null
+     */
+    shop.alien.entity.store.vo.StoreStaffDetailVo getStaffDetailWithCourse(Integer id);
+
+    /**
+     * 查询健身教练详情(包含员工信息、基本信息和认证/荣誉列表)
+     * <p>
+     * 查询员工基本信息、运动健身员工基本信息以及关联的认证和荣誉信息
+     * 认证和荣誉根据type字段分开(1-认证,2-荣誉)
+     * </p>
+     *
+     * @param id 员工主键ID,必须大于0
+     * @return 健身教练详情(包含员工信息、基本信息和认证/荣誉列表),如果员工不存在则返回null
+     */
+    shop.alien.entity.store.vo.StoreStaffFitnessDetailVo getFitnessCoachDetail(Integer id);
+
+    /**
+     * 查询指定店铺的员工职位统计信息
+     * <p>
+     * 统计指定店铺下各个职位的员工数量,只统计未删除的员工
+     * </p>
+     *
+     * @param storeId 店铺ID,必须大于0
+     * @return 员工职位统计列表,每个元素包含职位名称和对应员工数量
+     */
+    List<StoreStaffPositionCountVo> getStaffPositionCount(Integer storeId);
 }

+ 77 - 0
alien-store/src/main/java/shop/alien/store/service/StoreStaffFitnessBaseService.java

@@ -0,0 +1,77 @@
+package shop.alien.store.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreStaffFitnessBase;
+
+import java.util.List;
+
+/**
+ * 运动健身员工基本信息表 服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface StoreStaffFitnessBaseService extends IService<StoreStaffFitnessBase> {
+
+    /**
+     * 新增运动健身员工基本信息
+     *
+     * @param storeStaffFitnessBase 运动健身员工基本信息
+     * @return R<StoreStaffFitnessBase>
+     */
+    R<StoreStaffFitnessBase> addStoreStaffFitnessBase(StoreStaffFitnessBase storeStaffFitnessBase);
+
+    /**
+     * 编辑运动健身员工基本信息
+     *
+     * @param storeStaffFitnessBase 运动健身员工基本信息
+     * @return R<StoreStaffFitnessBase>
+     */
+    R<StoreStaffFitnessBase> editStoreStaffFitnessBase(StoreStaffFitnessBase storeStaffFitnessBase);
+
+    /**
+     * 删除运动健身员工基本信息
+     *
+     * @param id 主键
+     * @return R<Boolean>
+     */
+    R<Boolean> deleteStoreStaffFitnessBase(Integer id);
+
+    /**
+     * 根据ID查询运动健身员工基本信息
+     *
+     * @param id 主键
+     * @return R<StoreStaffFitnessBase>
+     */
+    R<StoreStaffFitnessBase> getStoreStaffFitnessBaseById(Integer id);
+
+    /**
+     * 根据员工ID查询基本信息
+     *
+     * @param staffId 员工id
+     * @return R<StoreStaffFitnessBase>
+     */
+    R<StoreStaffFitnessBase> getByStaffId(Integer staffId);
+
+    /**
+     * 分页查询运动健身员工基本信息列表
+     *
+     * @param pageNum  页码
+     * @param pageSize 页容
+     * @param staffId  员工id
+     * @param education 学历
+     * @return IPage<StoreStaffFitnessBase>
+     */
+    IPage<StoreStaffFitnessBase> getPage(int pageNum, int pageSize, Integer staffId, String education);
+
+    /**
+     * 批量删除运动健身员工基本信息(逻辑删除)
+     *
+     * @param ids 主键ID列表
+     * @return boolean
+     */
+    boolean deleteBatch(List<Integer> ids);
+}
+

+ 86 - 0
alien-store/src/main/java/shop/alien/store/service/StoreStaffFitnessCertificationService.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.result.R;
+import shop.alien.entity.store.StoreStaffFitnessCertification;
+
+import java.util.List;
+
+/**
+ * 运动健身员工认证/荣誉表 服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface StoreStaffFitnessCertificationService extends IService<StoreStaffFitnessCertification> {
+
+    /**
+     * 新增运动健身员工认证/荣誉
+     *
+     * @param storeStaffFitnessCertification 运动健身员工认证/荣誉
+     * @return R<StoreStaffFitnessCertification>
+     */
+    R<StoreStaffFitnessCertification> addStoreStaffFitnessCertification(StoreStaffFitnessCertification storeStaffFitnessCertification);
+
+    /**
+     * 编辑运动健身员工认证/荣誉
+     *
+     * @param storeStaffFitnessCertification 运动健身员工认证/荣誉
+     * @return R<StoreStaffFitnessCertification>
+     */
+    R<StoreStaffFitnessCertification> editStoreStaffFitnessCertification(StoreStaffFitnessCertification storeStaffFitnessCertification);
+
+    /**
+     * 删除运动健身员工认证/荣誉
+     *
+     * @param id 主键
+     * @return R<Boolean>
+     */
+    R<Boolean> deleteStoreStaffFitnessCertification(Integer id);
+
+    /**
+     * 根据ID查询运动健身员工认证/荣誉
+     *
+     * @param id 主键
+     * @return R<StoreStaffFitnessCertification>
+     */
+    R<StoreStaffFitnessCertification> getStoreStaffFitnessCertificationById(Integer id);
+
+    /**
+     * 分页查询运动健身员工认证/荣誉列表
+     *
+     * @param pageNum  页码
+     * @param pageSize 页容
+     * @param staffId  员工id
+     * @param type     类型(1-认证 2-荣誉)
+     * @param name     名称(模糊查询)
+     * @return IPage<StoreStaffFitnessCertification>
+     */
+    IPage<StoreStaffFitnessCertification> getPage(int pageNum, int pageSize, Integer staffId, Integer type, String name);
+
+    /**
+     * 根据员工ID查询认证/荣誉列表
+     *
+     * @param staffId 员工id
+     * @return List<StoreStaffFitnessCertification>
+     */
+    List<StoreStaffFitnessCertification> getListByStaffId(Integer staffId);
+
+    /**
+     * 根据员工ID和类型查询认证/荣誉列表
+     *
+     * @param staffId 员工id
+     * @param type    类型(1-认证 2-荣誉)
+     * @return List<StoreStaffFitnessCertification>
+     */
+    List<StoreStaffFitnessCertification> getListByStaffIdAndType(Integer staffId, Integer type);
+
+    /**
+     * 批量删除运动健身员工认证/荣誉(逻辑删除)
+     *
+     * @param ids 主键ID列表
+     * @return boolean
+     */
+    boolean deleteBatch(List<Integer> ids);
+}

+ 78 - 0
alien-store/src/main/java/shop/alien/store/service/StoreStaffFitnessCourseService.java

@@ -0,0 +1,78 @@
+package shop.alien.store.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreStaffFitnessCourse;
+
+import java.util.List;
+
+/**
+ * 运动健身员工课程信息表 服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface StoreStaffFitnessCourseService extends IService<StoreStaffFitnessCourse> {
+
+    /**
+     * 新增运动健身员工课程
+     *
+     * @param storeStaffFitnessCourse 运动健身员工课程
+     * @return R<StoreStaffFitnessCourse>
+     */
+    R<StoreStaffFitnessCourse> addStoreStaffFitnessCourse(StoreStaffFitnessCourse storeStaffFitnessCourse);
+
+    /**
+     * 编辑运动健身员工课程
+     *
+     * @param storeStaffFitnessCourse 运动健身员工课程
+     * @return R<StoreStaffFitnessCourse>
+     */
+    R<StoreStaffFitnessCourse> editStoreStaffFitnessCourse(StoreStaffFitnessCourse storeStaffFitnessCourse);
+
+    /**
+     * 删除运动健身员工课程
+     *
+     * @param id 主键
+     * @return R<Boolean>
+     */
+    R<Boolean> deleteStoreStaffFitnessCourse(Integer id);
+
+    /**
+     * 根据ID查询运动健身员工课程
+     *
+     * @param id 主键
+     * @return R<StoreStaffFitnessCourse>
+     */
+    R<StoreStaffFitnessCourse> getStoreStaffFitnessCourseById(Integer id);
+
+    /**
+     * 分页查询运动健身员工课程列表
+     *
+     * @param pageNum  页码
+     * @param pageSize 页容
+     * @param staffId  员工id
+     * @param courseType 课程类型
+     * @param courseName 项目名称(模糊查询)
+     * @return IPage<StoreStaffFitnessCourse>
+     */
+    IPage<StoreStaffFitnessCourse> getPage(int pageNum, int pageSize, Integer staffId, String courseType, String courseName);
+
+    /**
+     * 根据员工ID查询课程列表
+     *
+     * @param staffId 员工id
+     * @return List<StoreStaffFitnessCourse>
+     */
+    List<StoreStaffFitnessCourse> getListByStaffId(Integer staffId);
+
+    /**
+     * 批量删除运动健身员工课程(逻辑删除)
+     *
+     * @param ids 主键ID列表
+     * @return boolean
+     */
+    boolean deleteBatch(List<Integer> ids);
+}
+

+ 78 - 0
alien-store/src/main/java/shop/alien/store/service/StoreStaffFitnessExperienceService.java

@@ -0,0 +1,78 @@
+package shop.alien.store.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreStaffFitnessExperience;
+
+import java.util.List;
+
+/**
+ * 运动健身员工从业经历表 服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface StoreStaffFitnessExperienceService extends IService<StoreStaffFitnessExperience> {
+
+    /**
+     * 新增运动健身员工从业经历
+     *
+     * @param storeStaffFitnessExperience 运动健身员工从业经历
+     * @return R<StoreStaffFitnessExperience>
+     */
+    R<StoreStaffFitnessExperience> addStoreStaffFitnessExperience(StoreStaffFitnessExperience storeStaffFitnessExperience);
+
+    /**
+     * 编辑运动健身员工从业经历
+     *
+     * @param storeStaffFitnessExperience 运动健身员工从业经历
+     * @return R<StoreStaffFitnessExperience>
+     */
+    R<StoreStaffFitnessExperience> editStoreStaffFitnessExperience(StoreStaffFitnessExperience storeStaffFitnessExperience);
+
+    /**
+     * 删除运动健身员工从业经历
+     *
+     * @param id 主键
+     * @return R<Boolean>
+     */
+    R<Boolean> deleteStoreStaffFitnessExperience(Integer id);
+
+    /**
+     * 根据ID查询运动健身员工从业经历
+     *
+     * @param id 主键
+     * @return R<StoreStaffFitnessExperience>
+     */
+    R<StoreStaffFitnessExperience> getStoreStaffFitnessExperienceById(Integer id);
+
+    /**
+     * 分页查询运动健身员工从业经历列表
+     *
+     * @param pageNum  页码
+     * @param pageSize 页容
+     * @param staffId  员工id
+     * @param workUnit 工作单位(模糊查询)
+     * @param workPosition 职位(模糊查询)
+     * @return IPage<StoreStaffFitnessExperience>
+     */
+    IPage<StoreStaffFitnessExperience> getPage(int pageNum, int pageSize, Integer staffId, String workUnit, String workPosition);
+
+    /**
+     * 根据员工ID查询从业经历列表
+     *
+     * @param staffId 员工id
+     * @return List<StoreStaffFitnessExperience>
+     */
+    List<StoreStaffFitnessExperience> getListByStaffId(Integer staffId);
+
+    /**
+     * 批量删除运动健身员工从业经历(逻辑删除)
+     *
+     * @param ids 主键ID列表
+     * @return boolean
+     */
+    boolean deleteBatch(List<Integer> ids);
+}
+

+ 108 - 0
alien-store/src/main/java/shop/alien/store/service/impl/BarPerformanceAuditServiceImpl.java

@@ -0,0 +1,108 @@
+package shop.alien.store.service.impl;
+
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+import shop.alien.entity.store.BarPerformance;
+import shop.alien.mapper.BarPerformanceMapper;
+import shop.alien.store.service.BarPerformanceAuditService;
+import shop.alien.store.util.ai.AiContentModerationUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 酒吧演出审核服务实现类
+ * 负责异步执行AI内容审核
+ *
+ * @author assistant
+ * @since 2025-12-19
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class BarPerformanceAuditServiceImpl implements BarPerformanceAuditService {
+
+    private final BarPerformanceMapper barPerformanceMapper;
+    private final AiContentModerationUtil aiContentModerationUtil;
+
+    /**
+     * 异步执行AI内容审核
+     * 审核演出名称、演出详情、演出须知等文本内容以及演出海报、详情图片
+     *
+     * @param performanceId 演出ID
+     * @param barPerformance 演出信息
+     */
+    @Override
+    @Async("taskExecutor")
+    public void asyncAuditBarPerformance(Integer performanceId, BarPerformance barPerformance) {
+        try {
+            log.info("开始异步执行AI内容审核,演出ID:{}", performanceId);
+            
+            // 组装AI审核文本内容
+            StringBuilder textContent = new StringBuilder();
+            if (StringUtils.isNotEmpty(barPerformance.getPerformanceName())) {
+                textContent.append(barPerformance.getPerformanceName()).append(" ");
+            }
+            if (StringUtils.isNotEmpty(barPerformance.getPerformanceContent())) {
+                textContent.append(barPerformance.getPerformanceContent()).append(" ");
+            }
+            if (StringUtils.isNotEmpty(barPerformance.getPerformanceNotice())) {
+                textContent.append(barPerformance.getPerformanceNotice());
+            }
+            
+            // 组装图片URL列表
+            List<String> imageUrls = new ArrayList<>();
+            if (StringUtils.isNotEmpty(barPerformance.getPerformancePoster())) {
+                // 演出海报(可能有多张,逗号分隔)
+                String[] posterUrls = barPerformance.getPerformancePoster().split(",");
+                for (String url : posterUrls) {
+                    if (StringUtils.isNotEmpty(url.trim())) {
+                        imageUrls.add(url.trim());
+                    }
+                }
+            }
+            if (StringUtils.isNotEmpty(barPerformance.getPerformanceDetail())) {
+                // 演出详情图片(可能有多张,逗号分隔)
+                String[] detailUrls = barPerformance.getPerformanceDetail().split(",");
+                for (String url : detailUrls) {
+                    if (StringUtils.isNotEmpty(url.trim())) {
+                        imageUrls.add(url.trim());
+                    }
+                }
+            }
+            
+            // 调用AI审核接口
+            AiContentModerationUtil.AuditResult auditResult = aiContentModerationUtil.auditContent(
+                    textContent.toString().trim(), imageUrls);
+            
+            // 根据AI审核结果更新审核状态和拒绝原因
+            BarPerformance auditUpdate = new BarPerformance();
+            auditUpdate.setId(performanceId);
+            if (auditResult != null && auditResult.isPassed()) {
+                // 审核通过
+                auditUpdate.setReviewStatus(1);
+                auditUpdate.setRejectReason(null);
+                log.info("演出内容AI审核通过,演出ID:{}", performanceId);
+            } else {
+                // 审核失败
+                String reason = (auditResult != null && StringUtils.isNotEmpty(auditResult.getFailureReason()))
+                        ? auditResult.getFailureReason()
+                        : "审核未通过";
+                log.warn("演出内容AI审核失败,演出ID:{},原因:{}", performanceId, reason);
+                auditUpdate.setReviewStatus(2);
+                auditUpdate.setRejectReason(reason);
+            }
+            barPerformanceMapper.updateById(auditUpdate);
+            
+            log.info("AI内容审核完成,演出ID:{}", performanceId);
+            
+        } catch (Exception e) {
+            // AI审核异常不影响数据保存,记录日志即可
+            log.error("异步调用AI审核接口异常,演出ID:{}", performanceId, e);
+        }
+    }
+}
+

+ 489 - 0
alien-store/src/main/java/shop/alien/store/service/impl/BarPerformanceServiceImpl.java

@@ -0,0 +1,489 @@
+package shop.alien.store.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import shop.alien.entity.store.BarPerformance;
+import shop.alien.mapper.BarPerformanceMapper;
+import shop.alien.store.service.BarPerformanceService;
+import shop.alien.store.util.ai.AiContentModerationUtil;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 酒吧演出服务实现类
+ * 实现酒吧演出的CRUD、审核、上下线等核心业务功能
+ *
+ * @author assistant
+ * @since 2025-12-17
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class BarPerformanceServiceImpl implements BarPerformanceService {
+
+    private final BarPerformanceMapper barPerformanceMapper;
+    private final AiContentModerationUtil aiContentModerationUtil;
+
+    @Override
+    public IPage<BarPerformance> getBarPerformanceList(int page, int size, String status) {
+        IPage<BarPerformance> performancePage = new Page<>(page, size);
+        QueryWrapper<BarPerformance> queryWrapper = new QueryWrapper<>();
+        // 根据状态查询演出
+        queryWrapper.like(StringUtils.isNotEmpty(status), "status", status);
+        // 只查询未删除的记录
+        queryWrapper.eq("delete_flag", 0);
+        // 按创建时间降序排序
+        queryWrapper.orderByDesc("created_time");
+        return barPerformanceMapper.selectPage(performancePage, queryWrapper);
+    }
+
+    @Override
+    public int addOrUpdateBarPerformance(BarPerformance barPerformance) {
+        // 1. 演出名称验证:必填且限20字
+        if (StringUtils.isEmpty(barPerformance.getPerformanceName())) {
+            throw new IllegalArgumentException("演出名称不能为空");
+        }
+        if (barPerformance.getPerformanceName().length() > 20) {
+            throw new IllegalArgumentException("演出名称长度不能超过20个字符");
+        }
+        
+        // 2. 演出海报验证:必填且限1张
+        if (StringUtils.isEmpty(barPerformance.getPerformancePoster())) {
+            throw new IllegalArgumentException("演出海报不能为空");
+        }
+        // 检查海报是否有多张(逗号分隔)
+        String[] posterImages = barPerformance.getPerformancePoster().split(",");
+        if (posterImages.length > 1) {
+            throw new IllegalArgumentException("演出海报最多只能上传1张");
+        }
+        
+        // 3. 演出类型和频次的关联验证:特邀演出(0)只能选单次(0)
+        if (barPerformance.getPerformanceType() != null && barPerformance.getPerformanceType() == 0) {
+            // 特邀演出
+            if (StringUtils.isEmpty(barPerformance.getPerformanceFrequency()) || !"0".equals(barPerformance.getPerformanceFrequency())) {
+                throw new IllegalArgumentException("特邀演出只能选择单次演出");
+            }
+        }
+        
+        // 4. 演出频次相关验证
+        String performanceFrequency = barPerformance.getPerformanceFrequency();
+        if (StringUtils.isEmpty(performanceFrequency)) {
+            throw new IllegalArgumentException("演出频次不能为空");
+        }
+        
+        // 获取今天的开始时间(用于时间验证)
+        java.util.Calendar cal = java.util.Calendar.getInstance();
+        cal.set(java.util.Calendar.HOUR_OF_DAY, 0);
+        cal.set(java.util.Calendar.MINUTE, 0);
+        cal.set(java.util.Calendar.SECOND, 0);
+        cal.set(java.util.Calendar.MILLISECOND, 0);
+        Date todayStart = cal.getTime();
+        
+        switch (performanceFrequency) {
+            case "0": // 单次演出
+                if (barPerformance.getSingleStartDatetime() == null) {
+                    throw new IllegalArgumentException("单次演出必须填写开始时间");
+                }
+                if (barPerformance.getSingleEndDatetime() == null) {
+                    throw new IllegalArgumentException("单次演出必须填写结束时间");
+                }
+                // 验证开始时间必须是今天或以后
+                if (barPerformance.getSingleStartDatetime().before(todayStart)) {
+                    throw new IllegalArgumentException("开始时间必须是今天或以后");
+                }
+                // 确保开始时间早于结束时间
+                if (barPerformance.getSingleStartDatetime().after(barPerformance.getSingleEndDatetime())) {
+                    throw new IllegalArgumentException("开始时间不能晚于结束时间");
+                }
+                // 单次演出不需要周天数据,清除周天字段(设置为空字符串,确保数据库插入时包含该字段)
+                barPerformance.setPerformanceWeek("");
+                break;
+            case "1": // 每天定时
+                if (barPerformance.getDailyStartDate() == null) {
+                    throw new IllegalArgumentException("每天定时演出必须填写开始日期");
+                }
+                if (barPerformance.getDailyEndDate() == null) {
+                    throw new IllegalArgumentException("每天定时演出必须填写结束日期");
+                }
+                // 验证开始日期必须是今天或以后
+                if (barPerformance.getDailyStartDate().before(todayStart)) {
+                    throw new IllegalArgumentException("开始日期必须是今天或以后");
+                }
+                // 确保开始日期早于或等于结束日期
+                if (barPerformance.getDailyStartDate().after(barPerformance.getDailyEndDate())) {
+                    throw new IllegalArgumentException("开始日期不能晚于结束日期");
+                }
+                // 验证时间字段(复用single_start_datetime和single_end_datetime,只使用时间部分)
+                if (barPerformance.getSingleStartDatetime() == null) {
+                    throw new IllegalArgumentException("每天定时演出必须填写开始时间");
+                }
+                if (barPerformance.getSingleEndDatetime() == null) {
+                    throw new IllegalArgumentException("每天定时演出必须填写结束时间");
+                }
+                // 验证时间逻辑:开始时间必须早于结束时间(只比较时间部分,忽略日期部分)
+                if (!isTimeBefore(barPerformance.getSingleStartDatetime(), barPerformance.getSingleEndDatetime())) {
+                    throw new IllegalArgumentException("开始时间必须早于结束时间");
+                }
+                // 每天定时不需要周天数据,清除周天字段(设置为空字符串,确保数据库插入时包含该字段)
+                barPerformance.setPerformanceWeek("");
+                break;
+            case "2": // 每周定时
+                if (barPerformance.getDailyStartDate() == null) {
+                    throw new IllegalArgumentException("每周定时演出必须填写开始日期");
+                }
+                if (barPerformance.getDailyEndDate() == null) {
+                    throw new IllegalArgumentException("每周定时演出必须填写结束日期");
+                }
+                // 验证开始日期必须是今天或以后
+                if (barPerformance.getDailyStartDate().before(todayStart)) {
+                    throw new IllegalArgumentException("开始日期必须是今天或以后");
+                }
+                // 确保开始日期早于或等于结束日期
+                if (barPerformance.getDailyStartDate().after(barPerformance.getDailyEndDate())) {
+                    throw new IllegalArgumentException("开始日期不能晚于结束日期");
+                }
+                // 每周定时演出需要验证演出日期(周几)
+                if (StringUtils.isEmpty(barPerformance.getPerformanceWeek())) {
+                    throw new IllegalArgumentException("每周定时演出必须选择演出日期");
+                }
+                // 验证时间字段(复用single_start_datetime和single_end_datetime,只使用时间部分)
+                if (barPerformance.getSingleStartDatetime() == null) {
+                    throw new IllegalArgumentException("每周定时演出必须填写开始时间");
+                }
+                if (barPerformance.getSingleEndDatetime() == null) {
+                    throw new IllegalArgumentException("每周定时演出必须填写结束时间");
+                }
+                // 验证时间逻辑:开始时间必须早于结束时间(只比较时间部分,忽略日期部分)
+                if (!isTimeBefore(barPerformance.getSingleStartDatetime(), barPerformance.getSingleEndDatetime())) {
+                    throw new IllegalArgumentException("开始时间必须早于结束时间");
+                }
+                break;
+            default:
+                throw new IllegalArgumentException("演出频次类型无效");
+        }
+
+        // 5. 图文详情图片验证:最多9张
+        if (StringUtils.isNotEmpty(barPerformance.getPerformanceDetail())) {
+            String[] detailImages = barPerformance.getPerformanceDetail().split(",");
+            if (detailImages.length > 9) {
+                throw new IllegalArgumentException("图文详情图片最多只能上传9张");
+            }
+            // 检查每张图片URL格式是否有效
+            for (String imageUrl : detailImages) {
+                if (StringUtils.isEmpty(imageUrl.trim())) {
+                    throw new IllegalArgumentException("图文详情图片URL不能为空");
+                }
+            }
+        }
+
+        // 6. 演出须知字数验证:限300字
+        if (StringUtils.isNotEmpty(barPerformance.getPerformanceNotice()) && barPerformance.getPerformanceNotice().length() > 300) {
+            throw new IllegalArgumentException("演出须知不能超过300个字符");
+        }
+        
+        // 7. 图文详情文字验证:限300字(如果有performanceContent字段)
+        if (StringUtils.isNotEmpty(barPerformance.getPerformanceContent()) && barPerformance.getPerformanceContent().length() > 300) {
+            throw new IllegalArgumentException("图文详情文字不能超过300个字符");
+        }
+
+        // 8. 图文内容审核(调用AI内容审核接口)- 新增或更新时都必须进行
+        // - 文本:名称 + 详情 + 须知 + 风格(如果有)
+        // - 图片:海报URL(如果有) + 图文详情图片URL(如果有)
+        String moderationText = buildModerationText(barPerformance);
+        List<String> imageUrls = new ArrayList<>();
+        if (StringUtils.isNotEmpty(barPerformance.getPerformancePoster())) {
+            imageUrls.add(barPerformance.getPerformancePoster());
+        }
+        // 添加图文详情图片到审核列表
+        if (StringUtils.isNotEmpty(barPerformance.getPerformanceDetail())) {
+            String[] detailImages = barPerformance.getPerformanceDetail().split(",");
+            for (String imageUrl : detailImages) {
+                if (StringUtils.isNotEmpty(imageUrl.trim())) {
+                    imageUrls.add(imageUrl.trim());
+                }
+            }
+        }
+        // 调用AI内容审核接口,如果检测到违规内容会抛出异常
+        AiContentModerationUtil.AuditResult auditResult = aiContentModerationUtil.auditContent(moderationText, imageUrls);
+        if (auditResult == null || !auditResult.isPassed()) {
+            String failureReason = (auditResult != null && StringUtils.isNotEmpty(auditResult.getFailureReason()))
+                    ? auditResult.getFailureReason()
+                    : "内容包含违规信息";
+            log.warn("酒吧演出内容审核失败:{}", failureReason);
+            throw new IllegalArgumentException("内容审核未通过:" + failureReason);
+        }
+        log.info("酒吧演出内容审核通过");
+        
+        Integer id = barPerformance.getId();
+        
+        if (id == null || id == 0) {
+            // 新增操作
+            Date nowDate = new Date(System.currentTimeMillis());
+            barPerformance.setCreatedTime(nowDate);
+            // 设置删除标记为0(未删除)
+            if (barPerformance.getDeleteFlag() == null) {
+                barPerformance.setDeleteFlag(0);
+            }
+            // 设置默认状态为待审核(0)
+            if (barPerformance.getStatus() == null) {
+                barPerformance.setStatus(0);
+            }
+            // 设置默认审核状态为待审核(0),对应数据库字段 review_status
+            if (barPerformance.getReviewStatus() == null) {
+                barPerformance.setReviewStatus(0);
+            }
+            // 设置默认上线状态为下线(0)
+            if (barPerformance.getOnlineStatus() == null) {
+                barPerformance.setOnlineStatus(0);
+            }
+            // 设置默认隐藏状态为不隐藏(0)
+            if (barPerformance.getHidden() == null) {
+                barPerformance.setHidden(0);
+            }
+            // 重置id,让数据库自动生成
+            barPerformance.setId(null);
+            // 确保 performance_week 不为 null(如果为 null 则设置为空字符串,避免数据库插入错误)
+            if (barPerformance.getPerformanceWeek() == null) {
+                barPerformance.setPerformanceWeek("");
+            }
+            return barPerformanceMapper.insert(barPerformance);
+        } else {
+            // 更新操作:先查询记录是否存在
+            BarPerformance existing = barPerformanceMapper.selectById(id);
+            if (existing == null) {
+                // 记录不存在,转为新增操作
+                barPerformance.setId(null);
+                Date nowDate = new Date(System.currentTimeMillis());
+                barPerformance.setCreatedTime(nowDate);
+                // 设置默认值
+                if (barPerformance.getDeleteFlag() == null) {
+                    barPerformance.setDeleteFlag(0);
+                }
+                if (barPerformance.getStatus() == null) {
+                    barPerformance.setStatus(0);
+                }
+                if (barPerformance.getReviewStatus() == null) {
+                    barPerformance.setReviewStatus(0);
+                }
+                if (barPerformance.getOnlineStatus() == null) {
+                    barPerformance.setOnlineStatus(0);
+                }
+                if (barPerformance.getHidden() == null) {
+                    barPerformance.setHidden(0);
+                }
+                // 确保 performance_week 不为 null(如果为 null 则设置为空字符串,避免数据库插入错误)
+                if (barPerformance.getPerformanceWeek() == null) {
+                    barPerformance.setPerformanceWeek("");
+                }
+                return barPerformanceMapper.insert(barPerformance);
+            } else {
+                // 记录存在,执行更新
+                return barPerformanceMapper.updateById(barPerformance);
+            }
+        }
+    }
+
+    @Override
+    public BarPerformance getBarPerformanceDetail(Integer id) {
+        if (id == null || id <= 0) {
+            return null;
+        }
+        
+        QueryWrapper<BarPerformance> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq("id", id);
+        queryWrapper.eq("delete_flag", 0);
+        
+        return barPerformanceMapper.selectOne(queryWrapper);
+    }
+
+    @Override
+    public Integer deleteBarPerformance(Integer id) {
+        if (id == null || id <= 0) {
+            return 0;
+        }
+        // 使用 MyBatis-Plus 的逻辑删除,会自动将 deleteFlag 设置为 1
+        return barPerformanceMapper.deleteById(id);
+    }
+
+    @Override
+    public Integer setOnlineStatus(Integer id, Integer onlineStatus) {
+        if (id == null || onlineStatus == null) {
+            return 0;
+        }
+        
+        LambdaUpdateWrapper<BarPerformance> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
+        lambdaUpdateWrapper.eq(BarPerformance::getId, id);
+        lambdaUpdateWrapper.set(BarPerformance::getOnlineStatus, onlineStatus);
+        
+        return barPerformanceMapper.update(null, lambdaUpdateWrapper);
+    }
+
+    @Override
+    public IPage<BarPerformance> queryPerformanceListByStoreId(Integer page, Integer size, Integer storeId, String status) {
+        // 使用新的方法,支持按审核状态和分类筛选
+        // status参数转换为reviewStatus(对应数据库字段 review_status)
+        Integer reviewStatus = null;
+        if (StringUtils.isNotEmpty(status)) {
+            try {
+                reviewStatus = Integer.parseInt(status);
+            } catch (NumberFormatException e) {
+                // 忽略无效的状态值
+            }
+        }
+        return queryPerformanceListByStoreIdAndCategory(page, size, storeId, reviewStatus, "all", null);
+    }
+
+    @Override
+    public IPage<BarPerformance> queryPerformanceListByStoreIdAndCategory(Integer page, Integer size, Integer storeId, Integer reviewStatus, String category, String performanceName) {
+        if (page == null || size == null || storeId == null || storeId <= 0) {
+            return new Page<>();
+        }
+        
+        IPage<BarPerformance> performancePage = new Page<>(page, size);
+        QueryWrapper<BarPerformance> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq("store_id", storeId);
+        
+        // 按演出名称搜索(支持模糊搜索)
+        if (StringUtils.isNotEmpty(performanceName)) {
+            queryWrapper.like("performance_name", performanceName);
+        }
+        
+        // 按审核状态筛选(数据库字段 review_status)
+        if (reviewStatus != null) {
+            queryWrapper.eq("review_status", reviewStatus);
+        } else {
+            // 不查询草稿(review_status为null的记录)
+            queryWrapper.isNotNull("review_status");
+        }
+        
+        // 按演出分类筛选
+        if (StringUtils.isNotEmpty(category) && !"all".equals(category)) {
+            Date now = new Date();
+            // 获取今天的开始时间(00:00:00)
+            java.util.Calendar cal = java.util.Calendar.getInstance();
+            cal.setTime(now);
+            cal.set(java.util.Calendar.HOUR_OF_DAY, 0);
+            cal.set(java.util.Calendar.MINUTE, 0);
+            cal.set(java.util.Calendar.SECOND, 0);
+            cal.set(java.util.Calendar.MILLISECOND, 0);
+            Date todayStart = cal.getTime();
+            
+            switch (category) {
+                case "not_started": // 未开始
+                    queryWrapper.and(wrapper -> {
+                        // 单次演出:开始时间 > 现在
+                        wrapper.or(w -> w.eq("performance_frequency", "0")
+                                .gt("single_start_datetime", now));
+                        // 定时演出:开始日期 > 今天 或 (开始日期 = 今天 但还未到演出时间)
+                        wrapper.or(w -> w.in("performance_frequency", "1", "2")
+                                .gt("daily_start_date", todayStart));
+                    });
+                    break;
+                case "in_progress": // 进行中
+                    queryWrapper.and(wrapper -> {
+                        // 单次演出:开始时间 <= 现在 AND 结束时间 >= 现在
+                        wrapper.or(w -> w.eq("performance_frequency", "0")
+                                .le("single_start_datetime", now)
+                                .ge("single_end_datetime", now));
+                        // 定时演出:开始日期 <= 今天 AND 结束日期 >= 今天
+                        wrapper.or(w -> w.in("performance_frequency", "1", "2")
+                                .le("daily_start_date", todayStart)
+                                .ge("daily_end_date", todayStart));
+                    });
+                    break;
+                case "ended": // 已结束
+                    queryWrapper.and(wrapper -> {
+                        // 单次演出:结束时间 < 现在
+                        wrapper.or(w -> w.eq("performance_frequency", "0")
+                                .lt("single_end_datetime", now));
+                        // 定时演出:结束日期 < 今天
+                        wrapper.or(w -> w.in("performance_frequency", "1", "2")
+                                .lt("daily_end_date", todayStart));
+                    });
+                    break;
+                default:
+                    // 其他情况,不添加额外条件
+                    break;
+            }
+        }
+        
+        queryWrapper.eq("delete_flag", 0);
+        // 按提交时间(创建时间)倒序排序
+        queryWrapper.orderByDesc("created_time");
+        
+        return barPerformanceMapper.selectPage(performancePage, queryWrapper);
+    }
+
+    /**
+     * 比较两个时间(只比较时间部分,忽略日期部分)
+     * 用于每天定时和每周定时演出的时间验证
+     * 
+     * @param time1 开始时间
+     * @param time2 结束时间
+     * @return true表示time1早于time2,false表示time1晚于或等于time2
+     */
+    private boolean isTimeBefore(Date time1, Date time2) {
+        if (time1 == null || time2 == null) {
+            return false;
+        }
+        java.util.Calendar cal1 = java.util.Calendar.getInstance();
+        cal1.setTime(time1);
+        int hour1 = cal1.get(java.util.Calendar.HOUR_OF_DAY);
+        int minute1 = cal1.get(java.util.Calendar.MINUTE);
+        int second1 = cal1.get(java.util.Calendar.SECOND);
+        
+        java.util.Calendar cal2 = java.util.Calendar.getInstance();
+        cal2.setTime(time2);
+        int hour2 = cal2.get(java.util.Calendar.HOUR_OF_DAY);
+        int minute2 = cal2.get(java.util.Calendar.MINUTE);
+        int second2 = cal2.get(java.util.Calendar.SECOND);
+        
+        // 比较小时
+        if (hour1 < hour2) {
+            return true;
+        } else if (hour1 > hour2) {
+            return false;
+        }
+        
+        // 小时相同,比较分钟
+        if (minute1 < minute2) {
+            return true;
+        } else if (minute1 > minute2) {
+            return false;
+        }
+        
+        // 分钟相同,比较秒
+        return second1 < second2;
+    }
+
+    private String buildModerationText(BarPerformance barPerformance) {
+        StringBuilder sb = new StringBuilder();
+        if (barPerformance == null) {
+            return "";
+        }
+        if (StringUtils.isNotEmpty(barPerformance.getPerformanceName())) {
+            sb.append("演出名称:").append(barPerformance.getPerformanceName()).append("\n");
+        }
+        if (StringUtils.isNotEmpty(barPerformance.getPerformanceStyle())) {
+            sb.append("演出风格:").append(barPerformance.getPerformanceStyle()).append("\n");
+        }
+        if (StringUtils.isNotEmpty(barPerformance.getPerformanceContent())) {
+            sb.append("演出详情:").append(barPerformance.getPerformanceContent()).append("\n");
+        }
+        if (StringUtils.isNotEmpty(barPerformance.getPerformanceNotice())) {
+            sb.append("演出须知:").append(barPerformance.getPerformanceNotice()).append("\n");
+        }
+        if (StringUtils.isNotEmpty(barPerformance.getPerformanceDetail())) {
+            sb.append("图文详情:").append(barPerformance.getPerformanceDetail()).append("\n");
+        }
+        return sb.toString();
+    }
+}

+ 391 - 0
alien-store/src/main/java/shop/alien/store/service/impl/FitnessEquipmentInfoServiceImpl.java

@@ -0,0 +1,391 @@
+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.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+import shop.alien.entity.store.FitnessEquipmentInfo;
+import shop.alien.mapper.FitnessEquipmentInfoMapper;
+import shop.alien.store.service.FitnessEquipmentInfoService;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 健身设备信息服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+@Transactional(rollbackFor = Exception.class)
+public class FitnessEquipmentInfoServiceImpl extends ServiceImpl<FitnessEquipmentInfoMapper, FitnessEquipmentInfo>
+        implements FitnessEquipmentInfoService {
+
+    /**
+     * 删除标志:未删除
+     */
+    private static final int DELETE_FLAG_NOT_DELETED = 0;
+
+    /**
+     * 状态:启用
+     */
+    private static final int STATUS_ENABLED = 1;
+
+    /**
+     * 状态:禁用
+     */
+    private static final int STATUS_DISABLED = 0;
+
+    /**
+     * 默认排序号
+     */
+    private static final int DEFAULT_SORT_ORDER = 0;
+
+    /**
+     * 默认页码
+     */
+    private static final int DEFAULT_PAGE = 1;
+
+    /**
+     * 默认页大小
+     */
+    private static final int DEFAULT_PAGE_SIZE = 10;
+
+    /**
+     * 健身设备信息Mapper
+     */
+    private final FitnessEquipmentInfoMapper fitnessEquipmentInfoMapper;
+
+    /**
+     * 分页查询健身设备信息列表
+     *
+     * @param pageNum       页码
+     * @param pageSize      页大小
+     * @param equipmentType 设备类型
+     * @param equipmentName 设备名称(模糊查询)
+     * @param status        状态
+     * @return 健身设备信息分页列表
+     */
+    @Override
+    public IPage<FitnessEquipmentInfo> getPageList(Integer pageNum, Integer pageSize, 
+                                                   Integer equipmentType, String equipmentName, Integer status) {
+        log.info("分页查询健身设备信息列表,pageNum={}, pageSize={}, equipmentType={}, equipmentName={}, status={}", 
+                pageNum, pageSize, equipmentType, equipmentName, status);
+
+        // 参数验证
+        if (pageNum == null || pageNum < DEFAULT_PAGE) {
+            log.warn("分页查询健身设备信息列表,页码参数无效,使用默认值:{}", pageNum);
+            pageNum = DEFAULT_PAGE;
+        }
+        if (pageSize == null || pageSize < 1) {
+            log.warn("分页查询健身设备信息列表,页大小参数无效,使用默认值:{}", pageSize);
+            pageSize = DEFAULT_PAGE_SIZE;
+        }
+
+        try {
+            Page<FitnessEquipmentInfo> page = new Page<>(pageNum, pageSize);
+            LambdaQueryWrapper<FitnessEquipmentInfo> queryWrapper = new LambdaQueryWrapper<>();
+
+            queryWrapper.eq(equipmentType != null, FitnessEquipmentInfo::getEquipmentType, equipmentType)
+                    .like(StringUtils.hasText(equipmentName), FitnessEquipmentInfo::getEquipmentName, equipmentName)
+                    .eq(status != null, FitnessEquipmentInfo::getStatus, status)
+                    .eq(FitnessEquipmentInfo::getDeleteFlag, DELETE_FLAG_NOT_DELETED)
+                    .orderByAsc(FitnessEquipmentInfo::getSortOrder)
+                    .orderByDesc(FitnessEquipmentInfo::getCreatedTime);
+
+            IPage<FitnessEquipmentInfo> result = fitnessEquipmentInfoMapper.selectPage(page, queryWrapper);
+            log.info("分页查询健身设备信息列表成功,查询到{}条记录", result.getTotal());
+            return result;
+        } catch (Exception e) {
+            log.error("分页查询健身设备信息列表异常,异常信息:{}", e.getMessage(), e);
+            throw new RuntimeException("分页查询健身设备信息列表异常:" + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 查询健身设备信息列表
+     *
+     * @param equipmentType 设备类型
+     * @param status        状态
+     * @return 健身设备信息列表
+     */
+    @Override
+    public List<FitnessEquipmentInfo> getList(Integer equipmentType, Integer status) {
+        log.info("查询健身设备信息列表,equipmentType={}, status={}", equipmentType, status);
+
+        try {
+            LambdaQueryWrapper<FitnessEquipmentInfo> queryWrapper = new LambdaQueryWrapper<>();
+
+            queryWrapper.eq(equipmentType != null, FitnessEquipmentInfo::getEquipmentType, equipmentType)
+                    .eq(status != null, FitnessEquipmentInfo::getStatus, status)
+                    .eq(FitnessEquipmentInfo::getDeleteFlag, DELETE_FLAG_NOT_DELETED)
+                    .orderByAsc(FitnessEquipmentInfo::getSortOrder)
+                    .orderByDesc(FitnessEquipmentInfo::getCreatedTime);
+
+            List<FitnessEquipmentInfo> result = fitnessEquipmentInfoMapper.selectList(queryWrapper);
+            log.info("查询健身设备信息列表成功,查询到{}条记录", result.size());
+            return result;
+        } catch (Exception e) {
+            log.error("查询健身设备信息列表异常,异常信息:{}", e.getMessage(), e);
+            return Collections.emptyList();
+        }
+    }
+
+    /**
+     * 根据设备类型查询设备列表(用于前端分类展示)
+     *
+     * @param equipmentType 设备类型
+     * @return 健身设备信息列表
+     */
+    @Override
+    public List<FitnessEquipmentInfo> getListByType(Integer equipmentType) {
+        log.info("根据设备类型查询设备列表,equipmentType={}", equipmentType);
+
+        if (equipmentType == null || equipmentType <= 0) {
+            log.warn("根据设备类型查询设备列表失败,设备类型无效:{}", equipmentType);
+            return Collections.emptyList();
+        }
+
+        try {
+            LambdaQueryWrapper<FitnessEquipmentInfo> queryWrapper = new LambdaQueryWrapper<>();
+
+            queryWrapper.eq(FitnessEquipmentInfo::getEquipmentType, equipmentType)
+                    .eq(FitnessEquipmentInfo::getStatus, STATUS_ENABLED)
+                    .eq(FitnessEquipmentInfo::getDeleteFlag, DELETE_FLAG_NOT_DELETED)
+                    .orderByAsc(FitnessEquipmentInfo::getSortOrder)
+                    .orderByDesc(FitnessEquipmentInfo::getCreatedTime);
+
+            List<FitnessEquipmentInfo> result = fitnessEquipmentInfoMapper.selectList(queryWrapper);
+            log.info("根据设备类型查询设备列表成功,equipmentType={},查询到{}条记录", equipmentType, result.size());
+            return result;
+        } catch (Exception e) {
+            log.error("根据设备类型查询设备列表异常,equipmentType={},异常信息:{}", equipmentType, e.getMessage(), e);
+            return Collections.emptyList();
+        }
+    }
+
+    /**
+     * 根据ID查询健身设备信息详情
+     *
+     * @param id 主键ID
+     * @return 健身设备信息
+     */
+    @Override
+    public FitnessEquipmentInfo getDetailById(Integer id) {
+        log.info("根据ID查询健身设备信息详情,id={}", id);
+
+        if (id == null || id <= 0) {
+            log.warn("根据ID查询健身设备信息详情失败,ID无效:{}", id);
+            return null;
+        }
+
+        try {
+            FitnessEquipmentInfo result = fitnessEquipmentInfoMapper.selectById(id);
+            if (result != null && result.getDeleteFlag() != null && result.getDeleteFlag() == DELETE_FLAG_NOT_DELETED) {
+                log.info("根据ID查询健身设备信息详情成功,id={}", id);
+                return result;
+            } else {
+                log.warn("根据ID查询健身设备信息详情失败,设备不存在或已删除,id={}", id);
+                return null;
+            }
+        } catch (Exception e) {
+            log.error("根据ID查询健身设备信息详情异常,id={},异常信息:{}", id, e.getMessage(), e);
+            return null;
+        }
+    }
+
+    /**
+     * 新增健身设备信息
+     *
+     * @param fitnessEquipmentInfo 健身设备信息对象
+     * @return true表示新增成功,false表示新增失败
+     */
+    @Override
+    public boolean saveEquipment(FitnessEquipmentInfo fitnessEquipmentInfo) {
+        log.info("新增健身设备信息,请求参数:{}", fitnessEquipmentInfo);
+
+        if (fitnessEquipmentInfo == null) {
+            log.error("新增健身设备信息失败,设备信息对象为空");
+            return false;
+        }
+
+        // 参数验证
+        if (!StringUtils.hasText(fitnessEquipmentInfo.getEquipmentName())) {
+            log.error("新增健身设备信息失败,设备名称为空");
+            return false;
+        }
+        if (fitnessEquipmentInfo.getEquipmentType() == null || fitnessEquipmentInfo.getEquipmentType() <= 0) {
+            log.error("新增健身设备信息失败,设备类型无效:{}", fitnessEquipmentInfo.getEquipmentType());
+            return false;
+        }
+
+        try {
+            // 设置默认值
+            if (fitnessEquipmentInfo.getSortOrder() == null) {
+                fitnessEquipmentInfo.setSortOrder(DEFAULT_SORT_ORDER);
+            }
+            if (fitnessEquipmentInfo.getStatus() == null) {
+                fitnessEquipmentInfo.setStatus(STATUS_ENABLED);
+            }
+            fitnessEquipmentInfo.setDeleteFlag(DELETE_FLAG_NOT_DELETED);
+            // createdTime和updatedTime由MyBatis-Plus自动填充
+
+            int insertResult = fitnessEquipmentInfoMapper.insert(fitnessEquipmentInfo);
+            if (insertResult > 0) {
+                log.info("新增健身设备信息成功,id={}, equipmentName={}", 
+                        fitnessEquipmentInfo.getId(), fitnessEquipmentInfo.getEquipmentName());
+                return true;
+            } else {
+                log.warn("新增健身设备信息失败,插入记录数为0");
+                return false;
+            }
+        } catch (Exception e) {
+            log.error("新增健身设备信息异常,异常信息:{}", e.getMessage(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 修改健身设备信息
+     *
+     * @param fitnessEquipmentInfo 健身设备信息对象
+     * @return true表示修改成功,false表示修改失败
+     */
+    @Override
+    public boolean updateEquipment(FitnessEquipmentInfo fitnessEquipmentInfo) {
+        log.info("修改健身设备信息,请求参数:{}", fitnessEquipmentInfo);
+
+        if (fitnessEquipmentInfo == null || fitnessEquipmentInfo.getId() == null) {
+            log.error("修改健身设备信息失败,设备信息对象为空或ID为空");
+            return false;
+        }
+
+        // 参数验证
+        if (fitnessEquipmentInfo.getId() <= 0) {
+            log.error("修改健身设备信息失败,ID无效:{}", fitnessEquipmentInfo.getId());
+            return false;
+        }
+
+        try {
+            // 检查设备是否存在
+            FitnessEquipmentInfo existing = fitnessEquipmentInfoMapper.selectById(fitnessEquipmentInfo.getId());
+            if (existing == null || existing.getDeleteFlag() == null || existing.getDeleteFlag() != DELETE_FLAG_NOT_DELETED) {
+                log.warn("修改健身设备信息失败,设备不存在或已删除,id={}", fitnessEquipmentInfo.getId());
+                return false;
+            }
+
+            // updatedTime由MyBatis-Plus自动填充
+
+            int updateResult = fitnessEquipmentInfoMapper.updateById(fitnessEquipmentInfo);
+            if (updateResult > 0) {
+                log.info("修改健身设备信息成功,id={}", fitnessEquipmentInfo.getId());
+                return true;
+            } else {
+                log.warn("修改健身设备信息失败,更新记录数为0,id={}", fitnessEquipmentInfo.getId());
+                return false;
+            }
+        } catch (Exception e) {
+            log.error("修改健身设备信息异常,id={},异常信息:{}", fitnessEquipmentInfo.getId(), e.getMessage(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 删除健身设备信息(逻辑删除)
+     *
+     * @param id 主键ID
+     * @return true表示删除成功,false表示删除失败
+     */
+    @Override
+    public boolean deleteEquipment(Integer id) {
+        log.info("删除健身设备信息,id={}", id);
+
+        if (id == null || id <= 0) {
+            log.error("删除健身设备信息失败,ID无效:{}", id);
+            return false;
+        }
+
+        try {
+            // 检查设备是否存在
+            FitnessEquipmentInfo existing = fitnessEquipmentInfoMapper.selectById(id);
+            if (existing == null || existing.getDeleteFlag() == null || existing.getDeleteFlag() != DELETE_FLAG_NOT_DELETED) {
+                log.warn("删除健身设备信息失败,设备不存在或已删除,id={}", id);
+                return false;
+            }
+
+            // 逻辑删除
+            FitnessEquipmentInfo updateEntity = new FitnessEquipmentInfo();
+            updateEntity.setId(id);
+            updateEntity.setDeleteFlag(1);
+            // updatedTime由MyBatis-Plus自动填充
+
+            int updateResult = fitnessEquipmentInfoMapper.updateById(updateEntity);
+            if (updateResult > 0) {
+                log.info("删除健身设备信息成功,id={}", id);
+                return true;
+            } else {
+                log.warn("删除健身设备信息失败,更新记录数为0,id={}", id);
+                return false;
+            }
+        } catch (Exception e) {
+            log.error("删除健身设备信息异常,id={},异常信息:{}", id, e.getMessage(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 启用/禁用健身设备信息
+     *
+     * @param id     主键ID
+     * @param status 状态(0:禁用, 1:启用)
+     * @return true表示操作成功,false表示操作失败
+     */
+    @Override
+    public boolean updateStatus(Integer id, Integer status) {
+        log.info("更新健身设备信息状态,id={}, status={}", id, status);
+
+        if (id == null || id <= 0) {
+            log.error("更新健身设备信息状态失败,ID无效:{}", id);
+            return false;
+        }
+        if (status == null || (status != STATUS_ENABLED && status != STATUS_DISABLED)) {
+            log.error("更新健身设备信息状态失败,状态值无效:{}", status);
+            return false;
+        }
+
+        try {
+            // 检查设备是否存在
+            FitnessEquipmentInfo existing = fitnessEquipmentInfoMapper.selectById(id);
+            if (existing == null || existing.getDeleteFlag() == null || existing.getDeleteFlag() != DELETE_FLAG_NOT_DELETED) {
+                log.warn("更新健身设备信息状态失败,设备不存在或已删除,id={}", id);
+                return false;
+            }
+
+            FitnessEquipmentInfo updateEntity = new FitnessEquipmentInfo();
+            updateEntity.setId(id);
+            updateEntity.setStatus(status);
+            // updatedTime由MyBatis-Plus自动填充
+
+            int updateResult = fitnessEquipmentInfoMapper.updateById(updateEntity);
+            if (updateResult > 0) {
+                log.info("更新健身设备信息状态成功,id={}, status={}", id, status);
+                return true;
+            } else {
+                log.warn("更新健身设备信息状态失败,更新记录数为0,id={}", id);
+                return false;
+            }
+        } catch (Exception e) {
+            log.error("更新健身设备信息状态异常,id={}, status={},异常信息:{}", id, status, e.getMessage(), e);
+            return false;
+        }
+    }
+}
+

+ 1025 - 39
alien-store/src/main/java/shop/alien/store/service/impl/SportsEquipmentFacilityServiceImpl.java

@@ -10,12 +10,18 @@ import org.springframework.beans.BeanUtils;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import shop.alien.entity.store.FitnessEquipmentInfo;
 import shop.alien.entity.store.SportsEquipmentFacility;
 import shop.alien.entity.store.StoreImg;
+import shop.alien.entity.store.vo.FitnessEquipmentCategoryDetailVo;
+import shop.alien.entity.store.vo.FitnessEquipmentTypeSummaryVo;
+import shop.alien.entity.store.vo.SportsEquipmentFacilityCategorySummaryVo;
 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.FitnessEquipmentInfoService;
 import shop.alien.store.service.SportsEquipmentFacilityService;
 
 import java.util.ArrayList;
@@ -37,16 +43,67 @@ public class SportsEquipmentFacilityServiceImpl extends ServiceImpl<SportsEquipm
 
     private final SportsEquipmentFacilityMapper facilityMapper;
     private final StoreImgMapper storeImgMapper;
+    private final FitnessEquipmentInfoService fitnessEquipmentInfoService;
 
     /**
      * 运动器材设施图片类型
      */
     private static final Integer IMG_TYPE_SPORTS_EQUIPMENT = 28;
+    
+    /**
+     * 删除标志:未删除
+     */
+    private static final int DELETE_FLAG_NOT_DELETED = 0;
+    
+    /**
+     * 状态:启用
+     */
+    private static final int STATUS_ENABLED = 1;
+    
+    /**
+     * ID分隔符
+     */
+    private static final String ID_SEPARATOR = ",";
+    
+    /**
+     * 最小分类编号
+     */
+    private static final int MIN_CATEGORY_NUM = 1;
+    
+    /**
+     * 默认排序号
+     */
+    private static final int DEFAULT_SORT_ORDER = 0;
+    
+    /**
+     * 最小有效ID
+     */
+    private static final int MIN_VALID_ID = 1;
 
     /**
      * 设施分类名称映射
      */
     private static final String[] FACILITY_CATEGORY_NAMES = {"", "有氧区", "力量区", "单功能机械区"};
+    
+    /**
+     * 设备类型名称映射
+     */
+    private static final String[] EQUIPMENT_TYPE_NAMES = {"", "心肺训练", "核心训练", "臀腿训练", "上肢训练", "全身训练", "其他"};
+    
+    /**
+     * 最大设备类型编号
+     */
+    private static final int MAX_EQUIPMENT_TYPE = 6;
+    
+    /**
+     * 最小设备类型编号
+     */
+    private static final int MIN_EQUIPMENT_TYPE = 1;
+    
+    /**
+     * 最大设施分类编号
+     */
+    private static final int MAX_FACILITY_CATEGORY = 3;
 
     @Override
     public IPage<SportsEquipmentFacilityVo> getPageList(Integer pageNum, Integer pageSize, Integer storeId, Integer facilityCategory) {
@@ -140,61 +197,626 @@ public class SportsEquipmentFacilityServiceImpl extends ServiceImpl<SportsEquipm
         return this.removeById(id);
     }
 
+    /**
+     * 查询指定店铺按分类汇总的设备信息(用户端)
+     * 包含每个分类的设备数量、图片列表和设备列表
+     * 通过fitnessEquipmentIds关联FitnessEquipmentInfo信息
+     * 按facility_category_name分组查询,支持商户自定义分类名称
+     *
+     * @param storeId 门店ID,不能为空且必须大于0
+     * @return 分类汇总列表,不会返回null
+     */
     @Override
-    public List<SportsEquipmentFacilityCategoryVo> getCategorySummary(Integer storeId) {
-        List<SportsEquipmentFacilityCategoryVo> result = new ArrayList<>();
+    public List<SportsEquipmentFacilityCategorySummaryVo> getCategorySummary(Integer storeId) {
+        log.info("查询指定店铺按分类汇总的设备信息(用户端),storeId={}", storeId);
         
-        // 遍历所有分类(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]);
+        // 参数验证
+        if (storeId == null || storeId <= 0) {
+            log.warn("查询分类汇总失败,门店ID无效:{}", storeId);
+            return new ArrayList<>();
+        }
+        
+        try {
+            List<SportsEquipmentFacilityCategorySummaryVo> result = new ArrayList<>();
             
-            // 查询该分类下的设备列表(不分页)
-            LambdaQueryWrapper<SportsEquipmentFacility> facilityWrapper = new LambdaQueryWrapper<>();
-            facilityWrapper.eq(SportsEquipmentFacility::getStoreId, storeId)
-                    .eq(SportsEquipmentFacility::getFacilityCategory, category);
-            facilityWrapper.orderByDesc(SportsEquipmentFacility::getCreatedTime);
-            List<SportsEquipmentFacility> facilityList = facilityMapper.selectList(facilityWrapper);
+            // 查询该店铺下所有不同的设施分类名称(专门用于categorySummary)
+            List<String> categoryNameList = queryDistinctCategoryNamesForCategorySummary(storeId);
             
-            // 设置设备数量
-            categoryVo.setFacilityCount(facilityList != null ? facilityList.size() : 0);
+            if (CollectionUtils.isEmpty(categoryNameList)) {
+                log.info("查询指定店铺按分类汇总的设备信息完成,storeId={},未找到分类数据", storeId);
+                return result;
+            }
             
-            // 转换为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 ? "显示" : "隐藏");
+            // 按分类名称分组查询
+            for (String categoryName : categoryNameList) {
+                SportsEquipmentFacilityCategorySummaryVo categoryVo = buildCategorySummaryVoByCategoryNameForCategorySummary(
+                        storeId, categoryName);
+                // 只添加有设备数据的分类
+                result.add(categoryVo);
+
+            }
+            
+            log.info("查询指定店铺按分类汇总的设备信息完成,storeId={},分类数量:{}", storeId, result.size());
+            return result;
+        } catch (Exception e) {
+            log.error("查询指定店铺按分类汇总的设备信息异常,storeId={},异常信息:{}", storeId, e.getMessage(), e);
+            throw new RuntimeException("查询分类汇总异常:" + e.getMessage(), e);
+        }
+    }
+    
+    /**
+     * 查询指定店铺下所有不同的设施分类名称(专门用于categorySummary接口,不与其他接口共用)
+     *
+     * @param storeId 门店ID
+     * @return 分类名称列表,不会返回null
+     */
+    private List<String> queryDistinctCategoryNamesForCategorySummary(Integer storeId) {
+        try {
+            LambdaQueryWrapper<SportsEquipmentFacility> queryWrapper = new LambdaQueryWrapper<>();
+            queryWrapper.eq(SportsEquipmentFacility::getStoreId, storeId)
+                    .eq(SportsEquipmentFacility::getDeleteFlag, DELETE_FLAG_NOT_DELETED)
+                    .isNotNull(SportsEquipmentFacility::getFacilityCategoryName)
+                    .ne(SportsEquipmentFacility::getFacilityCategoryName, "");
+            
+            List<SportsEquipmentFacility> facilityList = facilityMapper.selectList(queryWrapper);
+            
+            if (CollectionUtils.isEmpty(facilityList)) {
+                return new ArrayList<>();
+            }
+            
+            // 提取所有不同的分类名称并排序
+            return facilityList.stream()
+                    .map(SportsEquipmentFacility::getFacilityCategoryName)
+                    .filter(StringUtils::isNotBlank)
+                    .distinct()
+                    .sorted()
+                    .collect(Collectors.toList());
+        } catch (Exception e) {
+            log.error("查询设施分类名称列表异常(categorySummary专用),storeId={},异常信息:{}", storeId, e.getMessage(), e);
+            return new ArrayList<>();
+        }
+    }
+    
+    /**
+     * 根据分类名称构建分类汇总VO对象(专门用于categorySummary接口,不与其他接口共用)
+     *
+     * @param storeId      门店ID
+     * @param categoryName 分类名称
+     * @return 分类汇总VO对象
+     */
+    private SportsEquipmentFacilityCategorySummaryVo buildCategorySummaryVoByCategoryNameForCategorySummary(Integer storeId, String categoryName) {
+        SportsEquipmentFacilityCategorySummaryVo categoryVo = new SportsEquipmentFacilityCategorySummaryVo();
+        categoryVo.setStoreId(storeId);
+        categoryVo.setFacilityCategoryName(categoryName);
+        
+        // 查询该分类名称下的设备列表(专门用于categorySummary)
+        List<SportsEquipmentFacility> facilityList = queryFacilityListByCategoryNameForCategorySummary(storeId, categoryName);
+        
+        // 收集并解析fitnessEquipmentIds(专门用于categorySummary)
+        List<Integer> fitnessEquipmentIdList = collectFitnessEquipmentIdsForCategorySummary(facilityList, storeId);
+        
+        // 查询FitnessEquipmentInfo列表(专门用于categorySummary)
+        List<FitnessEquipmentInfo> fitnessEquipmentInfoList = queryFitnessEquipmentInfoListForCategorySummary(fitnessEquipmentIdList, storeId);
+
+        // 设置设备列表
+        categoryVo.setFacilityList(fitnessEquipmentInfoList);
+        categoryVo.setFacilityCount(fitnessEquipmentInfoList.size());
+
+        // 查询并设置图片列表(专门用于categorySummary)
+        // 兼容处理:图片存储时business_id存储的是facility_category编号
+        // 需要从该分类名称下的设备中获取facility_category值来查询图片
+        List<String> imageList = queryCategoryImageListByCategoryNameForCategorySummary(storeId, categoryName, facilityList);
+        categoryVo.setImageList(imageList);
+        
+        return categoryVo;
+    }
+    
+    /**
+     * 根据分类名称查询设备列表(专门用于categorySummary接口,不与其他接口共用)
+     *
+     * @param storeId      门店ID
+     * @param categoryName 分类名称
+     * @return 设备列表
+     */
+    private List<SportsEquipmentFacility> queryFacilityListByCategoryNameForCategorySummary(Integer storeId, String categoryName) {
+        LambdaQueryWrapper<SportsEquipmentFacility> facilityWrapper = new LambdaQueryWrapper<>();
+        facilityWrapper.eq(SportsEquipmentFacility::getStoreId, storeId)
+                .eq(SportsEquipmentFacility::getFacilityCategoryName, categoryName)
+                .eq(SportsEquipmentFacility::getDeleteFlag, DELETE_FLAG_NOT_DELETED)
+                .orderByDesc(SportsEquipmentFacility::getCreatedTime);
+        return facilityMapper.selectList(facilityWrapper);
+    }
+    
+    /**
+     * 从设备列表中收集fitnessEquipmentIds并解析为整数列表(专门用于categorySummary接口,不与其他接口共用)
+     *
+     * @param facilityList 设备列表
+     * @param storeId      门店ID(用于日志)
+     * @return 去重后的设备ID列表
+     */
+    private List<Integer> collectFitnessEquipmentIdsForCategorySummary(List<SportsEquipmentFacility> facilityList, Integer storeId) {
+        List<Integer> fitnessEquipmentIdList = new ArrayList<>();
+        
+        if (CollectionUtils.isEmpty(facilityList)) {
+            return fitnessEquipmentIdList;
+        }
+        
+        for (SportsEquipmentFacility facility : facilityList) {
+            String fitnessEquipmentIds = facility.getFitnessEquipmentIds();
+            if (StringUtils.isNotBlank(fitnessEquipmentIds)) {
+                List<Integer> parsedIds = parseFitnessEquipmentIdsForCategorySummary(fitnessEquipmentIds, storeId);
+                for (Integer id : parsedIds) {
+                    if (!fitnessEquipmentIdList.contains(id)) {
+                        fitnessEquipmentIdList.add(id);
                     }
-                    facilityVoList.add(vo);
                 }
             }
-            categoryVo.setFacilityList(facilityVoList);
+        }
+        
+        return fitnessEquipmentIdList;
+    }
+    
+    /**
+     * 解析逗号分隔的ID字符串为整数列表(专门用于categorySummary接口,不与其他接口共用)
+     *
+     * @param fitnessEquipmentIds ID字符串(逗号分隔)
+     * @param storeId             门店ID(用于日志)
+     * @return 解析后的ID列表
+     */
+    private List<Integer> parseFitnessEquipmentIdsForCategorySummary(String fitnessEquipmentIds, Integer storeId) {
+        List<Integer> idList = new ArrayList<>();
+        
+        if (StringUtils.isBlank(fitnessEquipmentIds)) {
+            return idList;
+        }
+        
+        String[] idArray = fitnessEquipmentIds.split(ID_SEPARATOR);
+        for (String idStr : idArray) {
+            String trimmedId = idStr.trim();
+            if (StringUtils.isNotBlank(trimmedId)) {
+                try {
+                    Integer id = Integer.parseInt(trimmedId);
+                    if (id >= MIN_VALID_ID) {
+                        idList.add(id);
+                    } else {
+                        log.warn("解析fitnessEquipmentIds失败(categorySummary专用),ID小于最小值:{},storeId={}", 
+                                trimmedId, storeId);
+                    }
+                } catch (NumberFormatException e) {
+                    log.warn("解析fitnessEquipmentIds失败(categorySummary专用),无效的ID格式:{},storeId={},异常信息:{}", 
+                            trimmedId, storeId, e.getMessage());
+                }
+            }
+        }
+        
+        return idList;
+    }
+    
+    /**
+     * 查询健身设备信息列表(专门用于categorySummary接口,不与其他接口共用)
+     *
+     * @param fitnessEquipmentIdList 设备ID列表
+     * @param storeId                门店ID(用于日志)
+     * @return 过滤并排序后的设备信息列表
+     */
+    private List<FitnessEquipmentInfo> queryFitnessEquipmentInfoListForCategorySummary(List<Integer> fitnessEquipmentIdList, Integer storeId) {
+        if (CollectionUtils.isEmpty(fitnessEquipmentIdList)) {
+            return new ArrayList<>();
+        }
+        
+        try {
+            List<FitnessEquipmentInfo> allEquipmentList = new ArrayList<>(
+                    fitnessEquipmentInfoService.listByIds(fitnessEquipmentIdList));
             
-            // 查询该分类下的图片列表
+            // 过滤掉已删除和禁用的设备,并按sort_order排序
+            List<FitnessEquipmentInfo> filteredList = allEquipmentList.stream()
+                    .filter(this::isValidFitnessEquipment)
+                    .sorted(this::compareBySortOrder)
+                    .collect(Collectors.toList());
+            
+            log.info("查询到FitnessEquipmentInfo设备数量(categorySummary专用):{},storeId:{}", 
+                    filteredList.size(), storeId);
+            
+            return filteredList;
+        } catch (Exception e) {
+            log.error("查询FitnessEquipmentInfo列表异常(categorySummary专用),storeId={},异常信息:{}", 
+                    storeId, e.getMessage(), e);
+            return new ArrayList<>();
+        }
+    }
+    
+    /**
+     * 根据分类名称查询图片列表(专门用于categorySummary接口,不与其他接口共用)
+     * 兼容处理:图片存储时business_id存储的是facility_category编号
+     * 需要从该分类名称下的设备中获取facility_category值来查询图片
+     *
+     * @param storeId      门店ID
+     * @param categoryName 分类名称
+     * @param facilityList 该分类下的设备列表
+     * @return 图片URL列表
+     */
+    private List<String> queryCategoryImageListByCategoryNameForCategorySummary(Integer storeId, String categoryName, 
+                                                                               List<SportsEquipmentFacility> facilityList) {
+        try {
+            // 从设备列表中获取所有不同的facility_category值
+            List<Integer> categoryIdList = facilityList.stream()
+                    .map(SportsEquipmentFacility::getFacilityCategory)
+                    .filter(category -> category != null && category > 0)
+                    .distinct()
+                    .collect(Collectors.toList());
+            
+            if (CollectionUtils.isEmpty(categoryIdList)) {
+                return new ArrayList<>();
+            }
+            
+            // 查询所有相关分类的图片
             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);
+                    .in(StoreImg::getBusinessId, categoryIdList)
+                    .eq(StoreImg::getImgType, IMG_TYPE_SPORTS_EQUIPMENT)
+                    .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<>());
+            
+            if (CollectionUtils.isEmpty(imageList)) {
+                return new ArrayList<>();
             }
             
-            result.add(categoryVo);
+            return imageList.stream()
+                    .map(StoreImg::getImgUrl)
+                    .distinct()
+                    .collect(Collectors.toList());
+        } catch (Exception e) {
+            log.error("查询分类图片列表异常(categorySummary专用),storeId={},categoryName={},异常信息:{}", 
+                    storeId, categoryName, e.getMessage(), e);
+            return new ArrayList<>();
+        }
+    }
+    
+    /**
+     * 查询指定店铺下所有不同的设施分类名称
+     *
+     * @param storeId 门店ID
+     * @return 分类名称列表,不会返回null
+     */
+    private List<String> queryDistinctCategoryNames(Integer storeId) {
+        try {
+            LambdaQueryWrapper<SportsEquipmentFacility> queryWrapper = new LambdaQueryWrapper<>();
+            queryWrapper.eq(SportsEquipmentFacility::getStoreId, storeId)
+                    .eq(SportsEquipmentFacility::getDeleteFlag, DELETE_FLAG_NOT_DELETED)
+                    .isNotNull(SportsEquipmentFacility::getFacilityCategoryName)
+                    .ne(SportsEquipmentFacility::getFacilityCategoryName, "");
+            
+            List<SportsEquipmentFacility> facilityList = facilityMapper.selectList(queryWrapper);
+            
+            if (CollectionUtils.isEmpty(facilityList)) {
+                return new ArrayList<>();
+            }
+            
+            // 提取所有不同的分类名称并排序
+            return facilityList.stream()
+                    .map(SportsEquipmentFacility::getFacilityCategoryName)
+                    .filter(StringUtils::isNotBlank)
+                    .distinct()
+                    .sorted()
+                    .collect(Collectors.toList());
+        } catch (Exception e) {
+            log.error("查询设施分类名称列表异常,storeId={},异常信息:{}", storeId, e.getMessage(), e);
+            return new ArrayList<>();
         }
+    }
+    
+    /**
+     * 根据分类名称构建分类汇总VO对象
+     *
+     * @param storeId      门店ID
+     * @param categoryName 分类名称
+     * @return 分类汇总VO对象
+     */
+    private SportsEquipmentFacilityCategorySummaryVo buildCategorySummaryVoByCategoryName(Integer storeId, String categoryName) {
+        SportsEquipmentFacilityCategorySummaryVo categoryVo = new SportsEquipmentFacilityCategorySummaryVo();
+        categoryVo.setStoreId(storeId);
+
+        categoryVo.setFacilityCategoryName(categoryName);
         
-        return result;
+        // 查询该分类名称下的设备列表
+        List<SportsEquipmentFacility> facilityList = queryFacilityListByCategoryName(storeId, categoryName);
+        
+        // 设置设备数量:统计该分类名称下的SportsEquipmentFacility记录数量
+//        categoryVo.setFacilityCount(facilityList != null ? facilityList.size() : 0);
+        
+        // 收集并解析fitnessEquipmentIds
+        List<Integer> fitnessEquipmentIdList = collectFitnessEquipmentIds(facilityList, storeId, null);
+        
+        // 查询FitnessEquipmentInfo列表
+        List<FitnessEquipmentInfo> fitnessEquipmentInfoList = queryFitnessEquipmentInfoList(fitnessEquipmentIdList, null, storeId);
+
+        // 设置设备列表
+        categoryVo.setFacilityList(fitnessEquipmentInfoList);
+
+        categoryVo.setFacilityCount(fitnessEquipmentInfoList.size());
+
+        // 查询并设置图片列表
+        // 兼容处理:图片存储时business_id存储的是facility_category编号
+        // 需要从该分类名称下的设备中获取facility_category值来查询图片
+        List<String> imageList = queryCategoryImageListByCategoryName(storeId, categoryName, facilityList);
+        categoryVo.setImageList(imageList);
+        
+        return categoryVo;
+    }
+    
+    /**
+     * 根据分类名称查询设备列表
+     *
+     * @param storeId      门店ID
+     * @param categoryName 分类名称
+     * @return 设备列表
+     */
+    private List<SportsEquipmentFacility> queryFacilityListByCategoryName(Integer storeId, String categoryName) {
+        LambdaQueryWrapper<SportsEquipmentFacility> facilityWrapper = new LambdaQueryWrapper<>();
+        facilityWrapper.eq(SportsEquipmentFacility::getStoreId, storeId)
+                .eq(SportsEquipmentFacility::getFacilityCategoryName, categoryName)
+                .eq(SportsEquipmentFacility::getDeleteFlag, DELETE_FLAG_NOT_DELETED)
+                .orderByDesc(SportsEquipmentFacility::getCreatedTime);
+        return facilityMapper.selectList(facilityWrapper);
+    }
+    
+    /**
+     * 构建分类汇总VO对象(旧方法,已废弃,保留用于兼容)
+     *
+     * @param storeId  门店ID
+     * @param category 分类编号
+     * @return 分类汇总VO对象
+     * @deprecated 请使用 buildCategorySummaryVoByCategoryName 方法
+     */
+    @Deprecated
+    private SportsEquipmentFacilityCategorySummaryVo buildCategorySummaryVo(Integer storeId, Integer category) {
+        SportsEquipmentFacilityCategorySummaryVo categoryVo = new SportsEquipmentFacilityCategorySummaryVo();
+        categoryVo.setStoreId(storeId);
+        categoryVo.setFacilityCategory(category);
+        categoryVo.setFacilityCategoryName(FACILITY_CATEGORY_NAMES[category]);
+        
+        // 查询该分类下的设备列表
+        List<SportsEquipmentFacility> facilityList = queryFacilityListByCategory(storeId, category);
+        
+        // 收集并解析fitnessEquipmentIds
+        List<Integer> fitnessEquipmentIdList = collectFitnessEquipmentIds(facilityList, storeId, category);
+        
+        // 查询FitnessEquipmentInfo列表
+        List<FitnessEquipmentInfo> fitnessEquipmentInfoList = queryFitnessEquipmentInfoList(fitnessEquipmentIdList, category, storeId);
+        
+        // 设置设备信息
+        categoryVo.setFacilityCount(fitnessEquipmentInfoList.size());
+        categoryVo.setFacilityList(fitnessEquipmentInfoList);
+        
+        // 查询并设置图片列表
+        List<String> imageList = queryCategoryImageList(storeId, category);
+        categoryVo.setImageList(imageList);
+        
+        return categoryVo;
+    }
+    
+    /**
+     * 查询指定分类下的设备列表
+     *
+     * @param storeId  门店ID
+     * @param category 分类编号
+     * @return 设备列表
+     */
+    private List<SportsEquipmentFacility> queryFacilityListByCategory(Integer storeId, Integer category) {
+        LambdaQueryWrapper<SportsEquipmentFacility> facilityWrapper = new LambdaQueryWrapper<>();
+        facilityWrapper.eq(SportsEquipmentFacility::getStoreId, storeId)
+                .eq(SportsEquipmentFacility::getFacilityCategory, category)
+                .orderByDesc(SportsEquipmentFacility::getCreatedTime);
+        return facilityMapper.selectList(facilityWrapper);
+    }
+    
+    /**
+     * 从设备列表中收集fitnessEquipmentIds并解析为整数列表
+     *
+     * @param facilityList 设备列表
+     * @param storeId      门店ID(用于日志)
+     * @param category     分类编号(用于日志,可为null)
+     * @return 去重后的设备ID列表
+     */
+    private List<Integer> collectFitnessEquipmentIds(List<SportsEquipmentFacility> facilityList, 
+                                                      Integer storeId, Integer category) {
+        List<Integer> fitnessEquipmentIdList = new ArrayList<>();
+        
+        if (CollectionUtils.isEmpty(facilityList)) {
+            return fitnessEquipmentIdList;
+        }
+        
+        for (SportsEquipmentFacility facility : facilityList) {
+            String fitnessEquipmentIds = facility.getFitnessEquipmentIds();
+            if (StringUtils.isNotBlank(fitnessEquipmentIds)) {
+                List<Integer> parsedIds = parseFitnessEquipmentIds(fitnessEquipmentIds, storeId, category);
+                for (Integer id : parsedIds) {
+                    if (!fitnessEquipmentIdList.contains(id)) {
+                        fitnessEquipmentIdList.add(id);
+                    }
+                }
+            }
+        }
+        
+        return fitnessEquipmentIdList;
+    }
+    
+    /**
+     * 解析逗号分隔的ID字符串为整数列表
+     *
+     * @param fitnessEquipmentIds ID字符串(逗号分隔)
+     * @param storeId             门店ID(用于日志)
+     * @param category            分类编号(用于日志)
+     * @return 解析后的ID列表
+     */
+    private List<Integer> parseFitnessEquipmentIds(String fitnessEquipmentIds, Integer storeId, Integer category) {
+        List<Integer> idList = new ArrayList<>();
+        
+        if (StringUtils.isBlank(fitnessEquipmentIds)) {
+            return idList;
+        }
+        
+        String[] idArray = fitnessEquipmentIds.split(ID_SEPARATOR);
+        for (String idStr : idArray) {
+            String trimmedId = idStr.trim();
+            if (StringUtils.isNotBlank(trimmedId)) {
+                try {
+                    Integer id = Integer.parseInt(trimmedId);
+                    if (id >= MIN_VALID_ID) {
+                        idList.add(id);
+                    } else {
+                        log.warn("解析fitnessEquipmentIds失败,ID小于最小值:{},storeId={},category={}", 
+                                trimmedId, storeId, category);
+                    }
+                } catch (NumberFormatException e) {
+                    log.warn("解析fitnessEquipmentIds失败,无效的ID格式:{},storeId={},category={},异常信息:{}", 
+                            trimmedId, storeId, category, e.getMessage());
+                }
+            }
+        }
+        
+        return idList;
+    }
+    
+    /**
+     * 查询健身设备信息列表
+     *
+     * @param fitnessEquipmentIdList 设备ID列表
+     * @param category               分类编号(用于日志)
+     * @param storeId                门店ID(用于日志)
+     * @return 过滤并排序后的设备信息列表
+     */
+    private List<FitnessEquipmentInfo> queryFitnessEquipmentInfoList(List<Integer> fitnessEquipmentIdList, 
+                                                                      Integer category, Integer storeId) {
+        if (CollectionUtils.isEmpty(fitnessEquipmentIdList)) {
+            return new ArrayList<>();
+        }
+        
+        try {
+            List<FitnessEquipmentInfo> allEquipmentList = new ArrayList<>(
+                    fitnessEquipmentInfoService.listByIds(fitnessEquipmentIdList));
+            
+            // 过滤掉已删除和禁用的设备,并按sort_order排序
+            List<FitnessEquipmentInfo> filteredList = allEquipmentList.stream()
+                    .filter(this::isValidFitnessEquipment)
+                    .sorted(this::compareBySortOrder)
+                    .collect(Collectors.toList());
+            
+            log.info("查询到FitnessEquipmentInfo设备数量:{},分类:{},storeId:{}", 
+                    filteredList.size(), category, storeId);
+            
+            return filteredList;
+        } catch (Exception e) {
+            log.error("查询FitnessEquipmentInfo列表异常,category={},storeId={},异常信息:{}", 
+                    category, storeId, e.getMessage(), e);
+            return new ArrayList<>();
+        }
+    }
+    
+    /**
+     * 判断健身设备是否有效(未删除且已启用)
+     *
+     * @param info 健身设备信息
+     * @return true表示有效,false表示无效
+     */
+    private boolean isValidFitnessEquipment(FitnessEquipmentInfo info) {
+        return info.getDeleteFlag() != null 
+                && info.getDeleteFlag() == DELETE_FLAG_NOT_DELETED
+                && info.getStatus() != null 
+                && info.getStatus() == STATUS_ENABLED;
+    }
+    
+    /**
+     * 按排序号比较两个健身设备信息
+     *
+     * @param a 设备A
+     * @param b 设备B
+     * @return 比较结果
+     */
+    private int compareBySortOrder(FitnessEquipmentInfo a, FitnessEquipmentInfo b) {
+        Integer sortOrderA = a.getSortOrder() != null ? a.getSortOrder() : DEFAULT_SORT_ORDER;
+        Integer sortOrderB = b.getSortOrder() != null ? b.getSortOrder() : DEFAULT_SORT_ORDER;
+        return sortOrderA.compareTo(sortOrderB);
+    }
+    
+    /**
+     * 根据分类名称查询图片列表
+     * 兼容处理:图片存储时business_id存储的是facility_category编号
+     * 需要从该分类名称下的设备中获取facility_category值来查询图片
+     *
+     * @param storeId      门店ID
+     * @param categoryName 分类名称
+     * @param facilityList 该分类下的设备列表
+     * @return 图片URL列表
+     */
+    private List<String> queryCategoryImageListByCategoryName(Integer storeId, String categoryName, 
+                                                               List<SportsEquipmentFacility> facilityList) {
+        try {
+            // 从设备列表中获取所有不同的facility_category值
+            List<Integer> categoryIdList = facilityList.stream()
+                    .map(SportsEquipmentFacility::getFacilityCategory)
+                    .filter(category -> category != null && category > 0)
+                    .distinct()
+                    .collect(Collectors.toList());
+            
+            if (CollectionUtils.isEmpty(categoryIdList)) {
+                return new ArrayList<>();
+            }
+            
+            // 查询所有相关分类的图片
+            LambdaQueryWrapper<StoreImg> imageWrapper = new LambdaQueryWrapper<>();
+            imageWrapper.eq(StoreImg::getStoreId, storeId)
+                    .in(StoreImg::getBusinessId, categoryIdList)
+                    .eq(StoreImg::getImgType, IMG_TYPE_SPORTS_EQUIPMENT)
+                    .orderByAsc(StoreImg::getImgSort);
+            
+            List<StoreImg> imageList = storeImgMapper.selectList(imageWrapper);
+            
+            if (CollectionUtils.isEmpty(imageList)) {
+                return new ArrayList<>();
+            }
+            
+            return imageList.stream()
+                    .map(StoreImg::getImgUrl)
+                    .distinct()
+                    .collect(Collectors.toList());
+        } catch (Exception e) {
+            log.error("查询分类图片列表异常,storeId={},categoryName={},异常信息:{}", 
+                    storeId, categoryName, e.getMessage(), e);
+            return new ArrayList<>();
+        }
+    }
+    
+    /**
+     * 查询分类下的图片列表(旧方法,保留用于兼容)
+     *
+     * @param storeId  门店ID
+     * @param category 分类编号
+     * @return 图片URL列表
+     */
+    private List<String> queryCategoryImageList(Integer storeId, Integer category) {
+        try {
+            LambdaQueryWrapper<StoreImg> imageWrapper = new LambdaQueryWrapper<>();
+            imageWrapper.eq(StoreImg::getStoreId, storeId)
+                    .eq(StoreImg::getBusinessId, category)
+                    .eq(StoreImg::getImgType, IMG_TYPE_SPORTS_EQUIPMENT)
+                    .orderByAsc(StoreImg::getImgSort);
+            
+            List<StoreImg> imageList = storeImgMapper.selectList(imageWrapper);
+            
+            if (CollectionUtils.isEmpty(imageList)) {
+                return new ArrayList<>();
+            }
+            
+            return imageList.stream()
+                    .map(StoreImg::getImgUrl)
+                    .collect(Collectors.toList());
+        } catch (Exception e) {
+            log.error("查询分类图片列表异常,storeId={},category={},异常信息:{}", 
+                    storeId, category, e.getMessage(), e);
+            return new ArrayList<>();
+        }
     }
 
     /**
@@ -333,5 +955,369 @@ public class SportsEquipmentFacilityServiceImpl extends ServiceImpl<SportsEquipm
 
         return true;
     }
+
+    /**
+     * 查询分类的设备详情(用户端)
+     * 包含图片列表、各子分类的设备数量汇总、设备列表
+     * 通过fitnessEquipmentIds关联FitnessEquipmentInfo信息
+     * 按facility_category_name查询,支持商户自定义分类名称
+     * 如果facilityCategoryName为空,返回所有分类的数据
+     * 如果equipmentType为空,返回该分类下所有类型的数据
+     *
+     * @param storeId            门店ID,不能为空且必须大于0
+     * @param facilityCategoryName 设施分类名称(商户自定义),可选,为空时返回所有分类
+     * @param equipmentType       设备类型(1:心肺训练, 2:核心训练, 3:臀腿训练, 4:上肢训练, 5:全身训练, 6:其他),可选,为空时返回所有类型
+     * @return 分类详情列表,不会返回null
+     */
+    @Override
+    public List<FitnessEquipmentCategoryDetailVo> getCategoryDetail(Integer storeId, String facilityCategoryName, Integer equipmentType) {
+        log.info("查询分类的设备详情(用户端),storeId={},facilityCategoryName={},equipmentType={}", 
+                storeId, facilityCategoryName, equipmentType);
+        
+        // 参数验证
+        if (!isValidStoreId(storeId)) {
+            log.warn("查询分类详情失败,门店ID无效:{}", storeId);
+            return new ArrayList<>();
+        }
+        
+        try {
+            List<FitnessEquipmentCategoryDetailVo> resultList = new ArrayList<>();
+            
+            // 如果facilityCategoryName为空,返回所有分类的数据
+            if (StringUtils.isBlank(facilityCategoryName)) {
+                // 查询该店铺下所有不同的设施分类名称
+                List<String> categoryNameList = queryDistinctCategoryNames(storeId);
+                
+                if (CollectionUtils.isEmpty(categoryNameList)) {
+                    log.info("查询分类详情完成,storeId={},未找到分类数据", storeId);
+                    return resultList;
+                }
+                
+                // 遍历所有分类名称
+                for (String categoryName : categoryNameList) {
+                    FitnessEquipmentCategoryDetailVo detailVo = buildCategoryDetailVoByCategoryName(storeId, categoryName, equipmentType);
+                    // 只添加有设备数据的分类
+                    if (detailVo.getEquipmentTypeList() != null && !detailVo.getEquipmentTypeList().isEmpty()) {
+                        resultList.add(detailVo);
+                    }
+                }
+            } else {
+                // 返回指定分类名称的数据
+                FitnessEquipmentCategoryDetailVo detailVo = buildCategoryDetailVoByCategoryName(storeId, facilityCategoryName, equipmentType);
+                resultList.add(detailVo);
+            }
+            
+            log.info("查询分类详情成功,storeId={},facilityCategoryName={},equipmentType={},返回分类数量:{}", 
+                    storeId, facilityCategoryName, equipmentType, resultList.size());
+            return resultList;
+        } catch (Exception e) {
+            log.error("查询分类详情异常,storeId={},facilityCategoryName={},equipmentType={},异常信息:{}", 
+                    storeId, facilityCategoryName, equipmentType, e.getMessage(), e);
+            throw new RuntimeException("查询分类详情异常:" + e.getMessage(), e);
+        }
+    }
+    
+    /**
+     * 根据分类名称构建单个分类的详情VO对象
+     *
+     * @param storeId            门店ID
+     * @param facilityCategoryName 设施分类名称
+     * @param equipmentType       设备类型,可选,如果传入则只返回该类型的设备列表
+     * @return 分类详情VO对象
+     */
+    private FitnessEquipmentCategoryDetailVo buildCategoryDetailVoByCategoryName(Integer storeId, String facilityCategoryName, Integer equipmentType) {
+        FitnessEquipmentCategoryDetailVo detailVo = buildBaseCategoryDetailVoByCategoryName(storeId, facilityCategoryName);
+        
+        // 查询该分类名称下的设备列表(用于查询图片)
+        List<SportsEquipmentFacility> facilityList = queryFacilityListByCategoryName(storeId, facilityCategoryName);
+        
+        // 查询并设置设备相关信息
+        List<FitnessEquipmentInfo> allEquipmentList = queryCategoryEquipmentListByCategoryName(storeId, facilityCategoryName);
+        
+        // 如果传入了equipmentType,只筛选该类型的设备
+        List<FitnessEquipmentInfo> filteredEquipmentList = filterAndSortEquipmentList(allEquipmentList, equipmentType);
+        
+        // 构建设备类型汇总列表(包含设备列表)
+        List<FitnessEquipmentTypeSummaryVo> equipmentTypeList = buildEquipmentTypeSummaryList(filteredEquipmentList);
+        detailVo.setEquipmentTypeList(equipmentTypeList);
+        
+        // 计算所有equipmentList中的设备总数
+        int totalEquipmentCount = equipmentTypeList.stream()
+                .mapToInt(vo -> vo.getEquipmentList() != null ? vo.getEquipmentList().size() : 0)
+                .sum();
+        
+        // 设置设备数量:所有equipmentTypeList中equipmentList的设备总数
+        detailVo.setFacilityCount(totalEquipmentCount);
+        
+        // 查询并设置图片列表
+        List<String> imageList = queryCategoryImageListByCategoryName(storeId, facilityCategoryName, facilityList);
+        detailVo.setImageList(imageList);
+        
+        log.debug("构建分类详情VO完成,storeId={},facilityCategoryName={},equipmentType={},设备数量:{}", 
+                storeId, facilityCategoryName, equipmentType, totalEquipmentCount);
+        return detailVo;
+    }
+    
+    /**
+     * 构建单个分类的详情VO对象(旧方法,已废弃,保留用于兼容)
+     *
+     * @param storeId          门店ID
+     * @param facilityCategory 设施分类
+     * @param equipmentType    设备类型,可选,如果传入则只返回该类型的设备列表
+     * @return 分类详情VO对象
+     * @deprecated 请使用 buildCategoryDetailVoByCategoryName 方法
+     */
+    @Deprecated
+    private FitnessEquipmentCategoryDetailVo buildCategoryDetailVo(Integer storeId, Integer facilityCategory, Integer equipmentType) {
+        FitnessEquipmentCategoryDetailVo detailVo = buildBaseCategoryDetailVo(storeId, facilityCategory);
+        
+        // 查询并设置设备相关信息
+        List<FitnessEquipmentInfo> allEquipmentList = queryCategoryEquipmentList(storeId, facilityCategory);
+        
+        // 如果传入了equipmentType,只筛选该类型的设备
+        List<FitnessEquipmentInfo> filteredEquipmentList = filterAndSortEquipmentList(allEquipmentList, equipmentType);
+        
+        // 构建设备类型汇总列表(包含设备列表)
+        List<FitnessEquipmentTypeSummaryVo> equipmentTypeList = buildEquipmentTypeSummaryList(filteredEquipmentList);
+        detailVo.setEquipmentTypeList(equipmentTypeList);
+        
+        // 查询并设置图片列表
+        List<String> imageList = queryCategoryImageList(storeId, facilityCategory);
+        detailVo.setImageList(imageList);
+        
+        int totalEquipmentCount = equipmentTypeList.stream()
+                .mapToInt(vo -> vo.getEquipmentList() != null ? vo.getEquipmentList().size() : 0)
+                .sum();
+        log.debug("构建分类详情VO完成,storeId={},facilityCategory={},equipmentType={},设备数量:{}", 
+                storeId, facilityCategory, equipmentType, totalEquipmentCount);
+        return detailVo;
+    }
+    
+    /**
+     * 验证门店ID是否有效
+     *
+     * @param storeId 门店ID
+     * @return true表示有效,false表示无效
+     */
+    private boolean isValidStoreId(Integer storeId) {
+        return storeId != null && storeId >= MIN_VALID_ID;
+    }
+    
+    /**
+     * 验证设施分类是否有效
+     *
+     * @param facilityCategory 设施分类
+     * @return true表示有效,false表示无效
+     */
+    private boolean isValidFacilityCategory(Integer facilityCategory) {
+        return facilityCategory != null 
+                && facilityCategory >= MIN_CATEGORY_NUM 
+                && facilityCategory <= MAX_FACILITY_CATEGORY
+                && facilityCategory < FACILITY_CATEGORY_NAMES.length;
+    }
+    
+    /**
+     * 根据分类名称构建基础分类详情VO对象
+     *
+     * @param storeId            门店ID
+     * @param facilityCategoryName 设施分类名称
+     * @return 基础分类详情VO对象
+     */
+    private FitnessEquipmentCategoryDetailVo buildBaseCategoryDetailVoByCategoryName(Integer storeId, String facilityCategoryName) {
+        FitnessEquipmentCategoryDetailVo detailVo = new FitnessEquipmentCategoryDetailVo();
+        detailVo.setStoreId(storeId);
+        detailVo.setFacilityCategoryName(facilityCategoryName);
+        return detailVo;
+    }
+    
+    /**
+     * 构建基础分类详情VO对象(旧方法,已废弃,保留用于兼容)
+     *
+     * @param storeId          门店ID
+     * @param facilityCategory 设施分类
+     * @return 基础分类详情VO对象
+     * @deprecated 请使用 buildBaseCategoryDetailVoByCategoryName 方法
+     */
+    @Deprecated
+    private FitnessEquipmentCategoryDetailVo buildBaseCategoryDetailVo(Integer storeId, Integer facilityCategory) {
+        FitnessEquipmentCategoryDetailVo detailVo = new FitnessEquipmentCategoryDetailVo();
+        detailVo.setStoreId(storeId);
+        detailVo.setFacilityCategory(facilityCategory);
+        if (isValidFacilityCategory(facilityCategory)) {
+            detailVo.setFacilityCategoryName(FACILITY_CATEGORY_NAMES[facilityCategory]);
+        }
+        return detailVo;
+    }
+    
+    /**
+     * 根据分类名称查询分类下的设备列表
+     *
+     * @param storeId            门店ID
+     * @param facilityCategoryName 设施分类名称
+     * @return 设备信息列表
+     */
+    private List<FitnessEquipmentInfo> queryCategoryEquipmentListByCategoryName(Integer storeId, String facilityCategoryName) {
+        // 查询该分类名称下的设备列表
+        List<SportsEquipmentFacility> facilityList = queryFacilityListByCategoryName(storeId, facilityCategoryName);
+        
+        // 收集并解析fitnessEquipmentIds
+        List<Integer> fitnessEquipmentIdList = collectFitnessEquipmentIds(facilityList, storeId, null);
+        
+        // 查询所有有效的FitnessEquipmentInfo列表
+        return queryAllFitnessEquipmentInfo(fitnessEquipmentIdList, storeId, null);
+    }
+    
+    /**
+     * 查询分类下的设备列表(旧方法,已废弃,保留用于兼容)
+     *
+     * @param storeId          门店ID
+     * @param facilityCategory 设施分类
+     * @return 设备信息列表
+     * @deprecated 请使用 queryCategoryEquipmentListByCategoryName 方法
+     */
+    @Deprecated
+    private List<FitnessEquipmentInfo> queryCategoryEquipmentList(Integer storeId, Integer facilityCategory) {
+        // 查询该分类下的设备列表
+        List<SportsEquipmentFacility> facilityList = queryFacilityListByCategory(storeId, facilityCategory);
+        
+        // 收集并解析fitnessEquipmentIds
+        List<Integer> fitnessEquipmentIdList = collectFitnessEquipmentIds(facilityList, storeId, facilityCategory);
+        
+        // 查询所有有效的FitnessEquipmentInfo列表
+        return queryAllFitnessEquipmentInfo(fitnessEquipmentIdList, storeId, facilityCategory);
+    }
+    
+    /**
+     * 根据设备类型筛选并排序设备列表
+     *
+     * @param allEquipmentList 所有设备列表
+     * @param equipmentType    设备类型,可选
+     * @return 筛选并排序后的设备列表
+     */
+    private List<FitnessEquipmentInfo> filterAndSortEquipmentList(List<FitnessEquipmentInfo> allEquipmentList, 
+                                                                   Integer equipmentType) {
+        List<FitnessEquipmentInfo> filteredList;
+        
+        if (isValidEquipmentType(equipmentType)) {
+            // 根据equipmentType筛选设备
+            filteredList = allEquipmentList.stream()
+                    .filter(info -> equipmentType.equals(info.getEquipmentType()))
+                    .sorted(this::compareBySortOrder)
+                    .collect(Collectors.toList());
+        } else {
+            // 如果equipmentType为空或无效,返回所有设备
+            filteredList = allEquipmentList.stream()
+                    .sorted(this::compareBySortOrder)
+                    .collect(Collectors.toList());
+        }
+        
+        return filteredList;
+    }
+    
+    /**
+     * 验证设备类型是否有效
+     *
+     * @param equipmentType 设备类型
+     * @return true表示有效,false表示无效
+     */
+    private boolean isValidEquipmentType(Integer equipmentType) {
+        return equipmentType != null 
+                && equipmentType >= MIN_EQUIPMENT_TYPE 
+                && equipmentType <= MAX_EQUIPMENT_TYPE;
+    }
+    
+    /**
+     * 查询所有有效的FitnessEquipmentInfo列表
+     *
+     * @param fitnessEquipmentIdList 设备ID列表
+     * @param storeId                门店ID(用于日志)
+     * @param facilityCategory       设施分类(用于日志)
+     * @return 过滤后的设备信息列表,不会返回null
+     */
+    private List<FitnessEquipmentInfo> queryAllFitnessEquipmentInfo(List<Integer> fitnessEquipmentIdList, 
+                                                                     Integer storeId, Integer facilityCategory) {
+        if (CollectionUtils.isEmpty(fitnessEquipmentIdList)) {
+            log.debug("设备ID列表为空,返回空列表,storeId={},facilityCategory={}", storeId, facilityCategory);
+            return new ArrayList<>();
+        }
+        
+        try {
+            List<FitnessEquipmentInfo> allEquipmentList = new ArrayList<>(
+                    fitnessEquipmentInfoService.listByIds(fitnessEquipmentIdList));
+            
+            // 过滤掉已删除和禁用的设备
+            List<FitnessEquipmentInfo> validList = allEquipmentList.stream()
+                    .filter(this::isValidFitnessEquipment)
+                    .collect(Collectors.toList());
+            
+            log.debug("查询FitnessEquipmentInfo列表完成,原始数量:{},有效数量:{},storeId={},facilityCategory={}", 
+                    allEquipmentList.size(), validList.size(), storeId, facilityCategory);
+            return validList;
+        } catch (Exception e) {
+            log.error("查询FitnessEquipmentInfo列表异常,facilityCategory={},storeId={},异常信息:{}", 
+                    facilityCategory, storeId, e.getMessage(), e);
+            return new ArrayList<>();
+        }
+    }
+    
+    /**
+     * 构建设备类型汇总列表
+     * 按equipmentType分组统计各类型的设备数量,并将设备列表放入对应的汇总项中
+     *
+     * @param equipmentList 设备列表
+     * @return 设备类型汇总列表,不会返回null
+     */
+    private List<FitnessEquipmentTypeSummaryVo> buildEquipmentTypeSummaryList(List<FitnessEquipmentInfo> equipmentList) {
+        List<FitnessEquipmentTypeSummaryVo> typeSummaryList = new ArrayList<>();
+        
+        if (CollectionUtils.isEmpty(equipmentList)) {
+            log.debug("设备列表为空,返回空汇总列表");
+            return typeSummaryList;
+        }
+        
+        // 按equipmentType分组统计数量和设备列表
+        for (int type = MIN_EQUIPMENT_TYPE; type <= MAX_EQUIPMENT_TYPE; type++) {
+            final Integer equipmentType = Integer.valueOf(type);
+            
+            // 筛选该类型下的设备并排序
+            List<FitnessEquipmentInfo> typeEquipmentList = equipmentList.stream()
+                    .filter(info -> info.getEquipmentType() != null && equipmentType.equals(info.getEquipmentType()))
+                    .sorted(this::compareBySortOrder)
+                    .collect(Collectors.toList());
+            
+            if (!CollectionUtils.isEmpty(typeEquipmentList)) {
+                FitnessEquipmentTypeSummaryVo typeSummaryVo = createEquipmentTypeSummaryVo(
+                        equipmentType, typeEquipmentList.size(), typeEquipmentList);
+                typeSummaryList.add(typeSummaryVo);
+            }
+        }
+        
+        log.debug("构建设备类型汇总列表完成,汇总类型数量:{},总设备数量:{}", 
+                typeSummaryList.size(), equipmentList.size());
+        return typeSummaryList;
+    }
+    
+    /**
+     * 创建设备类型汇总VO对象
+     *
+     * @param equipmentType    设备类型
+     * @param count            设备数量
+     * @param equipmentList    设备列表
+     * @return 设备类型汇总VO对象
+     */
+    private FitnessEquipmentTypeSummaryVo createEquipmentTypeSummaryVo(Integer equipmentType, Integer count, 
+                                                                       List<FitnessEquipmentInfo> equipmentList) {
+        FitnessEquipmentTypeSummaryVo typeSummaryVo = new FitnessEquipmentTypeSummaryVo();
+        typeSummaryVo.setEquipmentType(equipmentType);
+        
+        if (equipmentType >= MIN_EQUIPMENT_TYPE && equipmentType <= MAX_EQUIPMENT_TYPE 
+                && equipmentType < EQUIPMENT_TYPE_NAMES.length) {
+            typeSummaryVo.setEquipmentTypeName(EQUIPMENT_TYPE_NAMES[equipmentType]);
+        }
+        
+        typeSummaryVo.setEquipmentCount(count);
+        typeSummaryVo.setEquipmentList(equipmentList);
+        return typeSummaryVo;
+    }
 }
 

+ 279 - 0
alien-store/src/main/java/shop/alien/store/service/impl/SportsFacilityAreaServiceImpl.java

@@ -0,0 +1,279 @@
+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.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+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.SportsFacilityArea;
+import shop.alien.entity.store.StoreImg;
+import shop.alien.mapper.SportsEquipmentFacilityMapper;
+import shop.alien.mapper.SportsFacilityAreaMapper;
+import shop.alien.mapper.StoreImgMapper;
+import shop.alien.store.service.SportsFacilityAreaService;
+
+import java.util.List;
+
+/**
+ * 运动设施区域服务实现类
+ *
+ * @author assistant
+ * @since 2025-12-19
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+@Transactional(rollbackFor = Exception.class)
+public class SportsFacilityAreaServiceImpl extends ServiceImpl<SportsFacilityAreaMapper, SportsFacilityArea>
+        implements SportsFacilityAreaService {
+
+    private final SportsFacilityAreaMapper areaMapper;
+    private final SportsEquipmentFacilityMapper facilityMapper;
+    private final StoreImgMapper storeImgMapper;
+
+    /**
+     * 运动器材设施图片类型
+     */
+    private static final Integer IMG_TYPE_SPORTS_EQUIPMENT = 28;
+
+    /**
+     * 删除标志:未删除
+     */
+    private static final int DELETE_FLAG_NOT_DELETED = 0;
+
+    /**
+     * 删除标志:已删除
+     */
+    private static final int DELETE_FLAG_DELETED = 1;
+
+    /**
+     * 默认排序号
+     */
+    private static final int DEFAULT_SORT_ORDER = 0;
+
+    @Override
+    public Integer createArea(Integer storeId, String areaName) {
+        log.info("新建区域,storeId={},areaName={}", storeId, areaName);
+
+        // 1. 参数验证
+        if (storeId == null || storeId <= 0) {
+            log.warn("新建区域失败,门店ID无效:{}", storeId);
+            throw new IllegalArgumentException("门店ID不能为空且必须大于0");
+        }
+
+        if (StringUtils.isBlank(areaName)) {
+            log.warn("新建区域失败,区域名称为空");
+            throw new IllegalArgumentException("区域名称不能为空");
+        }
+
+        // 2. 区域名称长度验证(限10字)
+        String trimmedAreaName = areaName.trim();
+        if (trimmedAreaName.length() > 10) {
+            log.warn("新建区域失败,区域名称长度超过10字:{},长度:{}", trimmedAreaName, trimmedAreaName.length());
+            throw new IllegalArgumentException("区域名称长度不能超过10个字符");
+        }
+
+        // 3. 检查该门店下是否已存在相同名称的区域(未删除的)
+        LambdaQueryWrapper<SportsFacilityArea> checkWrapper = new LambdaQueryWrapper<>();
+        checkWrapper.eq(SportsFacilityArea::getStoreId, storeId)
+                .eq(SportsFacilityArea::getAreaName, trimmedAreaName)
+                .eq(SportsFacilityArea::getDeleteFlag, DELETE_FLAG_NOT_DELETED);
+        long existCount = this.count(checkWrapper);
+        if (existCount > 0) {
+            log.warn("新建区域失败,该门店下已存在相同名称的区域:storeId={},areaName={}", storeId, trimmedAreaName);
+            throw new IllegalArgumentException("该门店下已存在相同名称的区域");
+        }
+
+        // 4. 创建区域记录
+        SportsFacilityArea area = new SportsFacilityArea();
+        area.setStoreId(storeId);
+        area.setAreaName(trimmedAreaName);
+        area.setSortOrder(DEFAULT_SORT_ORDER);
+        area.setDeleteFlag(DELETE_FLAG_NOT_DELETED);
+
+        // 5. 保存到数据库
+        boolean saveResult = this.save(area);
+        if (!saveResult) {
+            log.error("新建区域失败,保存数据库失败:storeId={},areaName={}", storeId, trimmedAreaName);
+            throw new RuntimeException("新建区域失败,保存数据库失败");
+        }
+
+        log.info("新建区域成功,storeId={},areaName={},区域ID={}", storeId, trimmedAreaName, area.getId());
+        return area.getId();
+    }
+
+    @Override
+    public List<SportsFacilityArea> listAreas(Integer storeId) {
+        log.info("查询区域列表,storeId={}", storeId);
+
+        // 参数验证
+        if (storeId == null || storeId <= 0) {
+            log.warn("查询区域列表失败,门店ID无效:{}", storeId);
+            throw new IllegalArgumentException("门店ID不能为空且必须大于0");
+        }
+
+        // 查询该门店下的所有未删除区域,按排序号升序,相同排序号按创建时间降序
+        LambdaQueryWrapper<SportsFacilityArea> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(SportsFacilityArea::getStoreId, storeId)
+                .eq(SportsFacilityArea::getDeleteFlag, DELETE_FLAG_NOT_DELETED)
+                .orderByAsc(SportsFacilityArea::getSortOrder)
+                .orderByDesc(SportsFacilityArea::getCreatedTime);
+
+        List<SportsFacilityArea> areaList = this.list(queryWrapper);
+        log.info("查询区域列表成功,storeId={},区域数量:{}", storeId, areaList.size());
+        return areaList;
+    }
+
+    @Override
+    public boolean updateArea(Integer areaId, String areaName, Integer sortOrder) {
+        log.info("更新区域,areaId={},areaName={},sortOrder={}", areaId, areaName, sortOrder);
+
+        // 参数验证
+        if (areaId == null || areaId <= 0) {
+            log.warn("更新区域失败,区域ID无效:{}", areaId);
+            throw new IllegalArgumentException("区域ID不能为空且必须大于0");
+        }
+
+        // 查询区域是否存在
+        SportsFacilityArea existingArea = this.getById(areaId);
+        if (existingArea == null || existingArea.getDeleteFlag() == DELETE_FLAG_DELETED) {
+            log.warn("更新区域失败,区域不存在或已删除:areaId={}", areaId);
+            throw new IllegalArgumentException("区域不存在或已删除");
+        }
+
+        // 构建更新条件
+        LambdaUpdateWrapper<SportsFacilityArea> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.eq(SportsFacilityArea::getId, areaId);
+
+        // 更新区域名称
+        if (StringUtils.isNotBlank(areaName)) {
+            String trimmedAreaName = areaName.trim();
+            // 验证长度
+            if (trimmedAreaName.length() > 10) {
+                log.warn("更新区域失败,区域名称长度超过10字:{},长度:{}", trimmedAreaName, trimmedAreaName.length());
+                throw new IllegalArgumentException("区域名称长度不能超过10个字符");
+            }
+            // 检查是否与其他区域重复(排除自己)
+            LambdaQueryWrapper<SportsFacilityArea> checkWrapper = new LambdaQueryWrapper<>();
+            checkWrapper.eq(SportsFacilityArea::getStoreId, existingArea.getStoreId())
+                    .eq(SportsFacilityArea::getAreaName, trimmedAreaName)
+                    .eq(SportsFacilityArea::getDeleteFlag, DELETE_FLAG_NOT_DELETED)
+                    .ne(SportsFacilityArea::getId, areaId);
+            long existCount = this.count(checkWrapper);
+            if (existCount > 0) {
+                log.warn("更新区域失败,该门店下已存在相同名称的区域:storeId={},areaName={}", existingArea.getStoreId(), trimmedAreaName);
+                throw new IllegalArgumentException("该门店下已存在相同名称的区域");
+            }
+            updateWrapper.set(SportsFacilityArea::getAreaName, trimmedAreaName);
+        }
+
+        // 更新排序号
+        if (sortOrder != null) {
+            updateWrapper.set(SportsFacilityArea::getSortOrder, sortOrder);
+        }
+
+        // 执行更新
+        boolean updateResult = this.update(updateWrapper);
+        log.info("更新区域{},areaId={}", updateResult ? "成功" : "失败", areaId);
+        return updateResult;
+    }
+
+    @Override
+    public boolean deleteArea(Integer areaId) {
+        log.info("删除区域,areaId={}", areaId);
+
+        // 参数验证
+        if (areaId == null || areaId <= 0) {
+            log.warn("删除区域失败,区域ID无效:{}", areaId);
+            throw new IllegalArgumentException("区域ID不能为空且必须大于0");
+        }
+
+        // 查询区域是否存在
+        SportsFacilityArea area = this.getById(areaId);
+        if (area == null || area.getDeleteFlag() == DELETE_FLAG_DELETED) {
+            log.warn("删除区域失败,区域不存在或已删除:areaId={}", areaId);
+            throw new IllegalArgumentException("区域不存在或已删除");
+        }
+
+        // 1. 逻辑删除区域
+        LambdaUpdateWrapper<SportsFacilityArea> areaUpdateWrapper = new LambdaUpdateWrapper<>();
+        areaUpdateWrapper.eq(SportsFacilityArea::getId, areaId)
+                .set(SportsFacilityArea::getDeleteFlag, DELETE_FLAG_DELETED);
+        boolean deleteAreaResult = this.update(areaUpdateWrapper);
+
+        // 2. 逻辑删除该区域下的所有设施
+        LambdaUpdateWrapper<SportsEquipmentFacility> facilityUpdateWrapper = new LambdaUpdateWrapper<>();
+        facilityUpdateWrapper.eq(SportsEquipmentFacility::getAreaId, areaId)
+                .set(SportsEquipmentFacility::getDeleteFlag, DELETE_FLAG_DELETED);
+        int deletedFacilityCount = facilityMapper.update(null, facilityUpdateWrapper);
+
+        // 3. 物理删除该区域下的所有实景图片
+        LambdaQueryWrapper<StoreImg> deleteImageWrapper = new LambdaQueryWrapper<>();
+        deleteImageWrapper.eq(StoreImg::getStoreId, area.getStoreId())
+                .eq(StoreImg::getBusinessId, areaId)
+                .eq(StoreImg::getImgType, IMG_TYPE_SPORTS_EQUIPMENT);
+        int deletedImageCount = storeImgMapper.delete(deleteImageWrapper);
+
+        log.info("删除区域{},areaId={},同时删除了{}个设施和{}张图片", 
+                deleteAreaResult ? "成功" : "失败", areaId, deletedFacilityCount, deletedImageCount);
+        return deleteAreaResult;
+    }
+
+    @Override
+    public boolean batchDeleteAreas(Integer storeId, List<Integer> areaIds) {
+        log.info("批量删除区域,storeId={},areaIds={}", storeId, areaIds);
+
+        // 参数验证
+        if (storeId == null || storeId <= 0) {
+            log.warn("批量删除区域失败,门店ID无效:{}", storeId);
+            throw new IllegalArgumentException("门店ID不能为空且必须大于0");
+        }
+
+        if (CollectionUtils.isEmpty(areaIds)) {
+            log.warn("批量删除区域失败,区域ID列表为空");
+            throw new IllegalArgumentException("区域ID列表不能为空");
+        }
+
+        // 验证所有区域都属于该门店
+        LambdaQueryWrapper<SportsFacilityArea> checkWrapper = new LambdaQueryWrapper<>();
+        checkWrapper.eq(SportsFacilityArea::getStoreId, storeId)
+                .in(SportsFacilityArea::getId, areaIds)
+                .eq(SportsFacilityArea::getDeleteFlag, DELETE_FLAG_NOT_DELETED);
+        long validCount = this.count(checkWrapper);
+        if (validCount != areaIds.size()) {
+            log.warn("批量删除区域失败,部分区域不存在或不属于该门店:storeId={},areaIds={},有效数量={}", storeId, areaIds, validCount);
+            throw new IllegalArgumentException("部分区域不存在或不属于该门店");
+        }
+
+        // 1. 批量逻辑删除区域
+        LambdaUpdateWrapper<SportsFacilityArea> areaUpdateWrapper = new LambdaUpdateWrapper<>();
+        areaUpdateWrapper.eq(SportsFacilityArea::getStoreId, storeId)
+                .in(SportsFacilityArea::getId, areaIds)
+                .set(SportsFacilityArea::getDeleteFlag, DELETE_FLAG_DELETED);
+        boolean deleteAreaResult = this.update(areaUpdateWrapper);
+
+        // 2. 批量逻辑删除这些区域下的所有设施
+        LambdaUpdateWrapper<SportsEquipmentFacility> facilityUpdateWrapper = new LambdaUpdateWrapper<>();
+        facilityUpdateWrapper.eq(SportsEquipmentFacility::getStoreId, storeId)
+                .in(SportsEquipmentFacility::getAreaId, areaIds)
+                .set(SportsEquipmentFacility::getDeleteFlag, DELETE_FLAG_DELETED);
+        int deletedFacilityCount = facilityMapper.update(null, facilityUpdateWrapper);
+
+        // 3. 批量物理删除这些区域下的所有实景图片
+        LambdaQueryWrapper<StoreImg> deleteImageWrapper = new LambdaQueryWrapper<>();
+        deleteImageWrapper.eq(StoreImg::getStoreId, storeId)
+                .in(StoreImg::getBusinessId, areaIds)
+                .eq(StoreImg::getImgType, IMG_TYPE_SPORTS_EQUIPMENT);
+        int deletedImageCount = storeImgMapper.delete(deleteImageWrapper);
+
+        log.info("批量删除区域{},storeId={},区域数量={},同时删除了{}个设施和{}张图片", 
+                deleteAreaResult ? "成功" : "失败", storeId, areaIds.size(), deletedFacilityCount, deletedImageCount);
+        return deleteAreaResult;
+    }
+}
+

+ 464 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StoreInfoServiceImpl.java

@@ -5709,5 +5709,469 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
         return vo;
     }
 
+
+    /**
+     * 查询七种类型店铺(酒吧、ktv、洗浴汗蒸、按摩足浴 、运动健身、丽人美发 、特色美食)并按距离筛选
+     *
+     * @param lon         经度
+     * @param lat         纬度
+     * @param distance    距离范围(单位:公里)
+     * @param sortType    排序模式(1:智能排序,2:好评优先,3:距离优先)
+     * @param businessType 店铺类型(KTV=3、洗浴汗蒸=4、按摩足浴=5,酒吧需要查询字典表),可选
+     * @param categoryId  分类ID(二级或三级分类的dictId,从getAllBusinessSection接口获取),可选
+     * @param storeName   店铺名称(模糊查询),可选
+     * @param pageNum     页码
+     * @param pageSize    页容
+     * @return IPage<StoreInfoVo> 分页的门店信息列表
+     */
+
+
+    @Override
+    public IPage<StoreInfoVo> getThreeCategoriesStoresByDistance(Double lon, Double lat, Double distance, Integer sortType, Integer businessType, Integer categoryId, String storeName,Integer storeType, int pageNum, int pageSize) {
+        // 参数校验
+        if (lon == null || lat == null) {
+            throw new IllegalArgumentException("经纬度参数不能为空");
+        }
+
+        // 智能检测:如果参数可能传反了(lat超出范围但lon在范围内),给出提示
+        if (Math.abs(lat) > 90 && Math.abs(lon) <= 90) {
+            throw new IllegalArgumentException(
+                    String.format("参数可能传反了!当前值: lon=%s, lat=%s。经度范围: [-180, 180],纬度范围: [-90, 90]。请检查参数顺序。", lon, lat)
+            );
+        }
+
+        // 校验经纬度范围:经度 [-180, 180],纬度 [-90, 90]
+        if (lon < -180 || lon > 180) {
+            throw new IllegalArgumentException("经度参数超出有效范围 [-180, 180],当前值: " + lon);
+        }
+        if (lat < -90 || lat > 90) {
+            throw new IllegalArgumentException("纬度参数超出有效范围 [-90, 90],当前值: " + lat);
+        }
+        final int finalSortType = (sortType == null || sortType < 1 || sortType > 3) ? 1 : sortType; // 默认智能排序
+        if (pageNum <= 0) {
+            pageNum = 1;
+        }
+        if (pageSize <= 0) {
+            pageSize = 10;
+        }
+
+        // 构建查询条件
+        QueryWrapper<StoreInfoVo> queryWrapper = new QueryWrapper<>();
+
+        // 如果传入了dictId,根据字典id查询经营板块、经营种类、分类并匹配店铺
+        if (categoryId != null) {
+            // 根据categoryId查询字典表记录
+            StoreDictionary dict = storeDictionaryMapper.selectById(categoryId);
+            if (dict == null || dict.getDeleteFlag() == 1) {
+                throw new IllegalArgumentException("字典id不存在或已删除: " + categoryId);
+            }
+
+            String typeName = dict.getTypeName();
+            String dictIdStr = dict.getDictId();
+
+            if ("business_section".equals(typeName)) {
+                // 如果是经营板块,直接匹配business_section
+                try {
+                    Integer sectionId = Integer.parseInt(dictIdStr);
+                    queryWrapper.eq("a.business_section", sectionId);
+                } catch (NumberFormatException e) {
+                    throw new IllegalArgumentException("经营板块dictId格式错误: " + dictIdStr);
+                }
+            } else if ("business_type".equals(typeName)) {
+                // 如果是经营种类,需要:
+                // 1. 向上查找父级(经营板块)
+                // 2. 匹配business_section和business_types
+                StoreDictionary parentDict = storeDictionaryMapper.selectById(dict.getParentId());
+                if (parentDict == null || !"business_section".equals(parentDict.getTypeName())) {
+                    throw new IllegalArgumentException("经营种类的父级不是经营板块,categoryId: " + categoryId);
+                }
+
+                try {
+                    Integer sectionId = Integer.parseInt(parentDict.getDictId());
+                    queryWrapper.eq("a.business_section", sectionId);
+                    // 使用FIND_IN_SET匹配business_types字段(逗号分隔)
+                    queryWrapper.apply("FIND_IN_SET({0}, a.business_types) > 0", dictIdStr);
+                } catch (NumberFormatException e) {
+                    throw new IllegalArgumentException("经营板块或经营种类dictId格式错误");
+                }
+            } else if ("business_classify".equals(typeName)) {
+                // 如果是分类,需要:
+                // 1. 向上查找父级(经营种类)
+                // 2. 向上查找祖父级(经营板块)
+                // 3. 匹配business_section、business_types和business_classify
+                StoreDictionary parentDict = storeDictionaryMapper.selectById(dict.getParentId());
+                if (parentDict == null || !"business_type".equals(parentDict.getTypeName())) {
+                    throw new IllegalArgumentException("分类的父级不是经营种类,categoryId: " + categoryId);
+                }
+
+                StoreDictionary grandParentDict = storeDictionaryMapper.selectById(parentDict.getParentId());
+                if (grandParentDict == null || !"business_section".equals(grandParentDict.getTypeName())) {
+                    throw new IllegalArgumentException("经营种类的父级不是经营板块,categoryId: " + categoryId);
+                }
+
+                try {
+                    Integer sectionId = Integer.parseInt(grandParentDict.getDictId());
+                    queryWrapper.eq("a.business_section", sectionId);
+                    // 使用FIND_IN_SET匹配business_types字段
+                    queryWrapper.apply("FIND_IN_SET({0}, a.business_types) > 0", parentDict.getDictId());
+                    // 使用FIND_IN_SET匹配business_classify字段
+                    queryWrapper.apply("FIND_IN_SET({0}, a.business_classify) > 0", dictIdStr);
+                } catch (NumberFormatException e) {
+                    throw new IllegalArgumentException("经营板块、经营种类或分类dictId格式错误");
+                }
+            } else {
+                throw new IllegalArgumentException("不支持的字典类型: " + typeName + ", categoryId: " + categoryId);
+            }
+        } else if (businessType != null) {
+            // 如果指定了businessType,则根据传入的数值进行筛选
+            // 直接使用传入的数值作为business_section进行筛选
+            // KTV=3、洗浴汗蒸=4、按摩足浴=5,酒吧的数值未知(由调用方传入)
+            queryWrapper.eq("a.business_section", businessType);
+        } else {
+            // 如果没有指定businessType,则查询所有四种类型的店铺
+            // 需要查询字典表获取所有四种类型的dictId
+//            List<String> storeTypeNames = Arrays.asList("酒吧", "KTV", "洗浴汗蒸", "按摩足疗");
+            List<String> storeTypeNames=new ArrayList<>();
+            if (storeType !=null &&   storeType ==1 ){
+                 storeTypeNames = Arrays.asList("酒吧", "KTV", "洗浴汗蒸", "按摩足疗");
+            } else if (storeType !=null &&   storeType==2) {
+                 storeTypeNames = Arrays.asList("丽人美发", "运动健身");
+            }else if ( storeType !=null && storeType==3) {
+                 storeTypeNames = Arrays.asList("特色美食");
+            }
+
+
+            List<StoreDictionary> storeDictionaries = storeDictionaryMapper.selectList(
+                    new LambdaQueryWrapper<StoreDictionary>()
+                            .eq(StoreDictionary::getTypeName, "business_section")
+                            .in(StoreDictionary::getDictDetail, storeTypeNames)
+                            .eq(StoreDictionary::getDeleteFlag, 0)
+            );
+
+            List<Integer> businessSectionIds = storeDictionaries.stream()
+                    .filter(d -> StringUtils.isNotEmpty(d.getDictId()))
+                    .map(StoreDictionary::getDictId)
+                    .map(dictIdStr -> {
+                        try {
+                            return Integer.parseInt(dictIdStr);
+                        } catch (NumberFormatException e) {
+                            return null;
+                        }
+                    })
+                    .filter(Objects::nonNull)
+                    .collect(Collectors.toList());
+
+            // 使用business_section字段进行筛选
+            queryWrapper.in("a.business_section", businessSectionIds);
+        }
+
+        // 过滤已删除的门店
+        queryWrapper.eq("a.delete_flag", 0);
+        // 过滤注销的门店
+        queryWrapper.eq("a.logout_flag", 0);
+        // 过滤禁用的门店
+        queryWrapper.ne("a.store_status", 0);
+        // 过滤永久关门的店铺
+        queryWrapper.ne("a.business_status", 99);
+        // 过滤过期的经营许可证
+//        queryWrapper.ge("a.entertainment_licence_expiration_time", new Date());
+        // 添加一个模糊查询,根据店铺名称进行查询
+        if (StringUtils.isNotEmpty(storeName)) {
+            queryWrapper.like("a.store_name", storeName);
+        }
+
+        // 距离优先模式:只显示10公里内且3.5星以上的店铺
+        final Double finalDistance;
+        if (finalSortType == 3) {
+            queryWrapper.ge("a.score_avg", 3.5);
+            if (distance == null || distance <= 0) {
+                finalDistance = 10.0; // 默认10公里
+            } else if (distance > 10) {
+                finalDistance = 10.0; // 距离优先最多10公里
+            } else {
+                finalDistance = distance;
+            }
+        } else {
+            finalDistance = distance;
+        }
+
+        // 先按距离排序获取所有数据(用于计算距离)
+        queryWrapper.orderByAsc("dist");
+
+        // 创建分页对象(先获取更多数据用于排序计算)
+        IPage<StoreInfoVo> page = new Page<>(1, 1000); // 先获取足够多的数据
+        IPage<StoreInfoVo> storeInfoIPage = storeInfoMapper.getPageForDistance(page, lon + "," + lat, queryWrapper);
+        List<StoreInfoVo> storeInfoVoList = storeInfoIPage.getRecords();
+
+        // 如果指定了距离范围,进行距离筛选
+        if (finalDistance != null && finalDistance > 0) {
+            storeInfoVoList = storeInfoVoList.stream()
+                    .filter(store -> {
+                        String distStr = store.getDist();
+                        if (distStr == null || distStr.isEmpty()) {
+                            return false;
+                        }
+                        try {
+                            double storeDistance = Double.parseDouble(distStr);
+                            return storeDistance <= finalDistance;
+                        } catch (NumberFormatException e) {
+                            return false;
+                        }
+                    })
+                    .collect(Collectors.toList());
+        }
+
+        // 计算时间范围
+        LocalDateTime now = LocalDateTime.now();
+        LocalDateTime sevenDaysAgo = now.minusDays(7);
+        LocalDateTime thirtyDaysAgo = now.minusDays(30);
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+        String sevenDaysAgoStr = sevenDaysAgo.format(formatter);
+        String thirtyDaysAgoStr = thirtyDaysAgo.format(formatter);
+
+        // 提取所有门店ID
+        List<Integer> storeIds = storeInfoVoList.stream().map(StoreInfoVo::getId).collect(Collectors.toList());
+
+        if (CollectionUtils.isEmpty(storeIds)) {
+            // 如果没有符合条件的店铺,返回空结果
+            IPage<StoreInfoVo> emptyPage = new Page<>(pageNum, pageSize);
+            emptyPage.setRecords(new ArrayList<>());
+            emptyPage.setTotal(0);
+            return emptyPage;
+        }
+
+        // 查询近7天销量(订单数)
+        Map<Integer, Long> sales7DaysMap = new HashMap<>();
+        if (finalSortType == 1) { // 智能排序需要销量数据
+            LambdaQueryWrapper<LifeUserOrder> orderWrapper = new LambdaQueryWrapper<>();
+            orderWrapper.in(LifeUserOrder::getStoreId, storeIds)
+                    .and(w -> w.and(w1 -> w1.eq(LifeUserOrder::getStatus, 7)
+                                    .ge(LifeUserOrder::getFinishTime, sevenDaysAgoStr))
+                            .or(w2 -> w2.eq(LifeUserOrder::getStatus, 1)
+                                    .ge(LifeUserOrder::getPayTime, sevenDaysAgoStr)))
+                    .eq(LifeUserOrder::getDeleteFlag, 0);
+            List<LifeUserOrder> orders7Days = lifeUserOrderMapper.selectList(orderWrapper);
+            Map<Integer, Long> tempSalesMap = orders7Days.stream()
+                    .filter(order -> order.getStoreId() != null)
+                    .collect(Collectors.groupingBy(
+                            order -> Integer.parseInt(order.getStoreId()),
+                            Collectors.counting()));
+            sales7DaysMap.putAll(tempSalesMap);
+        }
+
+        // 查询近30天好评数(score >= 4.5)
+        Map<Integer, Long> goodComment30DaysMap = new HashMap<>();
+        // 查询近7天评论数
+        Map<Integer, Long> comment7DaysMap = new HashMap<>();
+        // 查询总评论数
+        Map<Integer, Long> totalCommentMap = new HashMap<>();
+
+        if (finalSortType == 2) { // 好评优先需要评论数据
+            // 近30天好评数
+            LambdaQueryWrapper<StoreComment> goodCommentWrapper = new LambdaQueryWrapper<>();
+            goodCommentWrapper.in(StoreComment::getStoreId, storeIds)
+                    .eq(StoreComment::getBusinessType, 5)
+                    .eq(StoreComment::getDeleteFlag, 0)
+                    .isNull(StoreComment::getReplyId)
+                    .ge(StoreComment::getScore, 4.5)
+                    .ge(StoreComment::getCreatedTime, thirtyDaysAgoStr);
+            List<StoreComment> goodComments30Days = storeCommentMapper.selectList(goodCommentWrapper);
+            Map<Integer, Long> tempGoodCommentMap = goodComments30Days.stream()
+                    .collect(Collectors.groupingBy(StoreComment::getStoreId, Collectors.counting()));
+            goodComment30DaysMap.putAll(tempGoodCommentMap);
+
+            // 近7天评论数
+            LambdaQueryWrapper<StoreComment> comment7DaysWrapper = new LambdaQueryWrapper<>();
+            comment7DaysWrapper.in(StoreComment::getStoreId, storeIds)
+                    .eq(StoreComment::getBusinessType, 5)
+                    .eq(StoreComment::getDeleteFlag, 0)
+                    .isNull(StoreComment::getReplyId)
+                    .ge(StoreComment::getCreatedTime, sevenDaysAgoStr);
+            List<StoreComment> comments7Days = storeCommentMapper.selectList(comment7DaysWrapper);
+            Map<Integer, Long> tempComment7DaysMap = comments7Days.stream()
+                    .collect(Collectors.groupingBy(StoreComment::getStoreId, Collectors.counting()));
+            comment7DaysMap.putAll(tempComment7DaysMap);
+
+            // 总评论数
+            LambdaQueryWrapper<StoreComment> totalCommentWrapper = new LambdaQueryWrapper<>();
+            totalCommentWrapper.in(StoreComment::getStoreId, storeIds)
+                    .eq(StoreComment::getBusinessType, 5)
+                    .eq(StoreComment::getDeleteFlag, 0)
+                    .isNull(StoreComment::getReplyId);
+            List<StoreComment> totalComments = storeCommentMapper.selectList(totalCommentWrapper);
+            Map<Integer, Long> tempTotalCommentMap = totalComments.stream()
+                    .collect(Collectors.groupingBy(StoreComment::getStoreId, Collectors.counting()));
+            totalCommentMap.putAll(tempTotalCommentMap);
+        }
+
+        // 获取全部店铺的评分
+        Map<String, List<Map<String, Object>>> avgScoreMap = storeEvaluationMapper.allStoreAvgScore().stream()
+                .collect(Collectors.groupingBy(o -> o.get("store_id").toString()));
+        Map<String, List<Map<String, Object>>> avgPriceMap = lifeUserOrderMapper.allStoreAvgPrice().stream()
+                .collect(Collectors.groupingBy(o -> o.get("store_id").toString()));
+
+        // 计算综合得分并排序
+        List<StoreInfoVo> sortedList = storeInfoVoList.stream()
+                .map(store -> {
+                    // 获取基础评分(score_avg × 2,标准化为0-10分)
+                    Double scoreAvg = store.getScoreAvg() != null ? store.getScoreAvg() : 0.0;
+                    double baseScore = Math.min(scoreAvg * 2, 10.0); // 基础评分,最高10分
+
+                    // 获取距离
+                    double storeDistance = 999999;
+                    try {
+                        String distStr = store.getDist();
+                        if (distStr != null && !distStr.isEmpty()) {
+                            storeDistance = Double.parseDouble(distStr);
+                        }
+                    } catch (NumberFormatException e) {
+                        // 忽略
+                    }
+
+                    double finalScore = 0.0;
+
+                    if (finalSortType == 1) {
+                        // 智能排序:综合评分×50% + 近7天销量×30% + 距离得分×20%
+                        // 综合评分(基础评分)
+                        double scorePart = baseScore * 0.5;
+
+                        // 近7天销量(需要标准化,假设最大销量为100)
+                        long sales7Days = sales7DaysMap.getOrDefault(store.getId(), 0L);
+                        double salesScore = Math.min(sales7Days / 100.0 * 10, 10.0); // 标准化到0-10
+                        double salesPart = salesScore * 0.3;
+
+                        // 距离得分(距离越近得分越高,10公里内计算)
+                        double distanceScore = storeDistance <= 10 ? (10 - storeDistance) / 10.0 * 10 : 0;
+                        double distancePart = distanceScore * 0.2;
+
+                        finalScore = scorePart + salesPart + distancePart;
+                    } else if (finalSortType == 2) {
+                        // 好评优先:综合评分×50% + 近30天好评数×35% + 近7天新评占比×15%
+                        double scorePart = baseScore * 0.5;
+
+                        // 近30天好评数(需要标准化,假设最大好评数为50)
+                        long goodComment30Days = goodComment30DaysMap.getOrDefault(store.getId(), 0L);
+                        double goodCommentScore = Math.min(goodComment30Days / 50.0 * 10, 10.0);
+                        double goodCommentPart = goodCommentScore * 0.35;
+
+                        // 近7天新评占比
+                        long comment7Days = comment7DaysMap.getOrDefault(store.getId(), 0L);
+                        long totalComment = totalCommentMap.getOrDefault(store.getId(), 1L); // 避免除0
+                        double newCommentRatio = (double) comment7Days / totalComment;
+                        double newCommentPart = newCommentRatio * 10 * 0.15; // 占比转换为0-10分
+
+                        finalScore = scorePart + goodCommentPart + newCommentPart;
+                    } else if (finalSortType == 3) {
+                        // 距离优先:距离得分 = (10 - 实际距离) × 80% + 基础评分 × 20%(10公里内计算)
+                        if (storeDistance <= 10) {
+                            double distanceScore = (10 - storeDistance) / 10.0 * 10; // 标准化到0-10
+                            double distancePart = distanceScore * 0.8;
+                            double scorePart = baseScore * 0.2;
+                            finalScore = distancePart + scorePart;
+                        } else {
+                            finalScore = -1; // 超出范围,不展示
+                        }
+                    }
+
+                    // 设置综合得分(用于排序)
+                    store.setDistance(storeDistance);
+                    // 使用反射或扩展字段存储finalScore,这里我们使用一个临时字段
+                    // 由于StoreInfoVo没有finalScore字段,我们使用distance字段临时存储,排序后再恢复
+                    return new Object[]{store, finalScore};
+                })
+                .filter(item -> {
+                    // 距离优先模式:过滤掉超出范围的
+                    if (finalSortType == 3) {
+                        return ((Double) item[1]) >= 0;
+                    }
+                    return true;
+                })
+                .sorted((a, b) -> Double.compare((Double) b[1], (Double) a[1])) // 按得分降序
+                .map(item -> (StoreInfoVo) item[0])
+                .collect(Collectors.toList());
+
+        // 分页处理
+        long total = sortedList.size();
+        int start = (pageNum - 1) * pageSize;
+        int end = Math.min(start + pageSize, sortedList.size());
+        List<StoreInfoVo> pagedList;
+        if (start < sortedList.size()) {
+            pagedList = sortedList.subList(start, end);
+        } else {
+            pagedList = new ArrayList<>();
+        }
+
+        // 查询每个店铺的最新代金券(只显示一个,最新创建的)
+        Map<Integer, LifeCoupon> latestCouponMap = new HashMap<>();
+        if (!CollectionUtils.isEmpty(pagedList)) {
+            List<Integer> pagedStoreIds = pagedList.stream().map(StoreInfoVo::getId).collect(Collectors.toList());
+            // 查询所有店铺的代金券,按创建时间降序
+            LambdaQueryWrapper<LifeCoupon> couponWrapper = new LambdaQueryWrapper<>();
+            couponWrapper.in(LifeCoupon::getStoreId, pagedStoreIds.stream().map(String::valueOf).collect(Collectors.toList()))
+                    .eq(LifeCoupon::getStatus, CouponStatusEnum.ONGOING.getCode())
+                    .eq(LifeCoupon::getType, 1) // 代金券类型
+                    .eq(LifeCoupon::getDeleteFlag, 0)
+                    .orderByDesc(LifeCoupon::getCreatedTime);
+            List<LifeCoupon> allCoupons = lifeCouponMapper.selectList(couponWrapper);
+
+            // 为每个店铺只保留最新创建的一个代金券
+            for (LifeCoupon coupon : allCoupons) {
+                if (coupon.getStoreId() != null) {
+                    try {
+                        Integer storeId = Integer.parseInt(coupon.getStoreId());
+                        // 如果该店铺还没有代金券,或者当前代金券更新,则更新
+                        if (!latestCouponMap.containsKey(storeId)) {
+                            latestCouponMap.put(storeId, coupon);
+                        }
+                    } catch (NumberFormatException e) {
+                        // 忽略无效的storeId
+                    }
+                }
+            }
+        }
+
+        // 设置评分、平均消费和代金券
+        for (StoreInfoVo store : pagedList) {
+            if (avgScoreMap.containsKey(String.valueOf(store.getId()))) {
+                store.setAvgScore(new BigDecimal(avgScoreMap.get(String.valueOf(store.getId())).get(0).get("avg_score").toString())
+                        .setScale(1, RoundingMode.HALF_UP).toString());
+                store.setTotalNum(avgScoreMap.get(String.valueOf(store.getId())).get(0).get("total_num").toString());
+            } else {
+                store.setAvgScore("0");
+                store.setTotalNum("0");
+            }
+
+            if (avgPriceMap.containsKey(String.valueOf(store.getId()))) {
+                store.setAvgPrice(avgPriceMap.get(String.valueOf(store.getId())).get(0).get("avg_price").toString());
+            } else {
+                store.setAvgPrice("0");
+            }
+
+            // 设置最新代金券(只显示一个)
+            if (latestCouponMap.containsKey(store.getId())) {
+                LifeCoupon latestCoupon = latestCouponMap.get(store.getId());
+                LifeCouponVo couponVo = new LifeCouponVo();
+                BeanUtils.copyProperties(latestCoupon, couponVo);
+                // 只设置一个代金券到列表中
+                List<LifeCouponVo> couponList = new ArrayList<>();
+                couponList.add(couponVo);
+                store.setCouponList(couponList);
+            } else {
+                // 如果没有代金券,设置为空列表
+                store.setCouponList(new ArrayList<>());
+            }
+        }
+
+        // 创建分页对象
+        IPage<StoreInfoVo> resultPage = new Page<>(pageNum, pageSize);
+        resultPage.setRecords(pagedList);
+        resultPage.setTotal(total);
+        resultPage.setCurrent(pageNum);
+        resultPage.setSize(pageSize);
+        resultPage.setPages((total + pageSize - 1) / pageSize);
+
+        return resultPage;
+    }
+
 }
 

+ 183 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StoreProductBarServiceImpl.java

@@ -0,0 +1,183 @@
+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.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.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreProductBar;
+import shop.alien.mapper.StoreProductBarMapper;
+import shop.alien.store.service.StoreProductBarService;
+
+import java.util.List;
+
+/**
+ * 酒吧商品表 服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Transactional
+@Service
+@RequiredArgsConstructor
+public class StoreProductBarServiceImpl extends ServiceImpl<StoreProductBarMapper, StoreProductBar> implements StoreProductBarService {
+
+    @Override
+    public R<StoreProductBar> addStoreProductBar(StoreProductBar storeProductBar) {
+        log.info("StoreProductBarServiceImpl.addStoreProductBar?storeProductBar={}", storeProductBar);
+        // 设置默认值
+        if (storeProductBar.getUnit() == null || storeProductBar.getUnit().isEmpty()) {
+            storeProductBar.setUnit("份");
+        }
+        if (storeProductBar.getQuantity() == null) {
+            storeProductBar.setQuantity(0);
+        }
+        if (storeProductBar.getStatus() == null) {
+            storeProductBar.setStatus(1);
+        }
+        if (storeProductBar.getCreatedUserId() == null) {
+            storeProductBar.setCreatedUserId(0);
+        }
+        if (storeProductBar.getUpdatedUserId() == null) {
+            storeProductBar.setUpdatedUserId(0);
+        }
+
+        boolean result = this.save(storeProductBar);
+        if (result) {
+            return R.data(storeProductBar);
+        }
+        return R.fail("新增失败");
+    }
+
+    @Override
+    public R<StoreProductBar> editStoreProductBar(StoreProductBar storeProductBar) {
+        log.info("StoreProductBarServiceImpl.editStoreProductBar?storeProductBar={}", storeProductBar);
+        if (storeProductBar.getId() == null) {
+            log.error("更新酒吧商品失败:ID不能为空");
+            return R.fail("ID不能为空");
+        }
+
+        // 设置更新人
+        if (storeProductBar.getUpdatedUserId() == null) {
+            storeProductBar.setUpdatedUserId(0);
+        }
+
+        boolean result = this.updateById(storeProductBar);
+        if (result) {
+            return R.data(storeProductBar);
+        }
+        return R.fail("修改失败");
+    }
+
+    @Override
+    public R<Boolean> deleteStoreProductBar(Integer id) {
+        log.info("StoreProductBarServiceImpl.deleteStoreProductBar?id={}", id);
+        boolean result = this.removeById(id);
+        if (result) {
+            return R.success("删除成功");
+        }
+        return R.fail("删除失败");
+    }
+
+    @Override
+    public R<StoreProductBar> getStoreProductBarById(Integer id) {
+        log.info("StoreProductBarServiceImpl.getStoreProductBarById?id={}", id);
+        StoreProductBar storeProductBar = this.getById(id);
+        if (storeProductBar != null) {
+            return R.data(storeProductBar);
+        }
+        return R.fail("查询失败,数据不存在");
+    }
+
+    @Override
+    public IPage<StoreProductBar> getPage(int pageNum, int pageSize, String name, String category, Integer status, Integer extId) {
+        LambdaQueryWrapper<StoreProductBar> queryWrapper = new LambdaQueryWrapper<>();
+
+        // 名称模糊查询
+        if (StringUtils.hasText(name)) {
+            queryWrapper.like(StoreProductBar::getName, name);
+        }
+
+        // 品类查询
+        if (StringUtils.hasText(category)) {
+            queryWrapper.eq(StoreProductBar::getCategory, category);
+        }
+
+        // 状态查询
+        if (status != null) {
+            queryWrapper.eq(StoreProductBar::getStatus, status);
+        }
+
+        // 商品表主键查询
+        if (extId != null) {
+            queryWrapper.eq(StoreProductBar::getExtId, extId);
+        }
+
+        // 按创建时间倒序
+        queryWrapper.orderByDesc(StoreProductBar::getCreatedTime);
+
+        return this.page(new Page<>(pageNum, pageSize), queryWrapper);
+    }
+
+    @Override
+    public List<StoreProductBar> getListByExtId(Integer extId) {
+        if (extId == null) {
+            return null;
+        }
+        LambdaQueryWrapper<StoreProductBar> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(StoreProductBar::getExtId, extId)
+                .orderByDesc(StoreProductBar::getCreatedTime);
+        return this.list(queryWrapper);
+    }
+
+    @Override
+    public List<StoreProductBar> getListByCategory(String category) {
+        if (!StringUtils.hasText(category)) {
+            return null;
+        }
+        LambdaQueryWrapper<StoreProductBar> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(StoreProductBar::getCategory, category)
+                .orderByDesc(StoreProductBar::getCreatedTime);
+        return this.list(queryWrapper);
+    }
+
+    @Override
+    public boolean updateStatus(Integer id, Integer status) {
+        if (id == null) {
+            log.error("更新酒吧商品状态失败:ID不能为空");
+            return false;
+        }
+        if (status == null || (status != 0 && status != 1)) {
+            log.error("更新酒吧商品状态失败:状态值无效,只能为0或1");
+            return false;
+        }
+
+        LambdaUpdateWrapper<StoreProductBar> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.eq(StoreProductBar::getId, id)
+                .set(StoreProductBar::getStatus, status);
+
+        return this.update(updateWrapper);
+    }
+
+    @Override
+    public boolean deleteBatch(List<Integer> ids) {
+        if (ids == null || ids.isEmpty()) {
+            log.error("批量删除酒吧商品失败:ID列表不能为空");
+            return false;
+        }
+        // 逻辑删除,MyBatis-Plus会自动处理
+        return this.removeByIds(ids);
+    }
+
+    @Override
+    public List<StoreProductBar> getByExtId(Integer id) {
+        return this.lambdaQuery().eq(StoreProductBar::getExtId, id).list();
+    }
+}

+ 180 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StoreProductDelicaciesServiceImpl.java

@@ -0,0 +1,180 @@
+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.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.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+import shop.alien.entity.store.StoreProductDelicacies;
+import shop.alien.mapper.StoreProductDelicaciesMapper;
+import shop.alien.store.service.StoreProductDelicaciesService;
+
+import java.util.List;
+
+/**
+ * 美食商品表 服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+@Transactional
+public class StoreProductDelicaciesServiceImpl extends ServiceImpl<StoreProductDelicaciesMapper, StoreProductDelicacies> implements StoreProductDelicaciesService {
+
+    @Override
+    public IPage<StoreProductDelicacies> getPage(int pageNum, int pageSize, String name, String category, String extGroup, Integer status, Integer extId) {
+        LambdaQueryWrapper<StoreProductDelicacies> queryWrapper = new LambdaQueryWrapper<>();
+
+        // 名称模糊查询
+        if (StringUtils.hasText(name)) {
+            queryWrapper.like(StoreProductDelicacies::getName, name);
+        }
+
+        // 类别查询
+        if (StringUtils.hasText(category)) {
+            queryWrapper.eq(StoreProductDelicacies::getCategory, category);
+        }
+
+        // 菜品分组查询
+        if (StringUtils.hasText(extGroup)) {
+            queryWrapper.eq(StoreProductDelicacies::getExtGroup, extGroup);
+        }
+
+        // 状态查询
+        if (status != null) {
+            queryWrapper.eq(StoreProductDelicacies::getStatus, status);
+        }
+
+        // 商品表主键查询
+        if (extId != null) {
+            queryWrapper.eq(StoreProductDelicacies::getExtId, extId);
+        }
+
+        // 按创建时间倒序
+        queryWrapper.orderByDesc(StoreProductDelicacies::getCreatedTime);
+
+        return baseMapper.selectPage(new Page<>(pageNum, pageSize), queryWrapper);
+    }
+
+    @Override
+    public StoreProductDelicacies getById(Integer id) {
+        return baseMapper.selectById(id);
+    }
+
+    @Override
+    public boolean saveDelicacies(StoreProductDelicacies delicacies) {
+        // 设置默认值
+        if (delicacies.getUnit() == null || delicacies.getUnit().isEmpty()) {
+            delicacies.setUnit("份");
+        }
+        if (delicacies.getQuantity() == null) {
+            delicacies.setQuantity(0);
+        }
+        if (delicacies.getStatus() == null) {
+            delicacies.setStatus(1);
+        }
+        if (delicacies.getCreatedUserId() == null) {
+            delicacies.setCreatedUserId(0);
+        }
+        if (delicacies.getUpdatedUserId() == null) {
+            delicacies.setUpdatedUserId(0);
+        }
+
+        return baseMapper.insert(delicacies) > 0;
+    }
+
+    @Override
+    public boolean updateDelicacies(StoreProductDelicacies delicacies) {
+        if (delicacies.getId() == null) {
+            log.error("更新美食商品失败:ID不能为空");
+            return false;
+        }
+
+        // 设置更新人
+        if (delicacies.getUpdatedUserId() == null) {
+            delicacies.setUpdatedUserId(0);
+        }
+
+        return baseMapper.updateById(delicacies) > 0;
+    }
+
+    @Override
+    public boolean deleteDelicacies(Integer id) {
+        // 逻辑删除,MyBatis-Plus会自动处理
+        return baseMapper.deleteById(id) > 0;
+    }
+
+    @Override
+    public boolean deleteBatch(List<Integer> ids) {
+        if (ids == null || ids.isEmpty()) {
+            log.error("批量删除美食商品失败:ID列表不能为空");
+            return false;
+        }
+        // 逻辑删除,MyBatis-Plus会自动处理
+        return baseMapper.deleteBatchIds(ids) > 0;
+    }
+
+    @Override
+    public boolean updateStatus(Integer id, Integer status) {
+        if (id == null) {
+            log.error("更新美食商品状态失败:ID不能为空");
+            return false;
+        }
+        if (status == null || (status != 0 && status != 1)) {
+            log.error("更新美食商品状态失败:状态值无效,只能为0或1");
+            return false;
+        }
+
+        LambdaUpdateWrapper<StoreProductDelicacies> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.eq(StoreProductDelicacies::getId, id)
+                .set(StoreProductDelicacies::getStatus, status);
+
+        return baseMapper.update(null, updateWrapper) > 0;
+    }
+
+    @Override
+    public List<StoreProductDelicacies> getListByExtId(Integer extId) {
+        if (extId == null) {
+            return null;
+        }
+        LambdaQueryWrapper<StoreProductDelicacies> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(StoreProductDelicacies::getExtId, extId)
+                .orderByDesc(StoreProductDelicacies::getCreatedTime);
+        return baseMapper.selectList(queryWrapper);
+    }
+
+    @Override
+    public List<StoreProductDelicacies> getListByCategory(String category) {
+        if (!StringUtils.hasText(category)) {
+            return null;
+        }
+        LambdaQueryWrapper<StoreProductDelicacies> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(StoreProductDelicacies::getCategory, category)
+                .orderByDesc(StoreProductDelicacies::getCreatedTime);
+        return baseMapper.selectList(queryWrapper);
+    }
+
+    @Override
+    public List<StoreProductDelicacies> getListByExtGroup(String extGroup) {
+        if (!StringUtils.hasText(extGroup)) {
+            return null;
+        }
+        LambdaQueryWrapper<StoreProductDelicacies> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(StoreProductDelicacies::getExtGroup, extGroup)
+                .orderByDesc(StoreProductDelicacies::getCreatedTime);
+        return baseMapper.selectList(queryWrapper);
+    }
+
+    @Override
+    public List<StoreProductDelicacies> getByExtId(Integer id) {
+        return this.lambdaQuery().eq(StoreProductDelicacies::getExtId, id).list();
+    }
+}
+

+ 169 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StoreProductGymServiceImpl.java

@@ -0,0 +1,169 @@
+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.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.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreProductGym;
+import shop.alien.mapper.StoreProductGymMapper;
+import shop.alien.store.service.StoreProductGymService;
+
+import java.util.List;
+
+/**
+ * 运动健身商品表 服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Transactional
+@Service
+@RequiredArgsConstructor
+public class StoreProductGymServiceImpl extends ServiceImpl<StoreProductGymMapper, StoreProductGym> implements StoreProductGymService {
+
+    @Override
+    public R<StoreProductGym> addStoreProductGym(StoreProductGym storeProductGym) {
+        log.info("StoreProductGymServiceImpl.addStoreProductGym?storeProductGym={}", storeProductGym);
+        // 设置默认值
+        if (storeProductGym.getPrice() == null) {
+            storeProductGym.setPrice(new java.math.BigDecimal("0.00"));
+        }
+        if (storeProductGym.getStatus() == null) {
+            storeProductGym.setStatus(1);
+        }
+        if (storeProductGym.getCreatedUserId() == null) {
+            storeProductGym.setCreatedUserId(0);
+        }
+        if (storeProductGym.getUpdatedUserId() == null) {
+            storeProductGym.setUpdatedUserId(0);
+        }
+
+        boolean result = this.save(storeProductGym);
+        if (result) {
+            return R.data(storeProductGym);
+        }
+        return R.fail("新增失败");
+    }
+
+    @Override
+    public R<StoreProductGym> editStoreProductGym(StoreProductGym storeProductGym) {
+        log.info("StoreProductGymServiceImpl.editStoreProductGym?storeProductGym={}", storeProductGym);
+        if (storeProductGym.getId() == null) {
+            log.error("更新运动健身商品失败:ID不能为空");
+            return R.fail("ID不能为空");
+        }
+
+        // 设置更新人
+        if (storeProductGym.getUpdatedUserId() == null) {
+            storeProductGym.setUpdatedUserId(0);
+        }
+
+        boolean result = this.updateById(storeProductGym);
+        if (result) {
+            return R.data(storeProductGym);
+        }
+        return R.fail("修改失败");
+    }
+
+    @Override
+    public R<Boolean> deleteStoreProductGym(Integer id) {
+        log.info("StoreProductGymServiceImpl.deleteStoreProductGym?id={}", id);
+        boolean result = this.removeById(id);
+        if (result) {
+            return R.success("删除成功");
+        }
+        return R.fail("删除失败");
+    }
+
+    @Override
+    public R<StoreProductGym> getStoreProductGymById(Integer id) {
+        log.info("StoreProductGymServiceImpl.getStoreProductGymById?id={}", id);
+        StoreProductGym storeProductGym = this.getById(id);
+        if (storeProductGym != null) {
+            return R.data(storeProductGym);
+        }
+        return R.fail("查询失败,数据不存在");
+    }
+
+    @Override
+    public IPage<StoreProductGym> getPage(int pageNum, int pageSize, String name, String classMode, Integer status, Integer extId) {
+        LambdaQueryWrapper<StoreProductGym> queryWrapper = new LambdaQueryWrapper<>();
+
+        // 名称模糊查询
+        if (StringUtils.hasText(name)) {
+            queryWrapper.like(StoreProductGym::getName, name);
+        }
+
+        // 上课形式查询
+        if (StringUtils.hasText(classMode)) {
+            queryWrapper.eq(StoreProductGym::getClassMode, classMode);
+        }
+
+        // 状态查询
+        if (status != null) {
+            queryWrapper.eq(StoreProductGym::getStatus, status);
+        }
+
+        // 商品表主键查询
+        if (extId != null) {
+            queryWrapper.eq(StoreProductGym::getExtId, extId);
+        }
+
+        // 按创建时间倒序
+        queryWrapper.orderByDesc(StoreProductGym::getCreatedTime);
+
+        return this.page(new Page<>(pageNum, pageSize), queryWrapper);
+    }
+
+    @Override
+    public List<StoreProductGym> getListByExtId(Integer extId) {
+        if (extId == null) {
+            return null;
+        }
+        LambdaQueryWrapper<StoreProductGym> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(StoreProductGym::getExtId, extId)
+                .orderByDesc(StoreProductGym::getCreatedTime);
+        return this.list(queryWrapper);
+    }
+
+    @Override
+    public boolean updateStatus(Integer id, Integer status) {
+        if (id == null) {
+            log.error("更新运动健身商品状态失败:ID不能为空");
+            return false;
+        }
+        if (status == null || (status != 0 && status != 1)) {
+            log.error("更新运动健身商品状态失败:状态值无效,只能为0或1");
+            return false;
+        }
+
+        LambdaUpdateWrapper<StoreProductGym> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.eq(StoreProductGym::getId, id)
+                .set(StoreProductGym::getStatus, status);
+
+        return this.update(updateWrapper);
+    }
+
+    @Override
+    public boolean deleteBatch(List<Integer> ids) {
+        if (ids == null || ids.isEmpty()) {
+            log.error("批量删除运动健身商品失败:ID列表不能为空");
+            return false;
+        }
+        // 逻辑删除,MyBatis-Plus会自动处理
+        return this.removeByIds(ids);
+    }
+
+    @Override
+    public List<StoreProductGym> getByExtId(Integer id) {
+        return this.lambdaQuery().eq(StoreProductGym::getExtId, id).list();
+    }
+}

+ 461 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StoreProductItemServiceImpl.java

@@ -0,0 +1,461 @@
+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.StringUtils;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import shop.alien.entity.result.CommonEnum;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreProductBar;
+import shop.alien.entity.store.StoreProductDelicacies;
+import shop.alien.entity.store.StoreProductGym;
+import shop.alien.entity.store.StoreProductItem;
+import shop.alien.entity.store.vo.StoreProductItemGymVo;
+import shop.alien.entity.store.vo.StoreProductItemDelicaciesVo;
+import shop.alien.entity.store.vo.StoreProductDelicaciesVo;
+import shop.alien.entity.store.dto.StoreProductItemDto;
+import shop.alien.mapper.StoreProductItemMapper;
+import shop.alien.store.service.StoreProductBarService;
+import shop.alien.store.service.StoreProductDelicaciesService;
+import shop.alien.store.service.StoreProductGymService;
+import shop.alien.store.service.StoreProductItemService;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * 商品表 服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Transactional(rollbackFor = Exception.class)
+@Service
+@RequiredArgsConstructor
+public class StoreProductItemServiceImpl extends ServiceImpl<StoreProductItemMapper, StoreProductItem> implements StoreProductItemService {
+
+    private final ObjectMapper objectMapper;
+
+    private final StoreProductBarService storeProductBarService;
+
+    private final StoreProductDelicaciesService storeProductDelicaciesService;
+
+    private final StoreProductGymService storeProductGymService;
+
+    @Override
+    public R<StoreProductItem> addStoreProductItemDto(StoreProductItemDto storeProductItemDto) {
+        log.info("StoreProductItemServiceImpl.addStoreProductItem?storeProductItem={}", storeProductItemDto);
+        StoreProductItem storeProductItem = new StoreProductItem();
+        BeanUtils.copyProperties(storeProductItemDto, storeProductItem);
+        // 先保存主商品,确保可以拿到自增的 id 作为子表 extId。
+        this.save(storeProductItem);
+        // 根据商品类型分发子表保存逻辑。
+        processSubItems(storeProductItemDto, storeProductItem, false);
+        return R.data(storeProductItem);
+    }
+
+    @Override
+    public R<StoreProductItem> editStoreProductItem(StoreProductItemDto storeProductItemDto) {
+        log.info("StoreProductItemServiceImpl.editStoreProductItem?storeProductItem={}", storeProductItemDto);
+        StoreProductItem storeProductItem = new StoreProductItem();
+        BeanUtils.copyProperties(storeProductItemDto, storeProductItem);
+        this.updateById(storeProductItem);
+        // 统一的子表处理:先清理旧数据,再按商品类型重建。
+        processSubItems(storeProductItemDto, storeProductItem, true);
+        return R.data(storeProductItem);
+    }
+
+    /**
+     * 新增/编辑场景下的子表处理。
+     *
+     * @param storeProductItemDto 前端传入的数据,包含主表和子表信息
+     * @param storeProductItem    已保存/更新的主表实体,需从中读取 id 作为子表 extId
+     * @param replaceExisting     是否需要先删除同 extId 的旧数据(编辑场景)
+     */
+    private void processSubItems(StoreProductItemDto storeProductItemDto,
+                                 StoreProductItem storeProductItem,
+                                 boolean replaceExisting) {
+        List<?> rawSubList = storeProductItemDto.getSubList();
+        if (rawSubList == null || rawSubList.isEmpty()) {
+            return;
+        }
+        Integer modelType = storeProductItemDto.getModelType();
+        if (modelType == null) {
+            log.warn("商品缺少 modelType,跳过子项处理");
+            return;
+        }
+        Integer extId = storeProductItem.getId();
+
+        // 编辑场景:删除当前商品 extId 对应的旧子表数据,避免脏数据。
+        if (replaceExisting) {
+            clearSubItemsByModelType(modelType, extId);
+        }
+
+        // 按商品类型把原始子项转换为目标实体并批量入库。
+        if (CommonEnum.ModelType.BAR.getCode() == modelType) {
+            convertAndSave(rawSubList, extId, StoreProductBar.class,
+                    (item, id) -> item.setExtId(id), storeProductBarService::saveBatch);
+        } else if (CommonEnum.ModelType.DELICACY.getCode() == modelType) {
+            convertAndSave(rawSubList, extId, StoreProductDelicacies.class,
+                    (item, id) -> item.setExtId(id), storeProductDelicaciesService::saveBatch);
+        } else if (CommonEnum.ModelType.GYM.getCode() == modelType) {
+            convertAndSave(rawSubList, extId, StoreProductGym.class,
+                    (item, id) -> {
+                        item.setExtId(id);
+                        if (item.getUsageCount() == null) {
+                            item.setUsageCount(0);
+                        }
+                    }, storeProductGymService::saveBatch);
+        } else {
+            log.warn("未知的商品 modelType:{},子项未处理", modelType);
+        }
+    }
+
+    /**
+     * 根据商品模块清理同一个 extId 的旧子表数据,防止编辑后出现重复/脏数据。
+     */
+    private void clearSubItemsByModelType(Integer modelType, Integer extId) {
+        if (CommonEnum.ModelType.BAR.getCode() == modelType) {
+            storeProductBarService.lambdaUpdate().eq(StoreProductBar::getExtId, extId).remove();
+        } else if (CommonEnum.ModelType.DELICACY.getCode() == modelType) {
+            storeProductDelicaciesService.lambdaUpdate().eq(StoreProductDelicacies::getExtId, extId).remove();
+        } else if (CommonEnum.ModelType.GYM.getCode() == modelType) {
+            storeProductGymService.lambdaUpdate().eq(StoreProductGym::getExtId, extId).remove();
+        }
+    }
+
+    /**
+     * 通用转换 + 批量保存,保持原有异常行为。
+     */
+    private <T> void convertAndSave(List<?> rawSubList, Integer extId, Class<T> clazz,
+                                    BiConsumer<T, Integer> extSetter,
+                                    Function<List<T>, Boolean> saveBatchFunc) {
+        List<T> subList = rawSubList.stream()
+                .map(v -> objectMapper.convertValue(v, clazz))
+                .peek(v -> extSetter.accept(v, extId))
+                .collect(Collectors.toList());
+        boolean subSaved = saveBatchFunc.apply(subList);
+        if (!subSaved) {
+            throw new RuntimeException("子项保存失败");
+        }
+    }
+
+    @Override
+    public R<Boolean> deleteStoreProductItem(Integer id) {
+        log.info("StoreProductItemServiceImpl.deleteStoreProductItem?id={}", id);
+        boolean result = this.removeById(id);
+        storeProductBarService.lambdaUpdate().eq(StoreProductBar::getExtId, id).remove();
+        storeProductDelicaciesService.lambdaUpdate().eq(StoreProductDelicacies::getExtId, id).remove();
+        storeProductGymService.lambdaUpdate().eq(StoreProductGym::getExtId, id).remove();
+        if (result) {
+            return R.success("删除成功");
+        }
+        return R.fail("删除失败");
+    }
+
+    @Override
+    public R<List<StoreProductItemDto>> getStoreProductItemById(Integer id,Integer modelType) {
+        log.info("StoreProductItemServiceImpl.getStoreProductItemById?id={}", id);
+        StoreProductItem storeProductItem = this.getById(id);
+        if (storeProductItem != null) {
+            ArrayList<StoreProductItemDto> list = new ArrayList<>();
+            StoreProductItemDto storeProductItemDto = new StoreProductItemDto();
+            list.add(storeProductItemDto);
+            BeanUtils.copyProperties(storeProductItem,storeProductItemDto);
+            List<?> sublist =null;
+            if (modelType == CommonEnum.ModelType.BAR.getCode()) {
+                sublist = storeProductBarService.getByExtId(id);
+            }else if (modelType == CommonEnum.ModelType.DELICACY.getCode()) {
+                sublist = storeProductDelicaciesService.getByExtId(id);
+            }else if (modelType == CommonEnum.ModelType.GYM.getCode()) {
+                sublist = storeProductGymService.getByExtId(id);
+            }
+            storeProductItemDto.setSubList(sublist);
+            return R.data(list);
+        }
+        return R.fail("未找到数据");
+    }
+
+    @Override
+    public R<List<StoreProductItem>> listByProdType(Integer prodType) {
+        log.info("StoreProductItemServiceImpl.listByProdType?prodType={}", prodType);
+        if (prodType == null) {
+            return R.fail("prodType不能为空");
+        }
+        List<StoreProductItem> result = lambdaQuery().eq(StoreProductItem::getProdType, prodType)
+                .orderByDesc(StoreProductItem::getCreatedTime).list();
+        return R.data(result);
+    }
+
+    @Override
+    public IPage<StoreProductItem> pageStoreProductItems(int pageNum, int pageSize,
+                                                         Integer storeId, String prodName, Integer prodType,
+                                                         Integer status, Integer needReserve) {
+        log.info("StoreProductItemServiceImpl.pageStoreProductItems?pageNum={}, pageSize={}, storeId={}, prodName={}, prodType={}, status={}, needReserve={}",
+                pageNum, pageSize, storeId, prodName, prodType, status, needReserve);
+        Page<StoreProductItem> page = new Page<>(pageNum, pageSize);
+        LambdaQueryWrapper<StoreProductItem> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(storeId != null, StoreProductItem::getStoreId, storeId)
+                .eq(prodType != null, StoreProductItem::getProdType, prodType)
+                .eq(status != null, StoreProductItem::getStatus, status)
+                .eq(needReserve != null, StoreProductItem::getNeedReserve, needReserve)
+                .like(StringUtils.hasText(prodName), StoreProductItem::getProdName, prodName)
+                .orderByDesc(StoreProductItem::getCreatedTime);
+        return this.page(page, wrapper);
+    }
+
+    @Override
+    public IPage<StoreProductItemGymVo> getPageWithGym(int pageNum, int pageSize, Integer storeId, String prodName, Integer prodType) {
+        log.info("StoreProductItemServiceImpl.getPageWithGym?pageNum={}, pageSize={}, storeId={}, prodName={}, prodType={}",
+                pageNum, pageSize, storeId, prodName, prodType);
+        Page<StoreProductItemGymVo> page = new Page<>(pageNum, pageSize);
+        if(prodType == 0){
+            return baseMapper.getPageWithGym(page, storeId, prodName, null);
+        }else{
+            return baseMapper.getPageWithGym(page, storeId, prodName, prodType);
+        }
+    }
+
+    @Override
+    public R<StoreProductItemGymVo> getDetailWithGym(Long id) {
+        log.info("StoreProductItemServiceImpl.getDetailWithGym?id={}", id);
+        if (id == null) {
+            return R.fail("ID不能为空");
+        }
+        StoreProductItemGymVo detail = baseMapper.getDetailWithGym(id);
+        if (detail != null) {
+            return R.data(detail);
+        }
+        return R.fail("查询失败,数据不存在");
+    }
+
+    @Override
+    public IPage<StoreProductItemDelicaciesVo> getPageWithDelicacies(int pageNum, int pageSize,
+                                                                      Integer storeId, String name,
+                                                                      String category, String extGroup,
+                                                                      Integer status, Integer packageType) {
+        log.info("StoreProductItemServiceImpl.getPageWithDelicacies?pageNum={}, pageSize={}, storeId={}, name={}, category={}, extGroup={}, status={}, packageType={}",
+                pageNum, pageSize, storeId, name, category, extGroup, status, packageType);
+        
+        // 1. 查询主表store_product_item中关于美食的数据(prodType = 4)
+        LambdaQueryWrapper<StoreProductItem> itemWrapper = new LambdaQueryWrapper<>();
+        itemWrapper.eq(StoreProductItem::getProdType, 4)
+                .eq(StoreProductItem::getDeleteFlag, 0);
+        
+        if (storeId != null) {
+            itemWrapper.eq(StoreProductItem::getStoreId, storeId);
+        }
+        if (StringUtils.hasText(name)) {
+            itemWrapper.like(StoreProductItem::getProdName, name);
+        }
+        if (status != null) {
+            itemWrapper.eq(StoreProductItem::getStatus, status);
+        }
+        
+        // 查询所有主表数据(不分页,用于后续合并)
+        List<StoreProductItem> allItemList = this.list(itemWrapper);
+        List<Integer> allItemIds = allItemList != null ? allItemList.stream()
+                .map(StoreProductItem::getId)
+                .collect(Collectors.toList()) : new ArrayList<>();
+        
+        // 2. 查询所有关联的子表数据
+        LambdaQueryWrapper<StoreProductDelicacies> relatedDelicaciesWrapper = new LambdaQueryWrapper<>();
+        if (!allItemIds.isEmpty()) {
+            relatedDelicaciesWrapper.in(StoreProductDelicacies::getExtId, allItemIds)
+                    .eq(StoreProductDelicacies::getDeleteFlag, 0);
+        } else {
+            // 如果没有主表数据,设置一个不可能的条件
+            relatedDelicaciesWrapper.eq(StoreProductDelicacies::getExtId, -1)
+                    .eq(StoreProductDelicacies::getDeleteFlag, 0);
+        }
+        
+        // 子表筛选条件
+        if (StringUtils.hasText(name)) {
+            relatedDelicaciesWrapper.like(StoreProductDelicacies::getName, name);
+        }
+        if (StringUtils.hasText(category)) {
+            relatedDelicaciesWrapper.eq(StoreProductDelicacies::getCategory, category);
+        }
+        if (StringUtils.hasText(extGroup)) {
+            relatedDelicaciesWrapper.eq(StoreProductDelicacies::getExtGroup, extGroup);
+        }
+        if (status != null) {
+            relatedDelicaciesWrapper.eq(StoreProductDelicacies::getStatus, status);
+        }
+        
+        List<StoreProductDelicacies> relatedDelicaciesList = storeProductDelicaciesService.list(relatedDelicaciesWrapper);
+        
+        // 3. 查询独立的子表数据(extId为null或不在主表ID列表中的)
+        LambdaQueryWrapper<StoreProductDelicacies> independentDelicaciesWrapper = new LambdaQueryWrapper<>();
+        independentDelicaciesWrapper.eq(StoreProductDelicacies::getDeleteFlag, 0);
+        
+        // 独立的子表:extId为null,或者extId不在主表ID列表中
+        if (allItemIds.isEmpty()) {
+            // 如果没有主表数据,查询所有extId为null的子表数据
+            independentDelicaciesWrapper.isNull(StoreProductDelicacies::getExtId);
+        } else {
+            // extId为null,或者extId不在主表ID列表中
+            independentDelicaciesWrapper.and(wrapper -> wrapper.isNull(StoreProductDelicacies::getExtId)
+                    .or().notIn(StoreProductDelicacies::getExtId, allItemIds));
+        }
+        
+        // 子表筛选条件
+        if (StringUtils.hasText(name)) {
+            independentDelicaciesWrapper.like(StoreProductDelicacies::getName, name);
+        }
+        if (StringUtils.hasText(category)) {
+            independentDelicaciesWrapper.eq(StoreProductDelicacies::getCategory, category);
+        }
+        if (StringUtils.hasText(extGroup)) {
+            independentDelicaciesWrapper.eq(StoreProductDelicacies::getExtGroup, extGroup);
+        }
+        if (status != null) {
+            independentDelicaciesWrapper.eq(StoreProductDelicacies::getStatus, status);
+        }
+        
+        List<StoreProductDelicacies> independentDelicaciesList = storeProductDelicaciesService.list(independentDelicaciesWrapper);
+        
+        // 4. 按extId分组,构建关联子表数据映射
+        Map<Integer, List<StoreProductDelicaciesVo>> delicaciesMap = relatedDelicaciesList.stream()
+                .collect(Collectors.groupingBy(
+                        StoreProductDelicacies::getExtId,
+                        Collectors.mapping(delicacies -> convertToDelicaciesVo(delicacies), Collectors.toList())
+                ));
+        
+        // 5. 组装结果列表
+        List<StoreProductItemDelicaciesVo> resultList = new ArrayList<>();
+        
+        // 5.1 添加主表数据及其关联的子表数据
+        if (allItemList != null) {
+            for (StoreProductItem item : allItemList) {
+                StoreProductItemDelicaciesVo vo = convertItemToVo(item);
+                // 设置子表列表(一对多关系)
+                List<StoreProductDelicaciesVo> subList = delicaciesMap.getOrDefault(item.getId(), new ArrayList<>());
+                vo.setDelicaciesList(subList);
+                resultList.add(vo);
+            }
+        }
+        
+        // 5.2 添加独立的子表数据(作为独立的记录,主表字段为空)
+        for (StoreProductDelicacies delicacies : independentDelicaciesList) {
+            StoreProductItemDelicaciesVo vo = new StoreProductItemDelicaciesVo();
+            // 主表字段为空或null
+            vo.setId(null);
+            vo.setStoreId(null);
+            // 子表字段
+            vo.setDelicaciesId(delicacies.getId());
+            vo.setExtId(delicacies.getExtId());
+            vo.setName(delicacies.getName());
+            vo.setPrice(delicacies.getPrice());
+            vo.setCostPrice(delicacies.getCostPrice());
+            vo.setUnit(delicacies.getUnit());
+            vo.setQuantity(delicacies.getQuantity());
+            vo.setCategory(delicacies.getCategory());
+            vo.setExtGroup(delicacies.getExtGroup());
+            vo.setDelicaciesStatus(delicacies.getStatus());
+            vo.setDelicaciesDeleteFlag(delicacies.getDeleteFlag());
+            vo.setDelicaciesCreatedUserId(delicacies.getCreatedUserId());
+            vo.setDelicaciesUpdatedUserId(delicacies.getUpdatedUserId());
+            vo.setDelicaciesCreatedTime(delicacies.getCreatedTime());
+            vo.setDelicaciesUpdatedTime(delicacies.getUpdatedTime());
+            // 子表列表只包含自己
+            List<StoreProductDelicaciesVo> subList = new ArrayList<>();
+            subList.add(convertToDelicaciesVo(delicacies));
+            vo.setDelicaciesList(subList);
+            resultList.add(vo);
+        }
+        
+        // 6. 按创建时间倒序排序(主表或子表的创建时间)
+        resultList.sort((a, b) -> {
+            Date dateA = a.getDelicaciesCreatedTime() != null ? a.getDelicaciesCreatedTime() : a.getCreatedTime();
+            Date dateB = b.getDelicaciesCreatedTime() != null ? b.getDelicaciesCreatedTime() : b.getCreatedTime();
+            if (dateA == null && dateB == null) return 0;
+            if (dateA == null) return 1;
+            if (dateB == null) return -1;
+            return dateB.compareTo(dateA); // 倒序
+        });
+        
+        // 7. 手动分页
+        int total = resultList.size();
+        int start = (pageNum - 1) * pageSize;
+        int end = Math.min(start + pageSize, total);
+        
+        List<StoreProductItemDelicaciesVo> pageList = start < total ? resultList.subList(start, end) : new ArrayList<>();
+        
+        // 8. 构建分页结果
+        Page<StoreProductItemDelicaciesVo> resultPage = new Page<>(pageNum, pageSize);
+        resultPage.setRecords(pageList);
+        resultPage.setTotal(total);
+        resultPage.setPages((int) Math.ceil((double) total / pageSize));
+        resultPage.setCurrent(pageNum);
+        resultPage.setSize(pageSize);
+        
+        return resultPage;
+    }
+
+    /**
+     * 将StoreProductItem转换为VO
+     */
+    private StoreProductItemDelicaciesVo convertItemToVo(StoreProductItem item) {
+        StoreProductItemDelicaciesVo vo = new StoreProductItemDelicaciesVo();
+        vo.setId(item.getId());
+        vo.setStoreId(item.getStoreId());
+        vo.setProdName(item.getProdName());
+        vo.setTotalPrice(item.getTotalPrice());
+        vo.setProdType(item.getProdType());
+        vo.setImages(item.getImages());
+        vo.setImageContent(item.getImageContent());
+        vo.setDetailContent(item.getDetailContent());
+        vo.setExtraNote(item.getExtraNote());
+        vo.setNeedReserve(item.getNeedReserve());
+        vo.setReserveRule(item.getReserveRule());
+        vo.setPeopleLimit(item.getPeopleLimit());
+        vo.setUsageRule(item.getUsageRule());
+        vo.setStatus(item.getStatus());
+        vo.setRejectionReason(item.getRejectionReason());
+        vo.setDeleteFlag(item.getDeleteFlag());
+        vo.setCreatedUserId(item.getCreatedUserId());
+        vo.setUpdatedUserId(item.getUpdatedUserId());
+        vo.setCreatedTime(item.getCreatedTime());
+        vo.setUpdatedTime(item.getUpdatedTime());
+        return vo;
+    }
+
+    /**
+     * 将StoreProductDelicacies转换为VO
+     */
+    private StoreProductDelicaciesVo convertToDelicaciesVo(StoreProductDelicacies delicacies) {
+        StoreProductDelicaciesVo vo = new StoreProductDelicaciesVo();
+        vo.setId(delicacies.getId());
+        vo.setExtId(delicacies.getExtId());
+        vo.setName(delicacies.getName());
+        vo.setPrice(delicacies.getPrice());
+        vo.setCostPrice(delicacies.getCostPrice());
+        vo.setUnit(delicacies.getUnit());
+        vo.setQuantity(delicacies.getQuantity());
+        vo.setCategory(delicacies.getCategory());
+        vo.setExtGroup(delicacies.getExtGroup());
+        vo.setStatus(delicacies.getStatus());
+        vo.setDeleteFlag(delicacies.getDeleteFlag());
+        vo.setCreatedUserId(delicacies.getCreatedUserId());
+        vo.setUpdatedUserId(delicacies.getUpdatedUserId());
+        vo.setCreatedTime(delicacies.getCreatedTime());
+        vo.setUpdatedTime(delicacies.getUpdatedTime());
+        return vo;
+    }
+
+}
+

+ 907 - 54
alien-store/src/main/java/shop/alien/store/service/impl/StoreStaffConfigServiceImpl.java

@@ -1,20 +1,34 @@
 package shop.alien.store.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.StringUtils;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
-import shop.alien.entity.store.StoreStaffConfig;
-import shop.alien.entity.store.excelVo.StoreInfoExpiredRecordsExcelVo;
+import org.springframework.transaction.annotation.Transactional;
+import shop.alien.entity.store.*;
+import shop.alien.entity.store.dto.StoreStaffConfigListQueryDto;
+import shop.alien.entity.store.dto.StoreStaffFitnessCourseGroup;
+import shop.alien.entity.store.dto.StoreStaffFitnessCourseItem;
 import shop.alien.entity.store.excelVo.StoreStaffConfigExcelVo;
 import shop.alien.entity.store.excelVo.util.ExcelGenerator;
-import shop.alien.mapper.StoreStaffConfigMapper;
+import shop.alien.entity.store.vo.StoreStaffDetailVo;
+import shop.alien.entity.store.vo.StoreStaffFitnessDetailVo;
+import shop.alien.entity.store.vo.StoreStaffPositionCountVo;
+import shop.alien.mapper.*;
 import shop.alien.store.service.StoreStaffConfigService;
+import shop.alien.store.service.StoreStaffFitnessBaseService;
+import shop.alien.store.service.StoreStaffFitnessCertificationService;
+import shop.alien.store.service.StoreStaffFitnessCourseService;
+import shop.alien.store.service.StoreStaffFitnessExperienceService;
+import shop.alien.store.util.CommonConstant;
+import shop.alien.store.util.ai.AiContentModerationUtil;
 import shop.alien.util.ali.AliOSSUtil;
 
 import java.io.File;
@@ -22,7 +36,9 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
+import java.util.Map;
 import java.util.UUID;
+import java.util.stream.Collectors;
 
 /**
  * @Author: fcw
@@ -30,14 +46,49 @@ import java.util.UUID;
  * @Description: 员工管理
  */
 
+@Slf4j
 @Service
 @RequiredArgsConstructor
 public class StoreStaffConfigServiceImpl implements StoreStaffConfigService {
 
     private final StoreStaffConfigMapper storeStaffConfigMapper;
 
+    private final StoreDictionaryMapper storeDictionaryMapper;
+
+    private final StoreStaffFitnessCourseMapper storeStaffFitnessCourseMapper;
+
+    private final StoreStaffFitnessCertificationMapper storeStaffFitnessCertificationMapper;
+
+    private final StoreStaffFitnessExperienceMapper storeStaffFitnessExperienceMapper;
+
+    private final StoreStaffFitnessBaseMapper storeStaffFitnessBaseMapper;
+
+    private final StoreInfoMapper storeInfoMapper;
+
     private final AliOSSUtil aliOSSUtil;
 
+    private final StoreStaffFitnessCourseService storeStaffFitnessCourseService;
+
+    private final StoreStaffFitnessBaseService storeStaffFitnessBaseService;
+
+    private final StoreStaffFitnessCertificationService storeStaffFitnessCertificationService;
+
+    private final AiContentModerationUtil aiContentModerationUtil;
+
+    private final shop.alien.store.util.ai.AiVideoModerationUtil aiVideoModerationUtil;
+
+    private final StoreStaffFitnessExperienceService storeStaffFitnessExperienceService;
+
+    /**
+     * 认证类型:认证
+     */
+    private static final Integer CERTIFICATION_TYPE = 1;
+
+    /**
+     * 认证类型:荣誉
+     */
+    private static final Integer HONOR_TYPE = 2;
+
     @Value("${spring.web.resources.excel-path}")
     private String excelPath;
     @Value("${spring.web.resources.excel-clearing-receipt}")
@@ -48,58 +99,188 @@ public class StoreStaffConfigServiceImpl implements StoreStaffConfigService {
     private String fileUrl;
 
     @Override
-    public IPage<StoreStaffConfig> getStaffConfigList(int page, int size, String status) {
+    public IPage<StoreStaffConfig> getStaffConfigList(StoreStaffConfigListQueryDto query) {
+        int page = (query == null || query.getPage() == null) ? 1 : query.getPage();
+        int size = (query == null || query.getSize() == null) ? 10 : query.getSize();
+        Integer storeId = query == null ? null : query.getStoreId();
+        String status = query == null ? null : query.getStatus();
+        Integer businessSection = query == null ? null : query.getBusinessSection();
+        Integer onlineStatus = query == null ? null : query.getOnlineStatus();
+        String staffPosition = query == null ? null : query.getStaffPosition();
+
         IPage<StoreStaffConfig> storePage = new Page<>(page, size);
+        if (storeId == null) {
+            return storePage;
+        }
         QueryWrapper<StoreStaffConfig> queryWrapper = new QueryWrapper<>();
         queryWrapper.like(null != status && !status.isEmpty(), "status", status);
+        queryWrapper.eq(businessSection != null, "business_section", businessSection);
+        queryWrapper.eq(onlineStatus != null, "online_status", onlineStatus);
+        queryWrapper.eq("store_id", storeId);
+        queryWrapper.eq(StringUtils.isNotEmpty(staffPosition), "staff_position", staffPosition);
+        // 只查询未删除的记录
+        queryWrapper.eq("delete_flag", 0);
         // 排序规则:先按置顶状态降序(置顶的在前),再按置顶时间降序,最后按创建时间降序
         queryWrapper.orderByDesc("top_status", "top_time", "created_time");
         return storeStaffConfigMapper.selectPage(storePage, queryWrapper);
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public int addOrUpdateStaffConfig(StoreStaffConfig storeStaffConfig) {
-        // 判断是新增还是更新:id为null或0或负数表示新增
-        // 注意:新增时不应该传入id,如果传入了非0的id,需要先查询是否存在
+        if (storeStaffConfig == null) {
+            return 0;
+        }
+
         Integer id = storeStaffConfig.getId();
+        boolean isCreate = (id == null || id == 0);
+        StoreStaffConfig existing = null;
+
+        if (!isCreate) {
+            existing = storeStaffConfigMapper.selectById(id);
+            // 传了id但库里不存在:按新增处理
+            if (existing == null) {
+                isCreate = true;
+                storeStaffConfig.setId(null);
+            }
+        }
+
+        // businessSection:优先用入参;更新场景若未传则继承库里
+        Integer businessSection = storeStaffConfig.getBusinessSection();
+        if (!isCreate && businessSection == null) {
+            businessSection = existing.getBusinessSection();
+        }
+
+        // 新增默认值
+        if (isCreate) {
+            applyCreateDefaults(storeStaffConfig);
+        }
+
+        // 规范化擅长标签字段(proficient_id / proficient_projects)
+        normalizeProficientFields(storeStaffConfig, businessSection);
+
+        // 先落库,再调用 AI 审核
+        int affected = isCreate
+                ? insertStaff(storeStaffConfig)
+                : storeStaffConfigMapper.updateById(storeStaffConfig);
+
+        if (affected <= 0) {
+            return affected;
+        }
+
+        Integer staffId = isCreate ? storeStaffConfig.getId() : id;
+        if (isFitnessBusinessSection(businessSection)) {
+            saveOrUpdateFitnessDetails(staffId, storeStaffConfig);
+        }
+
+        // 新增 / 修改成功后,先将状态置为“审核中”(0),清空拒绝原因
+        StoreStaffConfig auditingUpdate = new StoreStaffConfig();
+        auditingUpdate.setId(staffId);
+        auditingUpdate.setStatus("0");
+        auditingUpdate.setRejectionReason(null);
+        storeStaffConfigMapper.updateById(auditingUpdate);
+
+        // 组装 AI 审核文本和图片
+        StringBuilder textContent = new StringBuilder();
+        if (StringUtils.isNotEmpty(storeStaffConfig.getName())) {
+            textContent.append(storeStaffConfig.getName()).append(" ");
+        }
+        if (StringUtils.isNotEmpty(storeStaffConfig.getStaffPosition())) {
+            textContent.append(storeStaffConfig.getStaffPosition()).append(" ");
+        }
+        if (StringUtils.isNotEmpty(storeStaffConfig.getPersonalIntroduction())) {
+            textContent.append(storeStaffConfig.getPersonalIntroduction());
+        }
+
+        List<String> imageUrls = new ArrayList<>();
+        List<String> videoUrls = new ArrayList<>();
         
-        if (id == null || id == 0) {
-            // 新增操作:id为null或0
-            Date nowDate = new Date(System.currentTimeMillis());
-            storeStaffConfig.setCreatedTime(nowDate);
-            // 设置删除标记为0(未删除)- 必须设置,否则可能插入失败
-            if (storeStaffConfig.getDeleteFlag() == null) {
-                storeStaffConfig.setDeleteFlag(0);
-            }
-            // 如果状态为空,设置默认状态为待审核(0)
-            if (StringUtils.isEmpty(storeStaffConfig.getStatus())) {
-                storeStaffConfig.setStatus("0");
-            }
-            // 新增时,确保id为null,让数据库自动生成
-            storeStaffConfig.setId(null);
-            return storeStaffConfigMapper.insert(storeStaffConfig);
+        if (StringUtils.isNotEmpty(storeStaffConfig.getStaffImage())) {
+            imageUrls.add(storeStaffConfig.getStaffImage());
+        }
+        if (StringUtils.isNotEmpty(storeStaffConfig.getBackgroundUrl())) {
+            String[] urls = storeStaffConfig.getBackgroundUrl().split(",");
+            for (String url : urls) {
+                if (StringUtils.isNotEmpty(url.trim())) {
+                    String trimmedUrl = url.trim();
+                    // 判断是视频还是图片
+                    if (isVideoUrl(trimmedUrl)) {
+                        videoUrls.add(trimmedUrl);
+                    } else {
+                        imageUrls.add(trimmedUrl);
+                    }
+                }
+            }
+        }
+
+        // 1. 审核文本和图片
+        AiContentModerationUtil.AuditResult textImageAuditResult = aiContentModerationUtil.auditContent(
+                textContent.toString().trim(), imageUrls
+        );
+
+        // 2. 审核视频(如果有)
+        shop.alien.store.util.ai.AiVideoModerationUtil.VideoAuditResult videoAuditResult = null;
+        if (!videoUrls.isEmpty()) {
+            log.info("开始审核视频,视频数量:{}", videoUrls.size());
+            videoAuditResult = aiVideoModerationUtil.auditVideos(videoUrls);
+        }
+
+        // 3. 综合审核结果:文本图片审核和视频审核都必须通过
+        boolean allPassed = (textImageAuditResult != null && textImageAuditResult.isPassed()) &&
+                            (videoAuditResult == null || videoAuditResult.isPassed());
+
+        // 根据 AI 审核结果更新状态
+        // 审核通过:状态保持为"0"(审核中),等待人工审核
+        // 审核失败:状态设置为"2"(审核拒绝)
+        StoreStaffConfig auditUpdate = new StoreStaffConfig();
+        auditUpdate.setId(staffId);
+        if (allPassed) {
+            // AI审核通过,状态保持为"审核中"(0),等待人工审核
+            auditUpdate.setStatus("0");
+            auditUpdate.setRejectionReason(null);
+            log.info("人员AI审核通过,状态保持为审核中,等待人工审核:staffId={}", staffId);
         } else {
-            // 更新操作:id不为null且不为0
-            // 先查询记录是否存在,如果不存在则转为新增
-            StoreStaffConfig existing = storeStaffConfigMapper.selectById(id);
-            if (existing == null) {
-                // 记录不存在,转为新增操作
-                storeStaffConfig.setId(null); // 重置id,让数据库自动生成
-                Date nowDate = new Date(System.currentTimeMillis());
-                storeStaffConfig.setCreatedTime(nowDate);
-                // 设置删除标记为0(未删除)
-                if (storeStaffConfig.getDeleteFlag() == null) {
-                    storeStaffConfig.setDeleteFlag(0);
-                }
-                // 如果状态为空,设置默认状态为待审核(0)
-                if (StringUtils.isEmpty(storeStaffConfig.getStatus())) {
-                    storeStaffConfig.setStatus("0");
-                }
-                return storeStaffConfigMapper.insert(storeStaffConfig);
-            } else {
-                // 记录存在,执行更新
-                return storeStaffConfigMapper.updateById(storeStaffConfig);
+            // AI审核失败,状态设置为"审核拒绝"(2)
+            // 收集所有失败原因
+            List<String> failureReasons = new ArrayList<>();
+            if (textImageAuditResult != null && !textImageAuditResult.isPassed()) {
+                if (StringUtils.isNotEmpty(textImageAuditResult.getFailureReason())) {
+                    failureReasons.add("图文审核:" + textImageAuditResult.getFailureReason());
+                } else {
+                    failureReasons.add("图文审核未通过");
+                }
+            }
+            if (videoAuditResult != null && !videoAuditResult.isPassed()) {
+                // 业务要求:视频审核失败统一记录“视频内容违规”
+                failureReasons.add("视频内容违规");
             }
+            
+            String reason = failureReasons.isEmpty() ? "审核未通过" : String.join("; ", failureReasons);
+            log.warn("人员AI审核失败,状态设置为审核拒绝:staffId={}, reason={}", staffId, reason);
+            auditUpdate.setStatus("2");
+            auditUpdate.setRejectionReason(reason);
+        }
+        storeStaffConfigMapper.updateById(auditUpdate);
+
+        return affected;
+    }
+
+    private int insertStaff(StoreStaffConfig staff) {
+        // 新增时,确保id为null,让数据库自增
+        staff.setId(null);
+        return storeStaffConfigMapper.insert(staff);
+    }
+
+    private void applyCreateDefaults(StoreStaffConfig staff) {
+        staff.setCreatedTime(new Date(System.currentTimeMillis()));
+        if (staff.getDeleteFlag() == null) {
+            staff.setDeleteFlag(0);
+        }
+        if (StringUtils.isEmpty(staff.getStatus())) {
+            staff.setStatus("0"); // 待审核
+        }
+        if (staff.getOnlineStatus() == null) {
+            staff.setOnlineStatus(0); // 默认上线
         }
     }
 
@@ -114,7 +295,14 @@ public class StoreStaffConfigServiceImpl implements StoreStaffConfigService {
 
     @Override
     public StoreStaffConfig getStaffConfigDeatail(Integer id) {
-        return storeStaffConfigMapper.selectById(id);
+        StoreStaffConfig staff = storeStaffConfigMapper.selectById(id);
+        if (staff == null) {
+            return null;
+        }
+        if (isFitnessBusinessSection(staff.getBusinessSection())) {
+            attachFitnessDetails(staff);
+        }
+        return staff;
     }
 
     @Override
@@ -151,7 +339,420 @@ public class StoreStaffConfigServiceImpl implements StoreStaffConfigService {
     public Integer deleteStaffConfig(Integer id) {
         // 使用 MyBatis-Plus 的逻辑删除,会自动将 deleteFlag 设置为 1
         // 因为实体类使用了 @TableLogic 注解
-        return storeStaffConfigMapper.deleteById(id);
+        StoreStaffConfig staff = storeStaffConfigMapper.selectById(id);
+        int deleted = storeStaffConfigMapper.deleteById(id);
+        if (deleted > 0 && staff != null && isFitnessBusinessSection(staff.getBusinessSection())) {
+            cascadeDeleteFitnessDetails(id);
+        }
+        return deleted;
+    }
+
+    private void attachFitnessDetails(StoreStaffConfig staff) {
+        Integer staffId = staff.getId();
+        if (staffId == null) {
+            return;
+        }
+
+        List<StoreStaffFitnessCourse> courses = storeStaffFitnessCourseMapper.selectList(
+                new LambdaQueryWrapper<StoreStaffFitnessCourse>()
+                        .eq(StoreStaffFitnessCourse::getStaffId, staffId)
+                        .eq(StoreStaffFitnessCourse::getDeleteFlag, 0)
+                        .orderByDesc(StoreStaffFitnessCourse::getId)
+        );
+        staff.setFitnessCourseList(courses);
+        staff.setFitnessCourseGroupList(buildCourseGroups(courses));
+
+        List<StoreStaffFitnessCertification> certAll = storeStaffFitnessCertificationMapper.selectList(
+                new LambdaQueryWrapper<StoreStaffFitnessCertification>()
+                        .eq(StoreStaffFitnessCertification::getStaffId, staffId)
+                        .eq(StoreStaffFitnessCertification::getDeleteFlag, 0)
+                        .orderByDesc(StoreStaffFitnessCertification::getId)
+        );
+        // 按 type 区分职业认证/荣誉奖项:1-认证 2-荣誉
+        List<StoreStaffFitnessCertification> certList = certAll.stream()
+                .filter(c -> c.getType() != null && c.getType() == 1)
+                .collect(Collectors.toList());
+        List<StoreStaffFitnessCertification> honorList = certAll.stream()
+                .filter(c -> c.getType() != null && c.getType() == 2)
+                .collect(Collectors.toList());
+        staff.setFitnessCertificationList(certList);
+        staff.setFitnessHonorList(honorList);
+
+        List<StoreStaffFitnessExperience> experiences = storeStaffFitnessExperienceMapper.selectList(
+                new LambdaQueryWrapper<StoreStaffFitnessExperience>()
+                        .eq(StoreStaffFitnessExperience::getStaffId, staffId)
+                        .eq(StoreStaffFitnessExperience::getDeleteFlag, 0)
+                        .orderByDesc(StoreStaffFitnessExperience::getId)
+        );
+        staff.setFitnessExperienceList(experiences);
+
+        StoreStaffFitnessBase base = storeStaffFitnessBaseMapper.selectOne(
+                new LambdaQueryWrapper<StoreStaffFitnessBase>()
+                        .eq(StoreStaffFitnessBase::getStaffId, staffId)
+                        .eq(StoreStaffFitnessBase::getDeleteFlag, 0)
+                        .last("limit 1")
+        );
+        staff.setFitnessBase(base);
+    }
+
+    private void cascadeDeleteFitnessDetails(Integer staffId) {
+        if (staffId == null) {
+            return;
+        }
+        storeStaffFitnessCourseMapper.delete(new QueryWrapper<StoreStaffFitnessCourse>().eq("staff_id", staffId));
+        storeStaffFitnessCertificationMapper.delete(new QueryWrapper<StoreStaffFitnessCertification>().eq("staff_id", staffId));
+        storeStaffFitnessExperienceMapper.delete(new QueryWrapper<StoreStaffFitnessExperience>().eq("staff_id", staffId));
+        storeStaffFitnessBaseMapper.delete(new QueryWrapper<StoreStaffFitnessBase>().eq("staff_id", staffId));
+    }
+
+    private void saveOrUpdateFitnessDetails(Integer staffId, StoreStaffConfig staff) {
+        if (staffId == null) {
+            return;
+        }
+
+        // 0) 兼容前端“课程类型分组”入参:优先使用分组字段,自动拍平成 fitnessCourseList 走现有落库逻辑
+        if (staff.getFitnessCourseGroupList() != null) {
+            staff.setFitnessCourseList(flattenCourseGroups(staff.getFitnessCourseGroupList()));
+        }
+
+        // 1) 课程信息:若前端传了列表(包括空列表),以传入为准覆盖
+        if (staff.getFitnessCourseList() != null) {
+            storeStaffFitnessCourseMapper.delete(new QueryWrapper<StoreStaffFitnessCourse>().eq("staff_id", staffId));
+            // 课程类型存 dictDetail(名称)。如传 dictId,则在无歧义时自动转换为 dictDetail。
+            java.util.Set<String> rawTypes = staff.getFitnessCourseList().stream()
+                    .filter(java.util.Objects::nonNull)
+                    .map(StoreStaffFitnessCourse::getCourseType)
+                    .filter(StringUtils::isNotEmpty)
+                    .map(String::trim)
+                    .collect(java.util.stream.Collectors.toSet());
+            java.util.Map<String, String> dictIdToDetail = new java.util.HashMap<>();
+            if (!rawTypes.isEmpty()) {
+                List<StoreDictionary> dicts = storeDictionaryMapper.selectList(
+                        new LambdaQueryWrapper<StoreDictionary>()
+                                .in(StoreDictionary::getTypeName, java.util.Arrays.asList("classOnLineType", "classOffLineType"))
+                                .in(StoreDictionary::getDictId, rawTypes)
+                                .eq(StoreDictionary::getDeleteFlag, 0)
+                );
+                // 仅在一个 dictId 唯一对应一个 dictDetail 时才转换(避免线上/线下 dictId 重复导致歧义)
+                java.util.Map<String, java.util.Set<String>> tmp = new java.util.HashMap<>();
+                for (StoreDictionary d : dicts) {
+                    if (StringUtils.isEmpty(d.getDictId()) || StringUtils.isEmpty(d.getDictDetail())) {
+                        continue;
+                    }
+                    tmp.computeIfAbsent(d.getDictId(), k -> new java.util.HashSet<>()).add(d.getDictDetail());
+                }
+                for (java.util.Map.Entry<String, java.util.Set<String>> e : tmp.entrySet()) {
+                    if (e.getValue().size() == 1) {
+                        dictIdToDetail.put(e.getKey(), e.getValue().iterator().next());
+                    }
+                }
+            }
+            for (StoreStaffFitnessCourse course : staff.getFitnessCourseList()) {
+                if (course == null) {
+                    continue;
+                }
+                course.setId(null);
+                course.setStaffId(staffId);
+                if (course.getDeleteFlag() == null) {
+                    course.setDeleteFlag(0);
+                }
+                if (StringUtils.isNotEmpty(course.getCourseType())) {
+                    String ct = course.getCourseType().trim();
+                    // 若传的是 dictId 且能唯一映射,则替换为 dictDetail(名称)
+                    if (dictIdToDetail.containsKey(ct)) {
+                        course.setCourseType(dictIdToDetail.get(ct));
+                    } else {
+                        course.setCourseType(ct);
+                    }
+                }
+                // 价格字段归一化:0-固定价写 course_price;1-区间价写 min/max
+                if (course.getCoursePriceType() != null) {
+                    if (course.getCoursePriceType() == 0) {
+                        course.setCourseMinPrice(null);
+                        course.setCourseMaxPrice(null);
+                    } else if (course.getCoursePriceType() == 1) {
+                        course.setCoursePrice(null);
+                    }
+                }
+                storeStaffFitnessCourseMapper.insert(course);
+            }
+        }
+
+        // 2) 认证/荣誉:任意一个列表非 null,则覆盖保存(两类共用一张表)
+        if (staff.getFitnessCertificationList() != null || staff.getFitnessHonorList() != null) {
+            storeStaffFitnessCertificationMapper.delete(new QueryWrapper<StoreStaffFitnessCertification>().eq("staff_id", staffId));
+
+            if (staff.getFitnessCertificationList() != null) {
+                for (StoreStaffFitnessCertification cert : staff.getFitnessCertificationList()) {
+                    if (cert == null) {
+                        continue;
+                    }
+                    StoreStaffFitnessCertification row = new StoreStaffFitnessCertification();
+                    row.setId(null);
+                    row.setStaffId(staffId);
+                    row.setType(1);
+                    row.setName(cert.getName());
+                    row.setImgUrls(cert.getImgUrls());
+                    row.setDeleteFlag(0);
+                    storeStaffFitnessCertificationMapper.insert(row);
+                }
+            }
+
+            if (staff.getFitnessHonorList() != null) {
+                for (StoreStaffFitnessCertification honor : staff.getFitnessHonorList()) {
+                    if (honor == null) {
+                        continue;
+                    }
+                    StoreStaffFitnessCertification row = new StoreStaffFitnessCertification();
+                    row.setId(null);
+                    row.setStaffId(staffId);
+                    row.setType(2);
+                    row.setName(honor.getName());
+                    row.setImgUrls(honor.getImgUrls());
+                    row.setDeleteFlag(0);
+                    storeStaffFitnessCertificationMapper.insert(row);
+                }
+            }
+        }
+
+        // 3) 从业经历:若前端传了列表(包括空列表),以传入为准覆盖
+        if (staff.getFitnessExperienceList() != null) {
+            storeStaffFitnessExperienceMapper.delete(new QueryWrapper<StoreStaffFitnessExperience>().eq("staff_id", staffId));
+            for (StoreStaffFitnessExperience exp : staff.getFitnessExperienceList()) {
+                if (exp == null) {
+                    continue;
+                }
+                exp.setId(null);
+                exp.setStaffId(staffId);
+                if (exp.getDeleteFlag() == null) {
+                    exp.setDeleteFlag(0);
+                }
+                storeStaffFitnessExperienceMapper.insert(exp);
+            }
+        }
+
+        // 4) 基本信息:若前端传了对象则 upsert,否则不动
+        if (staff.getFitnessBase() != null) {
+            StoreStaffFitnessBase base = staff.getFitnessBase();
+            base.setStaffId(staffId);
+            if (base.getDeleteFlag() == null) {
+                base.setDeleteFlag(0);
+            }
+            StoreStaffFitnessBase existBase = storeStaffFitnessBaseMapper.selectOne(
+                    new LambdaQueryWrapper<StoreStaffFitnessBase>()
+                            .eq(StoreStaffFitnessBase::getStaffId, staffId)
+                            .eq(StoreStaffFitnessBase::getDeleteFlag, 0)
+                            .last("limit 1")
+            );
+            if (existBase == null) {
+                base.setId(null);
+                storeStaffFitnessBaseMapper.insert(base);
+            } else {
+                base.setId(existBase.getId());
+                storeStaffFitnessBaseMapper.updateById(base);
+            }
+        }
+    }
+
+    /**
+     * 若 proficientProjects 未传,尝试根据 proficientId + businessSection 从字典表计算名称回填
+     * 规则:proficient_tag.parent_id = business_section 主键id;proficient_id 存 proficient_tag.dict_id 逗号分隔
+     */
+    private void normalizeProficientFields(StoreStaffConfig staff, Integer businessSection) {
+        if (staff == null) {
+            return;
+        }
+        if (StringUtils.isEmpty(staff.getProficientId()) || !StringUtils.isEmpty(staff.getProficientProjects()) || businessSection == null) {
+            return;
+        }
+
+        // 查询经营板块(business_section),注意:staff.businessSection 存的是 dict_id
+        StoreDictionary section = storeDictionaryMapper.selectOne(
+                new LambdaQueryWrapper<StoreDictionary>()
+                        .eq(StoreDictionary::getTypeName, "business_section")
+                        .eq(StoreDictionary::getDictId, String.valueOf(businessSection))
+                        .eq(StoreDictionary::getDeleteFlag, 0)
+                        .isNull(StoreDictionary::getParentId)
+                        .last("limit 1")
+        );
+        if (section == null || section.getId() == null) {
+            return;
+        }
+
+        List<String> dictIds = java.util.Arrays.stream(staff.getProficientId().split(","))
+                .map(String::trim)
+                .filter(s -> !s.isEmpty())
+                .collect(Collectors.toList());
+        if (dictIds.isEmpty()) {
+            return;
+        }
+
+        List<StoreDictionary> tags = storeDictionaryMapper.selectList(
+                new LambdaQueryWrapper<StoreDictionary>()
+                        .eq(StoreDictionary::getTypeName, "proficient_tag")
+                        .eq(StoreDictionary::getParentId, section.getId())
+                        .in(StoreDictionary::getDictId, dictIds)
+                        .eq(StoreDictionary::getDeleteFlag, 0)
+        );
+
+        java.util.Map<String, String> idToName = tags.stream()
+                .filter(d -> !StringUtils.isEmpty(d.getDictId()))
+                .collect(Collectors.toMap(StoreDictionary::getDictId, StoreDictionary::getDictDetail, (a, b) -> a));
+
+        List<String> namesInOrder = dictIds.stream()
+                .map(idToName::get)
+                .filter(s -> !StringUtils.isEmpty(s))
+                .collect(Collectors.toList());
+
+        if (!namesInOrder.isEmpty()) {
+            staff.setProficientProjects(String.join(",", namesInOrder));
+        }
+    }
+
+    /**
+     * 判断该经营板块是否为“运动健身”
+     * - 优先按常见 dict_id=7 判断
+     * - 若不一致,再按字典表 dict_detail=运动健身 判断
+     */
+    private boolean isFitnessBusinessSection(Integer businessSection) {
+        if (businessSection == null) {
+            return false;
+        }
+        if (businessSection == 7) {
+            return true;
+        }
+        StoreDictionary section = storeDictionaryMapper.selectOne(
+                new LambdaQueryWrapper<StoreDictionary>()
+                        .eq(StoreDictionary::getTypeName, "business_section")
+                        .eq(StoreDictionary::getDictId, String.valueOf(businessSection))
+                        .eq(StoreDictionary::getDeleteFlag, 0)
+                        .last("limit 1")
+        );
+        return section != null && "运动健身".equals(section.getDictDetail());
+    }
+
+    /**
+     * 将分组结构拍平成明细行结构(用于落库)
+     */
+    private List<StoreStaffFitnessCourse> flattenCourseGroups(List<StoreStaffFitnessCourseGroup> groups) {
+        List<StoreStaffFitnessCourse> flat = new ArrayList<>();
+        if (groups == null) {
+            return flat;
+        }
+        for (StoreStaffFitnessCourseGroup g : groups) {
+            if (g == null) {
+                continue;
+            }
+            String courseType = StringUtils.isNotEmpty(g.getCourseType()) ? g.getCourseType().trim() : null;
+            if (StringUtils.isEmpty(courseType) && StringUtils.isNotEmpty(g.getCourseTypeName())) {
+                courseType = g.getCourseTypeName().trim();
+            }
+            if (g.getItems() == null) {
+                continue;
+            }
+            for (StoreStaffFitnessCourseItem item : g.getItems()) {
+                if (item == null) {
+                    continue;
+                }
+                StoreStaffFitnessCourse row = new StoreStaffFitnessCourse();
+                row.setCourseType(courseType);
+                row.setCourseName(item.getCourseName());
+                row.setCoursePriceType(item.getCoursePriceType());
+                row.setCoursePrice(item.getCoursePrice());
+                row.setCourseMinPrice(item.getCourseMinPrice());
+                row.setCourseMaxPrice(item.getCourseMaxPrice());
+                flat.add(row);
+            }
+        }
+        return flat;
+    }
+
+    /**
+     * 判断URL是否为视频
+     *
+     * @param url URL地址
+     * @return 是否为视频
+     */
+    private boolean isVideoUrl(String url) {
+        if (url == null || url.isEmpty()) {
+            return false;
+        }
+        String lowerUrl = url.toLowerCase();
+        return lowerUrl.endsWith(".mp4") ||
+               lowerUrl.endsWith(".avi") ||
+               lowerUrl.endsWith(".flv") ||
+               lowerUrl.endsWith(".mkv") ||
+               lowerUrl.endsWith(".rmvb") ||
+               lowerUrl.endsWith(".wmv") ||
+               lowerUrl.endsWith(".3gp") ||
+               lowerUrl.endsWith(".mov");
+    }
+
+    /**
+     * 将明细行结构组装成分组结构(用于前端绑定)
+     * - 优先按 courseType 分组(DB里可能存 dictDetail 或 dictId)
+     * - courseTypeName:能从字典表解析到名称则回填,否则回填为原 courseType
+     */
+    private List<StoreStaffFitnessCourseGroup> buildCourseGroups(List<StoreStaffFitnessCourse> courses) {
+        List<StoreStaffFitnessCourseGroup> groups = new ArrayList<>();
+        if (courses == null || courses.isEmpty()) {
+            return groups;
+        }
+
+        // 收集类型并尝试从字典表解析名称(兼容:DB存 dictId 或 dictDetail)
+        java.util.Set<String> rawTypes = courses.stream()
+                .filter(java.util.Objects::nonNull)
+                .map(StoreStaffFitnessCourse::getCourseType)
+                .filter(StringUtils::isNotEmpty)
+                .map(String::trim)
+                .collect(java.util.stream.Collectors.toSet());
+
+        java.util.Map<String, String> typeToName = new java.util.HashMap<>();
+        if (!rawTypes.isEmpty()) {
+            List<StoreDictionary> dicts = storeDictionaryMapper.selectList(
+                    new LambdaQueryWrapper<StoreDictionary>()
+                            // 兼容历史:文档里是 classOnLineType/classOffLineType;部分环境可能是 courseType
+                            .in(StoreDictionary::getTypeName, java.util.Arrays.asList("classOnLineType", "classOffLineType", "courseType"))
+                            .and(w -> w.in(StoreDictionary::getDictId, rawTypes).or().in(StoreDictionary::getDictDetail, rawTypes))
+                            .eq(StoreDictionary::getDeleteFlag, 0)
+            );
+            for (StoreDictionary d : dicts) {
+                if (d == null) {
+                    continue;
+                }
+                if (StringUtils.isNotEmpty(d.getDictId()) && StringUtils.isNotEmpty(d.getDictDetail())) {
+                    // dictId -> name
+                    typeToName.putIfAbsent(d.getDictId().trim(), d.getDictDetail().trim());
+                    // name -> name(用于 DB 存 dictDetail 的场景)
+                    typeToName.putIfAbsent(d.getDictDetail().trim(), d.getDictDetail().trim());
+                }
+            }
+        }
+
+        java.util.Map<String, StoreStaffFitnessCourseGroup> map = new java.util.LinkedHashMap<>();
+        for (StoreStaffFitnessCourse c : courses) {
+            if (c == null) {
+                continue;
+            }
+            String ct = StringUtils.isNotEmpty(c.getCourseType()) ? c.getCourseType().trim() : "";
+            StoreStaffFitnessCourseGroup g = map.computeIfAbsent(ct, k -> {
+                StoreStaffFitnessCourseGroup ng = new StoreStaffFitnessCourseGroup();
+                ng.setCourseType(k);
+                ng.setCourseTypeName(typeToName.getOrDefault(k, k));
+                ng.setItems(new ArrayList<>());
+                return ng;
+            });
+
+            StoreStaffFitnessCourseItem item = new StoreStaffFitnessCourseItem();
+            item.setCourseName(c.getCourseName());
+            item.setCoursePriceType(c.getCoursePriceType());
+            item.setCoursePrice(c.getCoursePrice());
+            item.setCourseMinPrice(c.getCourseMinPrice());
+            item.setCourseMaxPrice(c.getCourseMaxPrice());
+            g.getItems().add(item);
+        }
+        groups.addAll(map.values());
+        return groups;
     }
 
     @Override
@@ -171,33 +772,285 @@ public class StoreStaffConfigServiceImpl implements StoreStaffConfigService {
     }
 
     @Override
-    public IPage<StoreStaffConfig> queryStaffList(Integer page, Integer size, Integer storeId, String status) {
+    public Integer setOnlineStatus(Integer id, Integer onlineStatus) {
+        if (id == null || onlineStatus == null) {
+            return 0;
+        }
+        LambdaUpdateWrapper<StoreStaffConfig> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
+        lambdaUpdateWrapper.eq(StoreStaffConfig::getId, id);
+        lambdaUpdateWrapper.set(StoreStaffConfig::getOnlineStatus, onlineStatus);
+        return storeStaffConfigMapper.update(null, lambdaUpdateWrapper);
+    }
+
+    @Override
+    public IPage<StoreStaffConfig> queryStaffList(Integer page, Integer size, Integer storeId, String status, String staffPosition) {
+        // 参数校验
+        if (page == null || page < 1) {
+            page = 1;
+        }
+        if (size == null || size < 1) {
+            size = 10;
+        }
+        if (storeId == null || storeId <= 0) {
+            throw new IllegalArgumentException("店铺ID不能为空且必须大于0");
+        }
+
+        // 构建分页对象
+        IPage<StoreStaffConfig> staffPage = new Page<>(page, size);
+
+        // 使用 LambdaQueryWrapper 构建查询条件,类型安全
+        LambdaQueryWrapper<StoreStaffConfig> queryWrapper = new LambdaQueryWrapper<>();
+
+        // 必须条件:店铺ID和未删除
+        queryWrapper.eq(StoreStaffConfig::getStoreId, storeId)
+                .eq(StoreStaffConfig::getDeleteFlag, CommonConstant.DELETE_FLAG_UNDELETE);
+
+        // 可选条件:员工状态筛选
+        if (StringUtils.isNotEmpty(status)) {
+            queryWrapper.eq(StoreStaffConfig::getStatus, status);
+        }
+
+        // 可选条件:员工职位筛选
+        if (StringUtils.isNotEmpty(staffPosition)) {
+            queryWrapper.eq(StoreStaffConfig::getStaffPosition, staffPosition);
+        }
+
+        // 排序规则:先按置顶状态降序(置顶的在前),再按置顶时间降序,最后按创建时间降序
+        queryWrapper.orderByDesc(StoreStaffConfig::getTopStatus)
+                .orderByDesc(StoreStaffConfig::getTopTime)
+                .orderByDesc(StoreStaffConfig::getCreatedTime);
+
+        return storeStaffConfigMapper.selectPage(staffPage, 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);
+
+
+    }
+
+    @Override
+    public IPage<StoreStaffConfig> getFoodStaffConfigList(int page, int size, Integer storeId, String staffPosition, 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);
-//        }
+        // 按照职位查询
+        if (StringUtils.isNotEmpty(staffPosition)) {
+            queryWrapper.eq("staff_position", staffPosition);
+        }
+        // 按照状态查询
+        if (StringUtils.isNotEmpty(status)) {
+            queryWrapper.eq("status", status);
+        }
         // 只查询未删除的记录
         queryWrapper.eq("delete_flag", 0);
-        queryWrapper.orderByDesc("created_time");
+        // 排序规则:先按置顶状态降序(置顶的在前),再按置顶时间降序,最后按创建时间降序
+        queryWrapper.orderByDesc("top_status", "top_time", "created_time");
         return storeStaffConfigMapper.selectPage(storePage, queryWrapper);
     }
 
     @Override
-    public StoreStaffConfig queryStaffDetail(Integer id) {
+    public StoreStaffDetailVo getStaffDetailWithCourse(Integer id) {
+        log.info("查询员工详情(包含课程信息),id={}", id);
+
+        // 参数校验
         if (id == null || id <= 0) {
+            log.warn("查询员工详情失败,员工ID无效:{}", id);
             return null;
         }
-        QueryWrapper<StoreStaffConfig> queryWrapper = new QueryWrapper<>();
-        queryWrapper.eq("id", id);
-        queryWrapper.eq("delete_flag", 0);
-        return storeStaffConfigMapper.selectOne(queryWrapper);
 
+        try {
+            // 查询员工基本信息
+            LambdaQueryWrapper<StoreStaffConfig> staffWrapper = new LambdaQueryWrapper<>();
+            staffWrapper.eq(StoreStaffConfig::getId, id)
+                    .eq(StoreStaffConfig::getDeleteFlag, CommonConstant.DELETE_FLAG_UNDELETE);
+            StoreStaffConfig staffConfig = storeStaffConfigMapper.selectOne(staffWrapper);
+
+            if (staffConfig == null) {
+                log.warn("查询员工详情失败,员工不存在:id={}", id);
+                return null;
+            }
+
+            // 查询关联的课程信息
+            List<shop.alien.entity.store.StoreStaffFitnessCourse> courseList =
+                    storeStaffFitnessCourseService.getListByStaffId(id);
+
+
+            // 根据店铺ID查询店铺名称
+            String storeName = null;
+            if (staffConfig.getStoreId() != null && staffConfig.getStoreId() > 0) {
+                try {
+                    StoreInfo storeInfo = storeInfoMapper.selectById(staffConfig.getStoreId());
+                    if (storeInfo != null) {
+                        storeName = storeInfo.getStoreName();
+                    } else {
+                        log.warn("查询店铺信息失败,店铺不存在:storeId={}", staffConfig.getStoreId());
+                    }
+                } catch (Exception e) {
+                    log.warn("查询店铺信息异常,storeId={},异常信息:{}", staffConfig.getStoreId(), e.getMessage());
+                    // 店铺信息查询失败不影响整体返回,设置为null即可
+                }
+            }
+
+            // 构建返回对象
+            StoreStaffDetailVo detailVo = new StoreStaffDetailVo();
+            detailVo.setStaffInfo(staffConfig);
+            detailVo.setCourseList(courseList);
+
+            log.info("查询员工详情成功,id={},课程数量:{}", id, courseList != null ? courseList.size() : 0);
+            detailVo.setStoreName(storeName);
+
+            log.info("查询员工详情成功,id={},课程数量:{},店铺名称:{}",
+                    id,
+                    courseList != null ? courseList.size() : 0,
+                    storeName);
+            return detailVo;
+        } catch (Exception e) {
+            log.error("查询员工详情异常,id={},异常信息:{}", id, e.getMessage(), e);
+            throw new RuntimeException("查询员工详情异常:" + e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public StoreStaffFitnessDetailVo getFitnessCoachDetail(Integer id) {
+        log.info("查询健身教练详情,id={}", id);
+
+        // 参数校验
+        if (id == null || id <= 0) {
+            log.warn("查询健身教练详情失败,员工ID无效:{}", id);
+            return null;
+        }
+
+        try {
+            // 查询员工基本信息
+            LambdaQueryWrapper<StoreStaffConfig> staffWrapper = new LambdaQueryWrapper<>();
+            staffWrapper.eq(StoreStaffConfig::getId, id)
+                    .eq(StoreStaffConfig::getDeleteFlag, CommonConstant.DELETE_FLAG_UNDELETE);
+            StoreStaffConfig staffConfig = storeStaffConfigMapper.selectOne(staffWrapper);
+
+            if (staffConfig == null) {
+                log.warn("查询健身教练详情失败,员工不存在:id={}", id);
+                return null;
+            }
+
+            // 查询员工基本信息(运动健身)
+            shop.alien.entity.store.StoreStaffFitnessBase baseInfo = null;
+            try {
+                shop.alien.entity.result.R<shop.alien.entity.store.StoreStaffFitnessBase> baseResult =
+                        storeStaffFitnessBaseService.getByStaffId(id);
+                if (baseResult != null && shop.alien.entity.result.R.isSuccess(baseResult) && baseResult.getData() != null) {
+                    baseInfo = baseResult.getData();
+                }
+            } catch (Exception e) {
+                log.warn("查询员工基本信息失败,id={},异常信息:{}", id, e.getMessage());
+                // 基本信息查询失败不影响整体返回,设置为null即可
+            }
+
+            // 查询认证列表(type=1)
+            List<shop.alien.entity.store.StoreStaffFitnessCertification> certificationList =
+                    storeStaffFitnessCertificationService.getListByStaffIdAndType(id, CERTIFICATION_TYPE);
+
+            // 查询荣誉列表(type=2)
+            List<shop.alien.entity.store.StoreStaffFitnessCertification> honorList =
+                    storeStaffFitnessCertificationService.getListByStaffIdAndType(id, HONOR_TYPE);
+
+
+            // 查询从业经历列表
+            List<shop.alien.entity.store.StoreStaffFitnessExperience> experienceList = null;
+            try {
+                experienceList = storeStaffFitnessExperienceService.getListByStaffId(id);
+            } catch (Exception e) {
+                log.warn("查询从业经历列表失败,id={},异常信息:{}", id, e.getMessage());
+                // 从业经历查询失败不影响整体返回,设置为null即可
+            }
+
+            // 根据店铺ID查询店铺名称
+            String storeName = null;
+            if (staffConfig.getStoreId() != null && staffConfig.getStoreId() > 0) {
+                try {
+                    StoreInfo storeInfo = storeInfoMapper.selectById(staffConfig.getStoreId());
+                    if (storeInfo != null) {
+                        storeName = storeInfo.getStoreName();
+                    } else {
+                        log.warn("查询店铺信息失败,店铺不存在:storeId={}", staffConfig.getStoreId());
+                    }
+                } catch (Exception e) {
+                    log.warn("查询店铺信息异常,storeId={},异常信息:{}", staffConfig.getStoreId(), e.getMessage());
+                    // 店铺信息查询失败不影响整体返回,设置为null即可
+                }
+            }
+
+            // 构建返回对象
+            StoreStaffFitnessDetailVo detailVo = new StoreStaffFitnessDetailVo();
+            detailVo.setStaffInfo(staffConfig);
+            detailVo.setBaseInfo(baseInfo);
+            detailVo.setCertificationList(certificationList != null ? certificationList : new ArrayList<>());
+            detailVo.setHonorList(honorList != null ? honorList : new ArrayList<>());
+            detailVo.setExperienceList(experienceList != null ? experienceList : new ArrayList<>());
+            detailVo.setStoreName(storeName);
+
+            log.info("查询健身教练详情成功,id={},认证数量:{},荣誉数量:{},从业经历数量:{},店铺名称:{}",
+                    id, 
+                    certificationList != null ? certificationList.size() : 0,
+                    honorList != null ? honorList.size() : 0,
+                    experienceList != null ? experienceList.size() : 0,
+                    storeName);
+            return detailVo;
+        } catch (Exception e) {
+            log.error("查询健身教练详情异常,id={},异常信息:{}", id, e.getMessage(), e);
+            throw new RuntimeException("查询健身教练详情异常:" + e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public List<StoreStaffPositionCountVo> getStaffPositionCount(Integer storeId) {
+        log.info("查询员工职位统计,storeId={}", storeId);
+
+        // 参数校验
+        if (storeId == null || storeId <= 0) {
+            log.warn("查询员工职位统计失败,店铺ID无效:storeId={}", storeId);
+            throw new IllegalArgumentException("店铺ID不能为空且必须大于0");
+        }
+
+        // 构建查询条件:查询指定店铺下未删除的员工
+        LambdaQueryWrapper<StoreStaffConfig> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(StoreStaffConfig::getStoreId, storeId)
+                .eq(StoreStaffConfig::getDeleteFlag, CommonConstant.DELETE_FLAG_UNDELETE)
+                // 只查询staff_position不为空的记录
+                .isNotNull(StoreStaffConfig::getStaffPosition)
+                .ne(StoreStaffConfig::getStaffPosition, "");
+
+        // 查询所有符合条件的员工
+        List<StoreStaffConfig> staffList = storeStaffConfigMapper.selectList(queryWrapper);
+
+        // 按职位分组统计(查询条件已过滤空值,此处直接分组即可)
+        Map<String, Long> positionCountMap = staffList.stream()
+                .collect(Collectors.groupingBy(
+                        StoreStaffConfig::getStaffPosition,
+                        Collectors.counting()
+                ));
+
+        // 转换为VO列表
+        List<StoreStaffPositionCountVo> result = positionCountMap.entrySet().stream()
+                .map(entry -> {
+                    StoreStaffPositionCountVo vo = new StoreStaffPositionCountVo();
+                    vo.setStaffPosition(entry.getKey());
+                    vo.setCount(entry.getValue());
+                    return vo;
+                })
+                .collect(Collectors.toList());
 
+        log.info("查询员工职位统计成功,storeId={},职位数量:{}", storeId, result.size());
+        return result;
     }
 }

+ 139 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StoreStaffFitnessBaseServiceImpl.java

@@ -0,0 +1,139 @@
+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.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreStaffFitnessBase;
+import shop.alien.mapper.StoreStaffFitnessBaseMapper;
+import shop.alien.store.service.StoreStaffFitnessBaseService;
+
+import java.util.List;
+
+/**
+ * 运动健身员工基本信息表 服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Transactional
+@Service
+@RequiredArgsConstructor
+public class StoreStaffFitnessBaseServiceImpl extends ServiceImpl<StoreStaffFitnessBaseMapper, StoreStaffFitnessBase> implements StoreStaffFitnessBaseService {
+
+    @Override
+    public R<StoreStaffFitnessBase> addStoreStaffFitnessBase(StoreStaffFitnessBase storeStaffFitnessBase) {
+        log.info("StoreStaffFitnessBaseServiceImpl.addStoreStaffFitnessBase?storeStaffFitnessBase={}", storeStaffFitnessBase);
+        // 设置默认值
+        if (storeStaffFitnessBase.getDeleteFlag() == null) {
+            storeStaffFitnessBase.setDeleteFlag(0);
+        }
+        if (storeStaffFitnessBase.getCreatedUserId() == null) {
+            storeStaffFitnessBase.setCreatedUserId(0);
+        }
+        if (storeStaffFitnessBase.getUpdatedUserId() == null) {
+            storeStaffFitnessBase.setUpdatedUserId(0);
+        }
+
+        boolean result = this.save(storeStaffFitnessBase);
+        if (result) {
+            return R.data(storeStaffFitnessBase);
+        }
+        return R.fail("新增失败");
+    }
+
+    @Override
+    public R<StoreStaffFitnessBase> editStoreStaffFitnessBase(StoreStaffFitnessBase storeStaffFitnessBase) {
+        log.info("StoreStaffFitnessBaseServiceImpl.editStoreStaffFitnessBase?storeStaffFitnessBase={}", storeStaffFitnessBase);
+        if (storeStaffFitnessBase.getId() == null) {
+            log.error("更新运动健身员工基本信息失败:ID不能为空");
+            return R.fail("ID不能为空");
+        }
+
+        // 设置更新人
+        if (storeStaffFitnessBase.getUpdatedUserId() == null) {
+            storeStaffFitnessBase.setUpdatedUserId(0);
+        }
+
+        boolean result = this.updateById(storeStaffFitnessBase);
+        if (result) {
+            return R.data(storeStaffFitnessBase);
+        }
+        return R.fail("修改失败");
+    }
+
+    @Override
+    public R<Boolean> deleteStoreStaffFitnessBase(Integer id) {
+        log.info("StoreStaffFitnessBaseServiceImpl.deleteStoreStaffFitnessBase?id={}", id);
+        boolean result = this.removeById(id);
+        if (result) {
+            return R.success("删除成功");
+        }
+        return R.fail("删除失败");
+    }
+
+    @Override
+    public R<StoreStaffFitnessBase> getStoreStaffFitnessBaseById(Integer id) {
+        log.info("StoreStaffFitnessBaseServiceImpl.getStoreStaffFitnessBaseById?id={}", id);
+        StoreStaffFitnessBase storeStaffFitnessBase = this.getById(id);
+        if (storeStaffFitnessBase != null) {
+            return R.data(storeStaffFitnessBase);
+        }
+        return R.fail("查询失败,数据不存在");
+    }
+
+    @Override
+    public R<StoreStaffFitnessBase> getByStaffId(Integer staffId) {
+        log.info("StoreStaffFitnessBaseServiceImpl.getByStaffId?staffId={}", staffId);
+        if (staffId == null) {
+            return R.fail("员工ID不能为空");
+        }
+        LambdaQueryWrapper<StoreStaffFitnessBase> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(StoreStaffFitnessBase::getStaffId, staffId)
+                .orderByDesc(StoreStaffFitnessBase::getCreatedTime)
+                .last("LIMIT 1");
+        StoreStaffFitnessBase storeStaffFitnessBase = this.getOne(queryWrapper);
+        if (storeStaffFitnessBase != null) {
+            return R.data(storeStaffFitnessBase);
+        }
+        return R.fail("查询失败,数据不存在");
+    }
+
+    @Override
+    public IPage<StoreStaffFitnessBase> getPage(int pageNum, int pageSize, Integer staffId, String education) {
+        LambdaQueryWrapper<StoreStaffFitnessBase> queryWrapper = new LambdaQueryWrapper<>();
+
+        // 员工ID查询
+        if (staffId != null) {
+            queryWrapper.eq(StoreStaffFitnessBase::getStaffId, staffId);
+        }
+
+        // 学历查询
+        if (StringUtils.hasText(education)) {
+            queryWrapper.eq(StoreStaffFitnessBase::getEducation, education);
+        }
+
+        // 按创建时间倒序
+        queryWrapper.orderByDesc(StoreStaffFitnessBase::getCreatedTime);
+
+        return this.page(new Page<>(pageNum, pageSize), queryWrapper);
+    }
+
+    @Override
+    public boolean deleteBatch(List<Integer> ids) {
+        if (ids == null || ids.isEmpty()) {
+            log.error("批量删除运动健身员工基本信息失败:ID列表不能为空");
+            return false;
+        }
+        // 逻辑删除,MyBatis-Plus会自动处理
+        return this.removeByIds(ids);
+    }
+}
+

+ 151 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StoreStaffFitnessCertificationServiceImpl.java

@@ -0,0 +1,151 @@
+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.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreStaffFitnessCertification;
+import shop.alien.mapper.StoreStaffFitnessCertificationMapper;
+import shop.alien.store.service.StoreStaffFitnessCertificationService;
+
+import java.util.List;
+
+/**
+ * 运动健身员工认证/荣誉表 服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Transactional
+@Service
+@RequiredArgsConstructor
+public class StoreStaffFitnessCertificationServiceImpl extends ServiceImpl<StoreStaffFitnessCertificationMapper, StoreStaffFitnessCertification> implements StoreStaffFitnessCertificationService {
+
+    @Override
+    public R<StoreStaffFitnessCertification> addStoreStaffFitnessCertification(StoreStaffFitnessCertification storeStaffFitnessCertification) {
+        log.info("StoreStaffFitnessCertificationServiceImpl.addStoreStaffFitnessCertification?storeStaffFitnessCertification={}", storeStaffFitnessCertification);
+        // 设置默认值
+        if (storeStaffFitnessCertification.getDeleteFlag() == null) {
+            storeStaffFitnessCertification.setDeleteFlag(0);
+        }
+        if (storeStaffFitnessCertification.getCreatedUserId() == null) {
+            storeStaffFitnessCertification.setCreatedUserId(0);
+        }
+        if (storeStaffFitnessCertification.getUpdatedUserId() == null) {
+            storeStaffFitnessCertification.setUpdatedUserId(0);
+        }
+
+        boolean result = this.save(storeStaffFitnessCertification);
+        if (result) {
+            return R.data(storeStaffFitnessCertification);
+        }
+        return R.fail("新增失败");
+    }
+
+    @Override
+    public R<StoreStaffFitnessCertification> editStoreStaffFitnessCertification(StoreStaffFitnessCertification storeStaffFitnessCertification) {
+        log.info("StoreStaffFitnessCertificationServiceImpl.editStoreStaffFitnessCertification?storeStaffFitnessCertification={}", storeStaffFitnessCertification);
+        if (storeStaffFitnessCertification.getId() == null) {
+            log.error("更新运动健身员工认证/荣誉失败:ID不能为空");
+            return R.fail("ID不能为空");
+        }
+
+        // 设置更新人
+        if (storeStaffFitnessCertification.getUpdatedUserId() == null) {
+            storeStaffFitnessCertification.setUpdatedUserId(0);
+        }
+
+        boolean result = this.updateById(storeStaffFitnessCertification);
+        if (result) {
+            return R.data(storeStaffFitnessCertification);
+        }
+        return R.fail("修改失败");
+    }
+
+    @Override
+    public R<Boolean> deleteStoreStaffFitnessCertification(Integer id) {
+        log.info("StoreStaffFitnessCertificationServiceImpl.deleteStoreStaffFitnessCertification?id={}", id);
+        boolean result = this.removeById(id);
+        if (result) {
+            return R.success("删除成功");
+        }
+        return R.fail("删除失败");
+    }
+
+    @Override
+    public R<StoreStaffFitnessCertification> getStoreStaffFitnessCertificationById(Integer id) {
+        log.info("StoreStaffFitnessCertificationServiceImpl.getStoreStaffFitnessCertificationById?id={}", id);
+        StoreStaffFitnessCertification storeStaffFitnessCertification = this.getById(id);
+        if (storeStaffFitnessCertification != null) {
+            return R.data(storeStaffFitnessCertification);
+        }
+        return R.fail("查询失败,数据不存在");
+    }
+
+    @Override
+    public IPage<StoreStaffFitnessCertification> getPage(int pageNum, int pageSize, Integer staffId, Integer type, String name) {
+        LambdaQueryWrapper<StoreStaffFitnessCertification> queryWrapper = new LambdaQueryWrapper<>();
+
+        // 员工ID查询
+        if (staffId != null) {
+            queryWrapper.eq(StoreStaffFitnessCertification::getStaffId, staffId);
+        }
+
+        // 类型查询
+        if (type != null) {
+            queryWrapper.eq(StoreStaffFitnessCertification::getType, type);
+        }
+
+        // 名称模糊查询
+        if (StringUtils.hasText(name)) {
+            queryWrapper.like(StoreStaffFitnessCertification::getName, name);
+        }
+
+        // 按创建时间倒序
+        queryWrapper.orderByDesc(StoreStaffFitnessCertification::getCreatedTime);
+
+        return this.page(new Page<>(pageNum, pageSize), queryWrapper);
+    }
+
+    @Override
+    public List<StoreStaffFitnessCertification> getListByStaffId(Integer staffId) {
+        if (staffId == null) {
+            return null;
+        }
+        LambdaQueryWrapper<StoreStaffFitnessCertification> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(StoreStaffFitnessCertification::getStaffId, staffId)
+                .orderByDesc(StoreStaffFitnessCertification::getCreatedTime);
+        return this.list(queryWrapper);
+    }
+
+    @Override
+    public List<StoreStaffFitnessCertification> getListByStaffIdAndType(Integer staffId, Integer type) {
+        if (staffId == null) {
+            return null;
+        }
+        LambdaQueryWrapper<StoreStaffFitnessCertification> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(StoreStaffFitnessCertification::getStaffId, staffId);
+        if (type != null) {
+            queryWrapper.eq(StoreStaffFitnessCertification::getType, type);
+        }
+        queryWrapper.orderByDesc(StoreStaffFitnessCertification::getCreatedTime);
+        return this.list(queryWrapper);
+    }
+
+    @Override
+    public boolean deleteBatch(List<Integer> ids) {
+        if (ids == null || ids.isEmpty()) {
+            log.error("批量删除运动健身员工认证/荣誉失败:ID列表不能为空");
+            return false;
+        }
+        // 逻辑删除,MyBatis-Plus会自动处理
+        return this.removeByIds(ids);
+    }
+}

+ 138 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StoreStaffFitnessCourseServiceImpl.java

@@ -0,0 +1,138 @@
+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.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreStaffFitnessCourse;
+import shop.alien.mapper.StoreStaffFitnessCourseMapper;
+import shop.alien.store.service.StoreStaffFitnessCourseService;
+
+import java.util.List;
+
+/**
+ * 运动健身员工课程信息表 服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Transactional
+@Service
+@RequiredArgsConstructor
+public class StoreStaffFitnessCourseServiceImpl extends ServiceImpl<StoreStaffFitnessCourseMapper, StoreStaffFitnessCourse> implements StoreStaffFitnessCourseService {
+
+    @Override
+    public R<StoreStaffFitnessCourse> addStoreStaffFitnessCourse(StoreStaffFitnessCourse storeStaffFitnessCourse) {
+        log.info("StoreStaffFitnessCourseServiceImpl.addStoreStaffFitnessCourse?storeStaffFitnessCourse={}", storeStaffFitnessCourse);
+        // 设置默认值
+        if (storeStaffFitnessCourse.getDeleteFlag() == null) {
+            storeStaffFitnessCourse.setDeleteFlag(0);
+        }
+        if (storeStaffFitnessCourse.getCreatedUserId() == null) {
+            storeStaffFitnessCourse.setCreatedUserId(0);
+        }
+        if (storeStaffFitnessCourse.getUpdatedUserId() == null) {
+            storeStaffFitnessCourse.setUpdatedUserId(0);
+        }
+
+        boolean result = this.save(storeStaffFitnessCourse);
+        if (result) {
+            return R.data(storeStaffFitnessCourse);
+        }
+        return R.fail("新增失败");
+    }
+
+    @Override
+    public R<StoreStaffFitnessCourse> editStoreStaffFitnessCourse(StoreStaffFitnessCourse storeStaffFitnessCourse) {
+        log.info("StoreStaffFitnessCourseServiceImpl.editStoreStaffFitnessCourse?storeStaffFitnessCourse={}", storeStaffFitnessCourse);
+        if (storeStaffFitnessCourse.getId() == null) {
+            log.error("更新运动健身员工课程失败:ID不能为空");
+            return R.fail("ID不能为空");
+        }
+
+        // 设置更新人
+        if (storeStaffFitnessCourse.getUpdatedUserId() == null) {
+            storeStaffFitnessCourse.setUpdatedUserId(0);
+        }
+
+        boolean result = this.updateById(storeStaffFitnessCourse);
+        if (result) {
+            return R.data(storeStaffFitnessCourse);
+        }
+        return R.fail("修改失败");
+    }
+
+    @Override
+    public R<Boolean> deleteStoreStaffFitnessCourse(Integer id) {
+        log.info("StoreStaffFitnessCourseServiceImpl.deleteStoreStaffFitnessCourse?id={}", id);
+        boolean result = this.removeById(id);
+        if (result) {
+            return R.success("删除成功");
+        }
+        return R.fail("删除失败");
+    }
+
+    @Override
+    public R<StoreStaffFitnessCourse> getStoreStaffFitnessCourseById(Integer id) {
+        log.info("StoreStaffFitnessCourseServiceImpl.getStoreStaffFitnessCourseById?id={}", id);
+        StoreStaffFitnessCourse storeStaffFitnessCourse = this.getById(id);
+        if (storeStaffFitnessCourse != null) {
+            return R.data(storeStaffFitnessCourse);
+        }
+        return R.fail("查询失败,数据不存在");
+    }
+
+    @Override
+    public IPage<StoreStaffFitnessCourse> getPage(int pageNum, int pageSize, Integer staffId, String courseType, String courseName) {
+        LambdaQueryWrapper<StoreStaffFitnessCourse> queryWrapper = new LambdaQueryWrapper<>();
+
+        // 员工ID查询
+        if (staffId != null) {
+            queryWrapper.eq(StoreStaffFitnessCourse::getStaffId, staffId);
+        }
+
+        // 课程类型查询
+        if (StringUtils.hasText(courseType)) {
+            queryWrapper.eq(StoreStaffFitnessCourse::getCourseType, courseType);
+        }
+
+        // 项目名称模糊查询
+        if (StringUtils.hasText(courseName)) {
+            queryWrapper.like(StoreStaffFitnessCourse::getCourseName, courseName);
+        }
+
+        // 按创建时间倒序
+        queryWrapper.orderByDesc(StoreStaffFitnessCourse::getCreatedTime);
+
+        return this.page(new Page<>(pageNum, pageSize), queryWrapper);
+    }
+
+    @Override
+    public List<StoreStaffFitnessCourse> getListByStaffId(Integer staffId) {
+        if (staffId == null) {
+            return null;
+        }
+        LambdaQueryWrapper<StoreStaffFitnessCourse> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(StoreStaffFitnessCourse::getStaffId, staffId)
+                .orderByDesc(StoreStaffFitnessCourse::getCreatedTime);
+        return this.list(queryWrapper);
+    }
+
+    @Override
+    public boolean deleteBatch(List<Integer> ids) {
+        if (ids == null || ids.isEmpty()) {
+            log.error("批量删除运动健身员工课程失败:ID列表不能为空");
+            return false;
+        }
+        // 逻辑删除,MyBatis-Plus会自动处理
+        return this.removeByIds(ids);
+    }
+}
+

+ 138 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StoreStaffFitnessExperienceServiceImpl.java

@@ -0,0 +1,138 @@
+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.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreStaffFitnessExperience;
+import shop.alien.mapper.StoreStaffFitnessExperienceMapper;
+import shop.alien.store.service.StoreStaffFitnessExperienceService;
+
+import java.util.List;
+
+/**
+ * 运动健身员工从业经历表 服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Transactional
+@Service
+@RequiredArgsConstructor
+public class StoreStaffFitnessExperienceServiceImpl extends ServiceImpl<StoreStaffFitnessExperienceMapper, StoreStaffFitnessExperience> implements StoreStaffFitnessExperienceService {
+
+    @Override
+    public R<StoreStaffFitnessExperience> addStoreStaffFitnessExperience(StoreStaffFitnessExperience storeStaffFitnessExperience) {
+        log.info("StoreStaffFitnessExperienceServiceImpl.addStoreStaffFitnessExperience?storeStaffFitnessExperience={}", storeStaffFitnessExperience);
+        // 设置默认值
+        if (storeStaffFitnessExperience.getDeleteFlag() == null) {
+            storeStaffFitnessExperience.setDeleteFlag(0);
+        }
+        if (storeStaffFitnessExperience.getCreatedUserId() == null) {
+            storeStaffFitnessExperience.setCreatedUserId(0);
+        }
+        if (storeStaffFitnessExperience.getUpdatedUserId() == null) {
+            storeStaffFitnessExperience.setUpdatedUserId(0);
+        }
+
+        boolean result = this.save(storeStaffFitnessExperience);
+        if (result) {
+            return R.data(storeStaffFitnessExperience);
+        }
+        return R.fail("新增失败");
+    }
+
+    @Override
+    public R<StoreStaffFitnessExperience> editStoreStaffFitnessExperience(StoreStaffFitnessExperience storeStaffFitnessExperience) {
+        log.info("StoreStaffFitnessExperienceServiceImpl.editStoreStaffFitnessExperience?storeStaffFitnessExperience={}", storeStaffFitnessExperience);
+        if (storeStaffFitnessExperience.getId() == null) {
+            log.error("更新运动健身员工从业经历失败:ID不能为空");
+            return R.fail("ID不能为空");
+        }
+
+        // 设置更新人
+        if (storeStaffFitnessExperience.getUpdatedUserId() == null) {
+            storeStaffFitnessExperience.setUpdatedUserId(0);
+        }
+
+        boolean result = this.updateById(storeStaffFitnessExperience);
+        if (result) {
+            return R.data(storeStaffFitnessExperience);
+        }
+        return R.fail("修改失败");
+    }
+
+    @Override
+    public R<Boolean> deleteStoreStaffFitnessExperience(Integer id) {
+        log.info("StoreStaffFitnessExperienceServiceImpl.deleteStoreStaffFitnessExperience?id={}", id);
+        boolean result = this.removeById(id);
+        if (result) {
+            return R.success("删除成功");
+        }
+        return R.fail("删除失败");
+    }
+
+    @Override
+    public R<StoreStaffFitnessExperience> getStoreStaffFitnessExperienceById(Integer id) {
+        log.info("StoreStaffFitnessExperienceServiceImpl.getStoreStaffFitnessExperienceById?id={}", id);
+        StoreStaffFitnessExperience storeStaffFitnessExperience = this.getById(id);
+        if (storeStaffFitnessExperience != null) {
+            return R.data(storeStaffFitnessExperience);
+        }
+        return R.fail("查询失败,数据不存在");
+    }
+
+    @Override
+    public IPage<StoreStaffFitnessExperience> getPage(int pageNum, int pageSize, Integer staffId, String workUnit, String workPosition) {
+        LambdaQueryWrapper<StoreStaffFitnessExperience> queryWrapper = new LambdaQueryWrapper<>();
+
+        // 员工ID查询
+        if (staffId != null) {
+            queryWrapper.eq(StoreStaffFitnessExperience::getStaffId, staffId);
+        }
+
+        // 工作单位模糊查询
+        if (StringUtils.hasText(workUnit)) {
+            queryWrapper.like(StoreStaffFitnessExperience::getWorkUnit, workUnit);
+        }
+
+        // 职位模糊查询
+        if (StringUtils.hasText(workPosition)) {
+            queryWrapper.like(StoreStaffFitnessExperience::getWorkPosition, workPosition);
+        }
+
+        // 按开始时间倒序
+        queryWrapper.orderByDesc(StoreStaffFitnessExperience::getStartTime);
+
+        return this.page(new Page<>(pageNum, pageSize), queryWrapper);
+    }
+
+    @Override
+    public List<StoreStaffFitnessExperience> getListByStaffId(Integer staffId) {
+        if (staffId == null) {
+            return null;
+        }
+        LambdaQueryWrapper<StoreStaffFitnessExperience> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(StoreStaffFitnessExperience::getStaffId, staffId)
+                .orderByDesc(StoreStaffFitnessExperience::getStartTime);
+        return this.list(queryWrapper);
+    }
+
+    @Override
+    public boolean deleteBatch(List<Integer> ids) {
+        if (ids == null || ids.isEmpty()) {
+            log.error("批量删除运动健身员工从业经历失败:ID列表不能为空");
+            return false;
+        }
+        // 逻辑删除,MyBatis-Plus会自动处理
+        return this.removeByIds(ids);
+    }
+}
+

+ 31 - 0
alien-store/src/main/java/shop/alien/store/util/CommonConstant.java

@@ -55,4 +55,35 @@ public class CommonConstant {
     public static final String FOOD_LICENCE_EXPIRE_STATUS = "0";
     public static final String FOOD_LICENCE_ABOUT_TO_EXPIRE_STATUS = "1";
     public static final String FOOD_LICENCE_NOT_EXPIRED_STATUS = "2";
+
+    /**
+     * 员工状态:0-待审核 1-审核通过 2-审核拒绝
+     */
+    public static final String STAFF_STATUS_PENDING = "0";
+    public static final String STAFF_STATUS_APPROVED = "1";
+    public static final String STAFF_STATUS_REJECTED = "2";
+
+    /**
+     * 置顶状态:0-未置顶 1-已置顶
+     */
+    public static final Integer TOP_STATUS_NOT_TOP = 0;
+    public static final Integer TOP_STATUS_TOP = 1;
+
+    /**
+     * 默认分页大小
+     */
+    public static final Integer DEFAULT_PAGE_SIZE = 10;
+    public static final Integer DEFAULT_PAGE_NUM = 1;
+
+    /**
+     * 点赞类型:1-评论 2-社区动态 3-活动 4-推荐菜 5-店铺打卡 6-二手商品 7-律师评分 8-点赞员工
+     */
+    public static final String LIKE_TYPE_COMMENT = "1";
+    public static final String LIKE_TYPE_DYNAMICS = "2";
+    public static final String LIKE_TYPE_ACTIVITY = "3";
+    public static final String LIKE_TYPE_MENU = "4";
+    public static final String LIKE_TYPE_CLOCK_IN = "5";
+    public static final String LIKE_TYPE_SECOND_GOODS = "6";
+    public static final String LIKE_TYPE_LAWYER_SCORE = "7";
+    public static final String LIKE_TYPE_STAFF = "8";
 }

+ 205 - 0
alien-store/src/main/java/shop/alien/store/util/ai/AiContentModerationUtil.java

@@ -0,0 +1,205 @@
+package shop.alien.store.util.ai;
+
+import com.alibaba.fastjson2.JSONObject;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * 通用图文审核工具类
+ * 调用AI图文审核接口审核文本和图片内容
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class AiContentModerationUtil {
+
+    private final RestTemplate restTemplate;
+
+    /**
+     * AI审核接口地址
+     */
+    @Value("${ai.service.moderate-url}")
+    private String moderateUrl;
+
+    /**
+     * 审核结果类
+     */
+    public static class AuditResult {
+        private boolean passed;
+        private String failureReason;
+
+        public AuditResult(boolean passed, String failureReason) {
+            this.passed = passed;
+            this.failureReason = failureReason;
+        }
+
+        public boolean isPassed() {
+            return passed;
+        }
+
+        public String getFailureReason() {
+            return failureReason;
+        }
+    }
+
+    /**
+     * 审核文本和图片内容
+     *
+     * @param text 文本内容
+     * @param imageUrls 图片URL列表
+     * @return 审核结果
+     */
+    public AuditResult auditContent(String text, List<String> imageUrls) {
+        log.info("开始审核内容:text={}, imageCount={}",
+                text, imageUrls != null ? imageUrls.size() : 0);
+
+        try {
+            // 调用审核接口
+            return callModerateApi(text, imageUrls);
+
+        } catch (Exception e) {
+            log.error("审核内容异常", e);
+            return new AuditResult(false, "审核异常");
+        }
+    }
+
+    /**
+     * 调用AI审核接口
+     *
+     * @param text 文本内容
+     * @param imageUrls 图片URL列表
+     * @return 审核结果
+     */
+    private AuditResult callModerateApi(String text, List<String> imageUrls) {
+        try {
+            // 构建 form-data 请求头
+            HttpHeaders headers = new HttpHeaders();
+            headers.setContentType(MediaType.MULTIPART_FORM_DATA);
+
+            // 构建 form-data 请求体
+            MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();
+            if (StringUtils.hasText(text)) {
+                formData.add("text", text);
+            }
+            if (imageUrls != null) {
+                for (String url : imageUrls) {
+                    if (StringUtils.hasText(url)) {
+                        // 支持多张图片:同一个 key 重复多次
+                        formData.add("image_urls", url);
+                    }
+                }
+            }
+
+            HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(formData, headers);
+
+            log.info("调用AI审核接口:url={}, text={}, imageCount={}",
+                    moderateUrl, text, imageUrls != null ? imageUrls.size() : 0);
+
+            // 发送请求
+            ResponseEntity<String> response = restTemplate.postForEntity(moderateUrl, requestEntity, String.class);
+
+            if (response.getStatusCode() == HttpStatus.OK) {
+                String responseBody = response.getBody();
+                log.info("AI审核接口响应:{}", responseBody);
+
+                if (StringUtils.hasText(responseBody)) {
+                    JSONObject jsonResponse = JSONObject.parseObject(responseBody);
+                    return parseAuditResult(jsonResponse);
+                } else {
+                    log.error("AI审核接口返回空响应");
+                    return new AuditResult(false, "审核异常");
+                }
+            } else {
+                log.error("AI审核接口调用失败,状态码:{}", response.getStatusCode());
+                return new AuditResult(false, "审核异常");
+            }
+
+        } catch (Exception e) {
+            log.error("调用AI审核接口异常", e);
+            return new AuditResult(false, "审核异常");
+        }
+    }
+
+    /**
+     * 解析审核结果
+     *
+     * @param jsonResponse API响应JSON
+     * @return 审核结果
+     */
+    private AuditResult parseAuditResult(JSONObject jsonResponse) {
+        try {
+            // API返回格式:
+            // {
+            //     "results": [
+            //         {
+            //             "filename": "[TEXT INPUT]" 或 URL,
+            //             "flagged": true/false,
+            //             "risk_level": "high"/"safe"等,
+            //             "violation_categories": [...],
+            //             "reason": "原因描述"
+            //         }
+            //     ],
+            //     "summary": "处理摘要"
+            // }
+
+            com.alibaba.fastjson2.JSONArray results = jsonResponse.getJSONArray("results");
+            if (results == null || results.isEmpty()) {
+                log.warn("AI审核接口返回结果为空");
+                return new AuditResult(false, "审核异常");
+            }
+
+            // 检查是否有任何项目被标记为违规
+            List<String> violationReasons = new ArrayList<>();
+            boolean hasViolations = false;
+
+            for (int i = 0; i < results.size(); i++) {
+                JSONObject result = results.getJSONObject(i);
+                if (result != null) {
+                    Boolean flagged = result.getBoolean("flagged");
+                    String filename = result.getString("filename");
+
+                    if (flagged != null && flagged) {
+                        hasViolations = true;
+                        // 根据filename判断是文本还是图片违规
+                        if ("[TEXT INPUT]".equals(filename)) {
+                            violationReasons.add("含违规词汇");
+                        } else if (StringUtils.hasText(filename) && filename.startsWith("http")) {
+                            violationReasons.add("图片内容违规");
+                        } else {
+                            violationReasons.add("内容违规");
+                        }
+                    }
+                }
+            }
+
+            if (hasViolations) {
+                // 有违规内容,审核失败
+                String failureReason = String.join("; ", violationReasons);
+                log.warn("AI审核失败:{}", failureReason);
+                return new AuditResult(false, failureReason);
+            } else {
+                // 所有内容都安全,审核通过
+                log.info("AI审核通过:所有内容安全");
+                return new AuditResult(true, null);
+            }
+
+        } catch (Exception e) {
+            log.error("解析审核结果异常", e);
+            return new AuditResult(false, "审核异常");
+        }
+    }
+}

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików