Przeglądaj źródła

Merge remote-tracking branch 'origin/sit' into sit

fcw 1 tydzień temu
rodzic
commit
6dee6d7b67
71 zmienionych plików z 6017 dodań i 160 usunięć
  1. 86 0
      alien-entity/src/main/java/shop/alien/entity/store/BathFacilityService.java
  2. 86 0
      alien-entity/src/main/java/shop/alien/entity/store/SportsEquipmentFacility.java
  3. 1 1
      alien-entity/src/main/java/shop/alien/entity/store/StoreImg.java
  4. 4 4
      alien-entity/src/main/java/shop/alien/entity/store/StoreInfo.java
  5. 19 3
      alien-entity/src/main/java/shop/alien/entity/store/StoreMenu.java
  6. 75 0
      alien-entity/src/main/java/shop/alien/entity/store/StorePersonnel.java
  7. 10 0
      alien-entity/src/main/java/shop/alien/entity/store/StoreStaffConfig.java
  8. 17 8
      alien-entity/src/main/java/shop/alien/entity/store/dto/StoreInfoDto.java
  9. 4 0
      alien-entity/src/main/java/shop/alien/entity/store/excelVo/StoreInfoExcelVo.java
  10. 1 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/AiApproveStoreInfo.java
  11. 39 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/BathFacilityServiceCategoryVo.java
  12. 66 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/BathFacilityServiceVo.java
  13. 24 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/CouponAndEventVo.java
  14. 0 1
      alien-entity/src/main/java/shop/alien/entity/store/vo/LawyerConsultationOrderVO.java
  15. 39 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/SportsEquipmentFacilityCategoryVo.java
  16. 61 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/SportsEquipmentFacilityVo.java
  17. 53 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StoreAlbumDetailVo.java
  18. 25 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StoreAlbumNameVo.java
  19. 4 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StoreCommentVo.java
  20. 30 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StoreInfoVo.java
  21. 6 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StoreMainInfoVo.java
  22. 29 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StoreOfficialAlbumImgVo.java
  23. 34 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StorePersonnelVo.java
  24. 32 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StoreThreeLevelStructureVo.java
  25. 17 0
      alien-entity/src/main/java/shop/alien/mapper/BathFacilityServiceMapper.java
  26. 177 0
      alien-entity/src/main/java/shop/alien/mapper/LifeCouponMapper.java
  27. 17 0
      alien-entity/src/main/java/shop/alien/mapper/SportsEquipmentFacilityMapper.java
  28. 13 0
      alien-entity/src/main/java/shop/alien/mapper/StoreCommentMapper.java
  29. 26 0
      alien-entity/src/main/java/shop/alien/mapper/StoreInfoMapper.java
  30. 54 0
      alien-entity/src/main/java/shop/alien/mapper/StoreMenuMapper.java
  31. 44 0
      alien-entity/src/main/java/shop/alien/mapper/StorePersonnelMapper.java
  32. 40 13
      alien-entity/src/main/java/shop/alien/mapper/TagsMainMapper.java
  33. 13 0
      alien-job/src/main/java/shop/alien/job/feign/AlienStoreFeign.java
  34. 0 1
      alien-job/src/main/java/shop/alien/job/store/AiTagJob.java
  35. 30 7
      alien-job/src/main/java/shop/alien/job/store/LifeUserOrderJob.java
  36. 1 0
      alien-lawyer/src/main/java/shop/alien/lawyer/controller/OrderReviewController.java
  37. 2 2
      alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/OrderReviewServiceImpl.java
  38. 168 0
      alien-store/src/main/java/shop/alien/store/controller/BathFacilityServiceController.java
  39. 17 0
      alien-store/src/main/java/shop/alien/store/controller/LifeCouponController.java
  40. 194 0
      alien-store/src/main/java/shop/alien/store/controller/SportsEquipmentFacilityController.java
  41. 389 18
      alien-store/src/main/java/shop/alien/store/controller/StoreInfoController.java
  42. 84 5
      alien-store/src/main/java/shop/alien/store/controller/StoreMenuController.java
  43. 90 4
      alien-store/src/main/java/shop/alien/store/controller/StoreOfficialAlbumController.java
  44. 87 0
      alien-store/src/main/java/shop/alien/store/controller/StorePersonnelController.java
  45. 48 0
      alien-store/src/main/java/shop/alien/store/controller/StoreStaffConfigController.java
  46. 82 0
      alien-store/src/main/java/shop/alien/store/service/BathFacilityServiceService.java
  47. 49 44
      alien-store/src/main/java/shop/alien/store/service/LifeCommentService.java
  48. 7 0
      alien-store/src/main/java/shop/alien/store/service/LifeCouponService.java
  49. 1 1
      alien-store/src/main/java/shop/alien/store/service/LifeUserDynamicsService.java
  50. 86 0
      alien-store/src/main/java/shop/alien/store/service/SportsEquipmentFacilityService.java
  51. 115 6
      alien-store/src/main/java/shop/alien/store/service/StoreInfoService.java
  52. 32 4
      alien-store/src/main/java/shop/alien/store/service/StoreMenuService.java
  53. 25 1
      alien-store/src/main/java/shop/alien/store/service/StoreOfficialAlbumService.java
  54. 58 0
      alien-store/src/main/java/shop/alien/store/service/StorePersonnelService.java
  55. 19 0
      alien-store/src/main/java/shop/alien/store/service/StoreStaffConfigService.java
  56. 258 0
      alien-store/src/main/java/shop/alien/store/service/impl/BathFacilityServiceServiceImpl.java
  57. 5 0
      alien-store/src/main/java/shop/alien/store/service/impl/LifeCouponServiceImpl.java
  58. 4 2
      alien-store/src/main/java/shop/alien/store/service/impl/LifeDiscountCouponServiceImpl.java
  59. 307 0
      alien-store/src/main/java/shop/alien/store/service/impl/SportsEquipmentFacilityServiceImpl.java
  60. 2042 13
      alien-store/src/main/java/shop/alien/store/service/impl/StoreInfoServiceImpl.java
  61. 279 20
      alien-store/src/main/java/shop/alien/store/service/impl/StoreMenuServiceImpl.java
  62. 182 0
      alien-store/src/main/java/shop/alien/store/service/impl/StoreOfficialAlbumServiceImpl.java
  63. 161 0
      alien-store/src/main/java/shop/alien/store/service/impl/StorePersonnelServiceImpl.java
  64. 31 0
      alien-store/src/main/java/shop/alien/store/service/impl/StoreStaffConfigServiceImpl.java
  65. 2 2
      alien-store/src/main/java/shop/alien/store/util/ali/ocr/AbstractOcrStrategy.java
  66. 3 0
      alien-store/src/main/java/shop/alien/store/util/ali/ocr/strategy/BusinessLicenseOcrStrategy.java
  67. 3 0
      alien-store/src/main/java/shop/alien/store/util/ali/ocr/strategy/FoodManageLicenseOcrStrategy.java
  68. 0 0
      logging.path_IS_UNDEFINED/DEBUG.log
  69. 0 0
      logging.path_IS_UNDEFINED/ERROR.log
  70. 6 0
      logging.path_IS_UNDEFINED/INFO.log
  71. 4 0
      logging.path_IS_UNDEFINED/WARN.log

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

@@ -0,0 +1,86 @@
+package shop.alien.entity.store;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 洗浴设施及服务表
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@JsonInclude
+@TableName("bath_facility_service")
+@ApiModel(value = "BathFacilityService对象", description = "洗浴设施及服务表")
+public class BathFacilityService implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "主键")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "门店ID")
+    @TableField("store_id")
+    private Integer storeId;
+
+    @ApiModelProperty(value = "设施分类(1:洗浴区, 2:汗蒸区, 3:休闲区, 4:餐饮区)")
+    @TableField("facility_category")
+    private Integer facilityCategory;
+
+    @ApiModelProperty(value = "名称")
+    @TableField("facility_name")
+    private String facilityName;
+
+    @ApiModelProperty(value = "收费标准")
+    @TableField("charging_standard")
+    private String chargingStandard;
+
+    @ApiModelProperty(value = "使用时间类型(0:全天, 1:选择时间)")
+    @TableField("usage_time_type")
+    private Integer usageTimeType;
+
+    @ApiModelProperty(value = "使用开始时间(HH:mm格式)")
+    @TableField("usage_start_time")
+    private String usageStartTime;
+
+    @ApiModelProperty(value = "使用结束时间(HH:mm格式)")
+    @TableField("usage_end_time")
+    private String usageEndTime;
+
+    @ApiModelProperty(value = "是否显示在店铺详情(0:隐藏, 1:显示)")
+    @TableField("display_in_store_detail")
+    private Integer displayInStoreDetail;
+
+    @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
+    @TableField("delete_flag")
+    @TableLogic
+    private Integer deleteFlag;
+
+    @ApiModelProperty(value = "创建时间")
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "创建人ID")
+    @TableField(value = "created_user_id", fill = FieldFill.INSERT)
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "修改时间")
+    @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+
+    @ApiModelProperty(value = "修改人ID")
+    @TableField(value = "updated_user_id", fill = FieldFill.INSERT_UPDATE)
+    private Integer updatedUserId;
+}
+

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

@@ -0,0 +1,86 @@
+package shop.alien.entity.store;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 运动器材设施表
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@JsonInclude
+@TableName("sports_equipment_facility")
+@ApiModel(value = "SportsEquipmentFacility对象", description = "运动器材设施表")
+public class SportsEquipmentFacility implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "主键")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "门店ID")
+    @TableField("store_id")
+    private Integer storeId;
+
+    @ApiModelProperty(value = "设施分类(1:有氧区, 2:力量区, 3:单功能机械区)")
+    @TableField("facility_category")
+    private Integer facilityCategory;
+
+    @ApiModelProperty(value = "设施名称")
+    @TableField("facility_name")
+    private String facilityName;
+
+    @ApiModelProperty(value = "数量")
+    @TableField("quantity")
+    private Integer quantity;
+
+    @ApiModelProperty(value = "品牌")
+    @TableField("brand")
+    private String brand;
+
+    @ApiModelProperty(value = "描述")
+    @TableField("description")
+    private String description;
+
+    @ApiModelProperty(value = "是否显示在店铺详情(0:隐藏, 1:显示)")
+    @TableField("display_in_store_detail")
+    private Integer displayInStoreDetail;
+
+    @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
+    @TableField("delete_flag")
+    @TableLogic
+    private Integer deleteFlag;
+
+    @ApiModelProperty(value = "创建时间")
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "创建人ID")
+    @TableField(value = "created_user_id", fill = FieldFill.INSERT)
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "修改时间")
+    @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+
+    @ApiModelProperty(value = "修改人ID")
+    @TableField(value = "updated_user_id", fill = FieldFill.INSERT_UPDATE)
+    private Integer updatedUserId;
+
+    @ApiModelProperty(value = "收费类型0-免费,1-收费")
+    @TableField("billing_type")
+    private Integer billingType;
+}
+

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

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

+ 4 - 4
alien-entity/src/main/java/shop/alien/entity/store/StoreInfo.java

@@ -235,7 +235,7 @@ public class StoreInfo {
     @TableField("update_renew_contract_time")
     private Date  updateRenewContractTime;
 
-    @ApiModelProperty(value = "分类id(词典表 键为 business_classify)")
+    @ApiModelProperty(value = "分类id(词典表 键为 business_classify)(多个ID用逗号拼接)")
     @TableField("business_classify")
     private String businessClassify;
 
@@ -243,9 +243,9 @@ public class StoreInfo {
     @TableField("business_classify_name")
     private String businessClassifyName;
 
-    @ApiModelProperty(value = "是否提供餐食 0 否 1 是")
-    @TableField("meal_provided")
-    private Integer mealProvided;
+    @ApiModelProperty(value = "是否提供餐食")
+    @TableField("meals_flag")
+    private Integer  mealsFlag;
 
     @ApiModelProperty(value = "娱乐经营许可证状态 字典 foodLicenceStatus")
     @TableField("entertainment_licence_status")

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

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

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

@@ -0,0 +1,75 @@
+package shop.alien.entity.store;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 店铺人员实体类
+ *
+ * @author system
+ * @since 2025-01-15
+ */
+@Data
+@JsonInclude
+@TableName("store_personnel")
+@ApiModel(value = "StorePersonnel对象", description = "店铺人员")
+public class StorePersonnel {
+
+    @ApiModelProperty(value = "主键")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "门店id")
+    @TableField("store_id")
+    private Integer storeId;
+
+    @ApiModelProperty(value = "人员姓名")
+    @TableField("personnel_name")
+    private String personnelName;
+
+    @ApiModelProperty(value = "角色标签,1-歌手,2-技师,3-教练,4-美发师/美容师")
+    @TableField("role_tags")
+    private String roleTags;
+
+    @ApiModelProperty(value = "图片id,关联store_img表")
+    @TableField("img_id")
+    private Integer imgId;
+
+    @ApiModelProperty(value = "人员描述")
+    @TableField("description")
+    private String description;
+
+    @ApiModelProperty(value = "排序")
+    @TableField("sort")
+    private Integer sort;
+
+    @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
+    @TableField("delete_flag")
+    @TableLogic
+    private Integer deleteFlag;
+
+    @ApiModelProperty(value = "创建时间")
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "创建人ID")
+    @TableField("created_user_id")
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "修改时间")
+    @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+
+    @ApiModelProperty(value = "修改人ID")
+    @TableField("updated_user_id")
+    private Integer updatedUserId;
+}
+

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

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

+ 17 - 8
alien-entity/src/main/java/shop/alien/entity/store/dto/StoreInfoDto.java

@@ -180,20 +180,29 @@ public class StoreInfoDto {
     @DateTimeFormat(pattern = "yyyy-MM-dd")
     private Date foodLicenceExpirationTime;
 
-    @ApiModelProperty(value = "分类id(词典表 键为 business_classify)")
-    private  List<String> businessClassifyList;
+    @ApiModelProperty(value = "分类id(词典表 键为 business_classify)(多个ID用逗号拼接)")
+    private List<String> businessClassify;
 
     @ApiModelProperty(value = "分类名称")
     private String businessClassifyName;
 
-    @ApiModelProperty(value = "分类id(词典表 键为 business_classify)")
-    @TableField("business_classify")
-    private String businessClassify;
+    @ApiModelProperty(value = "是否提供餐食")
+    private Integer  mealsFlag;
 
-    @ApiModelProperty(value = "是否提供餐食 0=不提供  1=提供")
-    @TableField("meal_provided")
-    private Integer mealProvided;
+    @ApiModelProperty(value = "娱乐经营许可证图片地址")
+    private String entertainmentLicenceUrl;
+
+    @ApiModelProperty(value = "娱乐经营许可证状态")
+    private Integer entertainmentLicenceStatus;
 
     @ApiModelProperty(value = "娱乐经营许可证")
     private List<String> entertainmentLicenseAddress;
+
+    @ApiModelProperty(value = "娱乐经营许可证原因")
+    private String entertainmentLicenceReason;
+
+    @ApiModelProperty(value = "娱乐经营许可证到期时间")
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private Date entertainmentLicenceExpirationTime;
 }

+ 4 - 0
alien-entity/src/main/java/shop/alien/entity/store/excelVo/StoreInfoExcelVo.java

@@ -44,6 +44,10 @@ public class StoreInfoExcelVo {
     @ApiModelProperty(value = "经营种类")
     private String businessTypesName;
 
+    @ExcelHeader("经营分类")
+    @ApiModelProperty(value = "经营分类")
+    private String businessClassifyName;
+
     @ExcelHeader(value = "详细地址")
     @ApiModelProperty(value = "详细地址")
     private String storeAddress;

+ 1 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/AiApproveStoreInfo.java

@@ -11,6 +11,7 @@ import java.util.List;
 @Data
 public class AiApproveStoreInfo {
 
+
     @ApiModelProperty(value = "用户id")
     private String userId;
 

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

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

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

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

+ 24 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/CouponAndEventVo.java

@@ -0,0 +1,24 @@
+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 lombok.NoArgsConstructor;
+import shop.alien.entity.store.StoreImg;
+
+import java.util.List;
+
+
+@Data
+@JsonInclude
+@NoArgsConstructor
+@ApiModel(value = "代金券和营销活动", description = "代金券和营销活动")
+public class CouponAndEventVo {
+
+    @ApiModelProperty(value = "代金券")
+    private List<LifeCouponVo> couponList;
+
+    @ApiModelProperty(value = "营销活动")
+    private List<StoreImg> marketingList;
+}

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

@@ -6,7 +6,6 @@ import com.fasterxml.jackson.annotation.JsonInclude;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
-import shop.alien.entity.store.LawyerLegalProblemScenario;
 
 import java.io.Serializable;
 import java.math.BigDecimal;

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

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

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

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

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

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

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

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

+ 4 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/StoreCommentVo.java

@@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonInclude;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 import shop.alien.entity.store.StoreComment;
+import shop.alien.entity.store.StoreImg;
 
 import java.util.List;
 
@@ -65,4 +66,7 @@ public class StoreCommentVo extends StoreComment {
 
     @ApiModelProperty(value = "门店名称")
     private String  storeName;
+
+    @ApiModelProperty(value = "图片地址")
+    private List<StoreImg> imgList;
 }

+ 30 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/StoreInfoVo.java

@@ -1,5 +1,6 @@
 package shop.alien.entity.store.vo;
 
+import com.alibaba.fastjson2.JSONObject;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import io.swagger.annotations.ApiModel;
@@ -215,6 +216,35 @@ public class StoreInfoVo extends StoreInfo {
     @ApiModelProperty(value = "动态数量")
     private Integer dynamicsNum;
 
+    @ApiModelProperty(value = "推荐列表距离(米)")
+    private String distance3;
+
+    @ApiModelProperty(value = "店铺头图(多图列表)")
+    private List<String> storeAlbumUrlList;
+
+    @ApiModelProperty(value = "分类id(词典表 键为 business_classify)(多个ID用逗号拼接)")
+    private String businessClassify;
+
+    @ApiModelProperty(value = "分类名称")
+    private String businessClassifyName;
+
+    @ApiModelProperty(value = "分类id(词典表 键为 business_classify)(多个ID用逗号拼接)")
+    private List<String> businessClassifyList;
+
+    @ApiModelProperty(value = "分类名称")
+    private List<String> businessClassifyNameList;
+
     @ApiModelProperty(value = "是否提供餐食")
     private Integer  mealsFlag;
+
+    @ApiModelProperty(value = "身份证正面")
+    private JSONObject idcardFace;
+    @ApiModelProperty(value = "身份证反面")
+    private JSONObject idcardBack;
+    @ApiModelProperty(value = "经营许可证")
+    private JSONObject jyxkz;
+    @ApiModelProperty(value = "食品经营许可证")
+    private JSONObject foodLicence;
+    @ApiModelProperty(value = "娱乐经营许可证")
+    private JSONObject entertainmentLicence;
 }

+ 6 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/StoreMainInfoVo.java

@@ -44,6 +44,12 @@ public class StoreMainInfoVo extends StoreInfo {
     @ApiModelProperty(value = "菜单")
     List<StoreMenuVo> menuUrl;
 
+    @ApiModelProperty(value = "推荐酒水")
+    List<StoreMenuVo> recommendBeverageUrl;
+
+    @ApiModelProperty(value = "酒水单")
+    List<StoreMenuVo> beverageUrl;
+
     @ApiModelProperty(value = "门店标签")
     StoreLabel storeLabel;
 

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

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

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

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

+ 32 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/StoreThreeLevelStructureVo.java

@@ -0,0 +1,32 @@
+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.StoreDictionary;
+
+import java.util.List;
+
+/**
+ * 门店三级分类结构VO
+ *
+ * @author ssk
+ * @since 2025-01-XX
+ */
+@Data
+@JsonInclude
+@ApiModel(value = "StoreThreeLevelStructureVo对象", description = "门店三级分类结构")
+public class StoreThreeLevelStructureVo {
+
+    @ApiModelProperty(value = "门店ID")
+    private Integer storeId;
+
+    @ApiModelProperty(value = "门店名称")
+    private String storeName;
+
+    @ApiModelProperty(value = "三级分类树状结构")
+    private List<StoreDictionary> threeLevelStructure;
+
+}
+

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

@@ -0,0 +1,17 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.entity.store.BathFacilityService;
+
+/**
+ * 洗浴设施及服务 Mapper 接口
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Mapper
+public interface BathFacilityServiceMapper extends BaseMapper<BathFacilityService> {
+
+}
+

+ 177 - 0
alien-entity/src/main/java/shop/alien/mapper/LifeCouponMapper.java

@@ -80,4 +80,181 @@ public interface LifeCouponMapper extends BaseMapper<LifeCoupon> {
             @Param("buyCount") int buyCount,
             @Param("soldOutStatus") int soldOutStatus
     );
+
+
+    /**
+     * 查询代金券详情(连表查询商铺信息)
+     *
+     * @param id 代金券ID
+     * @return LifeCouponVo
+     */
+    @Select("SELECT " +
+            "lc.id, " +
+            "lc.data_type, " +
+            "lc.buy_use_start_time, " +
+            "lc.buy_use_end_time, " +
+            "lc.use_festival, " +
+            "lc.coupon_code, " +
+            "lc.name, " +
+            "lc.store_id, " +
+            "lc.price, " +
+            "lc.offprice, " +
+            "lc.expiration_date, " +
+            "lc.start_date, " +
+            "lc.end_date, " +
+            "lc.unused_date, " +
+            "lc.single_qty, " +
+            "lc.buy_limit, " +
+            "lc.stock_qty, " +
+            "lc.use_rule, " +
+            "lc.min_use_peoples, " +
+            "lc.max_use_peoples, " +
+            "lc.dietary_preferences, " +
+            "lc.cooking_methods, " +
+            "lc.applicable_rule, " +
+            "lc.status, " +
+            "lc.image_path, " +
+            "lc.store_service, " +
+            "lc.fapiao_info, " +
+            "lc.type, " +
+            "lc.approval_comments, " +
+            "lc.open_price_protection, " +
+            "lc.price_below, " +
+            "lc.coupon_comp, " +
+            "lc.coupon_comp_date, " +
+            "lc.rate_score, " +
+            "lc.discount_tag, " +
+            "lc.discount_tag_name, " +
+            "lc.delete_flag, " +
+            "lc.status_delete_flag, " +
+            "lc.created_time, " +
+            "lc.created_user_id, " +
+            "lc.updated_time, " +
+            "lc.updated_user_id, " +
+            "lc.store_type, " +
+            "lc.sale_time_str_type, " +
+            "lc.sale_time_end_type, " +
+            "lc.purchase_limit_code, " +
+            "lc.original_price, " +
+            "lc.discounted_price, " +
+            "lc.business_types, " +
+            "lc.housing_type, " +
+            "lc.room_type, " +
+            "lc.room_type_code, " +
+            "lc.kitchen_xq, " +
+            "lc.bathroom_xq, " +
+            "lc.living_room, " +
+            "lc.location_of_the_property, " +
+            "lc.total_area, " +
+            "lc.entertainment_facilities_code, " +
+            "lc.entertainment_facilities, " +
+            "lc.convenience_facilities_code, " +
+            "lc.convenience_facilities, " +
+            "lc.bathroom_facilities_code, " +
+            "lc.bathroom_facilities, " +
+            "lc.bath_products_code, " +
+            "lc.bath_products, " +
+            "lc.service_code, " +
+            "lc.service, " +
+            "lc.safety_code, " +
+            "lc.safety, " +
+            "lc.reservation_instructions, " +
+            "lc.in_time, " +
+            "lc.out_time, " +
+            "lc.deposit_flag, " +
+            "lc.deposit_amount, " +
+            "lc.children_flag, " +
+            "lc.children_description, " +
+            "lc.pet_flag, " +
+            "lc.extra_bed_flag, " +
+            "lc.bed_price, " +
+            "lc.invoice_info, " +
+            "lc.invoice_instructions, " +
+            "lc.cancellation_policy_type, " +
+            "lc.cancellation_policy, " +
+            "lc.Cancellation_instructions, " +
+            "lc.insured_price, " +
+            "lc.validity_period_type, " +
+            "lc.box_type_code, " +
+            "lc.box_type, " +
+            "lc.snacks_code, " +
+            "lc.snacks, " +
+            "lc.available_duration, " +
+            "lc.write_off_type, " +
+            "lc.write_off, " +
+            "lc.refund_rules_type, " +
+            "lc.refund_rules, " +
+            "lc.refund_instructions, " +
+            "lc.edit_rules_code, " +
+            "lc.edit_rules, " +
+            "lc.edit_instructions, " +
+            "lc.tangchi_code, " +
+            "lc.tangchi, " +
+            "lc.sweat_sauna_code, " +
+            "lc.sweat_sauna, " +
+            "lc.pictures_and_text, " +
+            "lc.supplement, " +
+            "lc.unavailable_date_type, " +
+            "lc.reservation_rules, " +
+            "lc.applicable_num, " +
+            "lc.other_rules, " +
+            "lc.sales_type, " +
+            "lc.massage_technique_code, " +
+            "lc.massage_technique, " +
+            "lc.massage_area_code, " +
+            "lc.massage_area, " +
+            "lc.massage_tools_code, " +
+            "lc.massage_tools, " +
+            "lc.compress_tools_code, " +
+            "lc.compress_tools, " +
+            "lc.class_format_code, " +
+            "lc.class_format, " +
+            "lc.class_duration_code, " +
+            "lc.class_duration, " +
+            "lc.class_num_code, " +
+            "lc.class_num, " +
+            "lc.applicable_parts_code, " +
+            "lc.applicable_parts, " +
+            "lc.hygiene_code, " +
+            "lc.hygiene, " +
+            "lc.foundation_code, " +
+            "lc.foundation, " +
+            "lc.bathroom_code, " +
+            "lc.bathroom, " +
+            "lc.kitchen_code, " +
+            "lc.kitchen, " +
+            "lc.validity_period_yh_type, " +
+            "lc.validity_period_yh, " +
+            "lc.course_validity_period_code, " +
+            "lc.course_validity_period, " +
+            "lc.uploaded_picList, " +
+            "lc.validity_period, " +
+            "lc.stacking_type, " +
+            "lc.general_type, " +
+            "lc.single_can_use, " +
+            "lc.apply_type, " +
+            "lc.apply_desc, " +
+            "lc.expiration_type, " +
+            "lc.unused_type, " +
+            "lc.house_type, " +
+            "lc.house_type_b, " +
+            "lc.house_type_l, " +
+            "lc.house_type_k, " +
+            "lc.house_type_t, " +
+            "lc.sale_time_str, " +
+            "lc.sale_time_end, " +
+            "lc.unavai_lable_date, " +
+            "lc.room_type_str, " +
+            "lc.time_range, " +
+            "lc.cancellation_policy_str, " +
+            "lc.validity_periodyh_str, " +
+            "si.store_name, " +
+            "si.store_address, " +
+            "si.store_tel AS phone, " +
+            "si.business_status, " +
+            "si.store_status " +
+            "FROM life_coupon lc " +
+            "LEFT JOIN store_info si ON si.id = lc.store_id AND si.delete_flag = 0 " +
+            "WHERE lc.id = #{id} AND lc.delete_flag = 0")
+    LifeCouponVo getNewCouponDetail(@Param("id") String id);
 }

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

@@ -0,0 +1,17 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.entity.store.SportsEquipmentFacility;
+
+/**
+ * 运动器材设施 Mapper 接口
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Mapper
+public interface SportsEquipmentFacilityMapper extends BaseMapper<SportsEquipmentFacility> {
+
+}
+

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

@@ -185,4 +185,17 @@ WHERE
             "AND luo.user_id = #{userId} " +
             "AND luo.id IN (SELECT business_id FROM store_comment WHERE business_type = 5 AND user_id = #{userId} AND delete_flag = 0)")
     IPage<LifeUserOrderCommentVo> getCommentOrderYPJPage(IPage<LifeUserOrderCommentVo> page, @Param("userId") String userId);
+
+    @Select("SELECT\n" +
+            "\ts.*,\n" +
+            "\tlu.user_name, \n" +
+            "\tlu.user_image \n" +
+            "FROM\n" +
+            "\tstore_comment s\n" +
+            "\tLEFT JOIN life_user lu ON s.user_id = lu.id \n" +
+            "WHERE\n" +
+            "\ts.business_type = 5 \n" +
+            "\tAND s.store_id = #{storeId}\n" +
+            "\tLIMIT 1")
+    StoreCommentVo getCommentOneInfo(@Param("storeId") int storeId);
 }

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

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

+ 54 - 0
alien-entity/src/main/java/shop/alien/mapper/StoreMenuMapper.java

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

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

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

+ 40 - 13
alien-entity/src/main/java/shop/alien/mapper/TagsMainMapper.java

@@ -5,6 +5,7 @@ import org.apache.ibatis.annotations.Insert;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Select;
 import shop.alien.entity.store.TagsMain;
+import shop.alien.entity.store.vo.StoreCommentVo;
 import shop.alien.entity.store.vo.TagsMainVo;
 
 import java.util.List;
@@ -40,21 +41,47 @@ public interface TagsMainMapper extends BaseMapper<TagsMain> {
     })
     int insertBatchTagsMain(List<TagsMain> tagsMainList);
 
+
+    @Select("SELECT\n" +
+            "  main.id,\n" +
+            "  main.tag_name,\n" +
+            "  main.store_id,\n" +
+            "  main.tag_type,\n" +
+            "  IFNULL(GROUP_CONCAT(DISTINCT synonym.comment_id ORDER BY synonym.comment_id SEPARATOR ','), '') AS evaluate_ids,\n" +
+            "  IFNULL(COUNT(DISTINCT synonym.comment_id), 0) AS evaluateNumber\n" +
+            "FROM\n" +
+            "  tags_main main\n" +
+            "LEFT JOIN tags_synonym synonym \n" +
+            "  ON main.id = synonym.main_tag_id\n" +
+            "  AND synonym.delete_flag = 0  \n" +
+            "WHERE\n" +
+            "  main.store_id = #{storeId}\n" +
+            "  AND main.delete_flag = 0\n" +
+            "GROUP BY\n" +
+            "  main.id, main.tag_name, main.store_id, main.tag_type  \n" +
+            "ORDER BY\n" +
+            "  main.id;")
+    List<TagsMainVo> getStoreEvaluateTags(Integer storeId);
+
+
     @Select("SELECT\n" +
-            "    main.id,\n" +
-            "    main.tag_name,\n" +
-            "    main.store_id,\n" +
-            "    main.tag_type,\n" +
-            "    COUNT(DISTINCT synonym.comment_id) AS evaluateNumber\n" +
+            "  ts.comment_id,\n" +
+            "  MIN(ts.id) AS tag_id,  \n" +
+            "  ts.main_tag_id,\n" +
+            "  GROUP_CONCAT(DISTINCT ts.synonym_tag SEPARATOR ',') AS synonym_tags,  \n" +
+            "  MIN(ts.delete_flag) AS tag_delete_flag,\n" +
+            "  MAX(ts.created_time) AS tag_created_time,\n" +
+            "  sc.*\n" +
             "FROM\n" +
-            "    tags_main main\n" +
-            "    LEFT JOIN tags_synonym synonym \n" +
-            "        ON main.id = synonym.main_tag_id\n" +
-            "        AND synonym.delete_flag = 0  \n" +
+            "  tags_synonym ts\n" +
+            "LEFT JOIN store_comment sc \n" +
+            "  ON ts.comment_id = sc.id\n" +
             "WHERE\n" +
-            "    main.store_id = #{storeId} \n" +
-            "    AND main.delete_flag = 0\n" +
+            "  ts.main_tag_id = #{mainId}  \n" +
             "GROUP BY\n" +
-            "    main.id")
-    List<TagsMainVo> getStoreEvaluateTags(int storeId);
+            "  ts.comment_id, ts.main_tag_id,  \n" +
+            "  sc.id  \n" +
+            "ORDER BY\n" +
+            "  sc.created_time DESC; ")
+    List<StoreCommentVo> getstoreCommentList(Integer mainId);
 }

+ 13 - 0
alien-job/src/main/java/shop/alien/job/feign/AlienStoreFeign.java

@@ -3,7 +3,12 @@ package shop.alien.job.feign;
 import com.alibaba.fastjson.JSONObject;
 import org.springframework.cloud.openfeign.FeignClient;
 import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
+import shop.alien.entity.result.R;
+
+import java.util.Map;
 
 @FeignClient(url = "${feign.alienStore.url}", name = "alien-store")
 public interface AlienStoreFeign {
@@ -56,4 +61,12 @@ public interface AlienStoreFeign {
                                    @RequestParam(value = "refundReason") String refundReason,
                                    @RequestParam(value = "partialRefundCode") String partialRefundCode);
 
+    /**
+     * 退款接口
+     * @param params 退款参数(包含订单号、退款金额等)
+     * @return 退款结果
+     */
+    @RequestMapping("payment/refunds")
+    public R refunds(@RequestBody Map<String, String> params);
+
 }

+ 0 - 1
alien-job/src/main/java/shop/alien/job/store/AiTagJob.java

@@ -1022,5 +1022,4 @@ public class AiTagJob {
             this.end_time = end_time;
         }
     }
-
 }

+ 30 - 7
alien-job/src/main/java/shop/alien/job/store/LifeUserOrderJob.java

@@ -18,6 +18,7 @@ import shop.alien.util.common.UniqueRandomNumGenerator;
 import shop.alien.util.common.constant.CouponTypeEnum;
 import shop.alien.util.common.constant.DiscountCouponEnum;
 import shop.alien.util.common.constant.OrderStatusEnum;
+import shop.alien.util.common.constant.PaymentEnum;
 
 import java.math.BigDecimal;
 import java.text.ParseException;
@@ -212,15 +213,37 @@ public class LifeUserOrderJob {
                         // 统计退款金额
                         BigDecimal totalRefundAmount = refundList.stream().map(x -> x.getPrice()).reduce(BigDecimal.ZERO, BigDecimal::add);
                         // 判断本次是否是全退 执行退款
-                        String refundResult = null;
-                        if (refundList.size() == orderCouponMiddleList.size()) {
-                            // 全退
-                            refundResult = alienStoreFeign.processRefund(order.getOrderNo(), totalRefundAmount.toString(), "过期自动退","");
+                        String refundResult = "";
+                        String partialRefundCode = "";
+                        if (refundList.size() != orderCouponMiddleList.size()) {
+                            partialRefundCode = UniqueRandomNumGenerator.generateUniqueCode(12);
+                        }
+                        String payMethod = order.getPayMethod();
+                        String payType = "";
+                        Map<String, String> params = new HashMap<>();
+                        if("支付宝支付".equals(payMethod)) {
+                            payType = PaymentEnum.ALIPAY.getType();
+                            params.put("payType", payType);
+                            params.put("outTradeNo", order.getOrderNo());
+                            params.put("refundAmount", totalRefundAmount.toString());
+                            params.put("refundReason", "过期自动退");
+                            params.put("partialRefundCode",partialRefundCode);
                         } else {
-                            // TODO 本次部分退款
-                            String partialRefundCode = UniqueRandomNumGenerator.generateUniqueCode(12);
-                            refundResult = alienStoreFeign.processRefund(order.getOrderNo(), totalRefundAmount.toString(), "过期自动退",partialRefundCode);
+                            payType = PaymentEnum.WECHAT_PAY.getType();
+                            params.put("payType", payType);
+                            params.put("outTradeNo", order.getOrderNo());
+                            params.put("reason", "过期自动退");
+                            params.put("refundAmount", String.valueOf(totalRefundAmount.multiply(new BigDecimal(100)).longValue()));
+                            params.put("totalAmount", String.valueOf(new BigDecimal(order.getFinalPrice()).multiply(new BigDecimal(100)).longValue()));
+                        }
+                        // 调用支付策略
+                        try {
+                            refundResult = alienStoreFeign.refunds(params).getData().toString();
+                        } catch (Exception e) {
+                            log.error(String.format("LifeUserOrderJob requestRefund error, payType: %s, params: %s, error: %s", payType, params, e.getMessage()));
                         }
+
+
                         if ("调用成功".equals(refundResult)) {
                             //  1.更新中间表状态
                             int updateNum = orderCouponMiddleMapper.update(null, new LambdaUpdateWrapper<OrderCouponMiddle>()

+ 1 - 0
alien-lawyer/src/main/java/shop/alien/lawyer/controller/OrderReviewController.java

@@ -275,5 +275,6 @@ public class OrderReviewController {
         }
         return orderReviewService.getOrderEvaluation(orderId);
     }
+
 }
 

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

@@ -151,7 +151,7 @@ public class OrderReviewServiceImpl extends ServiceImpl<OrderReviewMapper, Order
         try {
             // 计算平均评分(1-5星)
             Double averageRating = orderReviewMapper.getAverageRatingByLawyerUserId(lawyerUserId);
-            
+
             Double serviceScore;
             if (averageRating != null) {
                 // 转换为0-5分,保留一位小数(直接截取,不四舍五入)
@@ -389,7 +389,7 @@ public class OrderReviewServiceImpl extends ServiceImpl<OrderReviewMapper, Order
                         log.warn("管理员删除评价时,更新订单申诉状态失败,评价ID={}, 订单ID={}", reviewId, review.getOrderId());
                     }
                 } catch (Exception e) {
-                    log.error("管理员删除评价时,更新订单申诉状态异常,评价ID={}, 订单ID={}, 错误信息={}", 
+                    log.error("管理员删除评价时,更新订单申诉状态异常,评价ID={}, 订单ID={}, 错误信息={}",
                             reviewId, review.getOrderId(), e.getMessage(), e);
                     // 更新订单申诉状态失败不影响删除评价的操作
                 }

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

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

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

@@ -10,6 +10,7 @@ import shop.alien.entity.result.R;
 import shop.alien.entity.store.EssentialHolidayComparison;
 import shop.alien.entity.store.LifeCoupon;
 import shop.alien.entity.store.vo.LifeCouponStatusVo;
+import shop.alien.entity.store.vo.LifeCouponVo;
 import shop.alien.store.service.LifeCouponService;
 
 import java.util.Map;
@@ -78,6 +79,22 @@ public class LifeCouponController {
         return R.data(lifeCouponService.getOne(objectLambdaQueryWrapper));
     }
 
+    @ApiOperation("代金劵详情")
+    @GetMapping("/getNewCouponDetail")
+    private R<LifeCouponVo> getNewCouponDetail(@RequestParam("id") String id) {
+        log.info("LifeCouponController.getNewCouponDetail?id={}", id);
+        try {
+            LifeCouponVo lifeCouponVo = lifeCouponService.getNewCouponDetail(id);
+            if (lifeCouponVo == null) {
+                return R.fail("代金券不存在或已删除");
+            }
+            return R.data(lifeCouponVo);
+        } catch (Exception e) {
+            log.error("LifeCouponController.getNewCouponDetail ERROR Msg={}", e.getMessage(), e);
+            return R.fail("查询失败");
+        }
+    }
+
     @ApiOperation("旧 核销订单")
     @ApiImplicitParams({@ApiImplicitParam(name = "storeId", value = "门店id", dataType = "Integer", paramType = "query", required = true), @ApiImplicitParam(name = "quanCode", value = "券码", dataType = "Integer", paramType = "query", required = true)})
     @GetMapping("/verify")

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

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

+ 389 - 18
alien-store/src/main/java/shop/alien/store/controller/StoreInfoController.java

@@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
 import io.swagger.annotations.*;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartRequest;
@@ -15,17 +16,13 @@ import shop.alien.entity.store.*;
 import shop.alien.entity.store.dto.StoreInfoDto;
 import shop.alien.entity.store.vo.*;
 import shop.alien.entity.storePlatform.StoreLicenseHistory;
-import shop.alien.mapper.StoreCommentSummaryInterestMapper;
-import shop.alien.mapper.TagsMainMapper;
-import shop.alien.mapper.WebAuditMapper;
+import shop.alien.mapper.*;
 import shop.alien.mapper.storePlantform.StoreLicenseHistoryMapper;
 import shop.alien.store.config.BaseRedisService;
 import shop.alien.store.service.StoreInfoService;
 
 import java.io.IOException;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 
 /**
  * 二期-门店信息Controller
@@ -55,6 +52,10 @@ public class StoreInfoController {
     /** 商户证照历史记录数据访问对象 */
     private final StoreLicenseHistoryMapper licenseHistoryMapper;
 
+    private final StoreImgMapper storeImgMapper;
+
+    private final StoreCommentMapper storeCommentMapper;
+
     @ApiOperation("获取所有门店")
     @ApiOperationSupport(order = 1)
     @GetMapping("/getAll")
@@ -392,6 +393,24 @@ public class StoreInfoController {
         return R.data(storeDetail);
     }
 
+    @ApiOperation(value = "获取门店代金券明细")
+    @ApiOperationSupport(order = 7)
+    @GetMapping("/getStoreCouponList")
+    @ResponseBody
+    public R getStoreCouponList(@RequestParam("storeId") String storeId) {
+        log.info("StoreInfoController.getStoreCouponList?storeId={}", storeId);
+        CouponAndEventVo couponAndEventVo = new CouponAndEventVo();
+        List<LifeCouponVo> lifeCouponVos = storeInfoService.getStoreCouponList(storeId);
+
+        List<StoreImg> storeImgs = storeInfoService.getBannerUrl(storeId);
+        couponAndEventVo.setCouponList(lifeCouponVos);
+        if (CollectionUtils.isNotEmpty(storeImgs)) {
+            couponAndEventVo.setMarketingList(storeImgs);
+        }
+        couponAndEventVo.setMarketingList(storeImgs);
+        return R.data(couponAndEventVo);
+    }
+
     @ApiOperation(value = "web端审批店铺")
     @ApiOperationSupport(order = 6)
     @GetMapping("/approveStoreInfo")
@@ -402,6 +421,28 @@ public class StoreInfoController {
         return R.success("审批完成");
     }
 
+    @ApiOperation(value = "中台web端获取店铺明细详情")
+    @ApiOperationSupport(order = 21)
+    @GetMapping("/getNewStoreDetail")
+    @ResponseBody
+    public R getNewStoreDetail(@RequestParam("id") String id) {
+        log.info("StoreInfoController.getStoreDetail?id={}", id);
+        StoreInfoVo storeDetail = storeInfoService.getNewStoreDetail(id);
+        return R.data(storeDetail);
+    }
+
+    @ApiOperation("新中台web端修改门店")
+    @ApiOperationSupport(order = 22)
+    @PostMapping("/editNewStoreInfo")
+    public R editNewStoreInfo(@RequestBody StoreInfoDto storeInfoDto) {
+        log.info("StoreInfoController.editStoreInfo?storeInfoDto={}", storeInfoDto);
+        StoreInfoVo storeInfoVo = storeInfoService.editNewStoreInfo(storeInfoDto);
+        if (storeInfoVo != null) {
+            return R.data(storeInfoVo);
+        }
+        return R.fail("失败");
+    }
+
     @ApiOperation(value = "web端导出店铺信息")
     @ApiOperationSupport(order = 6)
     @GetMapping("/exportExcel")
@@ -728,13 +769,53 @@ public class StoreInfoController {
 
     @ApiOperation(value = "AI服务-门店评价标签")
     @GetMapping("/getStoreEvaluateTags")
-    @ApiImplicitParams({@ApiImplicitParam(name = "storeId", value = "门店id", dataType = "int", paramType = "query")})
-    public R<List<TagsMainVo>> getStoreEvaluateTags(int storeId) {
-        log.info("StoreInfoController.getStoreEvaluateTags?storeId={}", storeId);
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店id", dataType = "int", paramType = "query", required = true)
+    })
+    public R<Map<String,Object>> getStoreEvaluateTags(@RequestParam("storeId") int storeId) {
+        log.info("StoreInfoController.getStoreEvaluateTags?storeId={}, tagType={}", storeId);
+        Map<String,Object> map = new HashMap<>();
         List<TagsMainVo> voList = tagsMainMapper.getStoreEvaluateTags(storeId);
-        return R.data(voList);
+        if(voList !=null && voList.size()>0){
+            StoreCommentVo storeComment = storeCommentMapper.getCommentOneInfo(storeId);
+            if(storeComment!=null){
+                if(storeComment.getImgId()!=null){
+                    List<String> imgIds = Arrays.asList(storeComment.getImgId().split(","));
+                    List<StoreImg> storeImgs = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().in(StoreImg::getId, imgIds));
+                    storeComment.setImgList(storeImgs);
+                }
+                LambdaQueryWrapper<StoreComment> lambdaQueryWrapper1 = new LambdaQueryWrapper<>();
+                lambdaQueryWrapper1.eq(StoreComment :: getStoreId, storeId);
+                lambdaQueryWrapper1.eq(StoreComment :: getBusinessType, 5);
+                lambdaQueryWrapper1.eq(StoreComment :: getDeleteFlag, 0);
+                Integer commitCount = storeCommentMapper.selectCount(lambdaQueryWrapper1);
+                map.put("commitCount",commitCount);
+            }
+            map.put("tag",voList);
+            map.put("evaluate",storeComment);
+        }
+        return R.data(map);
     }
 
+    @ApiOperation(value = "AI服务-门店评价标签下评价")
+    @GetMapping("/getStoreEvaluateTagsList")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "mainId", value = "标签id", dataType = "int", paramType = "query", required = true)
+    })
+    public R<List<StoreCommentVo>> getStoreEvaluateTagsList(@RequestParam("mainId") int mainId) {
+        log.info("StoreInfoController.getStoreEvaluateTagsList?mainId={}", mainId);
+        List<StoreCommentVo> list = tagsMainMapper.getstoreCommentList(mainId);
+        if(list!=null && list.size()>0){
+            for(int i = 0; i<list.size(); i++){
+                if(list.get(i).getImgId()!=null){
+                    List<String> imgIds = Arrays.asList(list.get(i).getImgId().split(","));
+                    List<StoreImg> storeImgs = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().in(StoreImg::getId, imgIds));
+                    list.get(i).setImgList(storeImgs);
+                }
+            }
+        }
+        return R.data(list);
+    }
     @ApiOperation(value = "AI服务-门店趣味信息")
     @GetMapping("/getStoreInterestInfo")
     @ApiImplicitParams({
@@ -760,25 +841,315 @@ public class StoreInfoController {
         }
     }
 
+    @ApiOperation(value = "更多推荐(用户端)")
+    @GetMapping("/getMoreRecommendedStores")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "lon", value = "经度", dataType = "double", paramType = "query", required = true),
+            @ApiImplicitParam(name = "lat", value = "纬度", dataType = "double", paramType = "query", required = true),
+            @ApiImplicitParam(name = "businessSection", value = "经营板块", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "businessTypes", value = "经营分类", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "businessClassify", value = "分类", dataType = "String", paramType = "query")
+    })
+    public R<List<StoreInfoVo>> getMoreRecommendedStores(Double lon , Double lat, String businessSection, String businessTypes, String businessClassify) {
+        log.info("StoreInfoController.getMoreRecommendedStores?lon={},lat={},businessSection={},businessTypes={},businessClassify={}", lon,lat, businessSection, businessTypes, businessClassify);
+        return R.data(storeInfoService.getMoreRecommendedStores(lon,lat, businessSection, businessTypes, businessClassify));
+
+    }
+
     @ApiOperation(value = "AI服务-门头识别")
     @ApiOperationSupport(order = 16)
     @ApiImplicitParams({
-            @ApiImplicitParam(name = "storeUserId", value = "门店用户id", dataType = "String", paramType = "query", required = true),
-            @ApiImplicitParam(name = "imageUrl", value = "图片URL", dataType = "String", paramType = "query", required = true)
+            @ApiImplicitParam(name = "imageUrl", value = "图片URL", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "merchantName", value = "门店名称", dataType = "String", paramType = "query", required = true)
     })
     @GetMapping("/getStoreOcrData")
-    public R<Map<String, Object>> getStoreOcrData(@RequestParam("storeUserId") String storeUserId,
-                                                  @RequestParam("imageUrl") String imageUrl) {
-        log.info("StoreInfoController.getStoreOcrData?storeId={},imageUrl={}", storeUserId, imageUrl);
-        if (storeUserId == null || storeUserId.trim().isEmpty() || imageUrl == null || imageUrl.trim().isEmpty()) {
-            return R.fail("门店ID与图片URL不能为空");
+    public R<Map<String, Object>> getStoreOcrData(@RequestParam("imageUrl") String imageUrl,
+                                                  @RequestParam("merchantName") String merchantName) {
+        log.info("StoreInfoController.getStoreOcrData?imageUrl={},merchantName{}", imageUrl, merchantName);
+        if (merchantName == null || merchantName.trim().isEmpty() || imageUrl == null || imageUrl.trim().isEmpty()) {
+            return R.fail("门店名称与图片URL不能为空");
         }
         Map<String, Object> ocrData = null;
         try {
-            ocrData = storeInfoService.getStoreOcrData(storeUserId, imageUrl);
+            ocrData = storeInfoService.getStoreOcrData(imageUrl, merchantName);
         } catch (Exception e) {
             return R.fail("未查询到OCR识别数据");
         }
         return R.data(ocrData);
     }
+
+    @ApiOperation(value = "获取店铺详情(用户端)")
+    @ApiOperationSupport(order = 17)
+    @GetMapping("/getClientStoreDetail")
+    @ResponseBody
+    public R getClientStoreDetail(@RequestParam("id") String id,
+                                  @RequestParam(value = "userId", required = false) String userId,
+                                  @RequestParam(value = "jingdu", required = false) String jingdu,
+                                  @RequestParam(value = "weidu", required = false) String weidu) {
+        log.info("StoreInfoController.getClientStoreDetail?id={},userId={},jingdu={},weidu={}", id, jingdu, weidu);
+        StoreInfoVo storeDetail = storeInfoService.getClientStoreDetail(id, userId, jingdu, weidu);
+        return R.data(storeDetail);
+    }
+    /**
+     * 查询四种类型店铺(酒吧、ktv、洗浴汗蒸、按摩足浴)并按距离筛选
+     *
+     * @param lon         经度
+     * @param lat         纬度
+     * @param distance    距离范围(单位:公里)
+     * @param sortType    排序模式(1:智能排序,2:好评优先,3:距离优先)
+     * @param businessType 店铺类型(KTV=3、洗浴汗蒸=4、按摩足浴=5,酒吧需要查询字典表),可选,如果指定则只查询该类型
+     * @param pageNum     页码
+     * @param pageSize    页容
+     * @return R<IPage<StoreInfoVo>> 分页的门店信息列表
+     */
+    @ApiOperation("查询四种类型店铺(酒吧、ktv、洗浴汗蒸、按摩足浴)并按距离筛选")
+    @ApiOperationSupport(order = 16)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "lon", value = "经度", dataType = "Double", paramType = "query", required = true),
+            @ApiImplicitParam(name = "lat", value = "纬度", dataType = "Double", paramType = "query", required = true),
+            @ApiImplicitParam(name = "distance", value = "距离范围(单位:公里)", dataType = "Double", paramType = "query"),
+            @ApiImplicitParam(name = "sortType", value = "排序模式(1:智能排序,2:好评优先,3:距离优先)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "businessType", value = "店铺类型(KTV=3、洗浴汗蒸=4、按摩足浴=5,丽人美发=6,运动健身=7,酒吧=11)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "categoryId", value = "字典表id,根据此id查询经营板块、经营种类、分类并匹配店铺", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "pageNum", value = "页码", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "pageSize", value = "页容", dataType = "int", paramType = "query", required = true)
+    })
+    @GetMapping("/getSpecialTypeStoresByDistance")
+    public R<IPage<StoreInfoVo>> getSpecialTypeStoresByDistance(
+            @RequestParam("lon") Double lon,
+            @RequestParam("lat") Double lat,
+            @RequestParam(value = "distance", required = false) Double distance,
+            @RequestParam(value = "sortType", required = false, defaultValue = "1") Integer sortType,
+            @RequestParam(value = "businessType", required = false) Integer businessType,
+            @RequestParam(value = "categoryId", required = false) Integer categoryId,
+            @RequestParam(defaultValue = "1") int pageNum,
+            @RequestParam(defaultValue = "10") int pageSize) {
+        log.info("StoreInfoController.getSpecialTypeStoresByDistance?lon={},lat={},distance={},sortType={},businessType={},categoryId={},pageNum={},pageSize={}",
+                lon, lat, distance, sortType, businessType, categoryId, pageNum, pageSize);
+
+        try {
+            // 参数校验:经度范围 [-180, 180],纬度范围 [-90, 90]
+            if (lon == null || lat == null) {
+                return R.fail("经纬度参数不能为空");
+            }
+
+            // 智能检测:如果参数可能传反了(lat超出范围但lon在范围内),给出提示
+            if (Math.abs(lat) > 90 && Math.abs(lon) <= 90) {
+                return R.fail(String.format("参数可能传反了!当前值: lon=%s, lat=%s。经度范围: [-180, 180],纬度范围: [-90, 90]。请检查参数顺序。", lon, lat));
+            }
+
+            if (lon < -180 || lon > 180) {
+                return R.fail("经度参数超出有效范围 [-180, 180],当前值: " + lon);
+            }
+            if (lat < -90 || lat > 90) {
+                return R.fail("纬度参数超出有效范围 [-90, 90],当前值: " + lat);
+            }
+
+            // 调用服务层获取筛选结果
+            IPage<StoreInfoVo> result = storeInfoService.getSpecialTypeStoresByDistance(lon, lat, distance, sortType, businessType, categoryId, pageNum, pageSize);
+
+            // 记录响应日志
+            log.info("四种类型店铺距离筛选响应 - 总记录数: {}, 当前页: {}, 页大小: {}",
+                    result.getTotal(), result.getCurrent(), result.getSize());
+
+            return R.data(result);
+        } catch (IllegalArgumentException e) {
+            log.warn("四种类型店铺距离筛选参数错误: {}", e.getMessage());
+            return R.fail(e.getMessage());
+        } catch (Exception e) {
+            log.error("四种类型店铺距离筛选异常", e);
+            return R.fail("筛选失败,请稍后重试");
+        }
+    }
+
+
+
+    /**
+     * 获取休闲娱乐分类数据(主分类和子分类)
+     * 根据图片需求,返回层级结构的分类数据
+     *
+     * @return R<List<StoreDictionaryVo>> 分类列表,包含主分类和子分类
+     */
+    @ApiOperation("获取休闲娱乐分类数据(主分类和子分类)")
+    @ApiOperationSupport(order = 18)
+    @GetMapping("/getLeisureEntertainmentCategories")
+    public R<List<StoreDictionaryVo>> getLeisureEntertainmentCategories() {
+        log.info("StoreInfoController.getLeisureEntertainmentCategories");
+        try {
+            List<StoreDictionaryVo> result = storeInfoService.getLeisureEntertainmentCategories();
+            return R.data(result);
+        } catch (Exception e) {
+            log.error("获取休闲娱乐分类数据异常", e);
+            return R.fail("获取分类数据失败,请稍后重试");
+        }
+    }
+
+    /**
+     * 获取休闲娱乐分类数据(主分类和子分类)
+     * 根据图片需求,返回层级结构的分类数据
+     *getAllBusinessSection
+     * @return R<List<StoreDictionary>> 分类列表,包含主分类和子分类
+     */
+
+
+    @ApiOperation("获取所有经营板块以及子类")
+    @ApiOperationSupport(order = 18)
+    @GetMapping("/getAllBusinessSection")
+    public R<List<StoreDictionary>> queryBusinessSectionTree(
+            @ApiParam(value = "一级分类dictId,可选,如果传入则只返回该一级分类下的二级和三级分类", required = false)
+            @RequestParam(required = false) String businessSection) {
+        log.info("platformBusinessSection.queryBusinessSectionTree, businessSection={}", businessSection);
+
+        try {
+            List<StoreDictionary> result = storeInfoService.getAllBusinessSection(businessSection);
+            return R.data(result);
+        }catch (Exception e){
+            log.error("获取休闲娱乐分类数据异常", e);
+            return R.fail("获取分类数据失败,请稍后重试");
+        }
+
+    }
+
+
+
+    /**
+     * 你可能还喜欢(推荐店铺)
+     * 根据一级分类、二级分类、三级分类进行店铺筛选
+     * 筛选顺序:先三级,然后二级,最后一级
+     *
+     * @param businessSection 一级分类(经营板块)
+     * @param businessTypes 二级分类(经营种类)
+     * @param businessClassify 三级分类(分类)
+     * @param lon 经度
+     * @param lat 纬度
+     * @return R<List<StoreInfoVo>> 店铺信息列表
+     */
+    @ApiOperation("你可能还喜欢(推荐店铺)")
+    @ApiOperationSupport(order = 19)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "businessSection", value = "一级分类(经营板块)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "businessTypes", value = "二级分类(经营种类)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "businessClassify", value = "三级分类(分类)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "lon", value = "经度", dataType = "Double", paramType = "query"),
+            @ApiImplicitParam(name = "lat", value = "纬度", dataType = "Double", paramType = "query")
+    })
+    @GetMapping("/getRecommendedStores")
+    public R<List<StoreInfoVo>> getRecommendedStores(
+            @RequestParam(value = "businessSection", required = true) String businessSection,
+            @RequestParam(value = "businessTypes", required = false) String businessTypes,
+            @RequestParam(value = "businessClassify", required = false) String businessClassify,
+            @RequestParam(value = "lon", required = false) Double lon,
+            @RequestParam(value = "lat", required = false) Double lat) {
+        log.info("StoreInfoController.getRecommendedStores?businessSection={},businessTypes={},businessClassify={},lon={},lat={}",
+                businessSection, businessTypes, businessClassify, lon, lat);
+        try {
+            List<StoreInfoVo> result = storeInfoService.getRecommendedStores(businessSection, businessTypes, businessClassify, lon, lat);
+            return R.data(result);
+        } catch (Exception e) {
+            log.error("获取推荐店铺异常", e);
+            return R.fail("获取推荐店铺失败,请稍后重试");
+        }
+    }
+
+
+
+    @ApiOperation(value = "获取banner图详情")
+    @ApiOperationSupport(order = 7)
+    @GetMapping("/getBannerUrlInfo")
+    @ResponseBody
+    public R getBannerUrlInfo(@RequestParam("storeId") String storeId, @RequestParam("businessId") Integer businessId) {
+        log.info("StoreInfoController.getBannerUrlInfo?storeId={},businessId{}", storeId, businessId);
+
+        try {
+
+            return R.data(storeInfoService.getBannerUrlInfo(storeId, businessId));
+
+        } catch (Exception e) {
+
+            log.error("StoreInfoController.getBannerUrlInfo ERROR Msg={}", e.getMessage(), e);
+
+            return R.fail("查询失败");
+        }
+
+    }
+
+
+    /**
+     * 查询两种类型店铺(丽人美发、运动健身)并按距离筛选
+     *
+     * @param lon         经度
+     * @param lat         纬度
+     * @param distance    距离范围(单位:公里)
+     * @param sortType    排序模式(1:智能排序,2:好评优先,3:距离优先)
+     * @param businessType 店铺类型(KTV=3、洗浴汗蒸=4、按摩足浴=5,丽人美发=6,运动健身=7),可选,如果指定则只查询该类型
+     * @param pageNum     页码
+     * @param pageSize    页容
+     * @return R<IPage<StoreInfoVo>> 分页的门店信息列表
+     */
+    @ApiOperation("查询四种类型店铺(丽人美发、运动健身)并按距离筛选")
+    @ApiOperationSupport(order = 16)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "lon", value = "经度", dataType = "Double", paramType = "query", required = true),
+            @ApiImplicitParam(name = "lat", value = "纬度", dataType = "Double", paramType = "query", required = true),
+            @ApiImplicitParam(name = "distance", value = "距离范围(单位:公里)", dataType = "Double", paramType = "query"),
+            @ApiImplicitParam(name = "sortType", value = "排序模式(1:智能排序,2:好评优先,3:距离优先)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "businessType", value = "店铺类型(KTV=3、洗浴汗蒸=4、按摩足浴=5,丽人美发=6,运动健身=7,酒吧=11)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "categoryId", value = "字典表id,根据此id查询经营板块、经营种类、分类并匹配店铺", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "pageNum", value = "页码", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "pageSize", value = "页容", dataType = "int", paramType = "query", required = true)
+    })
+    @GetMapping("/getLifeServicesByDistance")
+    public R<IPage<StoreInfoVo>> getLifeServicesByDistance(
+            @RequestParam("lon") Double lon,
+            @RequestParam("lat") Double lat,
+            @RequestParam(value = "distance", required = false) Double distance,
+            @RequestParam(value = "sortType", required = false, defaultValue = "1") Integer sortType,
+            @RequestParam(value = "businessType", required = false) Integer businessType,
+            @RequestParam(value = "categoryId", required = false) Integer categoryId,
+            @RequestParam(defaultValue = "1") int pageNum,
+            @RequestParam(defaultValue = "10") int pageSize) {
+        log.info("StoreInfoController.getLifeServicesByDistance?lon={},lat={},distance={},sortType={},businessType={},categoryId={},pageNum={},pageSize={}",
+                lon, lat, distance, sortType, businessType, categoryId, pageNum, pageSize);
+
+        try {
+            // 参数校验:经度范围 [-180, 180],纬度范围 [-90, 90]
+            if (lon == null || lat == null) {
+                return R.fail("经纬度参数不能为空");
+            }
+
+            // 智能检测:如果参数可能传反了(lat超出范围但lon在范围内),给出提示
+            if (Math.abs(lat) > 90 && Math.abs(lon) <= 90) {
+                return R.fail(String.format("参数可能传反了!当前值: lon=%s, lat=%s。经度范围: [-180, 180],纬度范围: [-90, 90]。请检查参数顺序。", lon, lat));
+            }
+
+            if (lon < -180 || lon > 180) {
+                return R.fail("经度参数超出有效范围 [-180, 180],当前值: " + lon);
+            }
+            if (lat < -90 || lat > 90) {
+                return R.fail("纬度参数超出有效范围 [-90, 90],当前值: " + lat);
+            }
+
+            // 调用服务层获取筛选结果
+            IPage<StoreInfoVo> result = storeInfoService.getLifeServicesByDistance(lon, lat, distance, sortType, businessType, categoryId, pageNum, pageSize);
+
+            // 记录响应日志
+            log.info("四种类型店铺距离筛选响应 - 总记录数: {}, 当前页: {}, 页大小: {}",
+                    result.getTotal(), result.getCurrent(), result.getSize());
+
+            return R.data(result);
+        } catch (IllegalArgumentException e) {
+            log.warn("四种类型店铺距离筛选参数错误: {}", e.getMessage());
+            return R.fail(e.getMessage());
+        } catch (Exception e) {
+            log.error("四种类型店铺距离筛选异常", e);
+            return R.fail("筛选失败,请稍后重试");
+        }
+    }
+
+
+
+
+
+
+
 }

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

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

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

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

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

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

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

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

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

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

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

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

+ 7 - 0
alien-store/src/main/java/shop/alien/store/service/LifeCouponService.java

@@ -56,4 +56,11 @@ public interface LifeCouponService extends IService<LifeCoupon> {
      * @return
      */
     R<String> orderVerify(String orderCode);
+
+    /**
+     * 获取代金券详情(包含商铺信息)
+     * @param id 代金券ID
+     * @return LifeCouponVo
+     */
+    shop.alien.entity.store.vo.LifeCouponVo getNewCouponDetail(String id);
 }

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

@@ -250,7 +250,7 @@ public class LifeUserDynamicsService extends ServiceImpl<LifeUserDynamicsMapper,
             // 查询拉黑的对象
             LambdaQueryWrapper<LifeBlacklist> myLifeBlacklistWrapper = new LambdaQueryWrapper<>();
 
-            if (null != myLifeUser.getId()) {
+            if (null != myLifeUser && null != myLifeUser.getId()) {
                 myLifeBlacklistWrapper.eq(LifeBlacklist::getBlockerType, "2");
                 myLifeBlacklistWrapper.eq(LifeBlacklist::getBlockerId, myLifeUser.getId());
             } else {

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

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

+ 115 - 6
alien-store/src/main/java/shop/alien/store/service/StoreInfoService.java

@@ -4,10 +4,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.IService;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.multipart.MultipartRequest;
-import shop.alien.entity.store.StoreBusinessInfo;
-import shop.alien.entity.store.StoreImg;
-import shop.alien.entity.store.StoreInfo;
-import shop.alien.entity.store.StoreInfoDraft;
+import shop.alien.entity.store.*;
 import shop.alien.entity.store.dto.StoreInfoDto;
 import shop.alien.entity.store.vo.*;
 
@@ -296,11 +293,123 @@ public interface StoreInfoService extends IService<StoreInfo> {
     /**
      * 根据门店及图片地址查询最新OCR识别数据
      *
-     * @param storeUserId  门店ID
      * @param imageUrl 图片URL
      * @return OCR识别记录
      */
-    Map<String, Object> getStoreOcrData(String storeUserId, String imageUrl);
+    Map<String, Object> getStoreOcrData(String imageUrl, String merchantName);
 
     void aiApproveStoreInfo(AiApproveStoreInfo aiApproveStoreInfo);
+
+
+    /**
+     * 查询四种类型店铺(酒吧、ktv、洗浴汗蒸、按摩足浴)并按距离筛选
+     *
+     * @param lon         经度
+     * @param lat         纬度
+     * @param distance    距离范围(单位:公里)
+     * @param sortType    排序模式(1:智能排序,2:好评优先,3:距离优先)
+     * @param businessType 店铺类型(KTV=3、洗浴汗蒸=4、按摩足浴=5,酒吧需要查询字典表),可选
+     * @param categoryId 字典表id,根据此id查询经营板块、经营种类、分类并匹配店铺,可选
+     * @param pageNum     页码
+     * @param pageSize    页容
+     * @return IPage<StoreInfoVo> 分页的门店信息列表
+     */
+    IPage<StoreInfoVo> getSpecialTypeStoresByDistance(Double lon, Double lat, Double distance, Integer sortType, Integer businessType, Integer categoryId, int pageNum, int pageSize);
+
+
+
+    /**
+     * 获取休闲娱乐分类数据(主分类和子分类)
+     * 返回层级结构的分类数据,包含主分类和对应的子分类
+     *
+     * @return List<StoreDictionaryVo> 分类列表,包含主分类和子分类
+     */
+    List<StoreDictionaryVo> getLeisureEntertainmentCategories();
+
+
+    List<StoreDictionary> getAllBusinessSection();
+
+    /**
+     * 根据一级分类获取该分类下的二级和三级分类
+     *
+     * @param businessSection 一级分类的dictId(可选,如果为空则返回所有分类)
+     * @return List<StoreDictionary> 分类树形结构
+     */
+    List<StoreDictionary> getAllBusinessSection(String businessSection);
+
+
+    /**
+     * 你可能还喜欢(推荐店铺)
+     * 根据一级分类、二级分类、三级分类进行店铺筛选
+     * 筛选顺序:先三级,然后二级,最后一级
+     *
+     * @param businessSection 一级分类(经营板块)
+     * @param businessTypes 二级分类(经营种类)
+     * @param businessClassify 三级分类(分类)
+     * @param lon 经度
+     * @param lat 纬度
+     * @return List<StoreInfoVo> 店铺信息列表
+     */
+    List<StoreInfoVo> getRecommendedStores(String businessSection, String businessTypes, String businessClassify, Double lon, Double lat);
+
+
+
+
+
+    /**
+     * 查询两种类型店铺(丽人美发、运动健身)并按距离筛选
+     *
+     * @param lon         经度
+     * @param lat         纬度
+     * @param distance    距离范围(单位:公里)
+     * @param sortType    排序模式(1:智能排序,2:好评优先,3:距离优先)
+     * @param businessType 店铺类型(KTV=3、洗浴汗蒸=4、按摩足浴=5,丽人美发=6,运动健身=7),可选,如果指定则只查询该类型
+     * @param pageNum     页码
+     * @param pageSize    页容
+     * @return R<IPage<StoreInfoVo>> 分页的门店信息列表
+     */
+    IPage<StoreInfoVo> getLifeServicesByDistance(Double lon, Double lat, Double distance, Integer sortType, Integer businessType, Integer categoryId, int pageNum, int pageSize);
+
+    /**
+     * 获取活动banner图
+     */
+    List<StoreImg> getBannerUrl (String storeId);
+    /**
+     * 获取活动详情banner图
+     */
+
+    List<StoreImg> getBannerUrlInfo(String storeId, Integer businessId);
+
+    /**
+     * web-分页查询店铺信息
+     *
+
+     * @return IPage<StoreInfoVo>
+     */
+    List<StoreInfoVo> getMoreRecommendedStores(Double lon , Double lat, String businessSection, String businessTypes, String businessClassify);
+
+
+    /**
+     * web端查询门店明细
+     */
+    StoreInfoVo getClientStoreDetail(String storeId, String userId, String jingdu, String weidu);
+
+    /**
+     * 获取门店代金券
+     */
+    List<LifeCouponVo> getStoreCouponList(String storeId);
+
+    /**
+     * 中台web端查询门店明细
+     */
+    StoreInfoVo getNewStoreDetail(String storeId);
+
+    /**
+     * 新中台web端修改门店及门店用户
+     *
+     * @return ResponseEntity
+     */
+    StoreInfoVo editNewStoreInfo(StoreInfoDto storeInfoDto);
+
+
 }

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

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

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

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

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

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

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

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

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

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

+ 5 - 0
alien-store/src/main/java/shop/alien/store/service/impl/LifeCouponServiceImpl.java

@@ -689,4 +689,9 @@ public class LifeCouponServiceImpl extends ServiceImpl<LifeCouponMapper, LifeCou
         return R.success("效验通过");
     }
 
+    @Override
+    public shop.alien.entity.store.vo.LifeCouponVo getNewCouponDetail(String id) {
+        return lifeCouponMapper.getNewCouponDetail(id);
+    }
+
 }

+ 4 - 2
alien-store/src/main/java/shop/alien/store/service/impl/LifeDiscountCouponServiceImpl.java

@@ -347,8 +347,10 @@ public class LifeDiscountCouponServiceImpl extends ServiceImpl<LifeDiscountCoupo
         try {
             LocalDateTime now = LocalDateTime.now();
             //根据店铺id查询该店铺的优惠券,状态是开启领取的券
-            List<LifeDiscountCoupon> lifeDiscountCoupons = lifeDiscountCouponMapper.selectList(new LambdaQueryWrapper<LifeDiscountCoupon>().eq(LifeDiscountCoupon::getStoreId, storeId).eq(LifeDiscountCoupon::getGetStatus, "1") //还有库存
-                    .ge(LifeDiscountCoupon::getEndGetDate, LocalDate.now()).orderByDesc(LifeDiscountCoupon::getCreatedTime));
+            List<LifeDiscountCoupon> lifeDiscountCoupons = lifeDiscountCouponMapper.selectList(new LambdaQueryWrapper<LifeDiscountCoupon>()
+                    .eq(LifeDiscountCoupon::getStoreId, storeId).eq(LifeDiscountCoupon::getGetStatus, "1") //还有库存
+                    .ge(LifeDiscountCoupon::getEndGetDate, LocalDate.now())
+                    .orderByDesc(LifeDiscountCoupon::getAttentionCanReceived));
             //根据优惠券列表查询该优惠券是否领取过
             for (LifeDiscountCoupon lifeDiscountCoupon : lifeDiscountCoupons) {
                 LifeDiscountCouponVo lifeDiscountCouponVo = new LifeDiscountCouponVo();

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

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

+ 2042 - 13
alien-store/src/main/java/shop/alien/store/service/impl/StoreInfoServiceImpl.java

@@ -11,6 +11,7 @@ import com.baomidou.mybatisplus.core.toolkit.StringUtils;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.data.geo.Point;
@@ -71,9 +72,12 @@ import java.util.stream.Collectors;
  * @since 2024-12-05
  */
 @Service
+@Slf4j
 @RequiredArgsConstructor
 @Transactional
 public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo> implements StoreInfoService {
+    private static final int DEFAULT_DISTANCE_METER = 1000;
+
 
     private final String DEFAULT_PASSWORD = "123456";
 
@@ -145,6 +149,9 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
 
     private final RestTemplate restTemplate;
 
+    private final StoreImgService storeImgService;
+
+
     /** 商户证照历史记录数据访问对象 */
     private final StoreLicenseHistoryMapper licenseHistoryMapper;
 
@@ -210,9 +217,16 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
         List<StoreImg> albumUrlList = storeImgMapper.selectList(albumUrlQueryWrapper);
         storeMainInfoVo.setAlbumUrl(albumUrlList.stream().sorted(Comparator.comparing(StoreImg::getImgSort)).map(StoreImg::getImgUrl).collect(Collectors.toList()));
         //推荐菜
-        storeMainInfoVo.setRecommendUrl(storeMenuMapper.getStoreMenuList(id, 1).stream().sorted(Comparator.comparing(StoreMenuVo::getImgSort)).collect(Collectors.toList()));
+        storeMainInfoVo.setRecommendUrl(storeMenuMapper.getStoreMenuList(id, 1,1).stream().sorted(Comparator.comparing(StoreMenuVo::getImgSort)).collect(Collectors.toList()));
         //菜单
-        storeMainInfoVo.setMenuUrl(storeMenuMapper.getStoreMenuList(id, 0).stream().sorted(Comparator.comparing(StoreMenuVo::getImgSort)).collect(Collectors.toList()));
+        storeMainInfoVo.setMenuUrl(storeMenuMapper.getStoreMenuList(id, 0,1).stream().sorted(Comparator.comparing(StoreMenuVo::getImgSort)).collect(Collectors.toList()));
+
+        // todo 需要根据门店类型来判断
+        // 推荐酒水
+        storeMainInfoVo.setRecommendBeverageUrl(storeMenuMapper.getStoreMenuList(id, 1,1).stream().sorted(Comparator.comparing(StoreMenuVo::getImgSort)).collect(Collectors.toList()));
+        //酒单
+        storeMainInfoVo.setBeverageUrl(storeMenuMapper.getStoreMenuList(id, 0,1).stream().sorted(Comparator.comparing(StoreMenuVo::getImgSort)).collect(Collectors.toList()));
+
         //门店标签
         storeMainInfoVo.setStoreLabel(storeLabelMapper.selectOne(new LambdaQueryWrapper<StoreLabel>().eq(StoreLabel::getStoreId, id)));
         //营业时间
@@ -860,6 +874,16 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
                 storeImg.setImgUrl(storeInfoDto.getFoodLicenceUrl());
                 storeImgMapper.insert(storeImg);
         }
+        //存入店铺娱乐经营许可证图片
+        if (StringUtils.isNotEmpty(storeInfoDto.getEntertainmentLicenceUrl())) {
+            StoreImg storeImg = new StoreImg();
+            storeImg.setStoreId(storeInfo.getId());
+            storeImg.setImgType(26);
+            storeImg.setImgSort(0);
+            storeImg.setImgDescription("娱乐经营许可证审核通过图片");
+            storeImg.setImgUrl(storeInfoDto.getEntertainmentLicenceUrl());
+            storeImgMapper.insert(storeImg);
+        }
 
         //初始化标签数据
         LambdaQueryWrapper<TagStoreRelation> tagStoreRelationLambdaQueryWrapper = new LambdaQueryWrapper<>();
@@ -1490,6 +1514,14 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
         if (!storeDictionaries.isEmpty()) {
             result.setBusinessStatusStr(storeDictionaries.get(0).getDictDetail());
         }
+        // TODO 之后修改********** 正常OcrType由前端传存储ocr表要加新字段。传参要由前端传。
+        // 查询并设置各类证件OCR信息
+        result.setJyxkz(convertOcrResultToJson(storeUser.getId(), "BUSINESS_LICENSE", "营业执照", true));
+        result.setIdcardFace(convertOcrResultToJson(storeUser.getId(), "ID_CARD", "face", true));
+        result.setIdcardBack(convertOcrResultToJson(storeUser.getId(), "ID_CARD", "back", true));
+        result.setFoodLicence(convertOcrResultToJson(storeUser.getId(), "FOOD_MANAGE_LICENSE", null, true));
+        result.setEntertainmentLicence(convertOcrResultToJson(storeUser.getId(), "BUSINESS_LICENSE", "娱乐", false));
+
         return result;
     }
 
@@ -2615,15 +2647,8 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
     }
 
     @Override
-    public Map<String, Object> getStoreOcrData(String storeUserId, String imageUrl) {
+    public Map<String, Object> getStoreOcrData(String imageUrl, String merchantName) {
         Map<String, Object> map = new HashMap<>();
-        LambdaQueryWrapper<OcrImageUpload> queryWrapper = new LambdaQueryWrapper<>();
-        queryWrapper.eq(OcrImageUpload::getStoreUserId, storeUserId)
-                .eq(OcrImageUpload::getOcrType, OcrTypeEnum.BUSINESS_LICENSE.getCode());
-        OcrImageUpload ocrImageUploads = ocrImageUploadMapper.selectOne(queryWrapper);
-        if(ocrImageUploads== null){
-            throw new RuntimeException("未找到OCI识别数据!");
-        }
         String accessToken = aiAuthTokenUtil.getAccessToken();
 
         HttpHeaders aiHeaders = new HttpHeaders();
@@ -2633,9 +2658,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
         List<String> imageUrls = new ArrayList<>();
         imageUrls.add(imageUrl);
         jsonBody.put("image_urls", imageUrls);
-        String ocrResult = ocrImageUploads.getOcrResult();
-        JSONObject jsonObject = JSONObject.parseObject(ocrResult);
-        jsonBody.put("merchant_name", jsonObject.get("companyName"));
+        jsonBody.put("merchant_name", merchantName);
 
         HttpEntity<Map<String, Object>> request = new HttpEntity<>(jsonBody, aiHeaders);
         ResponseEntity<String> response = null;
@@ -2673,4 +2696,2010 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
         }
         return map;
     }
+
+    /**
+     * 获取活动banner图
+     */
+
+    public List<StoreImg> getBannerUrl(String storeId){
+        LambdaQueryWrapper<StoreImg> queryWrapper = new LambdaQueryWrapper<StoreImg>()
+                .eq(StoreImg::getStoreId, Integer.parseInt(storeId))
+                .eq(StoreImg::getImgType, 26)
+                .eq(StoreImg::getDeleteFlag, 0);
+        return storeImgMapper.selectList(queryWrapper);
+    }
+
+
+
+    /**
+     * 获取活动详情banner图
+     */
+
+    public List<StoreImg> getBannerUrlInfo(String storeId, Integer businessId){
+        LambdaQueryWrapper<StoreImg> queryWrapper = new LambdaQueryWrapper<StoreImg>()
+                .eq(StoreImg::getStoreId, Integer.parseInt(storeId))
+                .eq(StoreImg::getImgType, 27)
+                .eq(StoreImg::getDeleteFlag, 0)
+                .eq(StoreImg::getBusinessId, businessId);
+        return storeImgMapper.selectList(queryWrapper);
+    }
+
+
+
+
+    @Override
+    public IPage<StoreInfoVo> getLifeServicesByDistance(Double lon, Double lat, Double distance, Integer sortType, Integer businessType, Integer categoryId, 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("丽人美发","运动健身");
+            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());
+
+        // 距离优先模式:只显示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;
+    }
+
+    /**
+     * 查询OCR图片上传记录并转换为JSONObject
+     *
+     * @param storeUserId 店铺用户ID
+     * @param ocrType     OCR类型
+     * @param likeKeyword 模糊查询关键词,可为null
+     * @param includeImageUrl 是否包含imageUrl字段
+     * @return JSONObject,如果查询结果为空则返回空的JSONObject
+     */
+    private com.alibaba.fastjson2.JSONObject convertOcrResultToJson(Integer storeUserId, String ocrType, String likeKeyword, boolean includeImageUrl) {
+        LambdaQueryWrapper<OcrImageUpload> wrapper = new LambdaQueryWrapper<OcrImageUpload>()
+                .eq(OcrImageUpload::getStoreUserId, storeUserId)
+                .eq(OcrImageUpload::getOcrType, ocrType)
+                .orderByDesc(OcrImageUpload::getCreateTime)
+                .last("limit 1");
+        
+        if (StringUtils.isNotEmpty(likeKeyword)) {
+            wrapper.like(OcrImageUpload::getOcrResult, likeKeyword);
+        }
+        
+        OcrImageUpload ocrImageUpload = ocrImageUploadMapper.selectOne(wrapper);
+        
+        if (ocrImageUpload == null || StringUtils.isEmpty(ocrImageUpload.getOcrResult())) {
+            return new com.alibaba.fastjson2.JSONObject();
+        }
+        
+        com.alibaba.fastjson2.JSONObject jsonObject = com.alibaba.fastjson2.JSONObject.parseObject(ocrImageUpload.getOcrResult());
+        if (includeImageUrl && StringUtils.isNotEmpty(ocrImageUpload.getImageUrl())) {
+            jsonObject.put("imageUrl", ocrImageUpload.getImageUrl());
+        }
+        
+        return jsonObject;
+    }
+
+
+
+    /**
+     * 你可能还喜欢(推荐店铺)
+     * 根据一级分类、二级分类、三级分类进行店铺筛选
+     * 筛选顺序:先三级,然后二级,最后一级
+     *
+     * @param businessSection 一级分类(经营板块)
+     * @param businessTypes 二级分类(经营种类)
+     * @param businessClassify 三级分类(分类)
+     * @param lon 经度
+     * @param lat 纬度
+     * @return List<StoreInfoVo> 店铺信息列表
+     */
+    @Override
+    public List<StoreInfoVo> getRecommendedStores(String businessSection, String businessTypes, String businessClassify, Double lon, Double lat) {
+        log.info("StoreInfoServiceImpl.getRecommendedStores?businessSection={},businessTypes={},businessClassify={},lon={},lat={}",
+                businessSection, businessTypes, businessClassify, lon, lat);
+
+        QueryWrapper<StoreInfoVo> queryWrapper = new QueryWrapper<>();
+        // 基础条件:未删除、已启用
+        queryWrapper.eq("a.delete_flag", 0)
+                .eq("b.delete_flag", 0)
+                .ne("a.business_status", 99) // 过滤永久关门的店铺
+                .ne("a.store_status", 0); // 过滤禁用的店铺
+
+        // 按照三级->二级->一级的顺序筛选
+
+        // 1. 先按三级分类(business_classify)筛选
+        if (StringUtils.isNotEmpty(businessClassify)) {
+            // 解析businessClassify参数(格式:1,2,3)
+            String[] classifyArray = businessClassify.split(",");
+            // 使用FIND_IN_SET函数检查数据库字段是否包含参数中的任何一个值
+            queryWrapper.and(wrapper -> {
+                for (int i = 0; i < classifyArray.length; i++) {
+                    String classify = classifyArray[i].trim();
+                    if (StringUtils.isNotEmpty(classify)) {
+                        if (i == 0) {
+                            wrapper.apply("FIND_IN_SET({0}, a.business_classify) > 0", classify);
+                        } else {
+                            wrapper.or().apply("FIND_IN_SET({0}, a.business_classify) > 0", classify);
+                        }
+                    }
+                }
+            });
+        }
+
+        // 2. 然后按二级分类(business_types)筛选
+        if (StringUtils.isNotEmpty(businessTypes)) {
+            // business_types可能是逗号分隔的字符串,使用FIND_IN_SET处理
+            String[] typesArray = businessTypes.split(",");
+            queryWrapper.and(wrapper -> {
+                for (int i = 0; i < typesArray.length; i++) {
+                    String type = typesArray[i].trim();
+                    if (StringUtils.isNotEmpty(type)) {
+                        if (i == 0) {
+                            wrapper.apply("FIND_IN_SET({0}, a.business_types) > 0", type);
+                        } else {
+                            wrapper.or().apply("FIND_IN_SET({0}, a.business_types) > 0", type);
+                        }
+                    }
+                }
+            });
+        }
+
+        // 3. 最后按一级分类(business_section)筛选
+        if (StringUtils.isNotEmpty(businessSection)) {
+            try {
+                Integer sectionId = Integer.parseInt(businessSection.trim());
+                queryWrapper.eq("a.business_section", sectionId);
+            } catch (NumberFormatException e) {
+                log.warn("一级分类参数格式错误: {}", businessSection);
+            }
+        }
+
+        // 查询店铺列表
+        List<StoreInfoVo> storeInfoVoList;
+        if (lon != null && lat != null) {
+            // 如果提供了经纬度,使用带距离计算的查询方法
+            String position = lon + "," + lat;
+            queryWrapper.isNotNull("a.store_position")
+                    .ne("a.store_position", "");
+            // 按距离排序
+            queryWrapper.orderByAsc("dist");
+            storeInfoVoList = storeInfoMapper.getStoreInfoVoListNew(queryWrapper, position);
+        } else {
+            // 如果没有提供经纬度,使用普通查询方法
+            queryWrapper.orderByDesc("a.created_time");
+            storeInfoVoList = storeInfoMapper.getStoreInfoVoList(queryWrapper);
+        }
+
+        if (CollectionUtils.isEmpty(storeInfoVoList)) {
+            return Collections.emptyList();
+        }
+
+        // 处理店铺信息,设置评分等
+        // 提前查询所有需要的字典数据
+        List<StoreInfoVo> collect = storeInfoVoList.stream()
+                .filter(record -> StringUtils.isNotEmpty(record.getStoreType()))
+                .collect(Collectors.toList());
+        Set<String> allTypes = collect.stream()
+                .map(StoreInfoVo::getStoreType)
+                .flatMap(type -> Arrays.stream(type.split(",")))
+                .collect(Collectors.toSet());
+
+        List<StoreDictionary> storeDictionaries = storeDictionaryMapper.selectList(
+                new LambdaQueryWrapper<StoreDictionary>()
+                        .eq(StoreDictionary::getTypeName, "storeType")
+                        .isNull(StoreDictionary::getParentId)
+                        .in(!allTypes.isEmpty(), StoreDictionary::getDictId, allTypes));
+        Map<String, String> typeMap = storeDictionaries.stream()
+                .collect(Collectors.toMap(StoreDictionary::getDictId, StoreDictionary::getDictDetail));
+
+        // 计算平均分和评价
+        Map<String, List<Map<String, Object>>> avgScoreMap = new HashMap<>();
+        Map<Integer, List<StoreComment>> commentMap = new HashMap<>();
+
+        avgScoreMap = storeEvaluationMapper.allStoreAvgScore().stream()
+                .collect(Collectors.groupingBy(o -> o.get("store_id").toString()));
+        commentMap = storeCommentMapper.selectList(
+                        new QueryWrapper<StoreComment>()
+                                .eq("business_type", "5")
+                                .eq("delete_flag", 0))
+                .stream()
+                .collect(Collectors.groupingBy(StoreComment::getStoreId));
+
+        for (StoreInfoVo record : storeInfoVoList) {
+            // 处理类型
+            if (StringUtils.isNotEmpty(record.getStoreType())) {
+                String[] types = record.getStoreType().split(",");
+                List<String> typeDetails = Arrays.stream(types)
+                        .map(typeMap::get)
+                        .filter(Objects::nonNull)
+                        .collect(Collectors.toList());
+                record.setStoreTypeStr(String.join(",", typeDetails));
+                record.setStoreTypeList(Arrays.asList(types));
+            }
+
+            // 处理经纬度
+            if (StringUtils.isNotEmpty(record.getStorePosition())) {
+                String[] split = record.getStorePosition().split(",");
+                if (split.length >= 2) {
+                    record.setStorePositionLongitude(split[0]);
+                    record.setStorePositionLatitude(split[1]);
+                }
+            }
+
+            // 如果提供了经纬度,处理距离信息
+            if (lon != null && lat != null && StringUtils.isNotEmpty(record.getStorePosition())) {
+                try {
+                    // 手动计算距离(单位:公里)
+                    String[] positionArray = record.getStorePosition().split(",");
+                    if (positionArray.length >= 2) {
+                        double storeLon = Double.parseDouble(positionArray[0].trim());
+                        double storeLat = Double.parseDouble(positionArray[1].trim());
+                        // 计算距离(单位:公里)
+                        double distanceKm = DistanceUtil.haversineCalculateDistance(lon, lat, storeLon, storeLat);
+                        record.setDistance(distanceKm);
+                    }
+                } catch (Exception e) {
+                    log.warn("计算店铺距离失败,storeId={},error={}", record.getId(), e.getMessage());
+                }
+            }
+
+            // 处理到期状态
+            Date expirationTime = record.getExpirationTime();
+            if (expirationTime != null) {
+                Date currentDate = new Date();
+                Calendar now = Calendar.getInstance();
+                Date nowCurrentDate = now.getTime();
+                now.add(Calendar.DAY_OF_YEAR, 30);
+                Date thirtyDaysLater = now.getTime();
+
+                if (expirationTime.after(currentDate)) {
+                    record.setExpiredState("0");
+                    if ((expirationTime.after(nowCurrentDate) || expirationTime.equals(nowCurrentDate))
+                            && expirationTime.before(thirtyDaysLater)) {
+                        record.setExpiredState("1");
+                    }
+                } else {
+                    record.setExpiredState("2");
+                }
+
+                LocalDate nowLocal = LocalDate.now();
+                LocalDate expDate = expirationTime.toInstant()
+                        .atZone(ZoneId.systemDefault())
+                        .toLocalDate();
+                long daysToExpire = ChronoUnit.DAYS.between(nowLocal, expDate);
+                record.setDaysToExpire(daysToExpire);
+            }
+
+            // 设置店铺得分和总评论数
+            if (avgScoreMap.containsKey(String.valueOf(record.getId()))) {
+                record.setAvgScore(String.valueOf(
+                        avgScoreMap.get(String.valueOf(record.getId())).get(0).get("avg_score")));
+            }
+            if (commentMap.containsKey(record.getId())) {
+                record.setTotalNum(String.valueOf(commentMap.get(record.getId()).size()));
+            }
+        }
+
+        return storeInfoVoList;
+    }
+
+
+
+    /**
+     * 构建树形结构(优化版)
+     *
+     * @param flatList 扁平列表
+     * @return 树形结构列表
+     */
+    private List<StoreDictionary> buildTreeOptimized(List<StoreDictionary> flatList) {
+        if (flatList == null || flatList.isEmpty()) {
+            return new ArrayList<>();
+        }
+
+        // 创建三个存储结构
+        Map<Integer, StoreDictionary> nodeMap = new HashMap<>();  // ID到节点的映射
+        Map<Integer, List<StoreDictionary>> parentChildMap = new HashMap<>();  // 父ID到子节点列表的映射
+        List<StoreDictionary> result = new ArrayList<>();  // 结果列表
+
+        // 填充nodeMap和parentChildMap
+        for (StoreDictionary entity : flatList) {
+            Integer id = entity.getId();
+            Integer parentId = entity.getParentId();
+
+            // 存入节点映射
+            nodeMap.put(id, entity);
+
+            // 初始化子节点列表
+            entity.setStoreDictionaryList(new ArrayList<>());
+
+            // 如果是根节点(parentId为null或0),直接添加到结果
+            if (parentId == null || parentId == 0) {
+                result.add(entity);
+            } else {
+                // 否则,记录父子关系
+                parentChildMap.computeIfAbsent(parentId, k -> new ArrayList<>()).add(entity);
+            }
+        }
+
+        // 建立父子关系
+        for (StoreDictionary entity : flatList) {
+            Integer id = entity.getId();
+            if (parentChildMap.containsKey(id)) {
+                entity.getStoreDictionaryList().addAll(parentChildMap.get(id));
+            }
+        }
+
+        return result;
+    }
+
+    @Override
+    public List<StoreDictionaryVo> getLeisureEntertainmentCategories() {
+        // 定义四种主分类名称
+        List<String> mainCategoryNames = Arrays.asList("酒吧", "KTV", "洗浴汗蒸", "按摩足疗");
+
+        // 查询所有主分类(business_section类型)
+        List<StoreDictionary> allMainCategories = storeDictionaryMapper.selectList(
+                new LambdaQueryWrapper<StoreDictionary>()
+                        .eq(StoreDictionary::getTypeName, "business_section")
+                        .in(StoreDictionary::getDictDetail, mainCategoryNames)
+                        .eq(StoreDictionary::getDeleteFlag, 0)
+        );
+
+        // 构建主分类列表,包含"全部"选项
+        List<StoreDictionaryVo> result = new ArrayList<>();
+
+        // 添加"全部"选项
+        StoreDictionaryVo allCategory = new StoreDictionaryVo();
+        allCategory.setId(0);
+        allCategory.setDictId("0");
+        allCategory.setDictDetail("全部");
+        allCategory.setTypeName("business_section");
+        allCategory.setSubDataList(new ArrayList<>());
+        result.add(allCategory);
+
+        // 查询所有子分类(根据parent_id关联)
+        Map<Integer, List<StoreDictionary>> subCategoryMap = new HashMap<>();
+        if (!CollectionUtils.isEmpty(allMainCategories)) {
+            List<Integer> mainCategoryIds = allMainCategories.stream()
+                    .map(StoreDictionary::getId)
+                    .collect(Collectors.toList());
+
+            // 查询所有子分类
+            List<StoreDictionary> allSubCategories = storeDictionaryMapper.selectList(
+                    new LambdaQueryWrapper<StoreDictionary>()
+                            .in(StoreDictionary::getParentId, mainCategoryIds)
+                            .eq(StoreDictionary::getDeleteFlag, 0)
+                            .in(StoreDictionary::getTypeName, "business_section","business_type","business_classify")
+            );
+
+            // 按parentId分组
+            subCategoryMap = allSubCategories.stream()
+                    .collect(Collectors.groupingBy(StoreDictionary::getParentId));
+        }
+
+        // 构建主分类及其子分类
+        for (StoreDictionary mainCategory : allMainCategories) {
+            StoreDictionaryVo mainVo = new StoreDictionaryVo();
+            BeanUtils.copyProperties(mainCategory, mainVo);
+
+            // 获取该主分类下的子分类
+            List<StoreDictionary> subCategories = subCategoryMap.getOrDefault(mainCategory.getId(), new ArrayList<>());
+            List<StoreDictionaryVo> subVoList = new ArrayList<>();
+
+            for (StoreDictionary subCategory : subCategories) {
+                StoreDictionaryVo subVo = new StoreDictionaryVo();
+                BeanUtils.copyProperties(subCategory, subVo);
+                subVoList.add(subVo);
+            }
+
+            mainVo.setSubDataList(subVoList);
+            result.add(mainVo);
+        }
+
+        return result;
+    }
+
+
+    @Override
+    public IPage<StoreInfoVo> getSpecialTypeStoresByDistance(Double lon, Double lat, Double distance, Integer sortType, Integer businessType, Integer categoryId, 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<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());
+
+        // 距离优先模式:只显示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;
+    }
+
+    @Override
+    public List<StoreDictionary> getAllBusinessSection() {
+        // 查询所有经营种类数据
+        LambdaQueryWrapper<StoreDictionary> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.in(StoreDictionary::getTypeName, "business_section","business_type","business_classify");
+        queryWrapper.eq(StoreDictionary::getDeleteFlag, 0);
+        queryWrapper.orderByAsc(StoreDictionary::getSortId);
+        List<StoreDictionary> storeDictionaryList = storeDictionaryMapper.selectList(queryWrapper);
+
+        // 构建三级树形结构
+        return buildTreeOptimized(storeDictionaryList);
+    }
+
+    @Override
+    public List<StoreDictionary> getAllBusinessSection(String businessSection) {
+        // 如果没有传入一级分类参数,返回所有分类
+        if (businessSection == null || businessSection.trim().isEmpty()) {
+            return getAllBusinessSection();
+        }
+
+        // 1. 根据dictId查询一级分类
+        StoreDictionary firstLevelDict = storeDictionaryMapper.selectOne(
+                new LambdaQueryWrapper<StoreDictionary>()
+                        .eq(StoreDictionary::getDictId, businessSection.trim())
+                        .eq(StoreDictionary::getTypeName, "business_section")
+                        .eq(StoreDictionary::getDeleteFlag, 0)
+        );
+
+        if (firstLevelDict == null) {
+            log.warn("未找到一级分类,businessSection={}", businessSection);
+            return new ArrayList<>();
+        }
+
+        // 2. 查询该一级分类下的所有二级分类(business_type)
+        List<StoreDictionary> secondLevelDicts = storeDictionaryMapper.selectList(
+                new LambdaQueryWrapper<StoreDictionary>()
+                        .eq(StoreDictionary::getTypeName, "business_type")
+                        .eq(StoreDictionary::getParentId, firstLevelDict.getId())
+                        .eq(StoreDictionary::getDeleteFlag, 0)
+                        .orderByAsc(StoreDictionary::getSortId)
+        );
+
+        if (secondLevelDicts.isEmpty()) {
+            // 如果没有二级分类,只返回一级分类
+            firstLevelDict.setStoreDictionaryList(new ArrayList<>());
+            return Collections.singletonList(firstLevelDict);
+        }
+
+        // 3. 获取所有二级分类的ID
+        List<Integer> secondLevelIds = secondLevelDicts.stream()
+                .map(StoreDictionary::getId)
+                .collect(Collectors.toList());
+
+        // 4. 查询这些二级分类下的所有三级分类(business_classify)
+        List<StoreDictionary> thirdLevelDicts = storeDictionaryMapper.selectList(
+                new LambdaQueryWrapper<StoreDictionary>()
+                        .eq(StoreDictionary::getTypeName, "business_classify")
+                        .in(StoreDictionary::getParentId, secondLevelIds)
+                        .eq(StoreDictionary::getDeleteFlag, 0)
+                        .orderByAsc(StoreDictionary::getSortId)
+        );
+
+        // 5. 构建树形结构
+        // 将一级分类、二级分类、三级分类合并
+        List<StoreDictionary> allDicts = new ArrayList<>();
+        allDicts.add(firstLevelDict);
+        allDicts.addAll(secondLevelDicts);
+        allDicts.addAll(thirdLevelDicts);
+
+        // 构建树形结构
+        return buildTreeOptimized(allDicts);
+    }
+
+
+    /**
+     * web-分页查询店铺信息
+     *
+
+     * @return IPage<StoreInfoVo>
+     */
+    @Override
+    public List<StoreInfoVo> getMoreRecommendedStores(Double lon , Double lat, String businessSection, String businessTypes, String businessClassify) {
+        // 参数校验
+        if (lon == null || lat == null) {
+            log.warn("获取更多推荐店铺失败,经纬度为空");
+            return Collections.emptyList();
+        }
+
+        QueryWrapper<StoreInfoVo> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq("a.delete_flag", 0).eq("b.delete_flag", 0);
+        //如果查询未过期
+        // 获取当前时刻
+        Date currentDate = new Date();
+        // 获取当前日期和时间
+        Calendar calendar = Calendar.getInstance();
+        // 将时间设置为 0 点
+        calendar.set(Calendar.HOUR_OF_DAY, 0);
+        calendar.set(Calendar.MINUTE, 0);
+        calendar.set(Calendar.SECOND, 0);
+        calendar.set(Calendar.MILLISECOND, 0);
+        // 加上 30 天
+        calendar.add(Calendar.DAY_OF_MONTH, 30);
+        // 如果 expiration_time 为空则不做过期判断;如果不为空则要求大于当前时间
+        queryWrapper.and(w -> w.isNull("a.expiration_time")
+                .or()
+                .gt("a.expiration_time", currentDate));
+
+        // 如果 food_licence_expiration_time 为空则不做过期判断;如果不为空则要求大于当前时间
+        queryWrapper.and(w -> w.isNull("a.food_licence_expiration_time")
+                .or()
+                .gt("a.food_licence_expiration_time", currentDate));
+
+        // 构建一级分类
+        if(StringUtils.isNotEmpty(businessSection)){
+            queryWrapper.eq("a.business_section", businessSection);
+            // 构建二级分类
+            if(StringUtils.isNotEmpty(businessTypes)){
+                queryWrapper.eq("a.business_types", businessTypes);
+                // 构建三级分类
+                if(StringUtils.isNotEmpty(businessClassify)){
+                    // 解析businessClassify参数(格式:1,2,3)
+                    String[] classifyArray = businessClassify.split(",");
+                    // 使用FIND_IN_SET函数检查数据库字段是否包含参数中的任何一个值
+                    queryWrapper.and(wrapper -> {
+                        for (int i = 0; i < classifyArray.length; i++) {
+                            String classify = classifyArray[i].trim();
+                            if (StringUtils.isNotEmpty(classify)) {
+                                if (i == 0) {
+                                    wrapper.apply("FIND_IN_SET({0}, a.business_classify) > 0", classify);
+                                } else {
+                                    wrapper.or().apply("FIND_IN_SET({0}, a.business_classify) > 0", classify);
+                                }
+                            }
+                        }
+                    });
+                }
+            }
+        }
+
+        // 构建position参数(格式:经度,纬度)
+        String position = lon + "," + lat;
+        List<StoreInfoVo> storeInfoVoList = storeInfoMapper.getMoreRecommendedStores(queryWrapper, position);
+        if (CollectionUtils.isEmpty(storeInfoVoList)) {
+            return Collections.emptyList();
+        }
+        // 提前查询所有需要的字典数据
+        List<StoreInfoVo> collect = storeInfoVoList.stream().filter(record -> StringUtils.isNotEmpty(record.getStoreType())).collect(Collectors.toList());
+        Set<String> allTypes = collect.stream().map(StoreInfoVo::getStoreType).flatMap(type -> Arrays.stream(type.split(","))).collect(Collectors.toSet());
+
+        List<StoreDictionary> storeDictionaries = storeDictionaryMapper.selectList(new LambdaQueryWrapper<StoreDictionary>().eq(StoreDictionary::getTypeName, "storeType").isNull(StoreDictionary::getParentId).in(!allTypes.isEmpty(), StoreDictionary::getDictId, allTypes));
+        Map<String, String> typeMap = storeDictionaries.stream().collect(Collectors.toMap(StoreDictionary::getDictId, StoreDictionary::getDictDetail));
+
+
+        // 计算平均分和评价
+        Map<String, List<Map<String, Object>>> avgScoreMap = new HashMap<>();
+        Map<Integer, List<StoreComment>> commentMap = new HashMap<>();
+
+        // 注意:需要将store_id转换为String类型,与后续containsKey判断保持一致
+        avgScoreMap = storeEvaluationMapper.allStoreAvgScore().stream().collect(Collectors.groupingBy(o -> o.get("store_id").toString()));
+        List<StoreComment> storeComments = storeCommentMapper.selectList(
+                new QueryWrapper<StoreComment>()
+                        .eq("business_type", "5")
+                        .eq("delete_flag", 0));
+        commentMap = storeComments.stream()
+                .filter(comment -> comment.getStoreId() != null) // 过滤无店铺ID的评论,避免 groupingBy NPE
+                .collect(Collectors.groupingBy(StoreComment::getStoreId));
+
+
+        // 查询入口头图
+        List<Integer> storeIds = storeInfoVoList.stream()
+                .map(StoreInfoVo::getId)  // 假设 StoreImg 有 getStoreUrl() 方法
+                .collect(Collectors.toList());
+
+        List<StoreImg> storeImgList = storeImgMapper.selectList(new QueryWrapper<StoreImg>().in("store_id", storeIds).eq("img_type", 1));
+        Map<Integer, List<StoreImg>>  storeCollect = storeImgList.stream()
+                .collect(Collectors.groupingBy(StoreImg::getStoreId));
+
+
+        for (StoreInfoVo record : storeInfoVoList) {
+            //处理类型
+            if (StringUtils.isNotEmpty(record.getStoreType())) {
+                String[] types = record.getStoreType().split(",");
+                List<String> typeDetails = Arrays.stream(types).map(typeMap::get).filter(Objects::nonNull).collect(Collectors.toList());
+                record.setStoreTypeStr(String.join(",", typeDetails));
+                record.setStoreTypeList(Arrays.asList(types));
+            }
+
+
+            // 加入头图
+            if(!CollectionUtils.isEmpty(storeCollect) && storeCollect.containsKey(record.getId())){
+                List<StoreImg> storeImgs = storeCollect.get(record.getId());
+                if(!CollectionUtils.isEmpty(storeImgs)){
+                    record.setEntranceImage(storeImgs.get(0).getImgUrl());
+                }
+            }
+
+            //写经纬度
+            String[] split = record.getStorePosition().split(",");
+            record.setStorePositionLongitude(split[0]);
+            record.setStorePositionLatitude(split[1]);
+            // 格式化距离,移除无意义的小数位
+            if (!StringUtils.isEmpty(record.getDistance3())) {
+                try {
+                    BigDecimal distanceValue = new BigDecimal(record.getDistance3());
+                    record.setDistance3(distanceValue.stripTrailingZeros().toPlainString());
+                } catch (NumberFormatException ex) {
+                    log.warn("店铺距离格式化失败, storeId: {}, distance3: {}", record.getId(), record.getDistance3(), ex);
+                }
+            }
+            //处理一下到期状态
+            Date expirationTime = record.getExpirationTime();
+            if (expirationTime != null) {
+                // 获取当前时间
+                Calendar now = Calendar.getInstance();
+                Date nowCurrentDate = now.getTime();
+                // 计算 30 天后的时间
+                now.add(Calendar.DAY_OF_YEAR, 30);
+                Date thirtyDaysLater = now.getTime();
+                // 比较两个日期
+                if (expirationTime.after(currentDate)) {
+                    record.setExpiredState("0");
+                    if ((expirationTime.after(nowCurrentDate) || expirationTime.equals(nowCurrentDate)) && expirationTime.before(thirtyDaysLater)) {
+                        record.setExpiredState("1");
+                    }
+                } else {
+                    record.setExpiredState("2");
+                }
+
+                // 获取当前时间
+                LocalDate nowLocal = LocalDate.now();
+                // 将 expirationTime 转换为 LocalDate
+                LocalDate expDate = expirationTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
+                // 计算距离到期的天数
+                long daysToExpire = ChronoUnit.DAYS.between(nowLocal, expDate);
+                record.setDaysToExpire(daysToExpire);
+            }
+
+            // 设置店铺得分,设置店铺人均消费,设置总评论数
+            if (avgScoreMap.containsKey(String.valueOf(record.getId()))) {
+                record.setAvgScore(String.valueOf(avgScoreMap.get(String.valueOf(record.getId())).get(0).get("avg_score")));
+            } else {
+                record.setAvgScore("0");
+            }
+            // 设置店铺得分,设置店铺人均消费,设置总评论数
+            if (commentMap.containsKey(record.getId())) {
+                record.setTotalNum(String.valueOf(commentMap.get(record.getId()).size()));
+            } else {
+                record.setTotalNum("0");
+            }
+
+        }
+
+        // SQL已经实现了距离过滤和排序,直接返回结果
+        return storeInfoVoList;
+    }
+
+
+    @Override
+    public StoreInfoVo getClientStoreDetail(String storeId, String userId, String jingdu, String weidu) {
+        StoreInfoVo result = new StoreInfoVo();
+        StoreInfo storeInfo = storeInfoMapper.selectById(storeId);
+        BeanUtils.copyProperties(storeInfo, result);
+        //将经营板块和种类拆分成集合
+        String businessTypes = storeInfo.getBusinessTypes();
+        if (StringUtils.isNotEmpty(businessTypes)) {
+            String[] split = businessTypes.split(",");
+            List<String> list = Arrays.asList(split);
+            result.setBusinessTypesList(list);
+        }
+        //存入用户账户
+        StoreUser storeUser = storeUserMapper.selectOne(new LambdaQueryWrapper<StoreUser>().eq(StoreUser::getStoreId, storeId));
+        if (storeUser != null) {
+            result.setUserAccount(storeUser.getId().toString());
+            result.setStorePhone(storeUser.getPhone());
+            result.setStoreUserName(storeUser.getName());
+            result.setIdCard(storeUser.getIdCard());
+        }
+//        //存入执照图片地址
+//        List<StoreImg> storeImgs = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 14));
+//        List<String> storeImgPaths = new ArrayList<>();
+//        for (StoreImg storeImg : storeImgs) {
+//            storeImgPaths.add(storeImg.getImgUrl());
+//        }
+//        result.setBusinessLicenseAddress(storeImgPaths);
+//        //存入合同图片地址
+//        List<StoreImg> storeContractImageImgs = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 15));
+//        List<String> storeContractImagePathImgs = new ArrayList<>();
+//        for (StoreImg storeImg : storeContractImageImgs) {
+//            storeContractImagePathImgs.add(storeImg.getImgUrl());
+//        }
+//        result.setContractImageList(storeContractImagePathImgs);
+//        //存入续签合同地址
+//        List<StoreImg> renewContractImgs = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 22));
+//        List<String> renewContractImagePathImgs = new ArrayList<>();
+//        for (StoreImg storeImg : renewContractImgs) {
+//            renewContractImagePathImgs.add(storeImg.getImgUrl());
+//        }
+//        result.setRenewContractImageList(renewContractImagePathImgs);
+//        //存入经营许可证通过地址
+//        List<StoreImg> foodLicenceImgs = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 25));
+//        List<String> foodLicenceImgsPathImgs = new ArrayList<>();
+//        for (StoreImg storeImg : foodLicenceImgs) {
+//            foodLicenceImgsPathImgs.add(storeImg.getImgUrl());
+//        }
+//        result.setFoodLicenceImageList(foodLicenceImgsPathImgs);
+//        //存入经营许可证未通过地址
+//        List<StoreImg> notPassFoodLicenceImgs = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 24));
+//        List<String> notPassFoodLicenceList = new ArrayList<>();
+//        for (StoreImg storeImg : notPassFoodLicenceImgs) {
+//            notPassFoodLicenceList.add(storeImg.getImgUrl());
+//        }
+//        result.setNotPassFoodLicenceImageList(notPassFoodLicenceList);
+        // 存放商家入口图
+        List<StoreImg> storeEntranceImageImgs = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 1));
+        if (!storeEntranceImageImgs.isEmpty()) {
+            result.setEntranceImage(storeEntranceImageImgs.get(0).getImgUrl());
+        } else {
+            result.setEntranceImage("null");
+        }
+        // 存放商家头像
+        List<StoreImg> storeImgs1 = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 10));
+        if (!storeImgs1.isEmpty()) {
+            result.setImgUrl(storeImgs1.get(0).getImgUrl());
+        } else {
+            result.setImgUrl("null");
+        }
+
+        // 获取店铺相册
+        List<StoreImg> storeAlbumList = new ArrayList<>();
+        if(storeInfo.getImgMode() != null && storeInfo.getImgMode() == 0){
+            storeAlbumList =  storeImgService.getStoreImg(Integer.parseInt(storeId), 20);
+        } else {
+            storeAlbumList =  storeImgService.getStoreImg(Integer.parseInt(storeId), 21);
+        }
+
+        if(!CollectionUtils.isEmpty(storeAlbumList)){
+            List<String> storeAlbumUrlList = storeAlbumList.stream().map(StoreImg::getImgUrl)  // 假设 StoreImg 有 getStoreUrl() 方法
+                    .filter(url -> url != null && !url.trim().isEmpty())  // 过滤空值
+                    .collect(Collectors.toList());
+            result.setStoreAlbumUrlList(storeAlbumUrlList);
+        } else {
+            result.setStoreAlbumUrlList(new ArrayList<>());
+        }
+
+        // 设置经纬度
+        result.setStorePositionLongitude(result.getStorePosition().split(",")[0]);
+        result.setStorePositionLatitude(result.getStorePosition().split(",")[1]);
+        // 设置距离
+        if ((jingdu != null && !jingdu.isEmpty()) && (weidu != null && !weidu.isEmpty())) {
+            /*double storeJing = Double.parseDouble(result.getStorePosition().split(",")[0]);
+            double storeWei = Double.parseDouble(result.getStorePosition().split(",")[1]);
+            double storeDistance = DistanceUtil.haversineCalculateDistance(Double.parseDouble(jingdu), Double.parseDouble(weidu), storeJing, storeWei);*/
+
+            Double distance = storeInfoMapper.getStoreDistance(jingdu + "," + weidu,result.getId());
+
+            result.setDistance(distance);
+        }
+        // 计算店铺到最近地铁站的距离
+        JSONObject nearbySubway = gaoDeMapUtil.getNearbySubway(result.getStorePosition().split(",")[0], result.getStorePosition().split(",")[1]);
+        // 地铁名
+        String subWayName = nearbySubway.getString("name");
+        result.setSubwayName(subWayName);
+        // 地铁站经纬度
+        String subWayJing = nearbySubway.getString("location") == null ? null : nearbySubway.getString("location").split(",")[0];
+        String subWayWei = nearbySubway.getString("location") == null ? null : nearbySubway.getString("location").split(",")[1];
+        if ((subWayJing != null && !subWayJing.isEmpty()) && (subWayWei != null && !subWayWei.isEmpty())) {
+            double storeJing = Double.parseDouble(result.getStorePosition().split(",")[0]);
+            double storeWei = Double.parseDouble(result.getStorePosition().split(",")[1]);
+            double storeDistance2 = DistanceUtil.haversineCalculateDistance(Double.parseDouble(subWayJing), Double.parseDouble(subWayWei), storeJing, storeWei);
+            result.setDistance2(storeDistance2);
+        } else {
+            result.setDistance2(0);
+        }
+        // 当前登录用户是否收藏
+        LambdaUpdateWrapper<LifeCollect> shouCangWrapper = new LambdaUpdateWrapper<>();
+        shouCangWrapper.eq(LifeCollect::getUserId, userId).eq(LifeCollect::getStoreId, storeId);
+        List<LifeCollect> shouCangList = lifeCollectMapper.selectList(shouCangWrapper);
+        if (null == shouCangList || shouCangList.isEmpty()) {
+            result.setCollection(0);
+        } else {
+            result.setCollection(1);
+        }
+
+        // 该用户的打卡记录
+        LambdaQueryWrapper<StoreClockIn> clockInWrapper = new LambdaQueryWrapper<>();
+        clockInWrapper.eq(StoreClockIn::getUserId, userId);
+        List<StoreClockIn> clockInList = storeClockInMapper.selectList(clockInWrapper);
+
+        List<StoreClockIn> clockStoreList = clockInList.stream().filter(item -> item.getStoreId() == Integer.parseInt(storeId)).collect(Collectors.toList());
+        // 该用户是否在该店铺打过卡
+        if (!clockStoreList.isEmpty()) {
+            result.setClockInStore(1);
+        } else {
+            result.setClockInStore(0);
+        }
+
+        // 今天在该店铺是否打过卡
+        int today = (int) clockStoreList.stream().filter(item -> item.getCreatedTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate().equals(LocalDate.now())).count();
+        if (today > 0) {
+            result.setClockInStoreToday(1);
+        } else {
+            result.setClockInStoreToday(0);
+        }
+
+
+        Map<String, Object> commitCountAndScore = storeCommentService.getCommitCountAndScore(null, 5, Integer.parseInt(storeId), null, null);
+        result.setScore(Double.parseDouble(commitCountAndScore.get("score").toString()));
+        result.setCommitCount(commitCountAndScore.get("commitCount").toString());
+
+
+        // 在该店铺的打卡次数
+        result.setClockInStoreNum(clockStoreList.size());
+
+        // 该用户打卡的所有店铺次数(一个店铺只算一次)
+        int clockInNum = (int) clockInList.stream().map(StoreClockIn::getStoreId).distinct().count();
+        result.setClockInNum(clockInNum);
+
+//        // 获取店铺动态列表
+//        QueryWrapper<LifeUserDynamics> dynamicsWrapper = new QueryWrapper<>();
+//        dynamicsWrapper.eq("phone_id", "store_" + result.getStorePhone()).orderByDesc("lud.created_time");
+//        dynamicsWrapper.eq("lud.delete_flag", 0);
+//
+//        LambdaQueryWrapper<LifeBlacklist> lambdaQueryWrapper1 = new LambdaQueryWrapper<>();
+//        lambdaQueryWrapper1.eq(LifeBlacklist :: getBlockerId, userId);
+//        lambdaQueryWrapper1.eq(LifeBlacklist :: getBlockedPhoneId, "store_" + result.getStorePhone());
+//        LifeBlacklist blacklist = lifeBlacklistMapper.selectOne(lambdaQueryWrapper1);
+//        List<LifeUserDynamicsVo> storeDynamicslist = new ArrayList<>();
+
+//        //判断没有拉黑当前门店账户 查出门店动态
+//        if(blacklist == null){
+//            storeDynamicslist = lifeUserDynamicsMapper.getStoreDynamicslist(userId, "store_" + result.getStorePhone());
+//        }
+//
+//        List<String> followList = new ArrayList<>();
+//        List<String> fansList = new ArrayList<>();
+
+//        if (StringUtils.isNotEmpty(userId)) {
+//            LifeUser lifeUser = lifeUserMapper.selectById(userId);
+//            if (lifeUser != null && StringUtils.isNotEmpty(lifeUser.getUserPhone())) {
+//                // 查询我的关注信息,构建关注者ID列表
+//                LambdaQueryWrapper<LifeFans> lifeFansWrapper = new LambdaQueryWrapper<>();
+//                lifeFansWrapper.eq(LifeFans::getFansId, "user_" + result.getStorePhone());
+//                List<LifeFans> lifeFansList = lifeFansMapper.selectList(lifeFansWrapper);
+//                if (!CollectionUtils.isEmpty(lifeFansList)) {
+//                    followList = lifeFansList.stream().map(LifeFans::getFollowedId).collect(Collectors.toList());
+//                }
+//
+//                // 查询我的粉丝信息,构建粉丝ID列表
+//                lifeFansWrapper = new LambdaQueryWrapper<>();
+//                lifeFansWrapper.eq(LifeFans::getFollowedId, "user_" + result.getStorePhone());
+//                lifeFansList = lifeFansMapper.selectList(lifeFansWrapper);
+//                if (!CollectionUtils.isEmpty(lifeFansList)) {
+//                    fansList = lifeFansList.stream().map(LifeFans::getFansId).collect(Collectors.toList());
+//                }
+//            }
+//        }
+
+//        for (LifeUserDynamicsVo vo : storeDynamicslist) {
+//            if (followList.contains(vo.getPhoneId())) {
+//                vo.setIsFollowThis("1");
+//            } else {
+//                vo.setIsFollowThis("0");
+//            }
+//            if (fansList.contains(vo.getPhoneId())) {
+//                vo.setIsFollowMe("1");
+//            } else {
+//                vo.setIsFollowMe("0");
+//            }
+//        }
+
+//        // 返回动态最新的5条
+//        List<LifeUserDynamicsVo> storeDynamicslist2 = storeDynamicslist.stream()
+//                .limit(5).collect(Collectors.toList());
+//        result.setDynamicsList(storeDynamicslist2);
+//        //设置动态条数
+//        Integer dynamicsNum = storeDynamicslist2.size();
+//        result.setDynamicsNum(dynamicsNum);
+//
+//        // 获取店铺动态总数
+//        result.setTotalDynamicsNum(storeDynamicslist.size());
+
+        //营业时间
+        List<StoreBusinessInfo> storeBusinessInfos = storeBusinessInfoMapper.selectList(new LambdaQueryWrapper<StoreBusinessInfo>().eq(StoreBusinessInfo::getStoreId, storeId).eq(StoreBusinessInfo::getDeleteFlag, 0));
+        if (ObjectUtils.isNotEmpty(storeBusinessInfos)) {
+            result.setStoreBusinessInfo(storeBusinessInfos.get(0));
+            result.setStoreBusinessInfos(storeBusinessInfos);
+            StoreBusinessInfo storeBusinessInfo = result.getStoreBusinessInfos().stream().filter(item -> item.getBusinessType() == 1).findFirst().orElse(null);
+            if (ObjectUtils.isNotEmpty(storeBusinessInfo)) {
+
+                Calendar calendar = Calendar.getInstance(); // 获取Calendar实例
+                int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); // 获取星期几,注意Calendar中的DAY_OF_WEEK是从1(代表星期天)开始的
+                String[] days = {"7", "1", "2", "3", "4", "5", "6"};
+                String day = days[dayOfWeek - 1];
+                if (storeBusinessInfo.getBusinessDate().contains(day)) {
+                    if (StringUtils.isNotEmpty(storeBusinessInfo.getStartTime()) && StringUtils.isNotEmpty(storeBusinessInfo.getEndTime())) {
+                        LocalTime now = LocalTime.now();
+                        List<String> startList = Arrays.asList(storeBusinessInfo.getStartTime().split(":"));
+                        List<String> endList = Arrays.asList(storeBusinessInfo.getEndTime().split(":"));
+                        LocalTime start = LocalTime.of(Integer.parseInt(startList.get(0)), Integer.parseInt(startList.get(1)));
+                        LocalTime end = LocalTime.of(Integer.parseInt(endList.get(0)), Integer.parseInt(startList.get(1)));
+                        if (now.isAfter(start) && now.isBefore(end)) {
+                            result.setYyFlag(1);
+                        } else {
+                            result.setYyFlag(0);
+                        }
+                    }
+                } else {
+                    result.setYyFlag(0);
+                }
+            }
+        }
+        LambdaQueryWrapper<StoreDictionary> storeDictionaryLambdaQueryWrapper = new LambdaQueryWrapper<>();
+        storeDictionaryLambdaQueryWrapper.eq(StoreDictionary::getTypeName, "businessStatus")
+                .eq(StringUtils.isNotEmpty(result.getBusinessStatus().toString()), StoreDictionary::getDictId, result.getBusinessStatus());
+        List<StoreDictionary> storeDictionaries = storeDictionaryMapper.selectList(storeDictionaryLambdaQueryWrapper);
+        if (!storeDictionaries.isEmpty()) {
+            result.setBusinessStatusStr(storeDictionaries.get(0).getDictDetail());
+        }
+        return result;
+    }
+
+    @Override
+    public List<LifeCouponVo> getStoreCouponList(String storeId) {
+        // 获取店铺代金券列表
+        LambdaUpdateWrapper<LifeCoupon> quanWrapper = new LambdaUpdateWrapper<>();
+        quanWrapper.eq(LifeCoupon::getStoreId, storeId).eq(LifeCoupon::getStatus, CouponStatusEnum.ONGOING.getCode()).eq(LifeCoupon::getType, 1);
+        List<LifeCoupon> quanList = lifeCouponMapper.selectList(quanWrapper);
+        List<LifeCouponVo> quanVoList = new ArrayList<>();
+        List<String> collect = quanList.stream().map(LifeCoupon::getId).collect(Collectors.toList());
+        // 设置已售数量
+        // 定义需要的订单状态集合
+        Set<Integer> excludeStatuses = new HashSet<>(Arrays.asList(
+                OrderStatusEnum.WAIT_PAY.getStatus(),
+                OrderStatusEnum.WAIT_USE.getStatus(),
+                OrderStatusEnum.USED.getStatus()
+        ));
+        if (!collect.isEmpty()) {
+            List<LifeUserOrderVo> quanCount = lifeUserOrderMapper.getQuanCount(new QueryWrapper<LifeUserOrderVo>()
+                    .eq("luo.store_id", storeId)
+                    .eq("luo.coupon_type", CouponTypeEnum.COUPON.getCode())
+                    .eq("luo.delete_flag", 0)
+                    .in("ocm.status", excludeStatuses)
+                    .groupBy("ocm.coupon_id"));
+            quanList.forEach(a -> {
+                LifeCouponVo lifeCouponVo = new LifeCouponVo();
+                BeanUtils.copyProperties(a, lifeCouponVo);
+                quanCount.forEach(item -> {
+                    if (a.getId().equals(item.getCouponId().toString())) {
+                        lifeCouponVo.setCount(item.getCount());
+                    }
+                });
+                quanVoList.add(lifeCouponVo);
+            });
+        }
+        return quanVoList;
+    }
+
+    @Override
+    public StoreInfoVo getNewStoreDetail(String storeId) {
+
+        StoreInfoVo result = new StoreInfoVo();
+        StoreInfo storeInfo = storeInfoMapper.selectById(storeId);
+        BeanUtils.copyProperties(storeInfo, result);
+        //将经营板块和种类拆分成集合
+        String businessTypes = storeInfo.getBusinessTypes();
+        if (StringUtils.isNotEmpty(businessTypes)) {
+            String[] split = businessTypes.split(",");
+            List<String> list = Arrays.asList(split);
+            result.setBusinessTypesList(list);
+        }
+        //存入用户账户
+        StoreUser storeUser = storeUserMapper.selectOne(new LambdaQueryWrapper<StoreUser>().eq(StoreUser::getStoreId, storeId));
+        if (storeUser != null) {
+            result.setUserAccount(storeUser.getId().toString());
+            result.setStorePhone(storeUser.getPhone());
+            result.setStoreUserName(storeUser.getName());
+            result.setIdCard(storeUser.getIdCard());
+        }
+        //存入执照图片地址
+        List<StoreImg> storeImgs = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 14));
+        List<String> storeImgPaths = new ArrayList<>();
+        for (StoreImg storeImg : storeImgs) {
+            storeImgPaths.add(storeImg.getImgUrl());
+        }
+        result.setBusinessLicenseAddress(storeImgPaths);
+        //存入合同图片地址
+        List<StoreImg> storeContractImageImgs = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 15));
+        List<String> storeContractImagePathImgs = new ArrayList<>();
+        for (StoreImg storeImg : storeContractImageImgs) {
+            storeContractImagePathImgs.add(storeImg.getImgUrl());
+        }
+        result.setContractImageList(storeContractImagePathImgs);
+        //存入续签合同地址
+        List<StoreImg> renewContractImgs = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 22));
+        List<String> renewContractImagePathImgs = new ArrayList<>();
+        for (StoreImg storeImg : renewContractImgs) {
+            renewContractImagePathImgs.add(storeImg.getImgUrl());
+        }
+        result.setRenewContractImageList(renewContractImagePathImgs);
+        //存入经营许可证通过地址
+        List<StoreImg> foodLicenceImgs = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 25));
+        List<String> foodLicenceImgsPathImgs = new ArrayList<>();
+        for (StoreImg storeImg : foodLicenceImgs) {
+            foodLicenceImgsPathImgs.add(storeImg.getImgUrl());
+        }
+        result.setFoodLicenceImageList(foodLicenceImgsPathImgs);
+        //存入经营许可证未通过地址
+        List<StoreImg> notPassFoodLicenceImgs = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 24));
+        List<String> notPassFoodLicenceList = new ArrayList<>();
+        for (StoreImg storeImg : notPassFoodLicenceImgs) {
+            notPassFoodLicenceList.add(storeImg.getImgUrl());
+        }
+        result.setNotPassFoodLicenceImageList(notPassFoodLicenceList);
+        // 存放商家入口图
+        List<StoreImg> storeEntranceImageImgs = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 1));
+        if (!storeEntranceImageImgs.isEmpty()) {
+            result.setEntranceImage(storeEntranceImageImgs.get(0).getImgUrl());
+        } else {
+            result.setEntranceImage("null");
+        }
+        // 存放商家头像
+        List<StoreImg> storeImgs1 = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 10));
+        if (!storeImgs1.isEmpty()) {
+            result.setImgUrl(storeImgs1.get(0).getImgUrl());
+        } else {
+            result.setImgUrl("null");
+        }
+
+
+        // 店铺平均分
+        /*Map<Object, List<Map<String, Object>>> avgScoreMap = storeEvaluationMapper.allStoreAvgScore().stream().collect(Collectors.groupingBy(o -> o.get("store_id")));
+        if (avgScoreMap.containsKey(String.valueOf(result.getId()))) {
+            result.setScore(Double.parseDouble(avgScoreMap.get(String.valueOf(result.getId())).get(0).get("avg_score").toString()));
+        }*/
+
+//        Map<String, Object> commitCountAndScore = storeCommentService.getCommitCountAndScore(null, 5, Integer.parseInt(storeId), null, null);
+//        result.setScore(Double.parseDouble(commitCountAndScore.get("score").toString()));
+//        result.setCommitCount(commitCountAndScore.get("commitCount").toString());
+//
+
+
+        //营业时间
+        List<StoreBusinessInfo> storeBusinessInfos = storeBusinessInfoMapper.selectList(new LambdaQueryWrapper<StoreBusinessInfo>().eq(StoreBusinessInfo::getStoreId, storeId).eq(StoreBusinessInfo::getDeleteFlag, 0));
+        if (ObjectUtils.isNotEmpty(storeBusinessInfos)) {
+            result.setStoreBusinessInfo(storeBusinessInfos.get(0));
+            result.setStoreBusinessInfos(storeBusinessInfos);
+            //StoreBusinessInfo storeBusinessInfo = result.getStoreBusinessInfo();
+            StoreBusinessInfo storeBusinessInfo = result.getStoreBusinessInfos().stream().filter(item -> item.getBusinessType() == 1).findFirst().orElse(null);
+            if (ObjectUtils.isNotEmpty(storeBusinessInfo)) {
+
+                Calendar calendar = Calendar.getInstance(); // 获取Calendar实例
+                int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); // 获取星期几,注意Calendar中的DAY_OF_WEEK是从1(代表星期天)开始的
+                String[] days = {"7", "1", "2", "3", "4", "5", "6"};
+                String day = days[dayOfWeek - 1];
+                if (storeBusinessInfo.getBusinessDate().contains(day)) {
+                    if (StringUtils.isNotEmpty(storeBusinessInfo.getStartTime()) && StringUtils.isNotEmpty(storeBusinessInfo.getEndTime())) {
+                        LocalTime now = LocalTime.now();
+                        List<String> startList = Arrays.asList(storeBusinessInfo.getStartTime().split(":"));
+                        List<String> endList = Arrays.asList(storeBusinessInfo.getEndTime().split(":"));
+                        LocalTime start = LocalTime.of(Integer.parseInt(startList.get(0)), Integer.parseInt(startList.get(1)));
+                        LocalTime end = LocalTime.of(Integer.parseInt(endList.get(0)), Integer.parseInt(startList.get(1)));
+                        if (now.isAfter(start) && now.isBefore(end)) {
+                            result.setYyFlag(1);
+                        } else {
+                            result.setYyFlag(0);
+                        }
+                    }
+                } else {
+                    result.setYyFlag(0);
+                }
+            }
+        }
+        LambdaQueryWrapper<StoreDictionary> storeDictionaryLambdaQueryWrapper = new LambdaQueryWrapper<>();
+        storeDictionaryLambdaQueryWrapper.eq(StoreDictionary::getTypeName, "businessStatus")
+                .eq(StringUtils.isNotEmpty(result.getBusinessStatus().toString()), StoreDictionary::getDictId, result.getBusinessStatus());
+        List<StoreDictionary> storeDictionaries = storeDictionaryMapper.selectList(storeDictionaryLambdaQueryWrapper);
+        if (!storeDictionaries.isEmpty()) {
+            result.setBusinessStatusStr(storeDictionaries.get(0).getDictDetail());
+        }
+        // TODO 之后修改********** 正常OcrType由前端传存储ocr表要加新字段。传参要由前端传。
+        // 查询并设置各类证件OCR信息
+        result.setJyxkz(convertOcrResultToJson(storeUser.getId(), "BUSINESS_LICENSE", "营业执照", true));
+        result.setIdcardFace(convertOcrResultToJson(storeUser.getId(), "ID_CARD", "face", true));
+        result.setIdcardBack(convertOcrResultToJson(storeUser.getId(), "ID_CARD", "back", true));
+        result.setFoodLicence(convertOcrResultToJson(storeUser.getId(), "FOOD_MANAGE_LICENSE", null, true));
+        result.setEntertainmentLicence(convertOcrResultToJson(storeUser.getId(), "BUSINESS_LICENSE", "娱乐", false));
+        return result;
+    }
+
+    @Override
+    public StoreInfoVo editNewStoreInfo(StoreInfoDto storeInfoDto) {
+
+        //获取经营板块id
+        Integer businessSection = storeInfoDto.getBusinessSection();
+        //查询经营板块名称
+        StoreDictionary businessSectionName = storeDictionaryMapper.selectOne(new LambdaQueryWrapper<StoreDictionary>().eq(StoreDictionary::getDictId, businessSection).eq(StoreDictionary::getTypeName, "business_section"));
+        //查询经营种类
+        List<String> businessTypes = storeInfoDto.getBusinessTypes();
+        List<String> businessTypeNames = new ArrayList<>();
+        //获取经营种类名称
+        for (String businessType : businessTypes) {
+            StoreDictionary storeDictionary = storeDictionaryMapper.selectOne(new LambdaQueryWrapper<StoreDictionary>().eq(StoreDictionary::getDictId, businessType).eq(StoreDictionary::getParentId, businessSectionName.getId()));
+            businessTypeNames.add(storeDictionary.getDictDetail());
+        }
+
+        StoreInfoVo result = new StoreInfoVo();
+        StoreInfo storeInfo = new StoreInfo();
+        BeanUtils.copyProperties(storeInfoDto, storeInfo);
+//        List<String> storeTypeList = storeInfoDto.getStoreTypeList();
+//        String storeType = String.join(",", storeTypeList);
+        //存入营运类型
+//        storeInfo.setStoreType(storeType);
+
+        //板块及类型
+        storeInfo.setBusinessSection(businessSection);
+        storeInfo.setBusinessSectionName(businessSectionName.getDictDetail());
+        storeInfo.setBusinessTypes(String.join(",", businessTypes));
+        storeInfo.setBusinessTypesName(String.join(",", businessTypeNames));
+
+        //处理分类信息
+        List<String> businessClassify = storeInfoDto.getBusinessClassify();
+        if (!CollectionUtils.isEmpty(businessClassify)) {
+            List<String> businessClassifyNames = new ArrayList<>();
+            //批量查询分类名称
+            List<StoreDictionary> classifyDicts = storeDictionaryMapper.selectList(
+                    new LambdaQueryWrapper<StoreDictionary>()
+                            .in(StoreDictionary::getDictId, businessClassify)
+                            .eq(StoreDictionary::getTypeName, "business_classify")
+                            .eq(StoreDictionary::getDeleteFlag, 0)
+            );
+            //转为Map方便快速获取
+            Map<String, StoreDictionary> classifyDictMap = classifyDicts.stream()
+                    .collect(Collectors.toMap(
+                            dict -> dict.getDictId().toString(),
+                            Function.identity(),
+                            (existing, replacement) -> existing
+                    ));
+            //提取分类名称
+            for (String classifyId : businessClassify) {
+                StoreDictionary dict = classifyDictMap.get(classifyId);
+                if (Objects.nonNull(dict)) {
+                    businessClassifyNames.add(dict.getDictDetail());
+                } else {
+                    log.warn("无效的分类id:" + classifyId);
+                }
+            }
+            storeInfo.setBusinessClassify(String.join(",", businessClassify));
+            storeInfo.setBusinessClassifyName(String.join(",", businessClassifyNames));
+        }
+
+        storeInfoMapper.updateById(storeInfo);
+        return result;
+    }
+
 }

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

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

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

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

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

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

+ 31 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StoreStaffConfigServiceImpl.java

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

+ 2 - 2
alien-store/src/main/java/shop/alien/store/util/ali/ocr/AbstractOcrStrategy.java

@@ -47,14 +47,14 @@ public abstract class AbstractOcrStrategy implements OcrStrategy {
             config.setAccessKeyId(accessKeyId);
         } else {
             // 默认值,仅用于测试,生产环境应该从配置文件读取
-            config.setAccessKeyId("LTAI5tLAUTQg7R1xaKvxAYJu");
+            config.setAccessKeyId("LTAI5tFDRZ31MXse5TEQyPwQ");
         }
         
         if (org.apache.commons.lang3.StringUtils.isNotBlank(accessKeySecret)) {
             config.setAccessKeySecret(accessKeySecret);
         } else {
             // 默认值,仅用于测试,生产环境应该从配置文件读取
-            config.setAccessKeySecret("ayVk34nK9vQZ2bs5vDCYftQCEXXN3B");
+            config.setAccessKeySecret("n0UHNuzIZMA7CnAt1uzb43FCbpbQDg");
         }
         
         // Endpoint 请参考 https://api.aliyun.com/product/ocr-api

+ 3 - 0
alien-store/src/main/java/shop/alien/store/util/ali/ocr/strategy/BusinessLicenseOcrStrategy.java

@@ -16,6 +16,7 @@ import shop.alien.store.service.StoreImgService;
 import shop.alien.store.util.ali.ocr.AbstractOcrStrategy;
 import shop.alien.util.common.constant.OcrTypeEnum;
 
+import java.util.Date;
 import java.util.Map;
 
 /**
@@ -94,6 +95,8 @@ public class BusinessLicenseOcrStrategy extends AbstractOcrStrategy {
                 ocrImageUpload.setImageUrl(imageUrl);
                 ocrImageUpload.setOcrResult(jsonObject.getJSONObject("data").toJSONString());
                 ocrImageUpload.setOcrType(OcrTypeEnum.BUSINESS_LICENSE.getCode());
+                ocrImageUpload.setCreateTime(new Date());
+                ocrImageUpload.setUpdateTime(new Date());
                 super.saveOcrImage(ocrImageUpload);
                 resultArray.add(jsonObject.getJSONObject("data"));
             } catch (TeaException error) {

+ 3 - 0
alien-store/src/main/java/shop/alien/store/util/ali/ocr/strategy/FoodManageLicenseOcrStrategy.java

@@ -16,6 +16,7 @@ import shop.alien.store.service.StoreImgService;
 import shop.alien.store.util.ali.ocr.AbstractOcrStrategy;
 import shop.alien.util.common.constant.OcrTypeEnum;
 
+import java.util.Date;
 import java.util.Map;
 
 /**
@@ -94,6 +95,8 @@ public class FoodManageLicenseOcrStrategy extends AbstractOcrStrategy {
                 ocrImageUpload.setImageUrl(imageUrl);
                 ocrImageUpload.setOcrResult(jsonObject.getJSONObject("data").toJSONString());
                 ocrImageUpload.setOcrType(OcrTypeEnum.FOOD_MANAGE_LICENSE.getCode());
+                ocrImageUpload.setCreateTime(new Date());
+                ocrImageUpload.setUpdateTime(new Date());
                 super.saveOcrImage(ocrImageUpload);
                 resultArray.add(jsonObject.getJSONObject("data"));
             } catch (TeaException error) {

+ 0 - 0
logging.path_IS_UNDEFINED/DEBUG.log


+ 0 - 0
logging.path_IS_UNDEFINED/ERROR.log


+ 6 - 0
logging.path_IS_UNDEFINED/INFO.log

@@ -0,0 +1,6 @@
+[11/28 08:38:36.388][main      ][INFO ][c.a.n.c.c.i.LocalConfigInfoProcessor    :  212]:[       <clinit>] || LOCAL_SNAPSHOT_PATH:C:\Users\Windows\nacos\config
+[11/28 08:38:36.484][main      ][INFO ][c.a.n.c.c.u.JvmUtil                     :   49]:[       <clinit>] || isMultiInstance:false
+[11/28 08:38:36.541][main      ][INFO ][b.c.PropertySourceBootstrapConfiguration:  109]:[     initialize] || Located property source: [BootstrapPropertySource {name='bootstrapProperties-alien-lawyer-dev.yml,DEFAULT_GROUP'}, BootstrapPropertySource {name='bootstrapProperties-alien-lawyer.yml,DEFAULT_GROUP'}, BootstrapPropertySource {name='bootstrapProperties-alien-lawyer,DEFAULT_GROUP'}]
+[11/28 11:37:14.297][main      ][INFO ][c.a.n.c.c.i.LocalConfigInfoProcessor    :  212]:[       <clinit>] || LOCAL_SNAPSHOT_PATH:C:\Users\Windows\nacos\config
+[11/28 11:37:14.326][main      ][INFO ][c.a.n.c.c.u.JvmUtil                     :   49]:[       <clinit>] || isMultiInstance:false
+[11/28 11:37:14.361][main      ][INFO ][b.c.PropertySourceBootstrapConfiguration:  109]:[     initialize] || Located property source: [BootstrapPropertySource {name='bootstrapProperties-alien-lawyer-dev.yml,DEFAULT_GROUP'}, BootstrapPropertySource {name='bootstrapProperties-alien-lawyer.yml,DEFAULT_GROUP'}, BootstrapPropertySource {name='bootstrapProperties-alien-lawyer,DEFAULT_GROUP'}]

+ 4 - 0
logging.path_IS_UNDEFINED/WARN.log

@@ -0,0 +1,4 @@
+[11/28 08:38:36.529][main      ][WARN ][c.a.c.n.c.NacosPropertySourceBuilder    :   87]:[  loadNacosData] || Ignore the empty nacos configuration and get it based on dataId[alien-lawyer.yml] & group[DEFAULT_GROUP]
+[11/28 08:38:36.538][main      ][WARN ][c.a.c.n.c.NacosPropertySourceBuilder    :   87]:[  loadNacosData] || Ignore the empty nacos configuration and get it based on dataId[alien-lawyer-dev.yml] & group[DEFAULT_GROUP]
+[11/28 11:37:14.355][main      ][WARN ][c.a.c.n.c.NacosPropertySourceBuilder    :   87]:[  loadNacosData] || Ignore the empty nacos configuration and get it based on dataId[alien-lawyer.yml] & group[DEFAULT_GROUP]
+[11/28 11:37:14.359][main      ][WARN ][c.a.c.n.c.NacosPropertySourceBuilder    :   87]:[  loadNacosData] || Ignore the empty nacos configuration and get it based on dataId[alien-lawyer-dev.yml] & group[DEFAULT_GROUP]