16 Commits c783086ddb ... 38e589ccf7

Tác giả SHA1 Thông báo Ngày
  qrs 38e589ccf7 Merge branch 'sit' of http://8.152.195.41:3000/alien/alien_cloud into store-plantform 1 tuần trước cách đây
  qrs 7df7ca97d8 商家pc端-洗浴汗蒸 1 tuần trước cách đây
  wxd be90c55232 Merge branch 'sit' of http://8.152.195.41:3000/alien/alien_cloud into store-plantform 1 tuần trước cách đây
  zjy cb06655d8f fix(entity): 修正店铺信息实体类字段注解与数据库映射 1 tuần trước cách đây
  qrs 3430d1544e 商家pc端-洗浴汗蒸 1 tuần trước cách đây
  zjy e32afdfc12 feat(license): 新增娱乐经营许可证相关功能 1 tuần trước cách đây
  wxd 4c40d05aa0 feat(store): add business section management with three-level classification 1 tuần trước cách đây
  qrs 5e8e048975 商家pc端-运动健身 1 tuần trước cách đây
  zjy 1aa079e3f3 fix(controller): 修正入驻状态判断及数据承载检查 1 tuần trước cách đây
  zjy b37866a2d2 Merge remote-tracking branch 'origin/store-plantform' into store-plantform 1 tuần trước cách đây
  qrs 4154f849bc 酒水菜单导入 2 tuần trước cách đây
  qrs 632fc10568 菜单导入 2 tuần trước cách đây
  zjy 5a665d8d4b Merge remote-tracking branch 'origin/store-plantform' into store-plantform 2 tuần trước cách đây
  lyx 70222faba8 add:运营活动,活动规则 2 tuần trước cách đây
  zjy dc26fb7dd6 Merge remote-tracking branch 'origin/store-plantform' into store-plantform 2 tuần trước cách đây
  zjy 1cc3089c27 运营活动 2 tuần trước cách đây
58 tập tin đã thay đổi với 6681 bổ sung35 xóa
  1. 25 0
      alien-entity/src/main/java/shop/alien/entity/second/vo/StoreImgDTO.java
  2. 29 3
      alien-entity/src/main/java/shop/alien/entity/store/StoreInfo.java
  3. 16 2
      alien-entity/src/main/java/shop/alien/entity/store/dto/StoreInfoDto.java
  4. 3 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StoreMainInfoVo.java
  5. 46 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StoreMenuImportVo.java
  6. 101 0
      alien-entity/src/main/java/shop/alien/entity/storePlatform/StoreOperationalActivity.java
  7. 77 0
      alien-entity/src/main/java/shop/alien/entity/storePlatform/vo/StoreOperationalActivityDTO.java
  8. 58 0
      alien-entity/src/main/java/shop/alien/entity/storePlatform/vo/StoreOperationalActivityVO.java
  9. 17 0
      alien-entity/src/main/java/shop/alien/mapper/storePlantform/StoreOperationalActivityMapper.java
  10. 32 0
      alien-entity/src/main/resources/mapper/storePlatform/StoreOperationalActivityMapper.xml
  11. 285 0
      alien-store-platform/doc/OPERATIONAL_ACTIVITY_README.md
  12. 1 1
      alien-store-platform/src/main/java/shop/alien/storeplatform/AlienStorePlatformApplication.java
  13. 178 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/controller/BusinessSectionController.java
  14. 63 4
      alien-store-platform/src/main/java/shop/alien/storeplatform/controller/LicenseController.java
  15. 1 1
      alien-store-platform/src/main/java/shop/alien/storeplatform/controller/LifeCouponPlatformController.java
  16. 148 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/controller/OperationalActivityController.java
  17. 72 13
      alien-store-platform/src/main/java/shop/alien/storeplatform/controller/StoreMenuPlatformController.java
  18. 189 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/controller/StorePlatformBarMenuController.java
  19. 186 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/controller/StorePlatformBathFacilityServiceController.java
  20. 187 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/controller/StorePlatformSportsEquipmentFacilityController.java
  21. 97 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/controller/StorePlatformStoreStaffConfigController.java
  22. 90 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/entity/StorePlatformBathFacility.java
  23. 82 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/entity/StorePlatformSportsEquipmentFacility.java
  24. 112 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/entity/StorePlatformStoreMenu.java
  25. 103 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/entity/StorePlatformStoreStaffConfig.java
  26. 7 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/feign/AlienStoreFeign.java
  27. 10 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/mapper/StorePlatformBathFacilityServiceMapper.java
  28. 9 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/mapper/StorePlatformSportsEquipmentFacilityMapper.java
  29. 9 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/mapper/StorePlatformStoreMenuMapper.java
  30. 9 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/mapper/StorePlatformStoreStaffConfigMapper.java
  31. 59 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/BusinessSectionService.java
  32. 23 2
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/LicenseService.java
  33. 71 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/OperationalActivityService.java
  34. 10 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/StoreMenuPlatformService.java
  35. 91 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/StorePlatformBarMenuService.java
  36. 85 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/StorePlatformBathFacilityService.java
  37. 93 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/StorePlatformSportsEquipmentFacilityService.java
  38. 48 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/StorePlatformStoreStaffConfigService.java
  39. 366 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/BusinessSectionServiceImpl.java
  40. 170 5
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/LicenseServiceImpl.java
  41. 300 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/OperationalActivityServiceImpl.java
  42. 44 1
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StoreManageServiceImpl.java
  43. 416 3
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StoreMenuPlatformServiceImpl.java
  44. 637 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StorePlatformBarMenuServiceImpl.java
  45. 655 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StorePlatformBathFacilityServiceImpl.java
  46. 4 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StorePlatformLoginServiceImpl.java
  47. 5 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StorePlatformRenovationServiceImpl.java
  48. 430 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StorePlatformSportsEquipmentFacilityServiceImpl.java
  49. 143 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StorePlatformStoreStaffConfigServiceImpl.java
  50. 13 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/vo/ExcelImportVo.java
  51. 40 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/vo/StorePlatformBathFacilityImportVo.java
  52. 31 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/vo/StorePlatformSportsEquipmentFacilityImportVo.java
  53. 47 0
      alien-store-platform/src/main/java/shop/alien/storeplatform/vo/StorePlatformStoreMenuImportVo.java
  54. BIN
      alien-store-platform/src/main/resources/templates/洗浴汗蒸导入模板.xlsx
  55. BIN
      alien-store-platform/src/main/resources/templates/美食导入模板.xlsx
  56. BIN
      alien-store-platform/src/main/resources/templates/运动健身导入模板.xlsx
  57. BIN
      alien-store-platform/src/main/resources/templates/酒水餐食导入模板.xlsx
  58. 658 0
      alien-store-platform/接口文档/OCR识别接口文档.md

+ 25 - 0
alien-entity/src/main/java/shop/alien/entity/second/vo/StoreImgDTO.java

@@ -0,0 +1,25 @@
+package shop.alien.entity.second.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import shop.alien.entity.store.StoreImg;
+
+import java.util.Date;
+
+/**
+ * 上传图片vo
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2025/1/13 10:35
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@JsonInclude
+public class StoreImgDTO extends StoreImg {
+
+    @JsonFormat(pattern = "yyyy/MM/dd", timezone = "GMT+8")
+    Date expirationTime;
+}

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

@@ -235,7 +235,33 @@ public class StoreInfo {
     @TableField("update_renew_contract_time")
     private Date  updateRenewContractTime;
 
-    @ApiModelProperty(value = "是否提供餐食")
-    @TableField("meals_flag")
-    private Integer  mealsFlag;
+    @ApiModelProperty(value = "分类id(词典表 键为 business_classify)")
+    @TableField("business_classify")
+    private String businessClassify;
+
+    @ApiModelProperty(value = "分类名称")
+    @TableField("business_classify_name")
+    private String businessClassifyName;
+
+    @ApiModelProperty(value = "是否提供餐食 0 否 1 是")
+    @TableField("meal_provided")
+    private Integer mealProvided;
+
+    @ApiModelProperty(value = "娱乐经营许可证状态 字典 foodLicenceStatus")
+    @TableField("entertainment_licence_status")
+    private Integer entertainmentLicenceStatus;
+
+    @ApiModelProperty(value = "娱乐经营许可证失败原因")
+    @TableField("entertainment_licence_reason")
+    private String entertainmentLicenceReason;
+
+    @ApiModelProperty(value = "娱乐经营许可证到期时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @TableField("entertainment_licence_expiration_time")
+    private Date entertainmentLicenceExpirationTime;
+
+    @ApiModelProperty(value = "变更娱乐经营许可证提交时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @TableField("update_entertainment_licence_time")
+    private Date updateEntertainmentLicenceTime;
 }

+ 16 - 2
alien-entity/src/main/java/shop/alien/entity/store/dto/StoreInfoDto.java

@@ -180,6 +180,20 @@ public class StoreInfoDto {
     @DateTimeFormat(pattern = "yyyy-MM-dd")
     private Date foodLicenceExpirationTime;
 
-    @ApiModelProperty(value = "是否提供餐食")
-    private Integer  mealsFlag;
+    @ApiModelProperty(value = "分类id(词典表 键为 business_classify)")
+    private  List<String> businessClassifyList;
+
+    @ApiModelProperty(value = "分类名称")
+    private String businessClassifyName;
+
+    @ApiModelProperty(value = "分类id(词典表 键为 business_classify)")
+    @TableField("business_classify")
+    private String businessClassify;
+
+    @ApiModelProperty(value = "是否提供餐食 0=不提供  1=提供")
+    @TableField("meal_provided")
+    private Integer mealProvided;
+
+    @ApiModelProperty(value = "娱乐经营许可证")
+    private List<String> entertainmentLicenseAddress;
 }

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

@@ -83,6 +83,9 @@ public class StoreMainInfoVo extends StoreInfo {
     @ApiModelProperty(value = "经营种类ids")
     private List<String> businessTypesList;
 
+    @ApiModelProperty(value = "分类DistIds")
+    private List<String> businessClassifyList;
+
     @ApiModelProperty(value = "到期时间")
     private String expirationDate;
 

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

@@ -0,0 +1,46 @@
+package shop.alien.entity.store.vo;
+
+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 shop.alien.entity.store.excelVo.util.ExcelImage;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 二期-门店菜单
+ *
+ * @author ssk
+ * @since 2024-12-05
+ */
+@Data
+@JsonInclude
+@ApiModel(value = "StoreMenuImportVo对象")
+public class StoreMenuImportVo {
+
+    @ApiModelProperty(value = "菜品名称")
+    private String dishName;
+
+    @ApiModelProperty(value = "价格")
+    private BigDecimal dishPrice;
+
+    @ApiModelProperty(value = "成本价")
+    private BigDecimal costPrice;
+
+//    @ApiModelProperty(value = "单位")
+//    private String dishesUnit;
+
+    @ApiModelProperty(value = "图片")
+    @ExcelImage
+    private String img;
+
+    @ApiModelProperty(value = "描述")
+    private String description;
+
+    @ApiModelProperty(value = "是否推荐")
+    private Integer dishType;
+}

+ 101 - 0
alien-entity/src/main/java/shop/alien/entity/storePlatform/StoreOperationalActivity.java

@@ -0,0 +1,101 @@
+package shop.alien.entity.storePlatform;
+
+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-11-26
+ */
+@Data
+@JsonInclude
+@TableName("store_operational_activity")
+@ApiModel(value = "StoreOperationalActivity对象", description = "运营活动表")
+public class StoreOperationalActivity {
+
+    @ApiModelProperty(value = "主键")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "商户ID")
+    @TableField("store_id")
+    private Integer storeId;
+
+    @ApiModelProperty(value = "活动名称")
+    @TableField("activity_name")
+    private String activityName;
+
+    @ApiModelProperty(value = "活动宣传图URL")
+    @TableField("promotional_image")
+    private String promotionalImage;
+
+    @ApiModelProperty(value = "活动开始时间")
+    @TableField("start_time")
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    private Date startTime;
+
+    @ApiModelProperty(value = "活动结束时间")
+    @TableField("end_time")
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    private Date endTime;
+
+    @ApiModelProperty(value = "用户可参与次数,0表示不限制")
+    @TableField("participation_limit")
+    private Integer participationLimit;
+
+    @ApiModelProperty(value = "活动规则:VERIFY_AND_COMMENT-核销并评论, VERIFY-核销, CHECK_IN-打卡")
+    @TableField("activity_rule")
+    private String activityRule;
+
+    @ApiModelProperty(value = "奖励类型:COUPON-优惠券, RED_PACKET-红包")
+    @TableField("reward_type")
+    private String rewardType;
+
+    @ApiModelProperty(value = "优惠券ID,关联优惠券表")
+    @TableField("coupon_id")
+    private Integer couponId;
+
+    @ApiModelProperty(value = "优惠券发放数量")
+    @TableField("coupon_quantity")
+    private Integer couponQuantity;
+
+    @ApiModelProperty(value = "状态:0-禁用, 1-启用")
+    @TableField("status")
+    private Integer status;
+
+    @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
+    @TableField("delete_flag")
+    @TableLogic
+    private Integer deleteFlag;
+
+    @ApiModelProperty(value = "创建时间")
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "创建人ID")
+    @TableField("created_user_id")
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "修改时间")
+    @TableField(value = "updated_time", fill = FieldFill.UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+
+    @ApiModelProperty(value = "修改人ID")
+    @TableField("updated_user_id")
+    private Integer updatedUserId;
+
+    @ApiModelProperty(value = "拒绝原因")
+    @TableField("approval_comments")
+    private String approvalComments;
+}
+

+ 77 - 0
alien-entity/src/main/java/shop/alien/entity/storePlatform/vo/StoreOperationalActivityDTO.java

@@ -0,0 +1,77 @@
+package shop.alien.entity.storePlatform.vo;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import shop.alien.entity.store.StoreImg;
+
+import java.util.Date;
+
+/**
+ * 运营活动 DTO
+ * 用于接收前端请求参数
+ *
+ * @author system
+ * @since 2025-11-26
+ */
+@Data
+@ApiModel(value = "StoreOperationalActivityDTO", description = "运营活动传输对象")
+public class StoreOperationalActivityDTO {
+
+    @ApiModelProperty(value = "主键ID(更新时必填)")
+    private Integer id;
+
+    @ApiModelProperty(value = "商户ID")
+    private Integer storeId;
+
+    @ApiModelProperty(value = "活动名称", required = true)
+    private String activityName;
+
+    @ApiModelProperty(value = "活动宣传图URL")
+    private String promotionalImage;
+
+    @ApiModelProperty(value = "活动开始时间", required = true)
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    private Date startTime;
+
+    @ApiModelProperty(value = "活动结束时间", required = true)
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    private Date endTime;
+
+    @ApiModelProperty(value = "用户可参与次数,0表示不限制")
+    private Integer participationLimit;
+
+    @ApiModelProperty(value = "活动规则:VERIFY_AND_COMMENT-核销并评论, VERIFY-核销, CHECK_IN-打卡", required = true)
+    private String activityRule;
+
+    @ApiModelProperty(value = "奖励类型:COUPON-优惠券, RED_PACKET-红包")
+    private String rewardType;
+
+    @ApiModelProperty(value = "优惠券ID")
+    private Integer couponId;
+
+    @ApiModelProperty(value = "优惠券发放数量")
+    private Integer couponQuantity;
+
+    @ApiModelProperty(value = "状态:0-禁用, 1-启用")
+    private Integer status;
+
+    @ApiModelProperty(value = "拒绝原因")
+    private String approvalComments;
+
+    @ApiModelProperty(value = "活动标题图片")
+    private StoreImg activityTitleImg;
+
+    @ApiModelProperty(value = "活动详情图片")
+    private StoreImg activityDetailImg;
+
+    @ApiModelProperty(value = "页码")
+    private Integer pageNum;
+
+    @ApiModelProperty(value = "页数")
+    private Integer pageSize;
+
+}
+

+ 58 - 0
alien-entity/src/main/java/shop/alien/entity/storePlatform/vo/StoreOperationalActivityVO.java

@@ -0,0 +1,58 @@
+package shop.alien.entity.storePlatform.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 shop.alien.entity.storePlatform.StoreOperationalActivity;
+
+/**
+ * 运营活动 VO
+ * 用于返回给前端的数据对象
+ *
+ * @author system
+ * @since 2025-11-26
+ */
+@Data
+@JsonInclude
+@ApiModel(value = "StoreOperationalActivityVO", description = "运营活动返回对象")
+public class StoreOperationalActivityVO extends StoreOperationalActivity {
+
+    @ApiModelProperty(value = "活动状态名称")
+    private String statusName;
+
+    @ApiModelProperty(value = "活动规则名称")
+    private String activityRuleName;
+
+    @ApiModelProperty(value = "奖励类型名称")
+    private String rewardTypeName;
+
+    @ApiModelProperty(value = "优惠券名称")
+    private String couponName;
+
+    @ApiModelProperty(value = "商户名称")
+    private String storeName;
+
+    @ApiModelProperty(value = "活动状态:0-未开始, 1-进行中, 2-已结束")
+    private Integer activityStatus;
+
+    @ApiModelProperty(value = "已参与人数")
+    private Integer participationCount;
+
+    @ApiModelProperty(value = "剩余优惠券数量")
+    private Integer remainingCouponQuantity;
+
+    @ApiModelProperty(value = "活动标题图片")
+    private StoreImg activityTitleImg;
+
+    @ApiModelProperty(value = "活动详情图片")
+    private StoreImg activityDetailImg;
+
+    @ApiModelProperty(value = "活动标题图片")
+    private String activityTitleImgUrl;
+
+    @ApiModelProperty(value = "活动详情图片")
+    private String activityDetailImgUrl;
+}
+

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

@@ -0,0 +1,17 @@
+package shop.alien.mapper.storePlantform;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.entity.storePlatform.StoreOperationalActivity;
+
+/**
+ * 运营活动 Mapper 接口
+ *
+ * @author system
+ * @since 2025-11-26
+ */
+@Mapper
+public interface StoreOperationalActivityMapper extends BaseMapper<StoreOperationalActivity> {
+
+}
+

+ 32 - 0
alien-entity/src/main/resources/mapper/storePlatform/StoreOperationalActivityMapper.xml

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="shop.alien.mapper.storePlantform.StoreOperationalActivityMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="shop.alien.entity.storePlatform.StoreOperationalActivity">
+        <id column="id" property="id" />
+        <result column="store_id" property="storeId" />
+        <result column="activity_name" property="activityName" />
+        <result column="promotional_image" property="promotionalImage" />
+        <result column="start_time" property="startTime" />
+        <result column="end_time" property="endTime" />
+        <result column="participation_limit" property="participationLimit" />
+        <result column="activity_rule" property="activityRule" />
+        <result column="reward_type" property="rewardType" />
+        <result column="coupon_id" property="couponId" />
+        <result column="coupon_quantity" property="couponQuantity" />
+        <result column="status" property="status" />
+        <result column="delete_flag" property="deleteFlag" />
+        <result column="created_time" property="createdTime" />
+        <result column="created_user_id" property="createdUserId" />
+        <result column="updated_time" property="updatedTime" />
+        <result column="updated_user_id" property="updatedUserId" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, store_id, activity_name, promotional_image, start_time, end_time, participation_limit, activity_rule, reward_type, coupon_id, coupon_quantity, status, delete_flag, created_time, created_user_id, updated_time, updated_user_id
+    </sql>
+
+</mapper>
+

+ 285 - 0
alien-store-platform/doc/OPERATIONAL_ACTIVITY_README.md

@@ -0,0 +1,285 @@
+# 运营活动管理模块
+
+## 功能说明
+
+本模块用于管理商户的运营活动,支持活动的创建、查询、更新和删除操作。活动可以设置参与规则、奖励类型、优惠券等信息。
+
+---
+
+## 数据库表结构
+
+### store_operational_activity 表
+
+```sql
+CREATE TABLE `store_operational_activity` (
+    `id` int NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `store_id` int NULL COMMENT '商户ID',
+    `activity_name` VARCHAR(100) NOT NULL COMMENT '活动名称',
+    `promotional_image` VARCHAR(500) DEFAULT NULL COMMENT '活动宣传图URL',
+    `start_time` DATETIME NOT NULL COMMENT '活动开始时间',
+    `end_time` DATETIME NOT NULL COMMENT '活动结束时间',
+    `participation_limit` int DEFAULT 0 COMMENT '用户可参与次数,0表示不限制',
+    `activity_rule` VARCHAR(50) NOT NULL COMMENT '活动规则:VERIFY_AND_COMMENT-核销并评论, VERIFY-核销, CHECK_IN-打卡',
+    `reward_type` VARCHAR(50) DEFAULT NULL COMMENT '奖励类型:COUPON-优惠券, RED_PACKET-红包',
+    `coupon_id` int DEFAULT NULL COMMENT '优惠券ID,关联优惠券表',
+    `coupon_quantity` int DEFAULT NULL COMMENT '优惠券发放数量',
+    `status` int DEFAULT 1 COMMENT '状态:0-禁用, 1-启用',
+    `delete_flag` int NOT NULL DEFAULT '0' COMMENT '删除标记, 0:未删除, 1:已删除',
+    `created_time` datetime(1) NOT NULL COMMENT '创建时间',
+    `created_user_id` int DEFAULT NULL COMMENT '创建人ID',
+    `updated_time` datetime DEFAULT NULL COMMENT '修改时间',
+    `updated_user_id` int DEFAULT NULL COMMENT '修改人ID',
+    PRIMARY KEY (`id`),
+    INDEX `idx_activity_time` (`start_time`, `end_time`),
+    INDEX `idx_status` (`status`, `delete_flag`),
+    INDEX `idx_coupon_id` (`coupon_id`),
+    INDEX `idx_create_time` (`created_time`)
+) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='运营活动表';
+```
+
+---
+
+## 文件清单
+
+### 1. Entity(实体类)
+**路径**: `alien-entity/src/main/java/shop/alien/entity/store/StoreOperationalActivity.java`
+
+**功能**: 运营活动实体类,对应数据库表 `store_operational_activity`
+
+**主要字段**:
+- `id`: 主键
+- `storeId`: 商户ID
+- `activityName`: 活动名称
+- `promotionalImage`: 活动宣传图URL
+- `startTime`: 活动开始时间
+- `endTime`: 活动结束时间
+- `participationLimit`: 用户可参与次数(0表示不限制)
+- `activityRule`: 活动规则(VERIFY_AND_COMMENT-核销并评论, VERIFY-核销, CHECK_IN-打卡)
+- `rewardType`: 奖励类型(COUPON-优惠券, RED_PACKET-红包)
+- `couponId`: 优惠券ID
+- `couponQuantity`: 优惠券发放数量
+- `status`: 状态(0-禁用, 1-启用)
+- `deleteFlag`: 删除标记(逻辑删除)
+- `createdTime`: 创建时间(自动填充)
+- `createdUserId`: 创建人ID
+- `updatedTime`: 修改时间(自动填充)
+- `updatedUserId`: 修改人ID
+
+---
+
+### 2. DTO(数据传输对象)
+**路径**: `alien-entity/src/main/java/shop/alien/entity/store/vo/StoreOperationalActivityDTO.java`
+
+**功能**: 用于接收前端请求参数,包含参数校验
+
+**校验规则**:
+- `activityName`: 必填,不能为空
+- `startTime`: 必填,不能为空
+- `endTime`: 必填,不能为空
+- `activityRule`: 必填,不能为空
+- 其他字段可选
+
+---
+
+### 3. VO(视图对象)
+**路径**: `alien-entity/src/main/java/shop/alien/entity/store/vo/StoreOperationalActivityVO.java`
+
+**功能**: 用于返回给前端的数据对象,继承自实体类并扩展了额外字段
+
+**扩展字段**:
+- `statusName`: 状态名称
+- `activityRuleName`: 活动规则名称
+- `rewardTypeName`: 奖励类型名称
+- `couponName`: 优惠券名称
+- `storeName`: 商户名称
+- `activityStatus`: 活动状态(0-未开始, 1-进行中, 2-已结束)
+- `participationCount`: 已参与人数
+- `remainingCouponQuantity`: 剩余优惠券数量
+
+---
+
+### 4. Mapper(数据访问层)
+**路径**: `alien-entity/src/main/java/shop/alien/mapper/StoreOperationalActivityMapper.java`
+
+**功能**: 继承 MyBatis-Plus 的 `BaseMapper`,提供基础 CRUD 操作
+
+---
+
+### 5. Mapper XML(映射文件)
+**路径**: `alien-entity/src/main/resources/mapper/StoreOperationalActivityMapper.xml`
+
+**功能**: MyBatis 映射配置文件,定义结果集映射和通用 SQL
+
+---
+
+### 6. Service(服务接口)
+**路径**: `alien-store-platform/src/main/java/shop/alien/storeplatform/service/OperationalActivityService.java`
+
+**功能**: 定义运营活动业务逻辑接口
+
+**方法列表**:
+- `createActivity(StoreOperationalActivityDTO dto)`: 创建运营活动
+- `updateActivity(StoreOperationalActivityDTO dto)`: 更新运营活动
+- `deleteActivity(Integer id)`: 删除运营活动(逻辑删除)
+- `getActivityById(Integer id)`: 根据ID获取活动详情
+- `getActivityListByStoreId(Integer storeId)`: 根据商户ID获取活动列表
+- `getActivityPage(Integer current, Integer size, Integer storeId, Integer status)`: 分页查询活动列表
+- `updateActivityStatus(Integer id, Integer status)`: 启用/禁用活动
+- `getOngoingActivities(Integer storeId)`: 获取进行中的活动列表
+
+---
+
+### 7. ServiceImpl(服务实现类)
+**路径**: `alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/OperationalActivityServiceImpl.java`
+
+**功能**: 实现运营活动业务逻辑
+
+**核心功能**:
+- 活动的增删改查
+- 实体与VO对象的转换
+- 活动状态的自动判断(未开始/进行中/已结束)
+- 活动规则和奖励类型的中文名称转换
+
+---
+
+### 8. Controller(控制器)
+**路径**: `alien-store-platform/src/main/java/shop/alien/storeplatform/controller/OperationalActivityController.java`
+
+**功能**: 提供运营活动管理的 REST API 接口
+
+**API 列表**:
+
+#### 8.1 创建运营活动
+- **接口**: `POST /operationalActivity/create`
+- **参数**: StoreOperationalActivityDTO(JSON)
+- **返回**: R<String>
+
+#### 8.2 更新运营活动
+- **接口**: `POST /operationalActivity/update`
+- **参数**: StoreOperationalActivityDTO(JSON)
+- **返回**: R<String>
+
+#### 8.3 删除运营活动
+- **接口**: `DELETE /operationalActivity/delete`
+- **参数**: id(活动ID)
+- **返回**: R<String>
+
+#### 8.4 根据ID获取活动详情
+- **接口**: `GET /operationalActivity/detail`
+- **参数**: id(活动ID)
+- **返回**: R<StoreOperationalActivityVO>
+
+#### 8.5 根据商户ID获取活动列表
+- **接口**: `GET /operationalActivity/list`
+- **参数**: storeId(商户ID)
+- **返回**: R<List<StoreOperationalActivityVO>>
+
+#### 8.6 分页查询活动列表
+- **接口**: `GET /operationalActivity/page`
+- **参数**: 
+  - current(当前页,默认1)
+  - size(每页大小,默认10)
+  - storeId(商户ID,可选)
+  - status(状态,可选)
+- **返回**: R<IPage<StoreOperationalActivityVO>>
+
+#### 8.7 启用/禁用活动
+- **接口**: `POST /operationalActivity/updateStatus`
+- **参数**: 
+  - id(活动ID)
+  - status(状态:0-禁用, 1-启用)
+- **返回**: R<String>
+
+#### 8.8 获取进行中的活动列表
+- **接口**: `GET /operationalActivity/ongoing`
+- **参数**: storeId(商户ID)
+- **返回**: R<List<StoreOperationalActivityVO>>
+
+---
+
+## 使用示例
+
+### 创建活动
+
+```json
+POST /operationalActivity/create
+
+{
+  "storeId": 1,
+  "activityName": "双十一优惠活动",
+  "promotionalImage": "https://example.com/image.jpg",
+  "startTime": "2025-11-11 00:00:00",
+  "endTime": "2025-11-11 23:59:59",
+  "participationLimit": 1,
+  "activityRule": "VERIFY_AND_COMMENT",
+  "rewardType": "COUPON",
+  "couponId": 100,
+  "couponQuantity": 1000,
+  "status": 1
+}
+```
+
+### 查询活动列表
+
+```
+GET /operationalActivity/list?storeId=1
+```
+
+### 分页查询
+
+```
+GET /operationalActivity/page?current=1&size=10&storeId=1&status=1
+```
+
+---
+
+## 数据字典
+
+### 活动规则(activity_rule)
+- `VERIFY_AND_COMMENT`: 核销并评论
+- `VERIFY`: 核销
+- `CHECK_IN`: 打卡
+
+### 奖励类型(reward_type)
+- `COUPON`: 优惠券
+- `RED_PACKET`: 红包
+
+### 活动状态(status)
+- `0`: 禁用
+- `1`: 启用
+
+### 活动进行状态(activityStatus)
+- `0`: 未开始
+- `1`: 进行中
+- `2`: 已结束
+
+---
+
+## 注意事项
+
+1. **时间校验**: 创建活动时,需要确保结束时间晚于开始时间
+2. **逻辑删除**: 删除操作为逻辑删除,不会真正删除数据
+3. **状态管理**: 活动状态由系统根据当前时间自动判断
+4. **参数校验**: DTO 中包含必填项校验,需要确保必填字段不为空
+5. **优惠券关联**: 如果奖励类型为优惠券,需要填写 couponId 和 couponQuantity
+
+---
+
+## 扩展建议
+
+1. **用户参与记录**: 可以创建用户参与活动记录表,跟踪用户参与情况
+2. **活动统计**: 添加活动效果统计功能,如参与人数、发放优惠券数量等
+3. **活动审核**: 添加活动审核流程,确保活动内容合规
+4. **活动推送**: 集成消息推送功能,在活动开始时通知用户
+5. **活动模板**: 提供常用活动模板,简化活动创建流程
+
+---
+
+## 更新日志
+
+### 2025-11-26
+- 初始版本发布
+- 实现基础的增删改查功能
+- 支持活动状态管理
+- 支持分页查询
+

+ 1 - 1
alien-store-platform/src/main/java/shop/alien/storeplatform/AlienStorePlatformApplication.java

@@ -16,7 +16,7 @@ import org.springframework.scheduling.annotation.EnableScheduling;
         "shop.alien.config.properties",
         "shop.alien.config.redis"})
 @EnableSwaggerBootstrapUI
-@MapperScan({"shop.alien.mapper"})
+@MapperScan({"shop.alien.mapper", "shop.alien.storeplatform.mapper"})
 @SpringBootApplication
 @EnableFeignClients(basePackages = "shop.alien.storeplatform.feign")
 @EnableScheduling

+ 178 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/controller/BusinessSectionController.java

@@ -0,0 +1,178 @@
+package shop.alien.storeplatform.controller;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiOperationSupport;
+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.vo.StoreDictionaryVo;
+import shop.alien.storeplatform.service.BusinessSectionService;
+
+import java.util.List;
+
+/**
+ * 经营板块管理控制器(平台端)
+ * 提供经营板块的三级分类查询接口
+ * 
+ * <p>业务说明:
+ * <ul>
+ *   <li>一级分类:经营板块(特色美食、酒店/民宿、KTV等)</li>
+ *   <li>二级分类:各板块下的子分类</li>
+ *   <li>三级分类:各二级分类下的细分类</li>
+ * </ul>
+ * </p>
+ *
+ * @author alien-cloud
+ * @date 2025-12-01
+ */
+@Api(tags = {"商户平台-经营板块管理"})
+@Slf4j
+@CrossOrigin
+@RestController
+@RequestMapping("/businessSection")
+@RequiredArgsConstructor
+public class BusinessSectionController {
+
+    private final BusinessSectionService businessSectionService;
+
+    /**
+     * 查询经营板块一级分类列表
+     * 返回所有一级分类(特色美食、酒店/民宿、KTV、洗浴汗蒸等)
+     *
+     * @return R<List<StoreDictionaryVo>> 一级分类列表
+     * @author alien-cloud
+     * @date 2025-12-01
+     */
+    @ApiOperation("查询经营板块一级分类列表")
+    @ApiOperationSupport(order = 1)
+    @GetMapping("/getFirstLevelList")
+    public R<List<StoreDictionaryVo>> getFirstLevelList() {
+        log.info("BusinessSectionController.getFirstLevelList - 查询一级分类列表");
+        try {
+            List<StoreDictionaryVo> result = businessSectionService.getFirstLevelList();
+            log.info("BusinessSectionController.getFirstLevelList - 查询成功,共 {} 条数据", result.size());
+            return R.data(result);
+        } catch (Exception e) {
+            log.error("BusinessSectionController.getFirstLevelList ERROR: {}", e.getMessage(), e);
+            return R.fail("查询失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 查询经营板块二级分类列表
+     * 根据一级分类ID查询对应的二级分类
+     *
+     * @param parentDictId 一级分类的字典ID(dict_id)
+     * @return R<List<StoreDictionaryVo>> 二级分类列表
+     * @author alien-cloud
+     * @date 2025-12-01
+     */
+    @ApiOperation("查询经营板块二级分类列表")
+    @ApiOperationSupport(order = 2)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "parentDictId", value = "一级分类字典ID(如:1-特色美食,2-酒店/民宿)", 
+                    dataType = "String", paramType = "query", required = true, example = "1")
+    })
+    @GetMapping("/getSecondLevelList")
+    public R<List<StoreDictionaryVo>> getSecondLevelList(
+            @RequestParam("parentDictId") String parentDictId) {
+        log.info("BusinessSectionController.getSecondLevelList - 查询二级分类列表,parentDictId={}", parentDictId);
+        try {
+            List<StoreDictionaryVo> result = businessSectionService.getSecondLevelList(parentDictId);
+            log.info("BusinessSectionController.getSecondLevelList - 查询成功,共 {} 条数据", result.size());
+            return R.data(result);
+        } catch (Exception e) {
+            log.error("BusinessSectionController.getSecondLevelList ERROR: parentDictId={}, error={}", 
+                    parentDictId, e.getMessage(), e);
+            return R.fail("查询失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 查询经营板块三级分类列表
+     * 根据二级分类ID查询对应的三级分类
+     *
+     * @param parentDictId 二级分类的字典ID(dict_id)
+     * @return R<List<StoreDictionaryVo>> 三级分类列表
+     * @author alien-cloud
+     * @date 2025-12-01
+     */
+    @ApiOperation("查询经营板块三级分类列表")
+    @ApiOperationSupport(order = 3)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "parentDictId", value = "二级分类字典ID", 
+                    dataType = "String", paramType = "query", required = true, example = "11")
+    })
+    @GetMapping("/getThirdLevelList")
+    public R<List<StoreDictionaryVo>> getThirdLevelList(
+            @RequestParam("parentDictId") String parentDictId) {
+        log.info("BusinessSectionController.getThirdLevelList - 查询三级分类列表,parentDictId={}", parentDictId);
+        try {
+            List<StoreDictionaryVo> result = businessSectionService.getThirdLevelList(parentDictId);
+            log.info("BusinessSectionController.getThirdLevelList - 查询成功,共 {} 条数据", result.size());
+            return R.data(result);
+        } catch (Exception e) {
+            log.error("BusinessSectionController.getThirdLevelList ERROR: parentDictId={}, error={}", 
+                    parentDictId, e.getMessage(), e);
+            return R.fail("查询失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 查询经营板块树形结构(三级联动)
+     * 返回完整的三级分类树形结构,包含所有层级的子节点
+     *
+     * @return R<List<StoreDictionaryVo>> 树形结构数据(包含一级、二级、三级)
+     * @author alien-cloud
+     * @date 2025-12-01
+     */
+    @ApiOperation("查询经营板块树形结构(三级联动)")
+    @ApiOperationSupport(order = 4)
+    @GetMapping("/getTreeStructure")
+    public R<List<StoreDictionaryVo>> getTreeStructure() {
+        log.info("BusinessSectionController.getTreeStructure - 查询三级树形结构");
+        try {
+            List<StoreDictionaryVo> result = businessSectionService.getTreeStructure();
+            log.info("BusinessSectionController.getTreeStructure - 查询成功");
+            return R.data(result);
+        } catch (Exception e) {
+            log.error("BusinessSectionController.getTreeStructure ERROR: {}", e.getMessage(), e);
+            return R.fail("查询失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 根据字典ID查询完整路径
+     * 返回从一级到当前级别的完整分类路径
+     *
+     * @param dictId 字典ID
+     * @return R<List<StoreDictionaryVo>> 分类路径(从一级到当前级)
+     * @author alien-cloud
+     * @date 2025-12-01
+     */
+    @ApiOperation("根据字典ID查询完整路径")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "dictId", value = "字典ID", 
+                    dataType = "String", paramType = "query", required = true, example = "1")
+    })
+    @GetMapping("/getFullPath")
+    public R<List<StoreDictionaryVo>> getFullPath(
+            @RequestParam("dictId") String dictId) {
+        log.info("BusinessSectionController.getFullPath - 查询完整路径,dictId={}", dictId);
+        try {
+            List<StoreDictionaryVo> result = businessSectionService.getFullPath(dictId);
+            log.info("BusinessSectionController.getFullPath - 查询成功");
+            return R.data(result);
+        } catch (Exception e) {
+            log.error("BusinessSectionController.getFullPath ERROR: dictId={}, error={}", 
+                    dictId, e.getMessage(), e);
+            return R.fail("查询失败:" + e.getMessage());
+        }
+    }
+}
+

+ 63 - 4
alien-store-platform/src/main/java/shop/alien/storeplatform/controller/LicenseController.java

@@ -5,8 +5,10 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.web.bind.annotation.*;
 import shop.alien.entity.result.R;
+import shop.alien.entity.second.vo.StoreImgDTO;
 import shop.alien.entity.store.StoreImg;
 import shop.alien.entity.store.WebAudit;
+import shop.alien.entity.store.vo.StoreImgVo;
 import shop.alien.entity.storePlatform.vo.StoreLicenseHistoryDto;
 import shop.alien.entity.storePlatform.vo.StoreLicenseHistoryVO;
 import shop.alien.mapper.WebAuditMapper;
@@ -49,9 +51,9 @@ public class LicenseController {
             @ApiImplicitParam(name = "id", value = "城市", dataType = "Integer", paramType = "query", required = false)
     })
     @GetMapping("/queryContractList")
-    public R<List<StoreImg>> queryContractList(Integer id) {
+    public R<List<StoreImgDTO>> queryContractList(Integer id) {
         log.info("LicenseController.queryContractList?id={}", id);
-        List<StoreImg> result = licenseService.getContractList(id);
+        List<StoreImgDTO> result = licenseService.getContractList(id);
         return R.data(result);
     }
 
@@ -61,9 +63,9 @@ public class LicenseController {
             @ApiImplicitParam(name = "id", value = "城市", dataType = "Integer", paramType = "query", required = false)
     })
     @GetMapping("/queryFoodLicenceList")
-    public R<List<StoreImg>> queryFoodLicenceList(Integer id) {
+    public R<List<StoreImgDTO>> queryFoodLicenceList(Integer id) {
         log.info("LicenseController.queryFoodLicenceList?id={}", id);
-        List<StoreImg> result = licenseService.getFoodLicenceList(id);
+        List<StoreImgDTO> result = licenseService.getFoodLicenceList(id);
         return R.data(result);
     }
 
@@ -145,6 +147,63 @@ public class LicenseController {
             return R.fail(e.getMessage());
         }
     }
+
+    @ApiOperation("获取娱乐经营许可证图片信息")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "Integer", value = "城市", dataType = "String", paramType = "query", required = false)
+    })
+    @GetMapping("/queryEntertainmentLicenseList")
+    public R<List<StoreImgDTO>> queryEntertainmentLicenseList(Integer id) {
+        log.info("LicenseController.queryEntertainmentLicenseList?id={}", id);
+        List<StoreImgDTO> result = licenseService.getEntertainmentLicenseList(id);
+        return R.data(result);
+    }
+
+
+    @ApiOperation(value = "门店装修-修改娱乐经营许可证")
+    @PostMapping("/uploadEntertainmentLicence")
+    public R<String> uploadEntertainmentLicence(@RequestBody StoreImg storeImg) {
+        log.info("StoreInfoController.uploadEntertainmentLicence?storeImg={}", storeImg);
+        int num = licenseService.uploadEntertainmentLicence(storeImg);
+        if (num > 0) {
+            WebAudit webAudit = new WebAudit();
+            webAudit.setStoreInfoId(storeImg.getStoreId().toString());
+            webAudit.setType("8");
+            webAudit.setStatus("0");
+            webAudit.setContent("娱乐经营许可证");
+            webAuditMapper.insert(webAudit);
+            return R.success("娱乐经营许可证图片添加成功");
+        }
+        return R.fail("娱乐经营许可证图片添加失败");
+    }
+
+    @ApiOperation("根据商户ID获取经营许可证历史记录")
+    @ApiOperationSupport(order = 3)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "商户ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/queryEntertainmentByStatusList")
+    public R<List<StoreLicenseHistoryDto>> queryEntertainmentByStatusList(@RequestParam("storeId") Integer storeId) {
+        log.info("LicenseController.queryLicenceByStatusList: storeId={}", storeId);
+        try {
+            List<StoreLicenseHistoryDto> result = licenseService.queryEntertainmentByStatusList(storeId);
+            return R.data(result);
+        } catch (Exception e) {
+            log.error("LicenseController.queryLicenceByStatusList ERROR: {}", e.getMessage(), e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+    @ApiOperation(value = "门店装修-娱乐经营许可证状态")
+    @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "门店id", dataType = "int", paramType = "query", required = true)})
+    @GetMapping("/queryStoreEntertainmentStatus")
+    public R<Map<String, Object>> queryStoreEntertainmentStatus(int id) {
+        log.info("StoreInfoController.getStoreFoodLicenceStatus?id={}", id);
+        return R.data(licenseService.getStoreEntertainmentStatus(id));
+    }
+
+
 }
 
 

+ 1 - 1
alien-store-platform/src/main/java/shop/alien/storeplatform/controller/LifeCouponPlatformController.java

@@ -257,7 +257,7 @@ public class LifeCouponPlatformController {
                                                       @RequestBody LifeCouponPlatformVo lifeCouponPlatformVo) {
         log.info("LifeCouponController.getCouponList?lifeCouponPlatformVo={}", lifeCouponPlatformVo);
         if (StringUtils.isBlank(lifeCouponPlatformVo.getStoreId())) {
-            return R.data(null, "当前用户未入!!!");
+            return R.data(null, "当前用户未入!!!");
         }
         DiscountCouponPlatformVo vo = new DiscountCouponPlatformVo();
         if (lifeCouponPlatformVo.getCouponType() == 1) {

+ 148 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/controller/OperationalActivityController.java

@@ -0,0 +1,148 @@
+package shop.alien.storeplatform.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.storePlatform.vo.StoreOperationalActivityDTO;
+import shop.alien.entity.storePlatform.vo.StoreOperationalActivityVO;
+import shop.alien.storeplatform.service.OperationalActivityService;
+
+import java.util.List;
+
+/**
+ * 运营活动管理控制器
+ *
+ * @author system
+ * @since 2025-11-26
+ */
+@Slf4j
+@Api(tags = {"商家端-运营活动管理"})
+@ApiSort(10)
+@CrossOrigin
+@RestController
+@RequestMapping("/operationalActivity")
+@RequiredArgsConstructor
+public class OperationalActivityController {
+
+    private final OperationalActivityService activityService;
+
+    @ApiOperation("创建运营活动")
+    @ApiOperationSupport(order = 1)
+    @PostMapping("/create")
+    public R<String> createActivity(@RequestBody StoreOperationalActivityDTO dto) {
+        log.info("OperationalActivityController.createActivity: dto={}", dto);
+        try {
+            int result = activityService.createActivity(dto);
+            if (result > 0) {
+                return R.success("活动创建成功");
+            }
+            return R.fail("活动创建失败");
+        } catch (Exception e) {
+            log.error("OperationalActivityController.createActivity ERROR: {}", e.getMessage(), e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+    @ApiOperation("更新运营活动")
+    @ApiOperationSupport(order = 2)
+    @PostMapping("/update")
+    public R<String> updateActivity(@RequestBody StoreOperationalActivityDTO dto) {
+        log.info("OperationalActivityController.updateActivity: dto={}", dto);
+        try {
+            int result = activityService.updateActivity(dto);
+            if (result > 0) {
+                return R.success("活动更新成功");
+            }
+            return R.fail("活动更新失败");
+        } catch (Exception e) {
+            log.error("OperationalActivityController.updateActivity ERROR: {}", e.getMessage(), e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+    @ApiOperation("删除运营活动")
+    @ApiOperationSupport(order = 3)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "活动ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/delete")
+    public R<String> deleteActivity(@RequestParam("id") Integer id) {
+        log.info("OperationalActivityController.deleteActivity: id={}", id);
+        try {
+            int result = activityService.deleteActivity(id);
+            if (result > 0) {
+                return R.success("活动删除成功");
+            }
+            return R.fail("活动删除失败");
+        } catch (Exception e) {
+            log.error("OperationalActivityController.deleteActivity ERROR: {}", e.getMessage(), e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+    @ApiOperation("根据ID获取活动详情")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "活动ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/queryActivityById")
+    public R<StoreOperationalActivityVO> queryActivityById(@RequestParam("id") Integer id) {
+        log.info("OperationalActivityController.getActivityById: id={}", id);
+        try {
+            StoreOperationalActivityVO result = activityService.queryActivityById(id);
+            return R.data(result);
+        } catch (Exception e) {
+            log.error("OperationalActivityController.getActivityById ERROR: {}", e.getMessage(), e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+    @ApiOperation("根据商户ID获取活动列表")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "商户ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @PostMapping("/queryActivityList")
+    public R<IPage<StoreOperationalActivityVO>> queryActivityList(@RequestBody StoreOperationalActivityDTO dto) {
+        log.info("OperationalActivityController.queryActivityList: storeId={}, status={}, activityName={}", dto.getStoreId(), dto.getStatus(), dto.getActivityName());
+        try {
+            if (dto.getStoreId() == null || dto.getStoreId() <= 0) {
+                 return R.data(null, "暂无承载数据!!!");
+            }
+            IPage<StoreOperationalActivityVO> result = activityService.queryActivityList( dto.getStoreId(), dto.getStatus(), dto.getActivityName(), dto.getPageNum(), dto.getPageSize());
+            return R.data(result);
+        } catch (Exception e) {
+            log.error("OperationalActivityController.qeryActivityList ERROR: {}", e.getMessage(), e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+
+    @ApiOperation("启用/禁用活动")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "活动ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "status", value = "状态:0-禁用, 1-启用", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/updateStatus")
+    public R<String> updateActivityStatus(
+            @RequestParam("id") Integer id,
+            @RequestParam("status") Integer status) {
+        log.info("OperationalActivityController.updateActivityStatus: id={}, status={}", id, status);
+        try {
+            int result = activityService.updateActivityStatus(id, status);
+            if (result > 0) {
+                return R.success("状态更新成功");
+            }
+            return R.fail("状态更新失败");
+        } catch (Exception e) {
+            log.error("OperationalActivityController.updateActivityStatus ERROR: {}", e.getMessage(), e);
+            return R.fail(e.getMessage());
+        }
+    }
+}
+

+ 72 - 13
alien-store-platform/src/main/java/shop/alien/storeplatform/controller/StoreMenuPlatformController.java

@@ -1,25 +1,23 @@
 package shop.alien.storeplatform.controller;
 
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiImplicitParam;
-import io.swagger.annotations.ApiImplicitParams;
-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.CrossOrigin;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
 import shop.alien.entity.result.R;
 import shop.alien.entity.store.StoreMenu;
 import shop.alien.entity.store.vo.StoreMenuVo;
 import shop.alien.storeplatform.service.StoreMenuPlatformService;
+import shop.alien.storeplatform.vo.ExcelImportVo;
 
+import javax.servlet.http.HttpServletResponse;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
 import java.util.List;
 
 /**
@@ -125,6 +123,67 @@ public class StoreMenuPlatformController {
         log.info("StoreMenuController.getMenuLikeStatus?userId={}&menuId={}", userId, menuId);
         return R.data(storeMenuService.getMenuLikeStatus(userId, menuId));
     }
+
+    @ApiOperation("Excel导入门店菜单")
+    @ApiOperationSupport(order = 9)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "file", value = "Excel文件", dataType = "file", paramType = "form", required = true),
+            @ApiImplicitParam(name = "storeId", value = "门店id", dataType = "Integer", paramType = "query", required = true)
+    })
+    @PostMapping("/importMenu")
+    public R<String> importMenu(ExcelImportVo vo) {
+        log.info("StoreMenuPlatformController.importMenu?storeId={}", vo.getStoreId());
+        return storeMenuService.importMenuFromExcel(vo.getFile(), vo.getStoreId());
+    }
+
+    @ApiOperation("下载美食导入模板")
+    @ApiOperationSupport(order = 10)
+    @GetMapping("/downloadTemplate")
+    public void downloadTemplate(HttpServletResponse response) {
+        log.info("StoreMenuPlatformController.downloadTemplate");
+        InputStream inputStream = null;
+        OutputStream outputStream = null;
+        
+        try {
+            // 从resources/templates目录读取模板文件
+            Resource resource = new ClassPathResource("templates/美食导入模板.xlsx");
+            inputStream = resource.getInputStream();
+            
+            // 设置响应头
+            response.reset();
+            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+            response.setCharacterEncoding("utf-8");
+            
+            String fileName = "美食导入模板.xlsx";
+            String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString()).replaceAll("\\+", "%20");
+            response.setHeader("Content-Disposition", "attachment;filename=\"" + encodedFileName + "\";filename*=utf-8''" + encodedFileName);
+            
+            // 输出文件流
+            outputStream = response.getOutputStream();
+            byte[] buffer = new byte[1024];
+            int length;
+            while ((length = inputStream.read(buffer)) > 0) {
+                outputStream.write(buffer, 0, length);
+            }
+            outputStream.flush();
+            
+            log.info("美食导入模板下载成功");
+        } catch (Exception e) {
+            log.error("下载美食导入模板失败", e);
+            throw new RuntimeException("下载模板失败:" + e.getMessage());
+        } finally {
+            try {
+                if (inputStream != null) {
+                    inputStream.close();
+                }
+                if (outputStream != null) {
+                    outputStream.close();
+                }
+            } catch (Exception e) {
+                log.error("关闭流失败", e);
+            }
+        }
+    }
 }
 
 

+ 189 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/controller/StorePlatformBarMenuController.java

@@ -0,0 +1,189 @@
+package shop.alien.storeplatform.controller;
+
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+import shop.alien.entity.result.R;
+import shop.alien.storeplatform.entity.StorePlatformStoreMenu;
+import shop.alien.storeplatform.service.StorePlatformBarMenuService;
+import shop.alien.storeplatform.vo.ExcelImportVo;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+/**
+ * 商户平台-酒吧菜单Controller
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"商户平台-酒吧菜单"})
+@ApiSort(4)
+@CrossOrigin
+@RestController
+@RequestMapping("/barMenu")
+@RequiredArgsConstructor
+public class StorePlatformBarMenuController {
+
+    private final StorePlatformBarMenuService barMenuService;
+
+    @ApiOperation("根据ID查询菜单")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "菜单ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getById")
+    public R<StorePlatformStoreMenu> getById(@RequestParam("id") Integer id) {
+        log.info("StorePlatformBarMenuController.getById id={}", id);
+        StorePlatformStoreMenu menu = barMenuService.getById(id);
+        if (menu == null) {
+            return R.fail("菜单不存在");
+        }
+        return R.data(menu);
+    }
+
+    @ApiOperation("根据门店ID查询菜单列表")
+    @ApiOperationSupport(order = 2)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getListByStoreId")
+    public R<List<StorePlatformStoreMenu>> getListByStoreId(@RequestParam("storeId") Integer storeId) {
+        log.info("StorePlatformBarMenuController.getListByStoreId storeId={}", storeId);
+        List<StorePlatformStoreMenu> list = barMenuService.getListByStoreId(storeId);
+        return R.data(list);
+    }
+
+    @ApiOperation("根据门店ID和菜单类型查询菜单列表")
+    @ApiOperationSupport(order = 3)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "dishMenuType", value = "菜单类型:1-菜单,2-酒水", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "dishType", value = "是否推荐, 0:非推荐, 1:推荐", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getListByStoreIdAndType")
+    public R<List<StorePlatformStoreMenu>> getListByStoreIdAndType(
+            @RequestParam("storeId") Integer storeId,
+            @RequestParam(value = "dishMenuType", required = false) String dishMenuType,
+            @RequestParam(value = "dishType", required = false) String dishType) {
+        log.info("StorePlatformBarMenuController.getListByStoreIdAndType storeId={}, dishMenuType={}, dishType={}", storeId, dishMenuType, dishType);
+        List<StorePlatformStoreMenu> list = barMenuService.getListByStoreIdAndType(storeId, dishMenuType, dishType);
+        return R.data(list);
+    }
+
+    @ApiOperation("新增菜单")
+    @ApiOperationSupport(order = 4)
+    @PostMapping("/save")
+    public R<String> save(@RequestBody StorePlatformStoreMenu storeMenu) {
+        log.info("StorePlatformBarMenuController.save storeMenu={}", storeMenu);
+        return barMenuService.saveMenu(storeMenu);
+    }
+
+    @ApiOperation("修改菜单")
+    @ApiOperationSupport(order = 5)
+    @PostMapping("/update")
+    public R<String> update(@RequestBody StorePlatformStoreMenu storeMenu) {
+        log.info("StorePlatformBarMenuController.update storeMenu={}", storeMenu);
+        return barMenuService.updateMenu(storeMenu);
+    }
+
+    @ApiOperation("新增或修改菜单")
+    @ApiOperationSupport(order = 6)
+    @PostMapping("/saveOrUpdate")
+    public R<String> saveOrUpdate(@RequestBody StorePlatformStoreMenu storeMenu) {
+        log.info("StorePlatformBarMenuController.saveOrUpdate storeMenu={}", storeMenu);
+        return barMenuService.saveOrUpdateMenu(storeMenu);
+    }
+
+    @ApiOperation("删除菜单")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "菜单ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/delete")
+    public R<String> delete(@RequestParam("id") Integer id) {
+        log.info("StorePlatformBarMenuController.delete id={}", id);
+        return barMenuService.deleteMenu(id);
+    }
+
+    @ApiOperation("批量删除菜单")
+    @ApiOperationSupport(order = 8)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "ids", value = "菜单ID列表", dataType = "List", paramType = "query", required = true)
+    })
+    @GetMapping("/deleteBatch")
+    public R<String> deleteBatch(@RequestParam("ids") List<Integer> ids) {
+        log.info("StorePlatformBarMenuController.deleteBatch ids={}", ids);
+        return barMenuService.deleteMenuBatch(ids);
+    }
+
+    @ApiOperation("Excel导入酒吧菜单")
+    @ApiOperationSupport(order = 9)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "file", value = "Excel文件", dataType = "file", paramType = "form", required = true),
+            @ApiImplicitParam(name = "storeId", value = "门店id", dataType = "Integer", paramType = "query", required = true)
+    })
+    @PostMapping("/importMenu")
+    public R<String> importMenu(ExcelImportVo vo) {
+        log.info("StorePlatformBarMenuController.importMenu storeId={}", vo.getStoreId());
+        return barMenuService.importMenuFromExcel(vo.getFile(), vo.getStoreId());
+    }
+
+    @ApiOperation("下载酒水餐食导入模板")
+    @ApiOperationSupport(order = 10)
+    @GetMapping("/downloadTemplate")
+    public void downloadTemplate(HttpServletResponse response) {
+        log.info("StoreMenuPlatformController.downloadTemplate");
+        InputStream inputStream = null;
+        OutputStream outputStream = null;
+
+        try {
+            // 从resources/templates目录读取模板文件
+            Resource resource = new ClassPathResource("templates/酒水餐食导入模板.xlsx");
+            inputStream = resource.getInputStream();
+
+            // 设置响应头
+            response.reset();
+            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+            response.setCharacterEncoding("utf-8");
+
+            String fileName = "酒水餐食导入模板.xlsx";
+            String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString()).replaceAll("\\+", "%20");
+            response.setHeader("Content-Disposition", "attachment;filename=\"" + encodedFileName + "\";filename*=utf-8''" + encodedFileName);
+
+            // 输出文件流
+            outputStream = response.getOutputStream();
+            byte[] buffer = new byte[1024];
+            int length;
+            while ((length = inputStream.read(buffer)) > 0) {
+                outputStream.write(buffer, 0, length);
+            }
+            outputStream.flush();
+
+            log.info("酒水餐食导入模板下载成功");
+        } catch (Exception e) {
+            log.error("下载酒水餐食导入模板失败", e);
+            throw new RuntimeException("下载模板失败:" + e.getMessage());
+        } finally {
+            try {
+                if (inputStream != null) {
+                    inputStream.close();
+                }
+                if (outputStream != null) {
+                    outputStream.close();
+                }
+            } catch (Exception e) {
+                log.error("关闭流失败", e);
+            }
+        }
+    }
+}

+ 186 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/controller/StorePlatformBathFacilityServiceController.java

@@ -0,0 +1,186 @@
+package shop.alien.storeplatform.controller;
+
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+import shop.alien.entity.result.R;
+import shop.alien.storeplatform.entity.StorePlatformBathFacility;
+import shop.alien.storeplatform.service.StorePlatformBathFacilityService;
+import shop.alien.storeplatform.vo.ExcelImportVo;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+/**
+ * 商户平台-洗浴设施及服务Controller
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"商户平台-洗浴汗蒸"})
+@ApiSort(4)
+@CrossOrigin
+@RestController
+@RequestMapping("/bathFacilityService")
+@RequiredArgsConstructor
+public class StorePlatformBathFacilityServiceController {
+
+    private final StorePlatformBathFacilityService facilityService;
+
+    @ApiOperation("根据ID查询设施")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "设施ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getById")
+    public R<StorePlatformBathFacility> getById(@RequestParam("id") Integer id) {
+        log.info("StorePlatformBathFacilityServiceController.getById id={}", id);
+        StorePlatformBathFacility facility = facilityService.getById(id);
+        if (facility == null) {
+            return R.fail("设施不存在");
+        }
+        return R.data(facility);
+    }
+
+    @ApiOperation("根据门店ID查询设施列表")
+    @ApiOperationSupport(order = 2)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getListByStoreId")
+    public R<List<StorePlatformBathFacility>> getListByStoreId(@RequestParam("storeId") Integer storeId) {
+        log.info("StorePlatformBathFacilityServiceController.getListByStoreId storeId={}", storeId);
+        List<StorePlatformBathFacility> list = facilityService.getListByStoreId(storeId);
+        return R.data(list);
+    }
+
+    @ApiOperation("根据门店ID和区域分类查询设施列表")
+    @ApiOperationSupport(order = 3)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "facilityCategory", value = "区域分类(1:洗浴区, 2:汗蒸区, 3:休闲区, 4:餐饮区)", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/getListByStoreIdAndCategory")
+    public R<List<StorePlatformBathFacility>> getListByStoreIdAndCategory(@RequestParam("storeId") Integer storeId,
+                                                                         @RequestParam(value = "facilityCategory", required = false) Integer facilityCategory) {
+        log.info("StorePlatformBathFacilityServiceController.getListByStoreIdAndCategory storeId={}, facilityCategory={}", storeId, facilityCategory);
+        List<StorePlatformBathFacility> list = facilityService.getListByStoreIdAndCategory(storeId, facilityCategory);
+        return R.data(list);
+    }
+
+    @ApiOperation("新增设施")
+    @ApiOperationSupport(order = 4)
+    @PostMapping("/save")
+    public R<StorePlatformBathFacility> save(@RequestBody StorePlatformBathFacility facility) {
+        log.info("StorePlatformBathFacilityServiceController.save facility={}", facility);
+        return facilityService.saveFacility(facility);
+    }
+
+    @ApiOperation("修改设施")
+    @ApiOperationSupport(order = 5)
+    @PostMapping("/update")
+    public R<StorePlatformBathFacility> update(@RequestBody StorePlatformBathFacility facility) {
+        log.info("StorePlatformBathFacilityServiceController.update facility={}", facility);
+        return facilityService.updateFacility(facility);
+    }
+
+    @ApiOperation("新增或修改设施")
+    @ApiOperationSupport(order = 6)
+    @PostMapping("/saveOrUpdate")
+    public R<StorePlatformBathFacility> saveOrUpdate(@RequestBody StorePlatformBathFacility facility) {
+        log.info("StorePlatformBathFacilityServiceController.saveOrUpdate facility={}", facility);
+        return facilityService.saveOrUpdateFacility(facility);
+    }
+
+    @ApiOperation("删除设施")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "设施ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/delete")
+    public R<String> delete(@RequestParam("id") Integer id) {
+        log.info("StorePlatformBathFacilityServiceController.delete id={}", id);
+        return facilityService.deleteFacility(id);
+    }
+
+    @ApiOperation("批量删除设施")
+    @ApiOperationSupport(order = 8)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "ids", value = "设施ID列表", dataType = "List", paramType = "query", required = true)
+    })
+    @GetMapping("/deleteBatch")
+    public R<String> deleteBatch(@RequestParam("ids") List<Integer> ids) {
+        log.info("StorePlatformBathFacilityServiceController.deleteBatch ids={}", ids);
+        return facilityService.deleteFacilityBatch(ids);
+    }
+
+    @ApiOperation("Excel导入洗浴汗蒸设施")
+    @ApiOperationSupport(order = 9)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "file", value = "Excel文件", dataType = "file", paramType = "form", required = true),
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @PostMapping("/importFacility")
+    public R<String> importFacility(ExcelImportVo vo) {
+        log.info("StorePlatformBathFacilityServiceController.importFacility storeId={}", vo.getStoreId());
+        return facilityService.importFacilityFromExcel(vo.getFile(), vo.getStoreId());
+    }
+
+    @ApiOperation("下载洗浴汗蒸导入模板")
+    @ApiOperationSupport(order = 10)
+    @GetMapping("/downloadTemplate")
+    public void downloadTemplate(HttpServletResponse response) {
+        log.info("StorePlatformBathFacilityServiceController.downloadTemplate");
+        InputStream inputStream = null;
+        OutputStream outputStream = null;
+
+        try {
+            // 从resources/templates目录读取模板文件
+            Resource resource = new ClassPathResource("templates/洗浴汗蒸导入模板.xlsx");
+            inputStream = resource.getInputStream();
+
+            // 设置响应头
+            response.reset();
+            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+            response.setCharacterEncoding("utf-8");
+
+            String fileName = "洗浴汗蒸导入模板.xlsx";
+            String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString()).replaceAll("\\+", "%20");
+            response.setHeader("Content-Disposition", "attachment;filename=\"" + encodedFileName + "\";filename*=utf-8''" + encodedFileName);
+
+            // 输出文件流
+            outputStream = response.getOutputStream();
+            byte[] buffer = new byte[1024];
+            int length;
+            while ((length = inputStream.read(buffer)) > 0) {
+                outputStream.write(buffer, 0, length);
+            }
+            outputStream.flush();
+
+            log.info("洗浴汗蒸导入模板下载成功");
+        } catch (Exception e) {
+            log.error("下载洗浴汗蒸导入模板失败", e);
+            throw new RuntimeException("下载模板失败:" + e.getMessage());
+        } finally {
+            try {
+                if (inputStream != null) {
+                    inputStream.close();
+                }
+                if (outputStream != null) {
+                    outputStream.close();
+                }
+            } catch (Exception e) {
+                log.error("关闭流失败", e);
+            }
+        }
+    }
+}

+ 187 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/controller/StorePlatformSportsEquipmentFacilityController.java

@@ -0,0 +1,187 @@
+package shop.alien.storeplatform.controller;
+
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+import shop.alien.entity.result.R;
+import shop.alien.storeplatform.entity.StorePlatformSportsEquipmentFacility;
+import shop.alien.storeplatform.service.StorePlatformSportsEquipmentFacilityService;
+import shop.alien.storeplatform.vo.ExcelImportVo;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+/**
+ * 商户平台-运动器材设施Controller
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"商户平台-运动健身-设施器材"})
+@ApiSort(1)
+@CrossOrigin
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/sportsEquipmentFacility")
+public class StorePlatformSportsEquipmentFacilityController {
+
+    private final StorePlatformSportsEquipmentFacilityService facilityService;
+
+    @ApiOperation("根据ID查询设施")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "设施ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getById")
+    public R<StorePlatformSportsEquipmentFacility> getById(@RequestParam("id") Integer id) {
+        log.info("StorePlatformSportsEquipmentFacilityController.getById id={}", id);
+        StorePlatformSportsEquipmentFacility facility = facilityService.getById(id);
+        if (facility == null) {
+            return R.fail("设施不存在");
+        }
+        return R.data(facility);
+    }
+
+    @ApiOperation("根据门店ID查询设施列表")
+    @ApiOperationSupport(order = 2)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getListByStoreId")
+    public R<List<StorePlatformSportsEquipmentFacility>> getListByStoreId(@RequestParam("storeId") Integer storeId) {
+        log.info("StorePlatformSportsEquipmentFacilityController.getListByStoreId storeId={}", storeId);
+        List<StorePlatformSportsEquipmentFacility> list = facilityService.getListByStoreId(storeId);
+        return R.data(list);
+    }
+
+    @ApiOperation("根据门店ID和设施分类查询设施列表")
+    @ApiOperationSupport(order = 3)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "facilityCategory", value = "设施分类(1:有氧区, 2:力量区, 3:单功能机械区)", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/getListByStoreIdAndCategory")
+    public R<List<StorePlatformSportsEquipmentFacility>> getListByStoreIdAndCategory(@RequestParam("storeId") Integer storeId,
+                                                                                     @RequestParam(value = "facilityCategory", required = false) Integer facilityCategory) {
+        log.info("StorePlatformSportsEquipmentFacilityController.getListByStoreIdAndCategory storeId={}, facilityCategory={}", storeId, facilityCategory);
+        List<StorePlatformSportsEquipmentFacility> list = facilityService.getListByStoreIdAndCategory(storeId, facilityCategory);
+        return R.data(list);
+    }
+
+    @ApiOperation("新增设施")
+    @ApiOperationSupport(order = 4)
+    @PostMapping("/save")
+    public R<String> save(@RequestBody StorePlatformSportsEquipmentFacility facility) {
+        log.info("StorePlatformSportsEquipmentFacilityController.save facility={}", facility);
+        return facilityService.saveFacility(facility);
+    }
+
+    @ApiOperation("修改设施")
+    @ApiOperationSupport(order = 5)
+    @PostMapping("/update")
+    public R<String> update(@RequestBody StorePlatformSportsEquipmentFacility facility) {
+        log.info("StorePlatformSportsEquipmentFacilityController.update facility={}", facility);
+        return facilityService.updateFacility(facility);
+    }
+
+    @ApiOperation("新增或修改设施")
+    @ApiOperationSupport(order = 6)
+    @PostMapping("/saveOrUpdate")
+    public R<String> saveOrUpdate(@RequestBody StorePlatformSportsEquipmentFacility facility) {
+        log.info("StorePlatformSportsEquipmentFacilityController.saveOrUpdate facility={}", facility);
+        return facilityService.saveOrUpdateFacility(facility);
+    }
+
+    @ApiOperation("删除设施")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "设施ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/delete")
+    public R<String> delete(@RequestParam("id") Integer id) {
+        log.info("StorePlatformSportsEquipmentFacilityController.delete id={}", id);
+        return facilityService.deleteFacility(id);
+    }
+
+    @ApiOperation("批量删除设施")
+    @ApiOperationSupport(order = 8)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "ids", value = "设施ID列表", dataType = "List", paramType = "query", required = true)
+    })
+    @GetMapping("/deleteBatch")
+    public R<String> deleteBatch(@RequestParam("ids") List<Integer> ids) {
+        log.info("StorePlatformSportsEquipmentFacilityController.deleteBatch ids={}", ids);
+        return facilityService.deleteFacilityBatch(ids);
+    }
+
+    @ApiOperation("Excel导入运动健身设施")
+    @ApiOperationSupport(order = 9)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "file", value = "Excel文件", dataType = "file", paramType = "form", required = true),
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @PostMapping("/importFacility")
+    public R<String> importFacility(ExcelImportVo vo) {
+        log.info("StorePlatformSportsEquipmentFacilityController.importFacility storeId={}", vo.getStoreId());
+        return facilityService.importFacilityFromExcel(vo.getFile(), vo.getStoreId());
+    }
+
+    @ApiOperation("下载运动健身导入模板")
+    @ApiOperationSupport(order = 10)
+    @GetMapping("/downloadTemplate")
+    public void downloadTemplate(HttpServletResponse response) {
+        log.info("StoreMenuPlatformController.downloadTemplate");
+        InputStream inputStream = null;
+        OutputStream outputStream = null;
+
+        try {
+            // 从resources/templates目录读取模板文件
+            Resource resource = new ClassPathResource("templates/运动健身导入模板.xlsx");
+            inputStream = resource.getInputStream();
+
+            // 设置响应头
+            response.reset();
+            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+            response.setCharacterEncoding("utf-8");
+
+            String fileName = "运动健身导入模板.xlsx";
+            String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString()).replaceAll("\\+", "%20");
+            response.setHeader("Content-Disposition", "attachment;filename=\"" + encodedFileName + "\";filename*=utf-8''" + encodedFileName);
+
+            // 输出文件流
+            outputStream = response.getOutputStream();
+            byte[] buffer = new byte[1024];
+            int length;
+            while ((length = inputStream.read(buffer)) > 0) {
+                outputStream.write(buffer, 0, length);
+            }
+            outputStream.flush();
+
+            log.info("运动健身导入模板下载成功");
+        } catch (Exception e) {
+            log.error("下载运动健身导入模板失败", e);
+            throw new RuntimeException("下载模板失败:" + e.getMessage());
+        } finally {
+            try {
+                if (inputStream != null) {
+                    inputStream.close();
+                }
+                if (outputStream != null) {
+                    outputStream.close();
+                }
+            } catch (Exception e) {
+                log.error("关闭流失败", e);
+            }
+        }
+    }
+}

+ 97 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/controller/StorePlatformStoreStaffConfigController.java

@@ -0,0 +1,97 @@
+package shop.alien.storeplatform.controller;
+
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.storeplatform.entity.StorePlatformStoreStaffConfig;
+import shop.alien.storeplatform.service.StorePlatformStoreStaffConfigService;
+
+import java.util.List;
+
+@Slf4j
+@Api(tags = {"商户平台-运动健身-人员配置"})
+@ApiSort(1)
+@CrossOrigin
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/storeStaffConfig")
+public class StorePlatformStoreStaffConfigController {
+
+    private final StorePlatformStoreStaffConfigService staffConfigService;
+
+    @ApiOperation("根据ID查询员工")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "员工ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getById")
+    public R<StorePlatformStoreStaffConfig> getById(@RequestParam("id") Integer id) {
+        log.info("StorePlatformStoreStaffConfigController.getById id={}", id);
+        StorePlatformStoreStaffConfig staffConfig = staffConfigService.getById(id);
+        if (staffConfig == null) {
+            return R.fail("员工不存在");
+        }
+        return R.data(staffConfig);
+    }
+
+    @ApiOperation("根据门店ID查询员工列表")
+    @ApiOperationSupport(order = 2)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getListByStoreId")
+    public R<List<StorePlatformStoreStaffConfig>> getListByStoreId(@RequestParam("storeId") Integer storeId) {
+        log.info("StorePlatformStoreStaffConfigController.getListByStoreId storeId={}", storeId);
+        List<StorePlatformStoreStaffConfig> list = staffConfigService.getListByStoreId(storeId);
+        return R.data(list);
+    }
+
+    @ApiOperation("新增员工")
+    @ApiOperationSupport(order = 3)
+    @PostMapping("/save")
+    public R<String> save(@RequestBody StorePlatformStoreStaffConfig staffConfig) {
+        log.info("StorePlatformStoreStaffConfigController.save staffConfig={}", staffConfig);
+        return staffConfigService.saveStaff(staffConfig);
+    }
+
+    @ApiOperation("修改员工")
+    @ApiOperationSupport(order = 4)
+    @PostMapping("/update")
+    public R<String> update(@RequestBody StorePlatformStoreStaffConfig staffConfig) {
+        log.info("StorePlatformStoreStaffConfigController.update staffConfig={}", staffConfig);
+        return staffConfigService.updateStaff(staffConfig);
+    }
+
+    @ApiOperation("新增或修改员工")
+    @ApiOperationSupport(order = 5)
+    @PostMapping("/saveOrUpdate")
+    public R<String> saveOrUpdate(@RequestBody StorePlatformStoreStaffConfig staffConfig) {
+        log.info("StorePlatformStoreStaffConfigController.saveOrUpdate staffConfig={}", staffConfig);
+        return staffConfigService.saveOrUpdateStaff(staffConfig);
+    }
+
+    @ApiOperation("删除员工")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "员工ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/delete")
+    public R<String> delete(@RequestParam("id") Integer id) {
+        log.info("StorePlatformStoreStaffConfigController.delete id={}", id);
+        return staffConfigService.deleteStaff(id);
+    }
+
+    @ApiOperation("批量删除员工")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "ids", value = "员工ID列表", dataType = "List", paramType = "query", required = true)
+    })
+    @GetMapping("/deleteBatch")
+    public R<String> deleteBatch(@RequestParam("ids") List<Integer> ids) {
+        log.info("StorePlatformStoreStaffConfigController.deleteBatch ids={}", ids);
+        return staffConfigService.deleteStaffBatch(ids);
+    }
+}

+ 90 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/entity/StorePlatformBathFacility.java

@@ -0,0 +1,90 @@
+package shop.alien.storeplatform.entity;
+
+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 StorePlatformBathFacility 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;
+
+    @ApiModelProperty(value = "图片id")
+    @TableField(exist = false)
+    private String imgUrl;
+}
+

+ 82 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/entity/StorePlatformSportsEquipmentFacility.java

@@ -0,0 +1,82 @@
+package shop.alien.storeplatform.entity;
+
+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 StorePlatformSportsEquipmentFacility 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;
+}
+

+ 112 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/entity/StorePlatformStoreMenu.java

@@ -0,0 +1,112 @@
+package shop.alien.storeplatform.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 二期-门店菜单
+ *
+ * @author ssk
+ * @since 2024-12-05
+ */
+@Data
+@JsonInclude
+@TableName("store_menu")
+@ApiModel(value = "StoreMenu对象", description = "门店菜单")
+public class StorePlatformStoreMenu {
+
+    @ApiModelProperty(value = "主键")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "门店id")
+    @TableField("store_id")
+    private Integer storeId;
+
+    @ApiModelProperty(value = "图片id")
+    @TableField("img_id")
+    private Integer imgId;
+
+    @ApiModelProperty(value = "图片id")
+    @TableField(exist = false)
+    private String imgUrl;
+
+    @ApiModelProperty(value = "菜单类型:1-菜单,2-酒水")
+    @TableField("dish_menu_type")
+    private Integer dishMenuType;
+
+    @ApiModelProperty(value = "名称")
+    @TableField("dish_name")
+    private String dishName;
+
+    @ApiModelProperty(value = "价格")
+    @TableField("dish_price")
+    private BigDecimal dishPrice;
+
+    @ApiModelProperty(value = "是否推荐, 0:非推荐, 1:推荐")
+    @TableField("dish_type")
+    private Integer dishType;
+
+    @ApiModelProperty(value = "点赞数")
+    @TableField("like_count")
+    private Integer likeCount;
+
+    @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;
+
+    @ApiModelProperty(value = "单位")
+    @TableField("dishes_unit")
+    private String dishesUnit;
+
+    @ApiModelProperty(value = "成本价")
+    @TableField("cost_price")
+    private BigDecimal costPrice;
+
+    @ApiModelProperty(value = "排序")
+    @TableField("sort")
+    private int sort;
+
+    @ApiModelProperty(value = "描述")
+    @TableField("description")
+    private String description;
+
+    @ApiModelProperty(value = "酒精度")
+    @TableField("alcohol_volume")
+    private String alcoholVolume;
+
+    @ApiModelProperty(value = "品类")
+    @TableField("category")
+    private String category;
+
+    @ApiModelProperty(value = "风味")
+    @TableField("flavor")
+    private String flavor;
+
+}

+ 103 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/entity/StorePlatformStoreStaffConfig.java

@@ -0,0 +1,103 @@
+package shop.alien.storeplatform.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+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 lombok.EqualsAndHashCode;
+
+import java.util.Date;
+
+/**
+ * @Author: fcw
+ * @CreateTime: 2025-04-11
+ * @Description: 员工管理
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@JsonInclude
+@TableName("store_staff_config")
+@ApiModel(value = "StoreStaffConfig对象", description = "员工管理")
+public class StorePlatformStoreStaffConfig extends Model<StorePlatformStoreStaffConfig> {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "主键id")
+    @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;
+
+    @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
+    @TableField("delete_flag")
+    @TableLogic
+    private Integer deleteFlag;
+
+    @ApiModelProperty(value = "创建时间")
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "创建人ID")
+    @TableField("created_user_id")
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "修改时间")
+    @TableField(value = "updated_time", fill = FieldFill.UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+
+    @ApiModelProperty(value = "修改人ID")
+    @TableField("updated_user_id")
+    private Integer updatedUserId;
+
+    @ApiModelProperty(value = "置顶状态, 0:未置顶, 1:已置顶")
+    @TableField("top_status")
+    private Integer topStatus;
+
+    @ApiModelProperty(value = "置顶时间")
+    @TableField("top_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date topTime;
+}

+ 7 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/feign/AlienStoreFeign.java

@@ -2,8 +2,12 @@ package shop.alien.storeplatform.feign;
 
 import com.alibaba.fastjson.JSONObject;
 import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.http.MediaType;
 import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RequestPart;
+import org.springframework.web.multipart.MultipartFile;
 
 @FeignClient(url = "${feign.alienStore.url}", name = "alien-store")
 public interface AlienStoreFeign {
@@ -13,4 +17,7 @@ public interface AlienStoreFeign {
             @RequestParam(value = "messageReceiverId") String messageReceiverId,
             @RequestParam(value = "webSocketVoStr") String webSocketVoStr);
 
+    @PostMapping(value = "/file/uploadMore", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+    JSONObject uploadFile(@RequestPart("multipartRequest") MultipartFile multipartRequest);
+
 }

+ 10 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/mapper/StorePlatformBathFacilityServiceMapper.java

@@ -0,0 +1,10 @@
+package shop.alien.storeplatform.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.storeplatform.entity.StorePlatformBathFacility;
+
+
+@Mapper
+public interface StorePlatformBathFacilityServiceMapper extends BaseMapper<StorePlatformBathFacility> {
+}

+ 9 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/mapper/StorePlatformSportsEquipmentFacilityMapper.java

@@ -0,0 +1,9 @@
+package shop.alien.storeplatform.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.storeplatform.entity.StorePlatformSportsEquipmentFacility;
+
+@Mapper
+public interface StorePlatformSportsEquipmentFacilityMapper extends BaseMapper<StorePlatformSportsEquipmentFacility> {
+}

+ 9 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/mapper/StorePlatformStoreMenuMapper.java

@@ -0,0 +1,9 @@
+package shop.alien.storeplatform.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.storeplatform.entity.StorePlatformStoreMenu;
+
+@Mapper
+public interface StorePlatformStoreMenuMapper extends BaseMapper<StorePlatformStoreMenu> {
+}

+ 9 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/mapper/StorePlatformStoreStaffConfigMapper.java

@@ -0,0 +1,9 @@
+package shop.alien.storeplatform.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.storeplatform.entity.StorePlatformStoreStaffConfig;
+
+@Mapper
+public interface StorePlatformStoreStaffConfigMapper extends BaseMapper<StorePlatformStoreStaffConfig> {
+}

+ 59 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/service/BusinessSectionService.java

@@ -0,0 +1,59 @@
+package shop.alien.storeplatform.service;
+
+import shop.alien.entity.store.vo.StoreDictionaryVo;
+
+import java.util.List;
+
+/**
+ * 经营板块服务接口
+ * 提供经营板块的三级分类查询服务
+ *
+ * @author alien-cloud
+ * @date 2025-12-01
+ */
+public interface BusinessSectionService {
+
+    /**
+     * 查询经营板块一级分类列表
+     * 返回所有一级分类(特色美食、酒店/民宿、KTV、洗浴汗蒸等)
+     *
+     * @return 一级分类列表
+     */
+    List<StoreDictionaryVo> getFirstLevelList();
+
+    /**
+     * 查询经营板块二级分类列表
+     * 根据一级分类的字典ID查询对应的二级分类
+     *
+     * @param parentDictId 一级分类的字典ID(dict_id)
+     * @return 二级分类列表
+     */
+    List<StoreDictionaryVo> getSecondLevelList(String parentDictId);
+
+    /**
+     * 查询经营板块三级分类列表
+     * 根据二级分类的字典ID查询对应的三级分类
+     *
+     * @param parentDictId 二级分类的字典ID(dict_id)
+     * @return 三级分类列表
+     */
+    List<StoreDictionaryVo> getThirdLevelList(String parentDictId);
+
+    /**
+     * 查询经营板块树形结构(三级联动)
+     * 返回完整的三级分类树形结构,包含所有层级的子节点
+     *
+     * @return 树形结构数据(包含一级、二级、三级)
+     */
+    List<StoreDictionaryVo> getTreeStructure();
+
+    /**
+     * 根据字典ID查询完整路径
+     * 返回从一级到当前级别的完整分类路径
+     *
+     * @param dictId 字典ID
+     * @return 分类路径(从一级到当前级)
+     */
+    List<StoreDictionaryVo> getFullPath(String dictId);
+}
+

+ 23 - 2
alien-store-platform/src/main/java/shop/alien/storeplatform/service/LicenseService.java

@@ -1,7 +1,9 @@
 package shop.alien.storeplatform.service;
 
 
+import shop.alien.entity.second.vo.StoreImgDTO;
 import shop.alien.entity.store.StoreImg;
+import shop.alien.entity.store.vo.StoreImgVo;
 import shop.alien.entity.storePlatform.vo.StoreLicenseHistoryDto;
 import shop.alien.entity.storePlatform.vo.StoreLicenseHistoryVO;
 
@@ -15,9 +17,11 @@ public interface LicenseService {
 
     List<StoreImg> getbusinessLicenseList (Integer id);
 
-    List<StoreImg> getContractList (Integer id);
+    List<StoreImgDTO> getEntertainmentLicenseList (Integer id);
 
-    List<StoreImg> getFoodLicenceList (Integer id);
+    List<StoreImgDTO> getContractList (Integer id);
+
+    List<StoreImgDTO> getFoodLicenceList (Integer id);
 
     /**
      * 根据商户ID查询证照历史记录列表(带格式化日期)
@@ -61,6 +65,23 @@ public interface LicenseService {
      * @return 按时间分组且已排序的证照历史记录列表
      */
     List<StoreLicenseHistoryDto> queryContractByStatusList (Integer storeId);
+
+    int uploadEntertainmentLicence(StoreImg storeImg);
+
+    /**
+     * 根据商户ID查询证照历史记录列表(带格式化日期)
+     *
+     * @param storeId 商户ID
+     * @return 证照历史记录VO列表
+     */
+    List<StoreLicenseHistoryDto> queryEntertainmentByStatusList (Integer storeId);
+
+    /**
+     * 获取店铺食品经营许可证状态以及店铺状态为审核中合同图片list
+     * @param id
+     * @return
+     */
+    Map<String,Object> getStoreEntertainmentStatus(int id);
 }
 
 

+ 71 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/service/OperationalActivityService.java

@@ -0,0 +1,71 @@
+package shop.alien.storeplatform.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import shop.alien.entity.storePlatform.vo.StoreOperationalActivityDTO;
+import shop.alien.entity.storePlatform.vo.StoreOperationalActivityVO;
+
+import java.util.List;
+
+/**
+ * 运营活动服务接口
+ *
+ * @author system
+ * @since 2025-11-26
+ */
+public interface OperationalActivityService {
+
+    /**
+     * 创建运营活动
+     *
+     * @param dto 活动信息
+     * @return 创建结果
+     */
+    int createActivity(StoreOperationalActivityDTO dto);
+
+    /**
+     * 更新运营活动
+     *
+     * @param dto 活动信息
+     * @return 更新结果
+     */
+    int updateActivity(StoreOperationalActivityDTO dto);
+
+    /**
+     * 删除运营活动(逻辑删除)
+     *
+     * @param id 活动ID
+     * @return 删除结果
+     */
+    int deleteActivity(Integer id);
+
+    /**
+     * 根据ID获取活动详情
+     *
+     * @param id 活动ID
+     * @return 活动详情
+     */
+    StoreOperationalActivityVO queryActivityById(Integer id);
+
+    /**
+     * 分页查询活动列表
+     *
+     * @param storeId      商户ID
+     * @param status       状态
+     * @param activityName 活动名称
+     * @param pageNum      页码
+     * @param pageSize     每页大小
+     * @return 分页结果
+     */
+    IPage<StoreOperationalActivityVO> queryActivityList(Integer storeId, Integer status, String activityName, Integer pageNum, Integer pageSize);
+
+    /**
+     * 启用/禁用活动
+     *
+     * @param id     活动ID
+     * @param status 状态:0-禁用, 1-启用
+     * @return 更新结果
+     */
+    int updateActivityStatus(Integer id, Integer status);
+
+}
+

+ 10 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/service/StoreMenuPlatformService.java

@@ -1,6 +1,7 @@
 package shop.alien.storeplatform.service;
 
 import com.baomidou.mybatisplus.extension.service.IService;
+import org.springframework.web.multipart.MultipartFile;
 import shop.alien.entity.result.R;
 import shop.alien.entity.store.StoreMenu;
 import shop.alien.entity.store.vo.StoreMenuVo;
@@ -90,6 +91,15 @@ public interface StoreMenuPlatformService extends IService<StoreMenu> {
      * @return boolean
      */
     boolean getMenuLikeStatus(String userId, Integer menuId);
+
+    /**
+     * Excel导入门店菜单
+     *
+     * @param file     Excel文件
+     * @param storeId  门店id
+     * @return 导入结果
+     */
+    R<String> importMenuFromExcel(MultipartFile file, Integer storeId);
 }
 
 

+ 91 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/service/StorePlatformBarMenuService.java

@@ -0,0 +1,91 @@
+package shop.alien.storeplatform.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.springframework.web.multipart.MultipartFile;
+import shop.alien.entity.result.R;
+import shop.alien.storeplatform.entity.StorePlatformStoreMenu;
+
+import java.util.List;
+
+/**
+ * 商户平台-酒吧菜单服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface StorePlatformBarMenuService extends IService<StorePlatformStoreMenu> {
+
+    /**
+     * 根据ID查询菜单
+     *
+     * @param id 菜单ID
+     * @return 菜单信息
+     */
+    StorePlatformStoreMenu getById(Integer id);
+
+    /**
+     * 根据门店ID查询菜单列表
+     *
+     * @param storeId 门店ID
+     * @return 菜单列表
+     */
+    List<StorePlatformStoreMenu> getListByStoreId(Integer storeId);
+
+    /**
+     * 根据门店ID和菜单类型查询菜单列表
+     *
+     * @param storeId     门店ID
+     * @param dishMenuType 菜单类型:1-菜单,2-酒水
+     * @return 菜单列表
+     */
+    List<StorePlatformStoreMenu> getListByStoreIdAndType(Integer storeId, String dishMenuType, String dishType);
+
+    /**
+     * 新增菜单
+     *
+     * @param storeMenu 菜单信息
+     * @return 操作结果
+     */
+    R<String> saveMenu(StorePlatformStoreMenu storeMenu);
+
+    /**
+     * 修改菜单
+     *
+     * @param storeMenu 菜单信息
+     * @return 操作结果
+     */
+    R<String> updateMenu(StorePlatformStoreMenu storeMenu);
+
+    /**
+     * 新增或修改菜单
+     *
+     * @param storeMenu 菜单信息
+     * @return 操作结果
+     */
+    R<String> saveOrUpdateMenu(StorePlatformStoreMenu storeMenu);
+
+    /**
+     * 删除菜单(逻辑删除)
+     *
+     * @param id 菜单ID
+     * @return 操作结果
+     */
+    R<String> deleteMenu(Integer id);
+
+    /**
+     * 批量删除菜单(逻辑删除)
+     *
+     * @param ids 菜单ID列表
+     * @return 操作结果
+     */
+    R<String> deleteMenuBatch(List<Integer> ids);
+
+    /**
+     * Excel导入酒吧菜单
+     *
+     * @param file     Excel文件
+     * @param storeId  门店id
+     * @return 导入结果
+     */
+    R<String> importMenuFromExcel(MultipartFile file, Integer storeId);
+}

+ 85 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/service/StorePlatformBathFacilityService.java

@@ -0,0 +1,85 @@
+package shop.alien.storeplatform.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.springframework.web.multipart.MultipartFile;
+import shop.alien.entity.result.R;
+import shop.alien.storeplatform.entity.StorePlatformBathFacility;
+
+import java.util.List;
+
+/**
+ * 商户平台-洗浴设施及服务服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface StorePlatformBathFacilityService extends IService<StorePlatformBathFacility> {
+
+    /**
+     * 根据ID查询设施
+     *
+     * @param id 设施ID
+     * @return 设施信息
+     */
+    StorePlatformBathFacility getById(Integer id);
+
+    /**
+     * 根据门店ID查询设施列表
+     *
+     * @param storeId 门店ID
+     * @return 设施列表
+     */
+    List<StorePlatformBathFacility> getListByStoreId(Integer storeId);
+
+    /**
+     * 根据门店ID和区域分类查询设施列表
+     *
+     * @param storeId         门店ID
+     * @param facilityCategory 区域分类(1:洗浴区, 2:汗蒸区, 3:休闲区, 4:餐饮区)
+     * @return 设施列表
+     */
+    List<StorePlatformBathFacility> getListByStoreIdAndCategory(Integer storeId, Integer facilityCategory);
+
+    /**
+     * 新增设施
+     *
+     * @param facility 设施信息
+     * @return 操作结果
+     */
+    R<StorePlatformBathFacility> saveFacility(StorePlatformBathFacility facility);
+
+    /**
+     * 修改设施
+     *
+     * @param facility 设施信息
+     * @return 操作结果
+     */
+    R<StorePlatformBathFacility> updateFacility(StorePlatformBathFacility facility);
+
+    /**
+     * 新增或修改设施
+     *
+     * @param facility 设施信息
+     * @return 操作结果
+     */
+    R<StorePlatformBathFacility> saveOrUpdateFacility(StorePlatformBathFacility facility);
+
+    /**
+     * 删除设施(逻辑删除)
+     *
+     * @param id 设施ID
+     * @return 操作结果
+     */
+    R<String> deleteFacility(Integer id);
+
+    /**
+     * 批量删除设施(逻辑删除)
+     *
+     * @param ids 设施ID列表
+     * @return 操作结果
+     */
+    R<String> deleteFacilityBatch(List<Integer> ids);
+
+    R<String> importFacilityFromExcel(MultipartFile file, Integer storeId);
+
+}

+ 93 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/service/StorePlatformSportsEquipmentFacilityService.java

@@ -0,0 +1,93 @@
+package shop.alien.storeplatform.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.springframework.web.multipart.MultipartFile;
+import shop.alien.entity.result.R;
+import shop.alien.storeplatform.entity.StorePlatformSportsEquipmentFacility;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * 商户平台-运动器材设施服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface StorePlatformSportsEquipmentFacilityService extends IService<StorePlatformSportsEquipmentFacility> {
+
+    /**
+     * 根据ID查询设施
+     *
+     * @param id 设施ID
+     * @return 设施信息
+     */
+    StorePlatformSportsEquipmentFacility getById(Integer id);
+
+    /**
+     * 根据门店ID查询设施列表
+     *
+     * @param storeId 门店ID
+     * @return 设施列表
+     */
+    List<StorePlatformSportsEquipmentFacility> getListByStoreId(Integer storeId);
+
+    /**
+     * 根据门店ID和设施分类查询设施列表
+     *
+     * @param storeId         门店ID
+     * @param facilityCategory 设施分类(1:有氧区, 2:力量区, 3:单功能机械区)
+     * @return 设施列表
+     */
+    List<StorePlatformSportsEquipmentFacility> getListByStoreIdAndCategory(Integer storeId, Integer facilityCategory);
+
+    /**
+     * 新增设施
+     *
+     * @param facility 设施信息
+     * @return 操作结果
+     */
+    R<String> saveFacility(StorePlatformSportsEquipmentFacility facility);
+
+    /**
+     * 修改设施
+     *
+     * @param facility 设施信息
+     * @return 操作结果
+     */
+    R<String> updateFacility(StorePlatformSportsEquipmentFacility facility);
+
+    /**
+     * 新增或修改设施
+     *
+     * @param facility 设施信息
+     * @return 操作结果
+     */
+    R<String> saveOrUpdateFacility(StorePlatformSportsEquipmentFacility facility);
+
+    /**
+     * 删除设施(逻辑删除)
+     *
+     * @param id 设施ID
+     * @return 操作结果
+     */
+    R<String> deleteFacility(Integer id);
+
+    /**
+     * 批量删除设施(逻辑删除)
+     *
+     * @param ids 设施ID列表
+     * @return 操作结果
+     */
+    R<String> deleteFacilityBatch(List<Integer> ids);
+
+    /**
+     * Excel导入运动健身设施
+     *
+     * @param file     Excel文件
+     * @param storeId  门店id
+     * @return 导入结果
+     */
+    R<String> importFacilityFromExcel(MultipartFile file, Integer storeId);
+}

+ 48 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/service/StorePlatformStoreStaffConfigService.java

@@ -0,0 +1,48 @@
+package shop.alien.storeplatform.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.result.R;
+import shop.alien.storeplatform.entity.StorePlatformStoreStaffConfig;
+
+import java.util.List;
+
+/**
+ * 商户平台-员工管理 服务类
+ */
+public interface StorePlatformStoreStaffConfigService extends IService<StorePlatformStoreStaffConfig>  {
+
+    /**
+     * 根据ID查询员工
+     */
+    StorePlatformStoreStaffConfig getById(Integer id);
+
+    /**
+     * 根据门店ID查询员工列表
+     */
+    List<StorePlatformStoreStaffConfig> getListByStoreId(Integer storeId);
+
+    /**
+     * 新增员工
+     */
+    R<String> saveStaff(StorePlatformStoreStaffConfig staffConfig);
+
+    /**
+     * 修改员工
+     */
+    R<String> updateStaff(StorePlatformStoreStaffConfig staffConfig);
+
+    /**
+     * 新增或修改员工
+     */
+    R<String> saveOrUpdateStaff(StorePlatformStoreStaffConfig staffConfig);
+
+    /**
+     * 删除员工
+     */
+    R<String> deleteStaff(Integer id);
+
+    /**
+     * 批量删除员工
+     */
+    R<String> deleteStaffBatch(List<Integer> ids);
+}

+ 366 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/BusinessSectionServiceImpl.java

@@ -0,0 +1,366 @@
+package shop.alien.storeplatform.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+import shop.alien.entity.store.StoreDictionary;
+import shop.alien.entity.store.vo.StoreDictionaryVo;
+import shop.alien.mapper.StoreDictionaryMapper;
+import shop.alien.storeplatform.service.BusinessSectionService;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 经营板块服务实现类
+ * 提供经营板块的三级分类查询服务实现
+ *
+ * @author alien-cloud
+ * @date 2025-12-01
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class BusinessSectionServiceImpl implements BusinessSectionService {
+
+    private final StoreDictionaryMapper storeDictionaryMapper;
+
+    /**
+     * 类型名称常量:经营板块
+     */
+    private static final String TYPE_NAME = "business_section";
+    /**
+     * 类型名称常量:二级
+     */
+    private static final String BUSINESS_TYPE = "business_type";
+    /**
+     * 类型名称常量:三级 分类
+     */
+    private static final String BUSINESS_CLASSIFY = "business_classify";
+
+    /**
+     * 一级分类的根节点 dict_id(经营板块根节点)
+     */
+    private static final String ROOT_DICT_ID = "0";
+
+    /**
+     * 查询经营板块一级分类列表
+     *
+     * @return 一级分类列表
+     */
+    @Override
+    public List<StoreDictionaryVo> getFirstLevelList() {
+        log.info("BusinessSectionServiceImpl.getFirstLevelList - 开始查询一级分类");
+
+//        // 1. 查询根节点(dict_id = 0)
+//        StoreDictionary root = storeDictionaryMapper.selectOne(
+//                new LambdaQueryWrapper<StoreDictionary>()
+//                        .eq(StoreDictionary::getTypeName, TYPE_NAME)
+//                        .isNull(StoreDictionary::getParentId)
+//                        .eq(StoreDictionary::getDeleteFlag, 0)
+//        );
+//
+//        if (root == null) {
+//            log.warn("BusinessSectionServiceImpl.getFirstLevelList - 未找到经营板块根节点");
+//            return new ArrayList<>();
+//        }
+
+        // 2. 查询一级分类(parent_id = 根节点的id,且 dict_id != 0)
+        List<StoreDictionary> firstLevelList = storeDictionaryMapper.selectList(
+                new LambdaQueryWrapper<StoreDictionary>()
+                        .eq(StoreDictionary::getTypeName, TYPE_NAME)
+                        .isNull(StoreDictionary::getParentId)
+                        .eq(StoreDictionary::getDeleteFlag, 0)
+                        .orderByAsc(StoreDictionary::getDictId)
+        );
+
+        // 3. 转换为 VO
+        List<StoreDictionaryVo> voList = convertToVoList(firstLevelList);
+
+        log.info("BusinessSectionServiceImpl.getFirstLevelList - 查询成功,共 {} 条数据", voList.size());
+        return voList;
+    }
+
+    /**
+     * 查询经营板块二级分类列表
+     *
+     * @param parentDictId 一级分类的字典ID(dict_id)
+     * @return 二级分类列表
+     */
+    @Override
+    public List<StoreDictionaryVo> getSecondLevelList(String parentDictId) {
+        log.info("BusinessSectionServiceImpl.getSecondLevelList - 开始查询二级分类,parentDictId={}", parentDictId);
+
+        // 1. 根据 dict_id 查询父节点
+//        StoreDictionary parent = storeDictionaryMapper.selectOne(
+//                new LambdaQueryWrapper<StoreDictionary>()
+//                        .eq(StoreDictionary::getTypeName, BUSINESS_TYPE)
+//                        .eq(StoreDictionary::getId, parentDictId)
+//                        .eq(StoreDictionary::getDeleteFlag, 0)
+//        );
+//
+//        if (parent == null) {
+//            log.warn("BusinessSectionServiceImpl.getSecondLevelList - 未找到父节点,parentDictId={}", parentDictId);
+//            return new ArrayList<>();
+//        }
+
+        // 获取一级分类id
+        Integer id = storeDictionaryMapper.selectOne(
+                new LambdaQueryWrapper<StoreDictionary>()
+                        .eq(StoreDictionary::getTypeName, TYPE_NAME)
+                        .eq(StoreDictionary::getDictId, parentDictId)
+                        .eq(StoreDictionary::getDeleteFlag, 0)
+        ).getId();
+
+        // 2. 查询二级分类(parent_id = 父节点的id)
+        List<StoreDictionary> secondLevelList = storeDictionaryMapper.selectList(
+                new LambdaQueryWrapper<StoreDictionary>()
+                        .eq(StoreDictionary::getTypeName, BUSINESS_TYPE)
+                        .eq(StoreDictionary::getParentId, id)
+                        .eq(StoreDictionary::getDeleteFlag, 0)
+                        .orderByAsc(StoreDictionary::getDictId)
+        );
+
+        // 3. 转换为 VO
+        List<StoreDictionaryVo> voList = convertToVoList(secondLevelList);
+
+        log.info("BusinessSectionServiceImpl.getSecondLevelList - 查询成功,共 {} 条数据", voList.size());
+        return voList;
+    }
+
+    /**
+     * 查询经营板块三级分类列表
+     *
+     * @param parentDictId 二级分类的字典ID(dict_id)
+     * @return 三级分类列表
+     */
+    @Override
+    public List<StoreDictionaryVo> getThirdLevelList(String parentDictId) {
+        log.info("BusinessSectionServiceImpl.getThirdLevelList - 开始查询三级分类,parentDictId={}", parentDictId);
+
+//        // 1. 根据 dict_id 查询父节点
+//        StoreDictionary parent = storeDictionaryMapper.selectOne(
+//                new LambdaQueryWrapper<StoreDictionary>()
+//                        .eq(StoreDictionary::getTypeName, BUSINESS_CLASSIFY)
+//                        .eq(StoreDictionary::getId, parentDictId)
+//                        .eq(StoreDictionary::getDeleteFlag, 0)
+//        );
+//
+//        if (parent == null) {
+//            log.warn("BusinessSectionServiceImpl.getThirdLevelList - 未找到父节点,parentDictId={}", parentDictId);
+//            return new ArrayList<>();
+//        }
+
+        // 获取二级分类id
+        Integer businessTypeId = storeDictionaryMapper.selectOne(
+                new LambdaQueryWrapper<StoreDictionary>()
+                        .eq(StoreDictionary::getTypeName, BUSINESS_TYPE)
+                        .eq(StoreDictionary::getDictId, parentDictId)
+                        .eq(StoreDictionary::getDeleteFlag, 0)
+        ).getId();
+
+        // 2. 查询三级分类(parent_id = 父节点的id)
+        List<StoreDictionary> thirdLevelList = storeDictionaryMapper.selectList(
+                new LambdaQueryWrapper<StoreDictionary>()
+                        .eq(StoreDictionary::getTypeName, BUSINESS_CLASSIFY)
+                        .eq(StoreDictionary::getParentId, businessTypeId)
+                        .eq(StoreDictionary::getDeleteFlag, 0)
+                        .orderByAsc(StoreDictionary::getDictId)
+        );
+
+        // 3. 转换为 VO
+        List<StoreDictionaryVo> voList = convertToVoList(thirdLevelList);
+
+        log.info("BusinessSectionServiceImpl.getThirdLevelList - 查询成功,共 {} 条数据", voList.size());
+        return voList;
+    }
+
+    /**
+     * 查询经营板块树形结构(三级联动)
+     *
+     * @return 树形结构数据(包含一级、二级、三级)
+     */
+    @Override
+    public List<StoreDictionaryVo> getTreeStructure() {
+        log.info("BusinessSectionServiceImpl.getTreeStructure - 开始查询三级树形结构");
+
+        // 1. 查询所有经营板块数据(排除根节点)
+        List<StoreDictionary> allList = storeDictionaryMapper.selectList(
+                new LambdaQueryWrapper<StoreDictionary>()
+                        .eq(StoreDictionary::getTypeName, TYPE_NAME)
+                        .eq(StoreDictionary::getDeleteFlag, 0)
+                        .orderByAsc(StoreDictionary::getParentId)
+                        .orderByAsc(StoreDictionary::getDictId)
+        );
+
+        if (allList == null || allList.isEmpty()) {
+            log.warn("BusinessSectionServiceImpl.getTreeStructure - 未查询到任何数据");
+            return new ArrayList<>();
+        }
+
+        // 2. 转换为 VO 列表
+        List<StoreDictionaryVo> allVoList = convertToVoList(allList);
+
+        // 3. 构建树形结构
+        List<StoreDictionaryVo> treeList = buildTree(allVoList, allList);
+
+        log.info("BusinessSectionServiceImpl.getTreeStructure - 查询成功,共 {} 个一级分类", treeList.size());
+        return treeList;
+    }
+
+    /**
+     * 根据字典ID查询完整路径
+     *
+     * @param dictId 字典ID
+     * @return 分类路径(从一级到当前级)
+     */
+    @Override
+    public List<StoreDictionaryVo> getFullPath(String dictId) {
+        log.info("BusinessSectionServiceImpl.getFullPath - 开始查询完整路径,dictId={}", dictId);
+
+        List<StoreDictionaryVo> pathList = new ArrayList<>();
+
+        // 1. 查询当前节点
+        StoreDictionary current = storeDictionaryMapper.selectOne(
+                new LambdaQueryWrapper<StoreDictionary>()
+                        .eq(StoreDictionary::getTypeName, TYPE_NAME)
+                        .eq(StoreDictionary::getId, dictId)
+                        .eq(StoreDictionary::getDeleteFlag, 0)
+        );
+
+        if (current == null) {
+            log.warn("BusinessSectionServiceImpl.getFullPath - 未找到节点,dictId={}", dictId);
+            return pathList;
+        }
+
+        // 2. 递归查询父节点,构建路径
+        buildPath(current, pathList);
+
+        // 3. 反转列表(从一级到当前级)
+        List<StoreDictionaryVo> result = new ArrayList<>();
+        for (int i = pathList.size() - 1; i >= 0; i--) {
+            result.add(pathList.get(i));
+        }
+
+        log.info("BusinessSectionServiceImpl.getFullPath - 查询成功,路径长度={}", result.size());
+        return result;
+    }
+
+    /**
+     * 构建树形结构
+     *
+     * @param allVoList 所有VO列表
+     * @param allList   所有实体列表
+     * @return 树形结构
+     */
+    private List<StoreDictionaryVo> buildTree(List<StoreDictionaryVo> allVoList, 
+                                               List<StoreDictionary> allList) {
+        // 1. 找出一级分类(查询根节点的id)
+        StoreDictionary root = storeDictionaryMapper.selectOne(
+                new LambdaQueryWrapper<StoreDictionary>()
+                        .eq(StoreDictionary::getTypeName, TYPE_NAME)
+                        .eq(StoreDictionary::getDictId, ROOT_DICT_ID)
+                        .eq(StoreDictionary::getDeleteFlag, 0)
+        );
+
+        if (root == null) {
+            return new ArrayList<>();
+        }
+
+        Integer rootId = root.getId();
+
+        // 2. 找出一级分类
+        List<StoreDictionaryVo> firstLevelList = allVoList.stream()
+                .filter(vo -> {
+                    StoreDictionary entity = allList.stream()
+                            .filter(e -> e.getId().equals(vo.getId()))
+                            .findFirst()
+                            .orElse(null);
+                    return entity != null && rootId.equals(entity.getParentId());
+                })
+                .collect(Collectors.toList());
+
+        // 3. 为每个一级分类填充子节点
+        for (StoreDictionaryVo firstLevel : firstLevelList) {
+            // 查询二级分类
+            List<StoreDictionaryVo> secondLevelList = allVoList.stream()
+                    .filter(vo -> {
+                        StoreDictionary entity = allList.stream()
+                                .filter(e -> e.getId().equals(vo.getId()))
+                                .findFirst()
+                                .orElse(null);
+                        return entity != null && firstLevel.getId().equals(entity.getParentId());
+                    })
+                    .collect(Collectors.toList());
+
+            // 为每个二级分类填充三级分类
+            for (StoreDictionaryVo secondLevel : secondLevelList) {
+                List<StoreDictionaryVo> thirdLevelList = allVoList.stream()
+                        .filter(vo -> {
+                            StoreDictionary entity = allList.stream()
+                                    .filter(e -> e.getId().equals(vo.getId()))
+                                    .findFirst()
+                                    .orElse(null);
+                            return entity != null && secondLevel.getId().equals(entity.getParentId());
+                        })
+                        .collect(Collectors.toList());
+
+                secondLevel.setSubDataList(thirdLevelList.isEmpty() ? null : thirdLevelList);
+            }
+
+            firstLevel.setSubDataList(secondLevelList.isEmpty() ? null : secondLevelList);
+        }
+
+        return firstLevelList;
+    }
+
+    /**
+     * 递归构建路径
+     *
+     * @param current  当前节点
+     * @param pathList 路径列表
+     */
+    private void buildPath(StoreDictionary current, List<StoreDictionaryVo> pathList) {
+        if (current == null) {
+            return;
+        }
+
+        // 跳过根节点
+        if (ROOT_DICT_ID.equals(current.getDictId())) {
+            return;
+        }
+
+        // 添加当前节点
+        StoreDictionaryVo vo = new StoreDictionaryVo();
+        BeanUtils.copyProperties(current, vo);
+        pathList.add(vo);
+
+        // 递归查询父节点
+        if (current.getParentId() != null) {
+            StoreDictionary parent = storeDictionaryMapper.selectById(current.getParentId());
+            buildPath(parent, pathList);
+        }
+    }
+
+    /**
+     * 实体列表转 VO 列表
+     *
+     * @param entityList 实体列表
+     * @return VO列表
+     */
+    private List<StoreDictionaryVo> convertToVoList(List<StoreDictionary> entityList) {
+        if (entityList == null || entityList.isEmpty()) {
+            return new ArrayList<>();
+        }
+
+        return entityList.stream().map(entity -> {
+            StoreDictionaryVo vo = new StoreDictionaryVo();
+            BeanUtils.copyProperties(entity, vo);
+            return vo;
+        }).collect(Collectors.toList());
+    }
+}
+

+ 170 - 5
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/LicenseServiceImpl.java

@@ -6,7 +6,9 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.BeanUtils;
 import org.springframework.stereotype.Service;
 import org.springframework.util.CollectionUtils;
+import shop.alien.entity.second.vo.StoreImgDTO;
 import shop.alien.entity.store.*;
+import shop.alien.entity.store.vo.StoreImgVo;
 import shop.alien.entity.storePlatform.vo.StoreLicenseHistoryDto;
 import shop.alien.entity.storePlatform.vo.StoreLicenseHistoryVO;
 import shop.alien.entity.storePlatform.StoreLicenseHistory;
@@ -95,11 +97,29 @@ public class LicenseServiceImpl implements LicenseService {
      * @return 合同图片列表
      */
     @Override
-    public List<StoreImg> getContractList (Integer id) {
-        // 查询图片类型为15(合同)的图片列表
-        return storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>()
+    public List<StoreImgDTO> getContractList (Integer id) {
+
+
+        StoreInfo storeInfo = storeInfoMapper.selectOne(new LambdaQueryWrapper<StoreInfo>().eq(StoreInfo::getId, id));
+        
+
+        log.info("getEntertainmentLicenseList - 查询商户ID为 {} 的娱乐经营许可证图片列表", id);
+        List<StoreImg> storeImgList = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>()
                 .eq(StoreImg::getImgType, 15)
                 .eq(StoreImg::getStoreId, id));
+
+        List<StoreImgDTO> storeImgVoList = new ArrayList<>();
+        for (StoreImg storeImg : storeImgList) {
+            StoreImgDTO storeImgVo = new StoreImgDTO();
+            BeanUtils.copyProperties(storeImg, storeImgVo);
+            if (storeInfo != null) {
+                storeImgVo.setExpirationTime(storeInfo.getExpirationTime());
+            }
+            storeImgVoList.add(storeImgVo);
+        }
+
+        // 查询图片类型为15(合同)的图片列表
+        return storeImgVoList;
     }
 
     /**
@@ -112,11 +132,28 @@ public class LicenseServiceImpl implements LicenseService {
      * @return 食品经营许可证图片列表
      */
     @Override
-    public List<StoreImg> getFoodLicenceList (Integer id) {
+    public List<StoreImgDTO> getFoodLicenceList (Integer id) {
+         log.info("getEntertainmentLicenseList - 查询商户ID为 {} 的娱乐经营许可证图片列表", id);
+        
+        StoreInfo storeInfo = storeInfoMapper.selectOne(new LambdaQueryWrapper<StoreInfo>().eq(StoreInfo::getId, id));
+        
         // 查询图片类型为25(食品经营许可证)的图片列表
-        return storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>()
+        List<StoreImg> storeImgList = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>()
                 .eq(StoreImg::getImgType, 25)
                 .eq(StoreImg::getStoreId, id));
+
+        List<StoreImgDTO> storeImgVoList = new ArrayList<>();
+        for (StoreImg storeImg : storeImgList) {
+            StoreImgDTO storeImgVo = new StoreImgDTO();
+            BeanUtils.copyProperties(storeImg, storeImgVo);
+            if (storeInfo != null) {
+                storeImgVo.setExpirationTime(storeInfo.getFoodLicenceExpirationTime());
+            }
+            storeImgVoList.add(storeImgVo);
+        }
+
+        // 查询图片类型为15(合同)的图片列表
+        return storeImgVoList;
     }
 
     /**
@@ -383,5 +420,133 @@ public class LicenseServiceImpl implements LicenseService {
 
         return resultList;
     }
+
+    /**
+     * 获取营业执照图片列表
+     * <p>
+     * 查询指定商户的营业执照图片信息(图片类型为14)
+     * </p>
+     *
+     * @param id 商户ID
+     * @return 营业执照图片列表
+     */
+    @Override
+    public List<StoreImgDTO> getEntertainmentLicenseList (Integer id) {
+
+        StoreInfo storeInfo = storeInfoMapper.selectOne(new LambdaQueryWrapper<StoreInfo>().eq(StoreInfo::getId, id));
+        
+        log.info("getEntertainmentLicenseList - 查询商户ID为 {} 的娱乐经营许可证图片列表", id);
+        List<StoreImg> storeImgList = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>()
+                .eq(StoreImg::getImgType, 31)
+                .eq(StoreImg::getStoreId, id));
+
+        List<StoreImgDTO> storeImgVoList = new ArrayList<>();
+        for (StoreImg storeImg : storeImgList) {
+            StoreImgDTO storeImgVo = new StoreImgDTO();
+            BeanUtils.copyProperties(storeImg, storeImgVo);
+            if (storeInfo != null) {
+                storeImgVo.setExpirationTime(storeInfo.getEntertainmentLicenceExpirationTime());
+            }
+            storeImgVoList.add(storeImgVo);
+        }
+        return storeImgVoList;
+    }
+
+    @Override
+    public int uploadEntertainmentLicence(StoreImg storeImg) {
+        storeImgMapper.delete(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getImgType,32).eq(StoreImg::getStoreId,storeImg.getStoreId()));
+        storeImg.setImgType(32);
+        storeImg.setImgDescription("经营许可证审核通过前图片");
+        storeImgMapper.insert(storeImg);
+
+
+        // 经营许可证历史表插入
+        StoreLicenseHistory licenseHistory = new StoreLicenseHistory();
+        licenseHistory.setStoreId(storeImg.getStoreId());
+        licenseHistory.setLicenseStatus(3);
+        licenseHistory.setLicenseExecuteStatus(2);
+        licenseHistory.setImgUrl(storeImg.getImgUrl());
+        licenseHistory.setDeleteFlag(0);
+        licenseHistoryMapper.insert(licenseHistory);
+
+        //更新店铺
+        StoreInfo storeInfo = new StoreInfo();
+        storeInfo.setEntertainmentLicenceStatus(2);
+        storeInfo.setId(storeImg.getStoreId());
+        storeInfo.setUpdateEntertainmentLicenceTime(new Date());
+        return storeInfoMapper.updateById(storeInfo);
+    }
+
+    /**
+     * 根据商户ID查询证照历史记录列表(带格式化日期)
+     * <p>
+     * 查询指定商户的证照历史记录,将创建时间格式化为 yyyy-MM-dd 格式,
+     * 并转换为 VO 对象返回
+     * </p>
+     *
+     * @param storeId 商户ID
+     * @return 证照历史记录VO列表
+     */
+    @Override
+    public List<StoreLicenseHistoryDto> queryEntertainmentByStatusList (Integer storeId) {
+
+        List<StoreLicenseHistory> licenseList = licenseHistoryMapper.queryLicenceByStatusList(storeId, 3, 1);
+
+        // 创建日期格式化对象,格式为 yyyy-MM-dd
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
+
+        // 创建返回的 VO 列表
+        List<StoreLicenseHistoryDto> voList = new java.util.ArrayList<>();
+
+        for (StoreLicenseHistory license : licenseList) {
+            // 创建 VO 对象
+            StoreLicenseHistoryDto vo = new StoreLicenseHistoryDto();
+
+            // 将实体对象属性复制到 VO 对象
+            BeanUtils.copyProperties(license, vo);
+
+            // 将创建时间格式化为 yyyy-MM-dd 格式,去除时分秒
+            if (license.getCreatedTime() != null) {
+                String dateStr = sdf.format(license.getCreatedTime());
+                vo.setCreatedDateFormat(dateStr);
+            }
+
+            // 设置审核状态名称
+            if (license.getLicenseExecuteStatus() != null) {
+                switch (license.getLicenseExecuteStatus()) {
+                    case 1:
+                        vo.setLicenseExecuteName("审核通过");
+                        break;
+                    case 2:
+                        vo.setLicenseExecuteName("审核中");
+                        break;
+                    case 3:
+                        vo.setLicenseExecuteName("审核失败");
+                        break;
+                    default:
+                        vo.setLicenseExecuteName("未知");
+                        break;
+                }
+            }
+
+            voList.add(vo);
+        }
+
+        return voList;
+    }
+
+
+    @Override
+    public Map<String, Object> getStoreEntertainmentStatus(int id) {
+        Map<String, Object> map = new HashMap<>();
+        StoreInfo storeInfo = storeInfoMapper.selectOne(new LambdaQueryWrapper<StoreInfo>().eq(StoreInfo::getId, id));
+        //审核通过给前台反显未提交
+        if (storeInfo.getEntertainmentLicenceStatus() != null && storeInfo.getEntertainmentLicenceStatus() == 1) {
+            map.put("entertainmentStatus", 0);
+        } else {
+            map.put("entertainmentStatus", storeInfo.getEntertainmentLicenceStatus());
+        }
+        return map;
+    }
 }
 

+ 300 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/OperationalActivityServiceImpl.java

@@ -0,0 +1,300 @@
+package shop.alien.storeplatform.service.impl;
+
+import com.alibaba.excel.util.StringUtils;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+import shop.alien.entity.store.LifeDiscountCoupon;
+import shop.alien.entity.store.LifeUser;
+import shop.alien.entity.store.StoreImg;
+import shop.alien.entity.store.StoreUser;
+import shop.alien.entity.storePlatform.StoreOperationalActivity;
+import shop.alien.entity.storePlatform.vo.StoreOperationalActivityDTO;
+import shop.alien.entity.storePlatform.vo.StoreOperationalActivityVO;
+import shop.alien.mapper.LifeDiscountCouponMapper;
+import shop.alien.mapper.StoreImgMapper;
+import shop.alien.mapper.storePlantform.StoreOperationalActivityMapper;
+import shop.alien.storeplatform.service.OperationalActivityService;
+
+import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 运营活动服务实现类
+ * <p>
+ * 提供运营活动的增删改查功能,包括:
+ * 1. 活动的创建、更新、删除
+ * 2. 活动列表查询、分页查询
+ * 3. 活动状态管理
+ * </p>
+ *
+ * @author system
+ * @since 2025-11-26
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class OperationalActivityServiceImpl implements OperationalActivityService {
+
+    private final StoreOperationalActivityMapper activityMapper;
+
+    private final StoreImgMapper imgMapper;
+
+    private final LifeDiscountCouponMapper lifeDiscountCouponMapper;
+
+    @Override
+    public int createActivity(StoreOperationalActivityDTO dto) {
+        log.info("OperationalActivityServiceImpl.createActivity: dto={}", dto);
+        
+        StoreOperationalActivity activity = new StoreOperationalActivity();
+        BeanUtils.copyProperties(dto, activity);
+        
+        // 设置默认值
+        if (activity.getParticipationLimit() == null) {
+            activity.setParticipationLimit(0);
+        }
+        if (activity.getStatus() == null) {
+            activity.setStatus(1);
+        }
+        Integer result = activityMapper.insert(activity);
+        if (result > 0) {
+            dto.getActivityTitleImg().setBusinessId(activity.getId());
+            dto.getActivityTitleImg().setImgType(26);
+            imgMapper.insert(dto.getActivityTitleImg());
+
+            dto.getActivityDetailImg().setBusinessId(activity.getId());
+            dto.getActivityDetailImg().setImgType(27);
+            imgMapper.insert(dto.getActivityDetailImg());
+        }
+        
+        return result;
+    }
+
+    @Override
+    public int updateActivity(StoreOperationalActivityDTO dto) {
+        log.info("OperationalActivityServiceImpl.updateActivity: dto={}", dto);
+        
+        if (dto.getId() == null) {
+            throw new IllegalArgumentException("活动ID不能为空");
+        }
+        
+        StoreOperationalActivity activity = new StoreOperationalActivity();
+        BeanUtils.copyProperties(dto, activity);
+        Integer result = activityMapper.updateById(activity);
+
+        // 添加
+        if (result > 0) {
+            // 删除原本的图片
+            LambdaUpdateWrapper<StoreImg> wrapper = new LambdaUpdateWrapper<>();
+            wrapper.eq(StoreImg::getBusinessId, dto.getId())
+                    .eq(StoreImg::getStoreId, dto.getStoreId())
+                    .set(StoreImg::getDeleteFlag, 1);
+            imgMapper.update(null, wrapper);
+
+
+
+            // 插入新图片
+            StoreImg activityTitleImg = new StoreImg();
+            activityTitleImg.setStoreId(dto.getStoreId());
+            activityTitleImg.setImgType(26);
+            activityTitleImg.setImgSort(dto.getActivityTitleImg().getImgSort());
+            activityTitleImg.setImgUrl(dto.getActivityTitleImg().getImgUrl());
+            activityTitleImg.setBusinessId(dto.getId());
+            imgMapper.insert(activityTitleImg);
+
+            // 插入新图片
+            StoreImg activityDetailImg = new StoreImg();
+            activityDetailImg.setStoreId(dto.getStoreId());
+            activityDetailImg.setImgType(27);
+            activityDetailImg.setImgSort(dto.getActivityDetailImg().getImgSort());
+            activityDetailImg.setImgUrl(dto.getActivityDetailImg().getImgUrl());
+            activityDetailImg.setBusinessId(dto.getId());
+            imgMapper.insert(activityDetailImg);
+        }
+        return result;
+    }
+
+    @Override
+    public int deleteActivity(Integer id) {
+        log.info("OperationalActivityServiceImpl.deleteActivity: id={}", id);
+        
+        if (id == null) {
+            throw new IllegalArgumentException("活动ID不能为空");
+        }
+        
+        // 逻辑删除
+        return activityMapper.deleteById(id);
+    }
+
+    @Override
+    public StoreOperationalActivityVO queryActivityById(Integer id) {
+        log.info("OperationalActivityServiceImpl.getActivityById: id={}", id);
+        
+        if (id == null) {
+            throw new IllegalArgumentException("活动ID不能为空");
+        }
+        
+        StoreOperationalActivity activity = activityMapper.selectById(id);
+
+        if (activity == null) {
+            return null;
+        }
+
+        // 创建实体类
+        StoreOperationalActivityVO vo = new StoreOperationalActivityVO();
+        BeanUtils.copyProperties(activity, vo);
+
+        if (activity.getStatus() == 1) {
+            vo.setStatusName("待审核");
+        } else if (activity.getStatus() == 2) {
+            vo.setStatusName("未开始");
+        } else if (activity.getStatus() == 3) {
+            vo.setStatusName("审核拒绝");
+        } else if (activity.getStatus() == 4) {
+            vo.setStatusName("已售罄");
+        } else if (activity.getStatus() == 5) {
+            vo.setStatusName("进行中");
+        } else if (activity.getStatus() == 6) {
+            vo.setStatusName("已下架");
+        } else if (activity.getStatus() == 7) {
+            vo.setStatusName("已结束");
+        }
+        
+        // 设置优惠券名称(判空处理)
+        if (activity.getCouponId() != null) {
+            LifeDiscountCoupon coupon = lifeDiscountCouponMapper.selectById(activity.getCouponId());
+            if (coupon != null) {
+                vo.setCouponName(coupon.getName());
+            }
+        }
+
+        StoreImg activityTitleImg = imgMapper.selectOne(new LambdaQueryWrapper<StoreImg>()
+                .eq(StoreImg::getStoreId, vo.getStoreId())
+                .eq(StoreImg::getImgType, 26)
+                .eq(StoreImg::getDeleteFlag, 0)
+                .eq(StoreImg::getBusinessId, activity.getId()));
+        if (activityTitleImg != null) {
+            vo.setActivityTitleImgUrl(activityTitleImg.getImgUrl());
+        }
+
+
+        StoreImg activityDetailImg = imgMapper.selectOne(new LambdaQueryWrapper<StoreImg>()
+                .eq(StoreImg::getStoreId, vo.getStoreId())
+                .eq(StoreImg::getImgType, 27)
+                .eq(StoreImg::getDeleteFlag, 0)
+                .eq(StoreImg::getBusinessId, activity.getId()));
+        if (activityDetailImg != null) {
+            vo.setActivityDetailImgUrl(activityDetailImg.getImgUrl());
+        }
+
+        return vo;
+    }
+
+
+    @Override
+    public IPage<StoreOperationalActivityVO> queryActivityList(Integer storeId, Integer status, String activityName, Integer pageNum, Integer pageSize) {
+        log.info("OperationalActivityServiceImpl.queryActivityList: storeId={}, status={}, activityName={}, pageNum={}, pageSize={}", 
+                storeId, status, activityName, pageNum, pageSize);
+        
+        LambdaQueryWrapper<StoreOperationalActivity> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(storeId != null, StoreOperationalActivity::getStoreId, storeId);
+        wrapper.like(activityName != null && activityName != "", StoreOperationalActivity::getActivityName, activityName);
+        wrapper.eq(status != null, StoreOperationalActivity::getStatus, status);
+
+        IPage<StoreOperationalActivity> list = activityMapper.selectPage(new Page<>(pageNum, pageSize), wrapper);
+        
+        // 将list复制到vo
+        List<StoreOperationalActivityVO> voRecords = new ArrayList<>();
+        
+        for (StoreOperationalActivity activity : list.getRecords()) {
+            // 创建实体类
+            StoreOperationalActivityVO vo = new StoreOperationalActivityVO();
+            BeanUtils.copyProperties(activity, vo);
+
+            if (activity.getStatus() == 1) {
+                vo.setStatusName("待审核");
+            } else if (activity.getStatus() == 2) {
+                vo.setStatusName("未开始");
+            } else if (activity.getStatus() == 3) {
+                vo.setStatusName("审核拒绝");
+            } else if (activity.getStatus() == 4) {
+                vo.setStatusName("已售罄");
+            } else if (activity.getStatus() == 5) {
+                vo.setStatusName("进行中");
+            } else if (activity.getStatus() == 6) {
+                vo.setStatusName("已下架");
+            } else if (activity.getStatus() == 7) {
+                vo.setStatusName("已结束");
+            }
+
+            vo.setCouponName(lifeDiscountCouponMapper.selectById(activity.getCouponId()).getName());
+
+            voRecords.add(vo);
+        }
+        
+        // 创建分页结果对象
+        Page<StoreOperationalActivityVO> voList = new Page<>(list.getCurrent(), list.getSize(), list.getTotal());
+        voList.setRecords(voRecords);
+        
+        return voList;
+    }
+
+    public static void main(String[] args) {
+        // 时间格式化
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+        Date now = new Date();
+        System.out.println("当前时间: " + sdf.format(now));
+        
+        // 获取当天零点零分零秒时间
+        Calendar todayStart = Calendar.getInstance();
+        todayStart.setTime(now);
+        todayStart.set(Calendar.HOUR_OF_DAY, 0);
+        todayStart.set(Calendar.MINUTE, 0);
+        todayStart.set(Calendar.SECOND, 0);
+        todayStart.set(Calendar.MILLISECOND, 0);
+        Date todayZero = todayStart.getTime();
+        System.out.println("当天零点时间: " + sdf.format(todayZero));
+        
+        // now + 1天(零点)
+        Calendar calendarPlus = Calendar.getInstance();
+        calendarPlus.setTime(now);
+        calendarPlus.add(Calendar.DAY_OF_MONTH, 1);
+        calendarPlus.set(Calendar.HOUR_OF_DAY, 0);
+        calendarPlus.set(Calendar.MINUTE, 0);
+        calendarPlus.set(Calendar.SECOND, 0);
+        calendarPlus.set(Calendar.MILLISECOND, 0);
+        Date nowPlusOneDay = calendarPlus.getTime();
+        System.out.println("当前时间+1天(零点): " + sdf.format(nowPlusOneDay));
+
+    }
+
+    @Override
+    public int updateActivityStatus(Integer id, Integer status) {
+        log.info("OperationalActivityServiceImpl.updateActivityStatus: id={}, status={}", id, status);
+        
+        if (id == null) {
+            throw new IllegalArgumentException("活动ID不能为空");
+        }
+        
+        StoreOperationalActivity activity = new StoreOperationalActivity();
+        activity.setId(id);
+        activity.setStatus(status);
+        
+        return activityMapper.updateById(activity);
+    }
+}
+

+ 44 - 1
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StoreManageServiceImpl.java

@@ -114,13 +114,30 @@ public class StoreManageServiceImpl implements StoreManageService {
             StoreDictionary typeDict = storeDictionaryMapper.selectOne(
                     new LambdaQueryWrapper<StoreDictionary>()
                             .eq(StoreDictionary::getDictId, businessType)
-                            .eq(StoreDictionary::getParentId, businessSectionDict.getId())
+                            .eq(StoreDictionary::getTypeName, "business_type")
             );
             if (typeDict != null) {
                 businessTypeNames.add(typeDict.getDictDetail());
             }
         }
 
+        // 3. 获取分类
+        List<String> businessClassifyNameList = new ArrayList<>();
+        List<String> businessClassifys = storeInfoDto.getBusinessTypes();
+        // 7.1 设置业务分类(business_classify)
+        if (storeInfoDto.getBusinessClassify() != null) {
+            for (String businessClassify : businessClassifys) {
+                StoreDictionary typeDict = storeDictionaryMapper.selectOne(
+                        new LambdaQueryWrapper<StoreDictionary>()
+                                .eq(StoreDictionary::getDictId, businessClassify)
+                                .eq(StoreDictionary::getTypeName, "business_classify")
+                );
+                if (typeDict != null) {
+                    businessClassifyNameList.add(typeDict.getDictDetail());
+                }
+            }
+        }
+
         // 3. 构建店铺信息对象
         StoreInfo storeInfo = new StoreInfo();
         BeanUtils.copyProperties(storeInfoDto, storeInfo);
@@ -149,6 +166,10 @@ public class StoreManageServiceImpl implements StoreManageService {
         storeInfo.setBusinessTypes(String.join(",", businessTypes));
         storeInfo.setBusinessTypesName(String.join(",", businessTypeNames));
 
+        // 分类设置
+        storeInfo.setBusinessClassify(String.join(",", businessClassifys));
+        storeInfo.setBusinessClassifyName(String.join(",", businessClassifyNameList));
+
         // 8. 设置审批状态为待审批
         storeInfo.setStoreApplicationStatus(0);
 
@@ -305,6 +326,28 @@ public class StoreManageServiceImpl implements StoreManageService {
             storeImg.setImgUrl(storeInfoDto.getFoodLicenceUrl());
             storeImgMapper.insert(storeImg);
         }
+
+        // 4. 保存 娱乐经营许可证 图片 (imgType=31)
+        List<String> entertainmentLicenseAddress = storeInfoDto.getEntertainmentLicenseAddress();
+        if (!CollectionUtils.isEmpty(entertainmentLicenseAddress)) {
+            // 先删除已存在的合同图片
+            storeImgMapper.delete(
+                    new LambdaQueryWrapper<StoreImg>()
+                            .eq(StoreImg::getStoreId, storeId)
+                            .eq(StoreImg::getImgType, 31)
+            );
+
+            // 插入娱乐经营许可证
+            for (int i = 0; i < entertainmentLicenseAddress.size(); i++) {
+                StoreImg storeImg = new StoreImg();
+                storeImg.setStoreId(storeId);
+                storeImg.setImgType(31);
+                storeImg.setImgSort(i);
+                storeImg.setImgDescription("娱乐经营许可证");
+                storeImg.setImgUrl(entertainmentLicenseAddress.get(i));
+                storeImgMapper.insert(storeImg);
+            }
+        }
     }
 
     /**

+ 416 - 3
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StoreMenuPlatformServiceImpl.java

@@ -1,6 +1,7 @@
 package shop.alien.storeplatform.service.impl;
 
 import cn.hutool.core.collection.CollectionUtil;
+import com.alibaba.fastjson.JSONObject;
 import com.alibaba.nacos.common.utils.CollectionUtils;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@@ -8,23 +9,34 @@ 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.apache.poi.ss.usermodel.*;
+import org.apache.poi.xssf.usermodel.*;
 import org.springframework.beans.BeanUtils;
 import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
 import shop.alien.entity.result.R;
 import shop.alien.entity.store.LifeGroupBuyThali;
 import shop.alien.entity.store.LifeLikeRecord;
 import shop.alien.entity.store.StoreImg;
 import shop.alien.entity.store.StoreMenu;
+import shop.alien.entity.store.excelVo.util.ExcelImage;
+import shop.alien.entity.store.vo.StoreMenuImportVo;
 import shop.alien.entity.store.vo.StoreMenuVo;
 import shop.alien.mapper.LifeGroupBuyThaliMapper;
 import shop.alien.mapper.LifeLikeRecordMapper;
 import shop.alien.mapper.StoreImgMapper;
 import shop.alien.mapper.StoreMenuMapper;
+import shop.alien.storeplatform.feign.AlienStoreFeign;
 import shop.alien.storeplatform.service.StoreMenuPlatformService;
+import shop.alien.util.ali.AliOSSUtil;
 
-import java.util.Comparator;
-import java.util.List;
-import java.util.Map;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.math.BigDecimal;
+import java.util.*;
 import java.util.stream.Collectors;
 
 /**
@@ -33,6 +45,7 @@ import java.util.stream.Collectors;
  * @author ssk
  * @since 2024-12-05
  */
+@Slf4j
 @Service
 @RequiredArgsConstructor
 public class StoreMenuPlatformServiceImpl extends ServiceImpl<StoreMenuMapper, StoreMenu> implements StoreMenuPlatformService {
@@ -45,6 +58,10 @@ public class StoreMenuPlatformServiceImpl extends ServiceImpl<StoreMenuMapper, S
 
     private final LifeGroupBuyThaliMapper lifeGroupBuyThaliMapper;
 
+    private final AliOSSUtil aliOSSUtil;
+
+    private final AlienStoreFeign alienStoreFeign;
+
     /**
      * 获取门店菜单
      *
@@ -280,6 +297,402 @@ public class StoreMenuPlatformServiceImpl extends ServiceImpl<StoreMenuMapper, S
     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;
     }
+    /**
+     * Excel导入门店菜单
+     *
+     * @param file     Excel文件
+     * @param storeId  门店id
+     * @return 导入结果
+     */
+    @Override
+    public R<String> importMenuFromExcel(MultipartFile file, Integer storeId) {
+        log.info("StoreMenuPlatformServiceImpl.importMenuFromExcel storeId={}", storeId);
+
+        if (file == null || file.isEmpty()) {
+            return R.fail("上传文件为空");
+        }
+
+        String fileName = file.getOriginalFilename();
+        if (fileName == null || (!fileName.endsWith(".xlsx") && !fileName.endsWith(".xls"))) {
+            return R.fail("文件格式不正确,请上传Excel文件");
+        }
+
+        if (storeId == null) {
+            return R.fail("门店ID不能为空");
+        }
+
+        List<String> errorMessages = new ArrayList<>();
+        int successCount = 0;
+        int totalCount = 0;
+
+        try (InputStream inputStream = file.getInputStream();
+             XSSFWorkbook workbook = new XSSFWorkbook(inputStream)) {
+            Sheet sheet = workbook.getSheetAt(0);
+
+            // 获取表头
+            Row headerRow = sheet.getRow(5);
+            if (headerRow == null) {
+                return R.fail("Excel文件格式不正确,缺少表头");
+            }
+
+            // 构建字段映射(表头名称 -> 列索引)
+            Map<String, Integer> headerMap = new HashMap<>();
+            Field[] fields = StoreMenuImportVo.class.getDeclaredFields();
+            for (int i = 0; i < headerRow.getLastCellNum(); i++) {
+                Cell cell = headerRow.getCell(i);
+                if (cell != null) {
+                    String headerName = getCellValueAsString(cell);
+                    if (StringUtils.isNotEmpty(headerName)) {
+                        headerMap.put(headerName.trim(), i);
+                    }
+                }
+            }
+
+            // 获取图片映射(行索引 -> 图片字节数组)
+            Map<Integer, byte[]> imageMap = extractImagesFromSheet(sheet);
+
+            // 读取数据行
+            for (int rowIndex = 6; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
+                Row row = sheet.getRow(rowIndex);
+                if (row == null) {
+                    continue;
+                }
+
+                // 检查是否为空行
+                boolean isEmptyRow = true;
+                for (int i = 0; i < row.getLastCellNum(); i++) {
+                    Cell cell = row.getCell(i);
+                    if (cell != null && cell.getCellType() != CellType.BLANK) {
+                        String cellValue = getCellValueAsString(cell);
+                        if (StringUtils.isNotEmpty(cellValue)) {
+                            isEmptyRow = false;
+                            break;
+                        }
+                    }
+                }
+                if (isEmptyRow) {
+                    continue;
+                }
+
+                totalCount++;
+                StoreMenuImportVo excelVo = new StoreMenuImportVo();
+
+                // 读取每个字段
+                for (Field field : fields) {
+                    field.setAccessible(true);
+                    String fieldName = field.getName();
+                    Integer colIndex = null;
+
+                    // 根据字段名查找对应的表头
+                    for (Map.Entry<String, Integer> entry : headerMap.entrySet()) {
+                        String headerName = entry.getKey();
+                        if (isFieldMatch(fieldName, headerName)) {
+                            colIndex = entry.getValue();
+                            break;
+                        }
+                    }
+
+                    if (colIndex == null) {
+                        continue;
+                    }
+
+                    Cell cell = row.getCell(colIndex);
+                    if (cell == null && !field.isAnnotationPresent(ExcelImage.class)) {
+                        continue;
+                    }
+
+                    try {
+                        if (field.isAnnotationPresent(ExcelImage.class)) {
+                            // 处理图片字段
+                            byte[] imageBytes = imageMap.get(rowIndex);
+                            if (imageBytes != null && imageBytes.length > 0) {
+                                String imageName = "menu_" + storeId + "_" + System.currentTimeMillis() + "_" + rowIndex + ".jpg";
+                                MultipartFile multipartFile = new ByteArrayMultipartFile(imageBytes, imageName);
+                                JSONObject jsonObject = alienStoreFeign.uploadFile(multipartFile);
+                                if (200 == jsonObject.getIntValue("code")) {
+                                    field.set(excelVo, jsonObject.getJSONArray("data").get(0));
+                                } else {
+                                    field.set(excelVo, "");
+                                }
+//                                String imageUrl = uploadImageToOSS(imageBytes, storeId, rowIndex);
+                            }
+                        } else {
+                            // 处理普通字段
+                            String cellValue = getCellValueAsString(cell);
+                            if (StringUtils.isNotEmpty(cellValue)) {
+                                setFieldValue(excelVo, field, cellValue.trim());
+                            }
+                        }
+                    } catch (Exception e) {
+                        log.warn("读取字段{}失败:{}", fieldName, e.getMessage());
+                    }
+                }
+
+                // 处理导入数据
+                try {
+                    validateAndSaveMenu(excelVo, storeId, rowIndex + 1);
+                    successCount++;
+                } catch (Exception e) {
+                    errorMessages.add(String.format("第%d行:%s", rowIndex + 1, e.getMessage()));
+                    log.error("导入第{}行数据失败", rowIndex + 1, e);
+                }
+            }
+        } catch (Exception e) {
+            log.error("导入Excel失败", e);
+            return R.fail("导入失败:" + e.getMessage());
+        }
+
+        // 构建返回消息
+        StringBuilder message = new StringBuilder();
+        message.append(String.format("导入完成:成功%d条,失败%d条", successCount, totalCount - successCount));
+        if (!errorMessages.isEmpty()) {
+            message.append("\n失败详情:\n");
+            for (int i = 0; i < Math.min(errorMessages.size(), 10); i++) {
+                message.append(errorMessages.get(i)).append("\n");
+            }
+            if (errorMessages.size() > 10) {
+                message.append("...还有").append(errorMessages.size() - 10).append("条错误信息");
+            }
+        }
+
+        return R.success(message.toString());
+    }
+
+    /**
+     * 从Sheet中提取图片
+     */
+    private Map<Integer, byte[]> extractImagesFromSheet(Sheet sheet) {
+        Map<Integer, byte[]> imageMap = new HashMap<>();
+        if (sheet instanceof XSSFSheet) {
+            XSSFSheet xssfSheet = (XSSFSheet) sheet;
+            XSSFDrawing drawing = xssfSheet.getDrawingPatriarch();
+            if (drawing != null) {
+                List<XSSFShape> shapes = drawing.getShapes();
+                for (XSSFShape shape : shapes) {
+                    if (shape instanceof XSSFPicture) {
+                        XSSFPicture picture = (XSSFPicture) shape;
+                        XSSFClientAnchor anchor = (XSSFClientAnchor) picture.getAnchor();
+                        int rowIndex = anchor.getRow1();
+                        try {
+                            // 直接使用 getData() 方法获取图片字节数组
+                            byte[] imageBytes = picture.getPictureData().getData();
+                            imageMap.put(rowIndex, imageBytes);
+                        } catch (Exception e) {
+                            log.warn("提取第{}行图片失败:{}", rowIndex, e.getMessage());
+                        }
+                    }
+                }
+            }
+        }
+        return imageMap;
+    }
+
+    /**
+     * 上传图片到OSS
+     */
+    private String uploadImageToOSS(byte[] imageBytes, Integer storeId, int rowIndex) {
+        try {
+            // 生成文件名
+            String fileName = "menu_" + storeId + "_" + System.currentTimeMillis() + "_" + rowIndex + ".jpg";
+            String prefix = "image/";
+
+            // 创建临时MultipartFile
+            MultipartFile multipartFile = new ByteArrayMultipartFile(imageBytes, fileName);
+
+            // 上传到OSS
+            return aliOSSUtil.uploadFile(multipartFile, prefix + fileName);
+        } catch (Exception e) {
+            log.error("上传图片失败", e);
+            throw new RuntimeException("上传图片失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 校验并保存菜单
+     */
+    private void validateAndSaveMenu(StoreMenuImportVo excelVo, Integer storeId, int rowNum) {
+        // 校验必填字段
+        if (StringUtils.isEmpty(excelVo.getDishName())) {
+            throw new RuntimeException("菜品名称不能为空");
+        }
+
+        if (excelVo.getDishPrice() == null || excelVo.getDishPrice().compareTo(BigDecimal.ZERO) <= 0) {
+            throw new RuntimeException("菜品价格必须大于0");
+        }
+
+        // 创建StoreMenu对象
+        StoreMenu storeMenu = new StoreMenu();
+        storeMenu.setStoreId(storeId);
+        storeMenu.setDishName(excelVo.getDishName());
+        storeMenu.setDishPrice(excelVo.getDishPrice());
+        storeMenu.setCostPrice(excelVo.getCostPrice());
+//        storeMenu.setDishesUnit(excelVo.getDishesUnit());
+        storeMenu.setDescription(excelVo.getDescription());
+        storeMenu.setDishType(excelVo.getDishType());
+
+        // 处理图片
+        if (StringUtils.isNotEmpty(excelVo.getImg())) {
+            StoreImg storeImg = new StoreImg();
+            storeImg.setStoreId(storeId);
+            storeImg.setImgType(7);
+            storeImg.setImgUrl(excelVo.getImg());
+            storeImg.setImgDescription(excelVo.getDishName());
+            storeImgMapper.insert(storeImg);
+            storeMenu.setImgId(storeImg.getId());
+        }
+
+        // 设置排序
+        LambdaQueryWrapper<StoreMenu> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(StoreMenu::getStoreId, storeId);
+        List<StoreMenu> menuList = this.list(queryWrapper);
+        if (CollectionUtil.isNotEmpty(menuList)) {
+            int maxSort = menuList.stream().map(StoreMenu::getSort).max(Integer::compareTo).orElse(0);
+            storeMenu.setSort(maxSort + 1);
+        } else {
+            storeMenu.setSort(1);
+        }
+
+        // 保存菜单
+        boolean flag = this.save(storeMenu);
+        if (!flag) {
+            throw new RuntimeException("保存菜单失败");
+        }
+    }
+
+    /**
+     * 判断字段名是否匹配表头
+     */
+    private boolean isFieldMatch(String fieldName, String headerName) {
+        // 简单的匹配逻辑,可以根据实际需求调整
+        Map<String, String> fieldMapping = new HashMap<>();
+        fieldMapping.put("dishName", "菜品名称");
+        fieldMapping.put("dishPrice", "价格(¥)");
+        fieldMapping.put("costPrice", "成本价(¥)");
+//        fieldMapping.put("dishesUnit", "单位");
+        fieldMapping.put("img", "图片");
+        fieldMapping.put("description", "描述");
+        fieldMapping.put("dishType", "是否设为推荐(是/否)");
+
+        String expectedHeader = fieldMapping.get(fieldName);
+        return expectedHeader != null && expectedHeader.equals(headerName);
+    }
+
+    /**
+     * 设置字段值
+     */
+    private void setFieldValue(StoreMenuImportVo excelVo, Field field, String cellValue) throws Exception {
+        Class<?> fieldType = field.getType();
+        String fieldName = field.getName();
+        if (fieldType == BigDecimal.class) {
+            try {
+                if (new BigDecimal(cellValue).scale() > 2) {
+                    throw new RuntimeException("价格或者成本价格式错误, 请输入数字, 小数点保留两位以内");
+                } else {
+                    field.set(excelVo, new BigDecimal(cellValue));
+                }
+            } catch (NumberFormatException e) {
+                throw new RuntimeException("价格或者成本价格式错误, 请输入数字, 小数点保留两位以内");
+            }
+        } else {
+            if ("dishType".equals(fieldName)) {
+                String trimmedValue = cellValue.trim();
+                if ("是".equals(trimmedValue)) {
+                    field.set(excelVo, 1);
+                } else if ("否".equals(trimmedValue)) {
+                    field.set(excelVo, 0);
+                } else {
+                    throw new RuntimeException("是否推荐字段格式错误,请输入'是'或'否'");
+                }
+            } else {
+                field.set(excelVo, cellValue);
+            }
+        }
+    }
+
+    /**
+     * 获取单元格值(字符串格式)
+     */
+    private String getCellValueAsString(Cell cell) {
+        if (cell == null) {
+            return null;
+        }
+
+        switch (cell.getCellType()) {
+            case STRING:
+                return cell.getStringCellValue();
+            case NUMERIC:
+                if (DateUtil.isCellDateFormatted(cell)) {
+                    return cell.getDateCellValue().toString();
+                } else {
+                    // 处理数字,避免科学计数法
+                    double numericValue = cell.getNumericCellValue();
+                    if (numericValue == (long) numericValue) {
+                        return String.valueOf((long) numericValue);
+                    } else {
+                        return String.valueOf(numericValue);
+                    }
+                }
+            case BOOLEAN:
+                return String.valueOf(cell.getBooleanCellValue());
+            case FORMULA:
+                return cell.getCellFormula();
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * 临时MultipartFile实现类,用于上传字节数组
+     */
+    private static class ByteArrayMultipartFile implements MultipartFile {
+        private final byte[] content;
+        private final String fileName;
+
+        public ByteArrayMultipartFile(byte[] content, String fileName) {
+            this.content = content;
+            this.fileName = fileName;
+        }
+
+        @Override
+        public String getName() {
+            return "file";
+        }
+
+        @Override
+        public String getOriginalFilename() {
+            return fileName;
+        }
+
+        @Override
+        public String getContentType() {
+            return "image/jpeg";
+        }
+
+        @Override
+        public boolean isEmpty() {
+            return content == null || content.length == 0;
+        }
+
+        @Override
+        public long getSize() {
+            return content.length;
+        }
+
+        @Override
+        public byte[] getBytes() throws IOException {
+            return content;
+        }
+
+        @Override
+        public InputStream getInputStream() throws IOException {
+            return new ByteArrayInputStream(content);
+        }
+
+        @Override
+        public void transferTo(java.io.File dest) throws IOException, IllegalStateException {
+            java.nio.file.Files.write(dest.toPath(), content);
+        }
+    }
 }
 
 

+ 637 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StorePlatformBarMenuServiceImpl.java

@@ -0,0 +1,637 @@
+package shop.alien.storeplatform.service.impl;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.alibaba.fastjson.JSONObject;
+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.apache.poi.ss.usermodel.*;
+import org.apache.poi.xssf.usermodel.*;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreImg;
+import shop.alien.entity.store.excelVo.util.ExcelImage;
+import shop.alien.mapper.StoreImgMapper;
+import shop.alien.storeplatform.entity.StorePlatformStoreMenu;
+import shop.alien.storeplatform.feign.AlienStoreFeign;
+import shop.alien.storeplatform.mapper.StorePlatformStoreMenuMapper;
+import shop.alien.storeplatform.service.StorePlatformBarMenuService;
+import shop.alien.storeplatform.util.FileUploadUtil;
+import shop.alien.storeplatform.vo.StorePlatformStoreMenuImportVo;
+import shop.alien.util.ali.AliOSSUtil;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 商户平台-酒吧菜单服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class StorePlatformBarMenuServiceImpl extends ServiceImpl<StorePlatformStoreMenuMapper, StorePlatformStoreMenu> implements StorePlatformBarMenuService {
+
+    private final StorePlatformStoreMenuMapper storeMenuMapper;
+
+    private final StoreImgMapper storeImgMapper;
+
+    private final AliOSSUtil aliOSSUtil;
+
+    private final AlienStoreFeign alienStoreFeign;
+
+    @Override
+    public StorePlatformStoreMenu getById(Integer id) {
+        log.info("StorePlatformBarMenuServiceImpl.getById id={}", id);
+        if (id == null) {
+            throw new RuntimeException("菜单ID不能为空");
+        }
+        return super.getById(id);
+    }
+
+    @Override
+    public List<StorePlatformStoreMenu> getListByStoreId(Integer storeId) {
+        log.info("StorePlatformBarMenuServiceImpl.getListByStoreId storeId={}", storeId);
+        if (storeId == null) {
+            throw new RuntimeException("门店ID不能为空");
+        }
+        LambdaQueryWrapper<StorePlatformStoreMenu> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(StorePlatformStoreMenu::getStoreId, storeId)
+                .eq(StorePlatformStoreMenu::getDeleteFlag, 0)
+                .orderByAsc(StorePlatformStoreMenu::getSort);
+        List<StorePlatformStoreMenu> list = this.list(queryWrapper);
+        return list.stream()
+                .sorted(Comparator.comparing(StorePlatformStoreMenu::getSort))
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public List<StorePlatformStoreMenu> getListByStoreIdAndType(Integer storeId, String dishMenuType, String dishType) {
+        log.info("StorePlatformBarMenuServiceImpl.getListByStoreIdAndType storeId={}, dishMenuType={}", storeId, dishMenuType);
+        if (storeId == null) {
+            throw new RuntimeException("门店ID不能为空");
+        }
+        LambdaQueryWrapper<StorePlatformStoreMenu> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(StorePlatformStoreMenu::getStoreId, storeId)
+                .eq(StorePlatformStoreMenu::getDeleteFlag, 0);
+        if (StringUtils.isNotEmpty(dishMenuType)) {
+            queryWrapper.eq(StorePlatformStoreMenu::getDishMenuType, dishMenuType);
+        }
+        if (StringUtils.isNotEmpty(dishType) && dishType.equals("1")) {
+            queryWrapper.eq(StorePlatformStoreMenu::getDishType, dishType);
+        }
+        queryWrapper.orderByAsc(StorePlatformStoreMenu::getSort);
+        List<StorePlatformStoreMenu> list = this.list(queryWrapper);
+
+        // 查询图片
+        List<Integer> ids = list.stream().map(StorePlatformStoreMenu::getImgId).collect(Collectors.toList());
+        List<StoreImg> imgList = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>()
+                .eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 7).in(StoreImg::getId, ids));
+
+        if (CollectionUtil.isNotEmpty(imgList)) {
+            list.forEach(menu -> {
+                imgList.stream()
+                        .filter(img -> img.getId().equals(menu.getImgId()))
+                        .findFirst()
+                        .ifPresent(img -> menu.setImgUrl(img.getImgUrl()));
+            });
+        }
+
+        return list.stream()
+                .sorted(Comparator.comparing(StorePlatformStoreMenu::getSort))
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public R<String> saveMenu(StorePlatformStoreMenu storeMenu) {
+        log.info("StorePlatformBarMenuServiceImpl.saveMenu storeMenu={}", storeMenu);
+        if (storeMenu == null) {
+            return R.fail("菜单信息不能为空");
+        }
+        if (storeMenu.getStoreId() == null) {
+            return R.fail("门店ID不能为空");
+        }
+        if (StringUtils.isEmpty(storeMenu.getDishName())) {
+            return R.fail("菜单名称不能为空");
+        }
+
+        // 设置排序
+        LambdaQueryWrapper<StorePlatformStoreMenu> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(StorePlatformStoreMenu::getStoreId, storeMenu.getStoreId())
+                .eq(StorePlatformStoreMenu::getDeleteFlag, 0);
+        List<StorePlatformStoreMenu> menuList = this.list(queryWrapper);
+        if (CollectionUtil.isNotEmpty(menuList)) {
+            int maxSort = menuList.stream()
+                    .map(StorePlatformStoreMenu::getSort)
+                    .max(Integer::compareTo)
+                    .orElse(0);
+            storeMenu.setSort(maxSort + 1);
+        } else {
+            storeMenu.setSort(1);
+        }
+
+        boolean flag = this.save(storeMenu);
+        if (!flag) {
+            return R.fail("新增菜单失败");
+        }
+        return R.success("新增菜单成功");
+    }
+
+    @Override
+    public R<String> updateMenu(StorePlatformStoreMenu storeMenu) {
+        log.info("StorePlatformBarMenuServiceImpl.updateMenu storeMenu={}", storeMenu);
+        if (storeMenu == null || storeMenu.getId() == null) {
+            return R.fail("菜单信息或菜单ID不能为空");
+        }
+        if (StringUtils.isEmpty(storeMenu.getDishName())) {
+            return R.fail("菜单名称不能为空");
+        }
+
+        boolean flag = this.updateById(storeMenu);
+        if (!flag) {
+            return R.fail("修改菜单失败");
+        }
+        return R.success("修改菜单成功");
+    }
+
+    @Override
+    public R<String> saveOrUpdateMenu(StorePlatformStoreMenu storeMenu) {
+        log.info("StorePlatformBarMenuServiceImpl.saveOrUpdateMenu storeMenu={}", storeMenu);
+        if (storeMenu == null) {
+            return R.fail("菜单信息不能为空");
+        }
+
+        if (storeMenu.getId() != null) {
+            // 修改
+            return updateMenu(storeMenu);
+        } else {
+            // 新增
+            return saveMenu(storeMenu);
+        }
+    }
+
+    @Override
+    public R<String> deleteMenu(Integer id) {
+        log.info("StorePlatformBarMenuServiceImpl.deleteMenu id={}", id);
+        if (id == null) {
+            return R.fail("菜单ID不能为空");
+        }
+
+        boolean flag = this.removeById(id);
+        if (!flag) {
+            return R.fail("删除菜单失败");
+        }
+        return R.success("删除菜单成功");
+    }
+
+    @Override
+    public R<String> deleteMenuBatch(List<Integer> ids) {
+        log.info("StorePlatformBarMenuServiceImpl.deleteMenuBatch ids={}", ids);
+        if (CollectionUtil.isEmpty(ids)) {
+            return R.fail("菜单ID列表不能为空");
+        }
+
+        boolean flag = this.removeByIds(ids);
+        if (!flag) {
+            return R.fail("批量删除菜单失败");
+        }
+        return R.success("批量删除菜单成功");
+    }
+
+    /**
+     * Excel导入酒吧菜单
+     *
+     * @param file     Excel文件
+     * @param storeId  门店id
+     * @return 导入结果
+     */
+    @Override
+    public R<String> importMenuFromExcel(MultipartFile file, Integer storeId) {
+        log.info("StorePlatformBarMenuServiceImpl.importMenuFromExcel storeId={}", storeId);
+
+        if (file == null || file.isEmpty()) {
+            return R.fail("上传文件为空");
+        }
+
+        String fileName = file.getOriginalFilename();
+        if (fileName == null || (!fileName.endsWith(".xlsx") && !fileName.endsWith(".xls"))) {
+            return R.fail("文件格式不正确,请上传Excel文件");
+        }
+
+        if (storeId == null) {
+            return R.fail("门店ID不能为空");
+        }
+
+        List<String> errorMessages = new ArrayList<>();
+        int successCount = 0;
+        int totalCount = 0;
+
+        try (InputStream inputStream = file.getInputStream();
+             XSSFWorkbook workbook = new XSSFWorkbook(inputStream)) {
+            Sheet sheet = workbook.getSheetAt(0);
+
+            // 获取表头(假设表头在第5行,索引为5)
+            Row headerRow = sheet.getRow(5);
+            if (headerRow == null) {
+                return R.fail("Excel文件格式不正确,缺少表头");
+            }
+
+            // 构建字段映射(表头名称 -> 列索引)
+            Map<String, Integer> headerMap = new HashMap<>();
+            Field[] fields = StorePlatformStoreMenuImportVo.class.getDeclaredFields();
+            for (int i = 0; i < headerRow.getLastCellNum(); i++) {
+                Cell cell = headerRow.getCell(i);
+                if (cell != null) {
+                    String headerName = getCellValueAsString(cell);
+                    if (StringUtils.isNotEmpty(headerName)) {
+                        headerMap.put(headerName.trim(), i);
+                    }
+                }
+            }
+
+            // 获取图片映射(行索引 -> 图片字节数组)
+            Map<Integer, byte[]> imageMap = extractImagesFromSheet(sheet);
+
+            // 读取数据行(从第6行开始,索引为6)
+            for (int rowIndex = 6; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
+                Row row = sheet.getRow(rowIndex);
+                if (row == null) {
+                    continue;
+                }
+
+                // 检查是否为空行
+                boolean isEmptyRow = true;
+                for (int i = 0; i < row.getLastCellNum(); i++) {
+                    Cell cell = row.getCell(i);
+                    if (cell != null && cell.getCellType() != CellType.BLANK) {
+                        String cellValue = getCellValueAsString(cell);
+                        if (StringUtils.isNotEmpty(cellValue)) {
+                            isEmptyRow = false;
+                            break;
+                        }
+                    }
+                }
+                if (isEmptyRow) {
+                    continue;
+                }
+
+                totalCount++;
+                StorePlatformStoreMenuImportVo excelVo = new StorePlatformStoreMenuImportVo();
+
+                // 读取每个字段
+                for (Field field : fields) {
+                    field.setAccessible(true);
+                    String fieldName = field.getName();
+                    Integer colIndex = null;
+
+                    // 根据字段名查找对应的表头
+                    for (Map.Entry<String, Integer> entry : headerMap.entrySet()) {
+                        String headerName = entry.getKey();
+                        if (isFieldMatch(fieldName, headerName)) {
+                            colIndex = entry.getValue();
+                            break;
+                        }
+                    }
+
+                    if (colIndex == null) {
+                        continue;
+                    }
+
+                    Cell cell = row.getCell(colIndex);
+                    if (cell == null && !field.isAnnotationPresent(ExcelImage.class)) {
+                        continue;
+                    }
+
+                    try {
+                        if (field.isAnnotationPresent(ExcelImage.class)) {
+                            // 处理图片字段
+                            byte[] imageBytes = imageMap.get(rowIndex);
+                            if (imageBytes != null && imageBytes.length > 0) {
+                                String imageName = "barMenu_" + storeId + "_" + System.currentTimeMillis() + "_" + rowIndex + ".jpg";
+                                MultipartFile multipartFile = new ByteArrayMultipartFile(imageBytes, imageName);
+                                JSONObject jsonObject = alienStoreFeign.uploadFile(multipartFile);
+                                if (200 == jsonObject.getIntValue("code")) {
+                                    field.set(excelVo, jsonObject.getJSONArray("data").get(0));
+                                } else {
+                                    field.set(excelVo, "");
+                                }
+//                                // 上传图片到OSS
+//                                String imageUrl = uploadImageToOSS(imageBytes, storeId, rowIndex);
+//                                field.set(excelVo, imageUrl);
+                            }
+                        } else {
+                            // 处理普通字段
+                            String cellValue = getCellValueAsString(cell);
+                            if (StringUtils.isNotEmpty(cellValue)) {
+                                setFieldValue(excelVo, field, cellValue.trim());
+                            }
+                        }
+                    } catch (Exception e) {
+                        log.warn("读取字段{}失败:{}", fieldName, e.getMessage());
+                    }
+                }
+
+                // 处理导入数据
+                try {
+                    validateAndSaveMenu(excelVo, storeId, rowIndex + 1);
+                    successCount++;
+                } catch (Exception e) {
+                    errorMessages.add(String.format("第%d行:%s", rowIndex + 1, e.getMessage()));
+                    log.error("导入第{}行数据失败", rowIndex + 1, e);
+                }
+            }
+        } catch (Exception e) {
+            log.error("导入Excel失败", e);
+            return R.fail("导入失败:" + e.getMessage());
+        }
+
+        // 构建返回消息
+        StringBuilder message = new StringBuilder();
+        message.append(String.format("导入完成:成功%d条,失败%d条", successCount, totalCount - successCount));
+        if (!errorMessages.isEmpty()) {
+            message.append("\n失败详情:\n");
+            for (int i = 0; i < Math.min(errorMessages.size(), 10); i++) {
+                message.append(errorMessages.get(i)).append("\n");
+            }
+            if (errorMessages.size() > 10) {
+                message.append("...还有").append(errorMessages.size() - 10).append("条错误信息");
+            }
+        }
+
+        return R.success(message.toString());
+    }
+
+    /**
+     * 从Sheet中提取图片
+     */
+    private Map<Integer, byte[]> extractImagesFromSheet(Sheet sheet) {
+        Map<Integer, byte[]> imageMap = new HashMap<>();
+        if (sheet instanceof XSSFSheet) {
+            XSSFSheet xssfSheet = (XSSFSheet) sheet;
+            XSSFDrawing drawing = xssfSheet.getDrawingPatriarch();
+            if (drawing != null) {
+                List<XSSFShape> shapes = drawing.getShapes();
+                for (XSSFShape shape : shapes) {
+                    if (shape instanceof XSSFPicture) {
+                        XSSFPicture picture = (XSSFPicture) shape;
+                        XSSFClientAnchor anchor = (XSSFClientAnchor) picture.getAnchor();
+                        int rowIndex = anchor.getRow1();
+                        try {
+                            // 直接使用 getData() 方法获取图片字节数组
+                            byte[] imageBytes = picture.getPictureData().getData();
+                            imageMap.put(rowIndex, imageBytes);
+                        } catch (Exception e) {
+                            log.warn("提取第{}行图片失败:{}", rowIndex, e.getMessage());
+                        }
+                    }
+                }
+            }
+        }
+        return imageMap;
+    }
+
+    /**
+     * 上传图片到OSS
+     */
+    private String uploadImageToOSS(byte[] imageBytes, Integer storeId, int rowIndex) {
+        try {
+            // 生成文件名
+            String fileName = "bar_menu_" + storeId + "_" + System.currentTimeMillis() + "_" + rowIndex + ".jpg";
+            String prefix = "image/";
+
+            // 创建临时MultipartFile
+            MultipartFile multipartFile = new ByteArrayMultipartFile(imageBytes, fileName);
+
+            // 上传到OSS
+            return aliOSSUtil.uploadFile(multipartFile, prefix + fileName);
+        } catch (Exception e) {
+            log.error("上传图片失败", e);
+            throw new RuntimeException("上传图片失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 校验并保存菜单
+     */
+    private void validateAndSaveMenu(StorePlatformStoreMenuImportVo excelVo, Integer storeId, int rowNum) {
+        // 校验必填字段
+        if (StringUtils.isEmpty(excelVo.getDishName())) {
+            throw new RuntimeException("名称不能为空");
+        }
+
+        if (excelVo.getDishPrice() == null || excelVo.getDishPrice().compareTo(BigDecimal.ZERO) <= 0) {
+            throw new RuntimeException("价格必须大于0");
+        }
+
+        // 创建StorePlatformStoreMenu对象
+        StorePlatformStoreMenu storeMenu = new StorePlatformStoreMenu();
+        storeMenu.setStoreId(storeId);
+        storeMenu.setDishMenuType(excelVo.getDishMenuType() != null ? excelVo.getDishMenuType() : 0);
+        storeMenu.setDishName(excelVo.getDishName());
+        storeMenu.setDishPrice(excelVo.getDishPrice());
+        storeMenu.setCostPrice(excelVo.getCostPrice());
+        storeMenu.setCategory(excelVo.getCategory());
+        storeMenu.setAlcoholVolume(excelVo.getAlcoholVolume());
+        storeMenu.setFlavor(excelVo.getFlavor());
+        storeMenu.setDescription(excelVo.getDescription());
+        storeMenu.setDishType(excelVo.getDishType() != null ? excelVo.getDishType() : 0);
+
+        // 处理图片
+        if (StringUtils.isNotEmpty(excelVo.getImg())) {
+            StoreImg storeImg = new StoreImg();
+            storeImg.setStoreId(storeId);
+            storeImg.setImgType(7); // 菜单图片类型
+            storeImg.setImgUrl(excelVo.getImg());
+            storeImg.setImgDescription(excelVo.getDishName());
+            storeImgMapper.insert(storeImg);
+            storeMenu.setImgId(storeImg.getId());
+        }
+
+        // 设置排序
+        LambdaQueryWrapper<StorePlatformStoreMenu> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(StorePlatformStoreMenu::getStoreId, storeId)
+                .eq(StorePlatformStoreMenu::getDeleteFlag, 0);
+        List<StorePlatformStoreMenu> menuList = this.list(queryWrapper);
+        if (CollectionUtil.isNotEmpty(menuList)) {
+            int maxSort = menuList.stream()
+                    .map(StorePlatformStoreMenu::getSort)
+                    .max(Integer::compareTo)
+                    .orElse(0);
+            storeMenu.setSort(maxSort + 1);
+        } else {
+            storeMenu.setSort(1);
+        }
+
+        // 保存菜单
+        boolean flag = this.save(storeMenu);
+        if (!flag) {
+            throw new RuntimeException("保存菜单失败");
+        }
+    }
+
+    /**
+     * 判断字段名是否匹配表头
+     */
+    private boolean isFieldMatch(String fieldName, String headerName) {
+        // 字段映射表
+        Map<String, String> fieldMapping = new HashMap<>();
+        fieldMapping.put("dishMenuType", "类型(酒水/餐食)");
+        fieldMapping.put("dishName", "名称");
+        fieldMapping.put("dishPrice", "价格(¥)");
+        fieldMapping.put("costPrice", "成本价(¥)");
+        fieldMapping.put("category", "品类");
+        fieldMapping.put("alcoholVolume", "酒精度(%vol)");
+        fieldMapping.put("flavor", "风味");
+        fieldMapping.put("img", "图片");
+        fieldMapping.put("description", "描述");
+        fieldMapping.put("dishType", "是否设为推荐(是/否)");
+
+        String expectedHeader = fieldMapping.get(fieldName);
+        return expectedHeader != null && expectedHeader.equals(headerName);
+    }
+
+    /**
+     * 设置字段值
+     */
+    private void setFieldValue(StorePlatformStoreMenuImportVo excelVo, Field field, String cellValue) throws Exception {
+        Class<?> fieldType = field.getType();
+        String fieldName = field.getName();
+
+        if (fieldType == BigDecimal.class) {
+            try {
+                BigDecimal value = new BigDecimal(cellValue);
+                if (value.scale() > 2) {
+                    throw new RuntimeException("价格或者成本价格式错误, 请输入数字, 小数点保留两位以内");
+                }
+                field.set(excelVo, value);
+            } catch (NumberFormatException e) {
+                throw new RuntimeException("价格或者成本价格式错误, 请输入数字, 小数点保留两位以内");
+            }
+        } else if (fieldType == Integer.class) {
+            // 处理 dishType 字段:将"是"/"否"转换为1/0
+            if ("dishType".equals(fieldName)) {
+                String trimmedValue = cellValue.trim();
+                if ("是".equals(trimmedValue)) {
+                    field.set(excelVo, 1);
+                } else if ("否".equals(trimmedValue)) {
+                    field.set(excelVo, 0);
+                } else {
+                    throw new RuntimeException("是否推荐字段格式错误,请输入'是'或'否'");
+                }
+            } else if ("dishMenuType".equals(fieldName)) {
+                String trimmedValue = cellValue.trim();
+                if ("餐食".equals(trimmedValue)) {
+                    field.set(excelVo, 1);
+                } else if ("酒水".equals(trimmedValue)) {
+                    field.set(excelVo, 2);
+                } else {
+                    throw new RuntimeException("类型字段格式错误,请输入'酒水'或'餐食'");
+                }
+            } else {
+                try {
+                    field.set(excelVo, Integer.parseInt(cellValue));
+                } catch (NumberFormatException e) {
+                    throw new RuntimeException("数字格式错误:" + cellValue);
+                }
+            }
+        } else {
+            // String类型
+            field.set(excelVo, cellValue);
+        }
+    }
+
+    /**
+     * 获取单元格值(字符串格式)
+     */
+    private String getCellValueAsString(Cell cell) {
+        if (cell == null) {
+            return null;
+        }
+
+        switch (cell.getCellType()) {
+            case STRING:
+                return cell.getStringCellValue();
+            case NUMERIC:
+                if (DateUtil.isCellDateFormatted(cell)) {
+                    return cell.getDateCellValue().toString();
+                } else {
+                    // 处理数字,避免科学计数法
+                    double numericValue = cell.getNumericCellValue();
+                    if (numericValue == (long) numericValue) {
+                        return String.valueOf((long) numericValue);
+                    } else {
+                        return String.valueOf(numericValue);
+                    }
+                }
+            case BOOLEAN:
+                return String.valueOf(cell.getBooleanCellValue());
+            case FORMULA:
+                return cell.getCellFormula();
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * 临时MultipartFile实现类,用于上传字节数组
+     */
+    private static class ByteArrayMultipartFile implements MultipartFile {
+        private final byte[] content;
+        private final String fileName;
+
+        public ByteArrayMultipartFile(byte[] content, String fileName) {
+            this.content = content;
+            this.fileName = fileName;
+        }
+
+        @Override
+        public String getName() {
+            return "file";
+        }
+
+        @Override
+        public String getOriginalFilename() {
+            return fileName;
+        }
+
+        @Override
+        public String getContentType() {
+            return "image/jpeg";
+        }
+
+        @Override
+        public boolean isEmpty() {
+            return content == null || content.length == 0;
+        }
+
+        @Override
+        public long getSize() {
+            return content.length;
+        }
+
+        @Override
+        public byte[] getBytes() throws IOException {
+            return content;
+        }
+
+        @Override
+        public InputStream getInputStream() throws IOException {
+            return new ByteArrayInputStream(content);
+        }
+
+        @Override
+        public void transferTo(java.io.File dest) throws IOException, IllegalStateException {
+            java.nio.file.Files.write(dest.toPath(), content);
+        }
+    }
+}

+ 655 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StorePlatformBathFacilityServiceImpl.java

@@ -0,0 +1,655 @@
+package shop.alien.storeplatform.service.impl;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.alibaba.fastjson.JSONObject;
+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.apache.poi.ss.usermodel.*;
+import org.apache.poi.xssf.usermodel.*;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreImg;
+import shop.alien.entity.store.excelVo.util.ExcelImage;
+import shop.alien.mapper.StoreImgMapper;
+import shop.alien.storeplatform.entity.StorePlatformBathFacility;
+import shop.alien.storeplatform.entity.StorePlatformStoreMenu;
+import shop.alien.storeplatform.feign.AlienStoreFeign;
+import shop.alien.storeplatform.mapper.StorePlatformBathFacilityServiceMapper;
+import shop.alien.storeplatform.service.StorePlatformBathFacilityService;
+import shop.alien.storeplatform.vo.StorePlatformBathFacilityImportVo;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 商户平台-洗浴设施及服务服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class StorePlatformBathFacilityServiceImpl extends ServiceImpl<StorePlatformBathFacilityServiceMapper, StorePlatformBathFacility> implements StorePlatformBathFacilityService {
+
+    private final StorePlatformBathFacilityServiceMapper facilityMapper;
+
+    private final AlienStoreFeign alienStoreFeign;
+
+    private final StoreImgMapper storeImgMapper;
+
+    @Override
+    public StorePlatformBathFacility getById(Integer id) {
+        log.info("StorePlatformBathFacilityServiceImpl.getById id={}", id);
+        if (id == null) {
+            throw new RuntimeException("设施ID不能为空");
+        }
+        StorePlatformBathFacility facility = super.getById(id);
+        List<StoreImg> imgList = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>()
+                .eq(StoreImg::getStoreId, facility.getStoreId()).eq(StoreImg::getImgType, 29).in(StoreImg::getBusinessId, facility.getId()));
+
+        if (CollectionUtil.isNotEmpty(imgList)) {
+            facility.setImgUrl(imgList.get(0).getImgUrl());
+        }
+        return facility;
+    }
+
+    @Override
+    public List<StorePlatformBathFacility> getListByStoreId(Integer storeId) {
+        log.info("StorePlatformBathFacilityServiceImpl.getListByStoreId storeId={}", storeId);
+        if (storeId == null) {
+            throw new RuntimeException("门店ID不能为空");
+        }
+        LambdaQueryWrapper<StorePlatformBathFacility> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(StorePlatformBathFacility::getStoreId, storeId)
+                .eq(StorePlatformBathFacility::getDeleteFlag, 0)
+                .orderByAsc(StorePlatformBathFacility::getFacilityCategory)
+                .orderByAsc(StorePlatformBathFacility::getId);
+
+        List<StorePlatformBathFacility> list = this.list(queryWrapper);
+
+        // 查询图片
+        List<Integer> ids = list.stream().map(StorePlatformBathFacility::getId).collect(Collectors.toList());
+        List<StoreImg> imgList = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>()
+                .eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 29).in(StoreImg::getBusinessId, ids));
+
+        if (CollectionUtil.isNotEmpty(imgList)) {
+            list.forEach(facility -> {
+                imgList.stream()
+                        .filter(img -> img.getBusinessId().equals(facility.getId()))
+                        .findFirst()
+                        .ifPresent(img -> facility.setImgUrl(img.getImgUrl()));
+            });
+        }
+        return list;
+    }
+
+    @Override
+    public List<StorePlatformBathFacility> getListByStoreIdAndCategory(Integer storeId, Integer facilityCategory) {
+        log.info("StorePlatformBathFacilityServiceImpl.getListByStoreIdAndCategory storeId={}, facilityCategory={}", storeId, facilityCategory);
+        if (storeId == null) {
+            throw new RuntimeException("门店ID不能为空");
+        }
+        LambdaQueryWrapper<StorePlatformBathFacility> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(StorePlatformBathFacility::getStoreId, storeId)
+                .eq(StorePlatformBathFacility::getDeleteFlag, 0);
+        if (facilityCategory != null) {
+            queryWrapper.eq(StorePlatformBathFacility::getFacilityCategory, facilityCategory);
+        }
+        queryWrapper.orderByAsc(StorePlatformBathFacility::getFacilityCategory)
+                .orderByAsc(StorePlatformBathFacility::getId);
+
+        List<StorePlatformBathFacility> list = this.list(queryWrapper);
+
+        // 查询图片
+        List<Integer> ids = list.stream().map(StorePlatformBathFacility::getId).collect(Collectors.toList());
+        List<StoreImg> imgList = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>()
+                .eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 29).in(StoreImg::getBusinessId, ids));
+
+        if (CollectionUtil.isNotEmpty(imgList)) {
+            list.forEach(facility -> {
+                imgList.stream()
+                        .filter(img -> img.getBusinessId().equals(facility.getId()))
+                        .findFirst()
+                        .ifPresent(img -> facility.setImgUrl(img.getImgUrl()));
+            });
+        }
+        return list;
+    }
+
+    @Override
+    public R<StorePlatformBathFacility> saveFacility(StorePlatformBathFacility facility) {
+        log.info("StorePlatformBathFacilityServiceImpl.saveFacility facility={}", facility);
+        if (facility == null) {
+            return R.fail("设施信息不能为空");
+        }
+        if (facility.getStoreId() == null) {
+            return R.fail("门店ID不能为空");
+        }
+        if (StringUtils.isEmpty(facility.getFacilityName())) {
+            return R.fail("名称不能为空");
+        }
+        if (facility.getFacilityCategory() == null) {
+            return R.fail("区域分类不能为空");
+        }
+        // 如果使用时间类型为选择时间,则必须填写开始和结束时间
+        if (facility.getUsageTimeType() != null && facility.getUsageTimeType() == 1) {
+            if (StringUtils.isEmpty(facility.getUsageStartTime()) || StringUtils.isEmpty(facility.getUsageEndTime())) {
+                return R.fail("使用时间类型为选择时间时,开始时间和结束时间不能为空");
+            }
+        }
+
+        boolean flag = this.save(facility);
+        if (!flag) {
+            return R.fail("新增设施失败");
+        }
+        return R.data(facility);
+    }
+
+    @Override
+    public R<StorePlatformBathFacility> updateFacility(StorePlatformBathFacility facility) {
+        log.info("StorePlatformBathFacilityServiceImpl.updateFacility facility={}", facility);
+        if (facility == null || facility.getId() == null) {
+            return R.fail("设施信息或设施ID不能为空");
+        }
+        if (StringUtils.isEmpty(facility.getFacilityName())) {
+            return R.fail("名称不能为空");
+        }
+        if (facility.getFacilityCategory() == null) {
+            return R.fail("区域分类不能为空");
+        }
+        // 如果使用时间类型为选择时间,则必须填写开始和结束时间
+        if (facility.getUsageTimeType() != null && facility.getUsageTimeType() == 1) {
+            if (StringUtils.isEmpty(facility.getUsageStartTime()) || StringUtils.isEmpty(facility.getUsageEndTime())) {
+                return R.fail("使用时间类型为选择时间时,开始时间和结束时间不能为空");
+            }
+        }
+
+        boolean flag = this.updateById(facility);
+        if (!flag) {
+            return R.fail("修改设施失败");
+        }
+        return R.data(facility);
+    }
+
+    @Override
+    public R<StorePlatformBathFacility> saveOrUpdateFacility(StorePlatformBathFacility facility) {
+        log.info("StorePlatformBathFacilityServiceImpl.saveOrUpdateFacility facility={}", facility);
+        if (facility == null) {
+            return R.fail("设施信息不能为空");
+        }
+
+        if (facility.getId() != null) {
+            // 修改
+            return updateFacility(facility);
+        } else {
+            // 新增
+            return saveFacility(facility);
+        }
+    }
+
+    @Override
+    public R<String> deleteFacility(Integer id) {
+        log.info("StorePlatformBathFacilityServiceImpl.deleteFacility id={}", id);
+        if (id == null) {
+            return R.fail("设施ID不能为空");
+        }
+
+        boolean flag = this.removeById(id);
+        if (!flag) {
+            return R.fail("删除设施失败");
+        }
+        return R.success("删除设施成功");
+    }
+
+    @Override
+    public R<String> deleteFacilityBatch(List<Integer> ids) {
+        log.info("StorePlatformBathFacilityServiceImpl.deleteFacilityBatch ids={}", ids);
+        if (CollectionUtil.isEmpty(ids)) {
+            return R.fail("设施ID列表不能为空");
+        }
+
+        boolean flag = this.removeByIds(ids);
+        if (!flag) {
+            return R.fail("批量删除设施失败");
+        }
+        return R.success("批量删除设施成功");
+    }
+
+    @Override
+    public R<String> importFacilityFromExcel(MultipartFile file, Integer storeId) {
+        log.info("StorePlatformBathFacilityServiceImpl.importFacilityFromExcel storeId={}", storeId);
+
+        if (file == null || file.isEmpty()) {
+            return R.fail("上传文件为空");
+        }
+
+        String fileName = file.getOriginalFilename();
+        if (fileName == null || (!fileName.endsWith(".xlsx") && !fileName.endsWith(".xls"))) {
+            return R.fail("文件格式不正确,请上传Excel文件");
+        }
+
+        if (storeId == null) {
+            return R.fail("门店ID不能为空");
+        }
+
+        List<String> errorMessages = new ArrayList<>();
+        int successCount = 0;
+        int totalCount = 0;
+
+        try (InputStream inputStream = file.getInputStream();
+             XSSFWorkbook workbook = new XSSFWorkbook(inputStream)) {
+            Sheet sheet = workbook.getSheetAt(0);
+
+            // 获取表头(假设表头在第5行,索引为5)
+            Row headerRow = sheet.getRow(5);
+            if (headerRow == null) {
+                return R.fail("Excel文件格式不正确,缺少表头");
+            }
+
+            // 构建字段映射(表头名称 -> 列索引)
+            Map<String, Integer> headerMap = new HashMap<>();
+            Field[] fields = StorePlatformBathFacilityImportVo.class.getDeclaredFields();
+            for (int i = 0; i < headerRow.getLastCellNum(); i++) {
+                Cell cell = headerRow.getCell(i);
+                if (cell != null) {
+                    String headerName = getCellValueAsString(cell);
+                    if (StringUtils.isNotEmpty(headerName)) {
+                        headerMap.put(headerName.trim(), i);
+                    }
+                }
+            }
+
+            // 获取图片映射(行索引 -> 图片字节数组)
+            Map<Integer, byte[]> imageMap = extractImagesFromSheet(sheet);
+
+            // 读取数据行(从第6行开始,索引为6)
+            for (int rowIndex = 6; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
+                Row row = sheet.getRow(rowIndex);
+                if (row == null) {
+                    continue;
+                }
+
+                // 检查是否为空行
+                boolean isEmptyRow = true;
+                for (int i = 0; i < row.getLastCellNum(); i++) {
+                    Cell cell = row.getCell(i);
+                    if (cell != null && cell.getCellType() != CellType.BLANK) {
+                        String cellValue = getCellValueAsString(cell);
+                        if (StringUtils.isNotEmpty(cellValue)) {
+                            isEmptyRow = false;
+                            break;
+                        }
+                    }
+                }
+                if (isEmptyRow) {
+                    continue;
+                }
+
+                totalCount++;
+                StorePlatformBathFacilityImportVo excelVo = new StorePlatformBathFacilityImportVo();
+
+                // 读取每个字段
+                for (Field field : fields) {
+                    field.setAccessible(true);
+                    String fieldName = field.getName();
+                    Integer colIndex = null;
+
+                    // 根据字段名查找对应的表头
+                    for (Map.Entry<String, Integer> entry : headerMap.entrySet()) {
+                        String headerName = entry.getKey();
+                        if (isFieldMatch(fieldName, headerName)) {
+                            colIndex = entry.getValue();
+                            break;
+                        }
+                    }
+
+                    if (colIndex == null) {
+                        continue;
+                    }
+
+                    Cell cell = row.getCell(colIndex);
+                    if (cell == null && !field.isAnnotationPresent(ExcelImage.class)) {
+                        continue;
+                    }
+
+                    try {
+                        if (field.isAnnotationPresent(ExcelImage.class)) {
+                            // 处理图片字段
+                            byte[] imageBytes = imageMap.get(rowIndex);
+                            if (imageBytes != null && imageBytes.length > 0) {
+                                String imageName = "bathFacility_" + storeId + "_" + System.currentTimeMillis() + "_" + rowIndex + ".jpg";
+                                MultipartFile multipartFile = new ByteArrayMultipartFile(imageBytes, imageName);
+                                JSONObject jsonObject = alienStoreFeign.uploadFile(multipartFile);
+                                if (200 == jsonObject.getIntValue("code")) {
+                                    field.set(excelVo, jsonObject.getJSONArray("data").get(0));
+                                } else {
+                                    field.set(excelVo, "");
+                                }
+                            }
+                        } else {
+                            // 处理普通字段
+                            String cellValue = getCellValueAsString(cell);
+                            if (StringUtils.isNotEmpty(cellValue)) {
+                                setFieldValue(excelVo, field, cellValue.trim());
+                            }
+                        }
+                    } catch (Exception e) {
+                        log.warn("读取字段{}失败:{}", fieldName, e.getMessage());
+                    }
+                }
+
+                // 处理导入数据
+                try {
+                    validateAndSaveFacility(excelVo, storeId, rowIndex + 1);
+                    successCount++;
+                } catch (Exception e) {
+                    errorMessages.add(String.format("第%d行:%s", rowIndex + 1, e.getMessage()));
+                    log.error("导入第{}行数据失败", rowIndex + 1, e);
+                }
+            }
+        } catch (Exception e) {
+            log.error("导入Excel失败", e);
+            return R.fail("导入失败:" + e.getMessage());
+        }
+
+        // 构建返回消息
+        StringBuilder message = new StringBuilder();
+        message.append(String.format("导入完成:成功%d条,失败%d条", successCount, totalCount - successCount));
+        if (!errorMessages.isEmpty()) {
+            message.append("\n失败详情:\n");
+            for (int i = 0; i < Math.min(errorMessages.size(), 10); i++) {
+                message.append(errorMessages.get(i)).append("\n");
+            }
+            if (errorMessages.size() > 10) {
+                message.append("...还有").append(errorMessages.size() - 10).append("条错误信息");
+            }
+        }
+
+        return R.success(message.toString());
+    }
+
+    /**
+     * 判断字段名是否匹配表头
+     */
+    private boolean isFieldMatch(String fieldName, String headerName) {
+        Map<String, String> fieldMapping = new HashMap<>();
+        fieldMapping.put("facilityCategory", "区域(洗浴区/汗蒸区/休闲区/餐饮区)");
+        fieldMapping.put("facilityName", "名称");
+        fieldMapping.put("img", "图片");
+        fieldMapping.put("chargingStandard", "收费标准(元)");
+        fieldMapping.put("usageTimeType", "开放时间(全天/自定义时间)");
+        fieldMapping.put("usageStartTime", "自定义开始时间");
+        fieldMapping.put("usageEndTime", "自定义结束时间");
+        fieldMapping.put("displayInStoreDetail", "状态(展示/隐藏)");
+
+        String expectedHeader = fieldMapping.get(fieldName);
+        return expectedHeader != null && expectedHeader.equals(headerName);
+    }
+
+    /**
+     * 设置字段值
+     */
+    private void setFieldValue(StorePlatformBathFacilityImportVo excelVo, Field field, String cellValue) throws Exception {
+        Class<?> fieldType = field.getType();
+        String fieldName = field.getName();
+
+        if (fieldType == Integer.class) {
+            if ("facilityCategory".equals(fieldName)) {
+                // 处理区域分类:洗浴区->1, 汗蒸区->2, 休闲区->3, 餐饮区->4
+                String trimmedValue = cellValue.trim();
+                if ("洗浴区".equals(trimmedValue)) {
+                    field.set(excelVo, 1);
+                } else if ("汗蒸区".equals(trimmedValue)) {
+                    field.set(excelVo, 2);
+                } else if ("休闲区".equals(trimmedValue)) {
+                    field.set(excelVo, 3);
+                } else if ("餐饮区".equals(trimmedValue)) {
+                    field.set(excelVo, 4);
+                } else {
+                    throw new RuntimeException("区域分类格式错误,请输入'洗浴区'、'汗蒸区'、'休闲区'或'餐饮区'");
+                }
+            } else if ("usageTimeType".equals(fieldName)) {
+                // 处理开放时间:全天->0, 自定义时间->1
+                String trimmedValue = cellValue.trim();
+                if ("全天".equals(trimmedValue)) {
+                    field.set(excelVo, 0);
+                } else if ("自定义时间".equals(trimmedValue)) {
+                    field.set(excelVo, 1);
+                } else {
+                    throw new RuntimeException("开放时间格式错误,请输入'全天'或'自定义时间'");
+                }
+            } else if ("displayInStoreDetail".equals(fieldName)) {
+                // 处理状态:展示->1, 隐藏->0
+                String trimmedValue = cellValue.trim();
+                if ("展示".equals(trimmedValue)) {
+                    field.set(excelVo, 1);
+                } else if ("隐藏".equals(trimmedValue)) {
+                    field.set(excelVo, 0);
+                } else {
+                    throw new RuntimeException("状态字段格式错误,请输入'展示'或'隐藏'");
+                }
+            } else {
+                // 其他Integer字段直接解析
+                try {
+                    field.set(excelVo, Integer.parseInt(cellValue));
+                } catch (NumberFormatException e) {
+                    throw new RuntimeException(fieldName + "字段格式错误,请输入数字");
+                }
+            }
+        } else {
+            // String类型
+            field.set(excelVo, cellValue);
+        }
+    }
+
+    /**
+     * 校验并保存设施
+     */
+    private void validateAndSaveFacility(StorePlatformBathFacilityImportVo excelVo, Integer storeId, int rowNum) {
+        // 校验必填字段
+        if (StringUtils.isEmpty(excelVo.getFacilityName())) {
+            throw new RuntimeException("名称不能为空");
+        }
+        if (excelVo.getFacilityCategory() == null) {
+            throw new RuntimeException("区域分类不能为空");
+        }
+
+        // 校验开放时间和自定义时间段
+        Integer usageTimeType = excelVo.getUsageTimeType() != null ? excelVo.getUsageTimeType() : 0;
+        String usageStartTime = excelVo.getUsageStartTime();
+        String usageEndTime = excelVo.getUsageEndTime();
+
+        if (usageTimeType == 0) {
+            // 全天:自定义开始时间和结束时间应该为空
+            if (StringUtils.isNotEmpty(usageStartTime) || StringUtils.isNotEmpty(usageEndTime)) {
+                throw new RuntimeException("开放时间为'全天'时,自定义开始时间和结束时间不能填写");
+            }
+        } else if (usageTimeType == 1) {
+            // 自定义时间:自定义开始时间和结束时间必须填写
+            if (StringUtils.isEmpty(usageStartTime)) {
+                throw new RuntimeException("开放时间为'自定义时间'时,自定义开始时间不能为空");
+            }
+            if (StringUtils.isEmpty(usageEndTime)) {
+                throw new RuntimeException("开放时间为'自定义时间'时,自定义结束时间不能为空");
+            }
+            // 校验时间格式:HH:mm
+            if (!isValidTimeFormat(usageStartTime)) {
+                throw new RuntimeException("自定义开始时间格式错误,请输入'HH:mm'格式,如'16:00'");
+            }
+            if (!isValidTimeFormat(usageEndTime)) {
+                throw new RuntimeException("自定义结束时间格式错误,请输入'HH:mm'格式,如'18:00'");
+            }
+        }
+
+        // 创建StorePlatformBathFacility对象
+        StorePlatformBathFacility facility = new StorePlatformBathFacility();
+        facility.setStoreId(storeId);
+        facility.setFacilityCategory(excelVo.getFacilityCategory());
+        facility.setFacilityName(excelVo.getFacilityName());
+        facility.setChargingStandard(excelVo.getChargingStandard());
+        facility.setUsageTimeType(usageTimeType);
+        facility.setDisplayInStoreDetail(excelVo.getDisplayInStoreDetail() != null ? excelVo.getDisplayInStoreDetail() : 0);
+
+        // 设置开始和结束时间
+        if (usageTimeType == 1) {
+            facility.setUsageStartTime(usageStartTime);
+            facility.setUsageEndTime(usageEndTime);
+        } else {
+            facility.setUsageStartTime(null);
+            facility.setUsageEndTime(null);
+        }
+
+        // 保存设施
+        boolean flag = this.save(facility);
+
+        // 处理图片
+        if (StringUtils.isNotEmpty(excelVo.getImg())) {
+            StoreImg storeImg = new StoreImg();
+            storeImg.setStoreId(storeId);
+            storeImg.setImgType(29); // 菜单图片类型
+            storeImg.setImgUrl(excelVo.getImg());
+            storeImg.setBusinessId(facility.getId());
+            storeImgMapper.insert(storeImg);
+        }
+        if (!flag) {
+            throw new RuntimeException("保存设施失败");
+        }
+    }
+
+    /**
+     * 校验时间格式是否为 HH:mm
+     */
+    private boolean isValidTimeFormat(String time) {
+        if (StringUtils.isEmpty(time)) {
+            return false;
+        }
+        // 格式:HH:mm (00:00-23:59)
+        String timePattern = "^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$";
+        return time.matches(timePattern);
+    }
+
+    /**
+     * 获取单元格值(字符串格式)
+     */
+    private String getCellValueAsString(Cell cell) {
+        if (cell == null) {
+            return null;
+        }
+
+        switch (cell.getCellType()) {
+            case STRING:
+                return cell.getStringCellValue();
+            case NUMERIC:
+                if (org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(cell)) {
+                    return cell.getDateCellValue().toString();
+                } else {
+                    // 处理数字,避免科学计数法
+                    double numericValue = cell.getNumericCellValue();
+                    if (numericValue == (long) numericValue) {
+                        return String.valueOf((long) numericValue);
+                    } else {
+                        return String.valueOf(numericValue);
+                    }
+                }
+            case BOOLEAN:
+                return String.valueOf(cell.getBooleanCellValue());
+            case FORMULA:
+                return cell.getCellFormula();
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * 从Sheet中提取图片
+     */
+    private Map<Integer, byte[]> extractImagesFromSheet(Sheet sheet) {
+        Map<Integer, byte[]> imageMap = new HashMap<>();
+        if (sheet instanceof XSSFSheet) {
+            XSSFSheet xssfSheet = (XSSFSheet) sheet;
+            XSSFDrawing drawing = xssfSheet.getDrawingPatriarch();
+            if (drawing != null) {
+                List<XSSFShape> shapes = drawing.getShapes();
+                for (XSSFShape shape : shapes) {
+                    if (shape instanceof XSSFPicture) {
+                        XSSFPicture picture = (XSSFPicture) shape;
+                        XSSFClientAnchor anchor = (XSSFClientAnchor) picture.getAnchor();
+                        int rowIndex = anchor.getRow1();
+                        try {
+                            // 直接使用 getData() 方法获取图片字节数组
+                            byte[] imageBytes = picture.getPictureData().getData();
+                            imageMap.put(rowIndex, imageBytes);
+                        } catch (Exception e) {
+                            log.warn("提取第{}行图片失败:{}", rowIndex, e.getMessage());
+                        }
+                    }
+                }
+            }
+        }
+        return imageMap;
+    }
+
+    /**
+     * 临时MultipartFile实现类,用于上传字节数组
+     */
+    private static class ByteArrayMultipartFile implements MultipartFile {
+        private final byte[] content;
+        private final String fileName;
+
+        public ByteArrayMultipartFile(byte[] content, String fileName) {
+            this.content = content;
+            this.fileName = fileName;
+        }
+
+        @Override
+        public String getName() {
+            return "file";
+        }
+
+        @Override
+        public String getOriginalFilename() {
+            return fileName;
+        }
+
+        @Override
+        public String getContentType() {
+            return "image/jpeg";
+        }
+
+        @Override
+        public boolean isEmpty() {
+            return content == null || content.length == 0;
+        }
+
+        @Override
+        public long getSize() {
+            return content.length;
+        }
+
+        @Override
+        public byte[] getBytes() throws IOException {
+            return content;
+        }
+
+        @Override
+        public InputStream getInputStream() throws IOException {
+            return new ByteArrayInputStream(content);
+        }
+
+        @Override
+        public void transferTo(java.io.File dest) throws IOException, IllegalStateException {
+            java.nio.file.Files.write(dest.toPath(), content);
+        }
+    }
+
+}

+ 4 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StorePlatformLoginServiceImpl.java

@@ -209,6 +209,7 @@ public class StorePlatformLoginServiceImpl extends ServiceImpl<StoreUserMapper,
             JSONObject jsonObject = new JSONObject();
             jsonObject.put("expirationTime", 1);
             jsonObject.put("foodLicenceExpirationTime", 1);
+            jsonObject.put("entertainmentLicenceExpirationTime", 1);
 
             StoreInfo storeInfo = storeInfoMapper.selectById(storeId);
             if (storeInfo != null) {
@@ -218,6 +219,9 @@ public class StorePlatformLoginServiceImpl extends ServiceImpl<StoreUserMapper,
                 if (storeInfo.getFoodLicenceExpirationTime().compareTo(new Date()) < 0) {
                     jsonObject.put("foodLicenceExpirationTime", 0);
                 }
+                if (storeInfo.getEntertainmentLicenceExpirationTime() != null && storeInfo.getEntertainmentLicenceExpirationTime().compareTo(new Date()) < 0) {
+                    jsonObject.put("entertainmentLicenceExpirationTime", 0);
+                }
             }
             return jsonObject;
         } catch (Exception e) {

+ 5 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StorePlatformRenovationServiceImpl.java

@@ -85,6 +85,11 @@ public class StorePlatformRenovationServiceImpl extends ServiceImpl<StoreInfoMap
             String[] strings = storeInfo.getBusinessTypes().split(",");
             storeMainInfoVo.setBusinessTypesList(Arrays.stream(strings).collect(Collectors.toList()));
         }
+        // 分类
+        if (storeInfo.getBusinessClassify() != null) {
+            String[] strings = storeInfo.getBusinessClassify().split(",");
+            storeMainInfoVo.setBusinessClassifyList(Arrays.stream(strings).collect(Collectors.toList()));
+        }
         //门店标签
         storeMainInfoVo.setStoreLabel(storeLabelMapper.selectOne(new LambdaQueryWrapper<StoreLabel>().eq(StoreLabel::getStoreId, id)));
         //营业时间

+ 430 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StorePlatformSportsEquipmentFacilityServiceImpl.java

@@ -0,0 +1,430 @@
+package shop.alien.storeplatform.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.apache.poi.ss.usermodel.*;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+import shop.alien.entity.result.R;
+import shop.alien.storeplatform.entity.StorePlatformSportsEquipmentFacility;
+import shop.alien.storeplatform.mapper.StorePlatformSportsEquipmentFacilityMapper;
+import shop.alien.storeplatform.service.StorePlatformSportsEquipmentFacilityService;
+import shop.alien.storeplatform.vo.StorePlatformSportsEquipmentFacilityImportVo;
+
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 商户平台-运动器材设施服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class StorePlatformSportsEquipmentFacilityServiceImpl extends ServiceImpl<StorePlatformSportsEquipmentFacilityMapper, StorePlatformSportsEquipmentFacility> implements StorePlatformSportsEquipmentFacilityService {
+
+    private final StorePlatformSportsEquipmentFacilityMapper facilityMapper;
+
+    @Override
+    public StorePlatformSportsEquipmentFacility getById(Integer id) {
+        log.info("StorePlatformSportsEquipmentFacilityServiceImpl.getById id={}", id);
+        if (id == null) {
+            throw new RuntimeException("设施ID不能为空");
+        }
+        return super.getById(id);
+    }
+
+    @Override
+    public List<StorePlatformSportsEquipmentFacility> getListByStoreId(Integer storeId) {
+        log.info("StorePlatformSportsEquipmentFacilityServiceImpl.getListByStoreId storeId={}", storeId);
+        if (storeId == null) {
+            throw new RuntimeException("门店ID不能为空");
+        }
+        LambdaQueryWrapper<StorePlatformSportsEquipmentFacility> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(StorePlatformSportsEquipmentFacility::getStoreId, storeId)
+                .eq(StorePlatformSportsEquipmentFacility::getDeleteFlag, 0)
+                .orderByAsc(StorePlatformSportsEquipmentFacility::getFacilityCategory)
+                .orderByAsc(StorePlatformSportsEquipmentFacility::getId);
+        return this.list(queryWrapper);
+    }
+
+    @Override
+    public List<StorePlatformSportsEquipmentFacility> getListByStoreIdAndCategory(Integer storeId, Integer facilityCategory) {
+        log.info("StorePlatformSportsEquipmentFacilityServiceImpl.getListByStoreIdAndCategory storeId={}, facilityCategory={}", storeId, facilityCategory);
+        if (storeId == null) {
+            throw new RuntimeException("门店ID不能为空");
+        }
+        LambdaQueryWrapper<StorePlatformSportsEquipmentFacility> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(StorePlatformSportsEquipmentFacility::getStoreId, storeId)
+                .eq(StorePlatformSportsEquipmentFacility::getDeleteFlag, 0);
+        if (facilityCategory != null) {
+            queryWrapper.eq(StorePlatformSportsEquipmentFacility::getFacilityCategory, facilityCategory);
+        }
+        queryWrapper.orderByAsc(StorePlatformSportsEquipmentFacility::getFacilityCategory)
+                .orderByAsc(StorePlatformSportsEquipmentFacility::getId);
+        return this.list(queryWrapper);
+    }
+
+    @Override
+    public R<String> saveFacility(StorePlatformSportsEquipmentFacility facility) {
+        log.info("StorePlatformSportsEquipmentFacilityServiceImpl.saveFacility facility={}", facility);
+        if (facility == null) {
+            return R.fail("设施信息不能为空");
+        }
+        if (facility.getStoreId() == null) {
+            return R.fail("门店ID不能为空");
+        }
+        if (StringUtils.isEmpty(facility.getFacilityName())) {
+            return R.fail("设施名称不能为空");
+        }
+        if (facility.getFacilityCategory() == null) {
+            return R.fail("设施分类不能为空");
+        }
+        if (facility.getQuantity() == null || facility.getQuantity() <= 0) {
+            return R.fail("数量必须大于0");
+        }
+
+        boolean flag = this.save(facility);
+        if (!flag) {
+            return R.fail("新增设施失败");
+        }
+        return R.success("新增设施成功");
+    }
+
+    @Override
+    public R<String> updateFacility(StorePlatformSportsEquipmentFacility facility) {
+        log.info("StorePlatformSportsEquipmentFacilityServiceImpl.updateFacility facility={}", facility);
+        if (facility == null || facility.getId() == null) {
+            return R.fail("设施信息或设施ID不能为空");
+        }
+        if (StringUtils.isEmpty(facility.getFacilityName())) {
+            return R.fail("设施名称不能为空");
+        }
+        if (facility.getFacilityCategory() == null) {
+            return R.fail("设施分类不能为空");
+        }
+        if (facility.getQuantity() == null || facility.getQuantity() <= 0) {
+            return R.fail("数量必须大于0");
+        }
+
+        boolean flag = this.updateById(facility);
+        if (!flag) {
+            return R.fail("修改设施失败");
+        }
+        return R.success("修改设施成功");
+    }
+
+    @Override
+    public R<String> saveOrUpdateFacility(StorePlatformSportsEquipmentFacility facility) {
+        log.info("StorePlatformSportsEquipmentFacilityServiceImpl.saveOrUpdateFacility facility={}", facility);
+        if (facility == null) {
+            return R.fail("设施信息不能为空");
+        }
+
+        if (facility.getId() != null) {
+            // 修改
+            return updateFacility(facility);
+        } else {
+            // 新增
+            return saveFacility(facility);
+        }
+    }
+
+    @Override
+    public R<String> deleteFacility(Integer id) {
+        log.info("StorePlatformSportsEquipmentFacilityServiceImpl.deleteFacility id={}", id);
+        if (id == null) {
+            return R.fail("设施ID不能为空");
+        }
+
+        boolean flag = this.removeById(id);
+        if (!flag) {
+            return R.fail("删除设施失败");
+        }
+        return R.success("删除设施成功");
+    }
+
+    @Override
+    public R<String> deleteFacilityBatch(List<Integer> ids) {
+        log.info("StorePlatformSportsEquipmentFacilityServiceImpl.deleteFacilityBatch ids={}", ids);
+        if (CollectionUtil.isEmpty(ids)) {
+            return R.fail("设施ID列表不能为空");
+        }
+
+        boolean flag = this.removeByIds(ids);
+        if (!flag) {
+            return R.fail("批量删除设施失败");
+        }
+        return R.success("批量删除设施成功");
+    }
+
+    @Override
+    public R<String> importFacilityFromExcel(MultipartFile file, Integer storeId) {
+        log.info("StorePlatformSportsEquipmentFacilityServiceImpl.importFacilityFromExcel storeId={}", storeId);
+
+        if (file == null || file.isEmpty()) {
+            return R.fail("上传文件为空");
+        }
+
+        String fileName = file.getOriginalFilename();
+        if (fileName == null || (!fileName.endsWith(".xlsx") && !fileName.endsWith(".xls"))) {
+            return R.fail("文件格式不正确,请上传Excel文件");
+        }
+
+        if (storeId == null) {
+            return R.fail("门店ID不能为空");
+        }
+
+        List<String> errorMessages = new ArrayList<>();
+        int successCount = 0;
+        int totalCount = 0;
+
+        try (InputStream inputStream = file.getInputStream();
+             XSSFWorkbook workbook = new XSSFWorkbook(inputStream)) {
+            Sheet sheet = workbook.getSheetAt(0);
+
+            // 获取表头(假设表头在第5行,索引为5)
+            Row headerRow = sheet.getRow(5);
+            if (headerRow == null) {
+                return R.fail("Excel文件格式不正确,缺少表头");
+            }
+
+            // 构建字段映射(表头名称 -> 列索引)
+            Map<String, Integer> headerMap = new HashMap<>();
+            Field[] fields = StorePlatformSportsEquipmentFacilityImportVo.class.getDeclaredFields();
+            for (int i = 0; i < headerRow.getLastCellNum(); i++) {
+                Cell cell = headerRow.getCell(i);
+                if (cell != null) {
+                    String headerName = getCellValueAsString(cell);
+                    if (StringUtils.isNotEmpty(headerName)) {
+                        headerMap.put(headerName.trim(), i);
+                    }
+                }
+            }
+
+            // 读取数据行(从第6行开始,索引为6)
+            for (int rowIndex = 6; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
+                Row row = sheet.getRow(rowIndex);
+                if (row == null) {
+                    continue;
+                }
+
+                // 检查是否为空行
+                boolean isEmptyRow = true;
+                for (int i = 0; i < row.getLastCellNum(); i++) {
+                    Cell cell = row.getCell(i);
+                    if (cell != null && cell.getCellType() != CellType.BLANK) {
+                        String cellValue = getCellValueAsString(cell);
+                        if (StringUtils.isNotEmpty(cellValue)) {
+                            isEmptyRow = false;
+                            break;
+                        }
+                    }
+                }
+                if (isEmptyRow) {
+                    continue;
+                }
+
+                totalCount++;
+                StorePlatformSportsEquipmentFacilityImportVo excelVo = new StorePlatformSportsEquipmentFacilityImportVo();
+
+                // 读取每个字段
+                for (Field field : fields) {
+                    field.setAccessible(true);
+                    String fieldName = field.getName();
+                    Integer colIndex = null;
+
+                    // 根据字段名查找对应的表头
+                    for (Map.Entry<String, Integer> entry : headerMap.entrySet()) {
+                        String headerName = entry.getKey();
+                        if (isFieldMatch(fieldName, headerName)) {
+                            colIndex = entry.getValue();
+                            break;
+                        }
+                    }
+
+                    if (colIndex == null) {
+                        continue;
+                    }
+
+                    Cell cell = row.getCell(colIndex);
+                    if (cell == null) {
+                        continue;
+                    }
+
+                    try {
+                        // 处理普通字段
+                        String cellValue = getCellValueAsString(cell);
+                        if (StringUtils.isNotEmpty(cellValue)) {
+                            setFieldValue(excelVo, field, cellValue.trim());
+                        }
+                    } catch (Exception e) {
+                        log.warn("读取字段{}失败:{}", fieldName, e.getMessage());
+                    }
+                }
+
+                // 处理导入数据
+                try {
+                    validateAndSaveFacility(excelVo, storeId, rowIndex + 1);
+                    successCount++;
+                } catch (Exception e) {
+                    errorMessages.add(String.format("第%d行:%s", rowIndex + 1, e.getMessage()));
+                    log.error("导入第{}行数据失败", rowIndex + 1, e);
+                }
+            }
+        } catch (Exception e) {
+            log.error("导入Excel失败", e);
+            return R.fail("导入失败:" + e.getMessage());
+        }
+
+        // 构建返回消息
+        StringBuilder message = new StringBuilder();
+        message.append(String.format("导入完成:成功%d条,失败%d条", successCount, totalCount - successCount));
+        if (!errorMessages.isEmpty()) {
+            message.append("\n失败详情:\n");
+            for (int i = 0; i < Math.min(errorMessages.size(), 10); i++) {
+                message.append(errorMessages.get(i)).append("\n");
+            }
+            if (errorMessages.size() > 10) {
+                message.append("...还有").append(errorMessages.size() - 10).append("条错误信息");
+            }
+        }
+
+        return R.success(message.toString());
+    }
+
+    /**
+     * 判断字段名是否匹配表头
+     */
+    private boolean isFieldMatch(String fieldName, String headerName) {
+        Map<String, String> fieldMapping = new HashMap<>();
+        fieldMapping.put("facilityCategory", "分类(有氧区/力量区/单功能机械区)");
+        fieldMapping.put("facilityName", "设施名称");
+        fieldMapping.put("quantity", "数量");
+        fieldMapping.put("brand", "品牌");
+        fieldMapping.put("description", "描述");
+        fieldMapping.put("displayInStoreDetail", "展示在店铺详情(展示/隐藏)");
+
+        String expectedHeader = fieldMapping.get(fieldName);
+        return expectedHeader != null && expectedHeader.equals(headerName);
+    }
+
+    /**
+     * 设置字段值
+     */
+    private void setFieldValue(StorePlatformSportsEquipmentFacilityImportVo excelVo, Field field, String cellValue) throws Exception {
+        Class<?> fieldType = field.getType();
+        String fieldName = field.getName();
+
+        if (fieldType == Integer.class) {
+            if ("facilityCategory".equals(fieldName)) {
+                // 处理设施分类:有氧区->1, 力量区->2, 单功能机械区->3
+                String trimmedValue = cellValue.trim();
+                if ("有氧区".equals(trimmedValue)) {
+                    field.set(excelVo, 1);
+                } else if ("力量区".equals(trimmedValue)) {
+                    field.set(excelVo, 2);
+                } else if ("单功能机械区".equals(trimmedValue)) {
+                    field.set(excelVo, 3);
+                } else {
+                    throw new RuntimeException("设施分类格式错误,请输入'有氧区'、'力量区'或'单功能机械区'");
+                }
+            } else if ("displayInStoreDetail".equals(fieldName)) {
+                // 处理是否显示在店铺详情:是->1, 否->0
+                String trimmedValue = cellValue.trim();
+                if ("展示".equals(trimmedValue)) {
+                    field.set(excelVo, 1);
+                } else if ("隐藏".equals(trimmedValue)) {
+                    field.set(excelVo, 0);
+                } else {
+                    throw new RuntimeException("是否显示在店铺详情字段格式错误,请输入'是'或'否'");
+                }
+            } else {
+                // 其他Integer字段直接解析
+                try {
+                    field.set(excelVo, Integer.parseInt(cellValue));
+                } catch (NumberFormatException e) {
+                    throw new RuntimeException(fieldName + "字段格式错误,请输入数字");
+                }
+            }
+        } else {
+            // String类型
+            field.set(excelVo, cellValue);
+        }
+    }
+
+    /**
+     * 校验并保存设施
+     */
+    private void validateAndSaveFacility(StorePlatformSportsEquipmentFacilityImportVo excelVo, Integer storeId, int rowNum) {
+        // 校验必填字段
+        if (StringUtils.isEmpty(excelVo.getFacilityName())) {
+            throw new RuntimeException("设施名称不能为空");
+        }
+        if (excelVo.getFacilityCategory() == null) {
+            throw new RuntimeException("设施分类不能为空");
+        }
+        if (excelVo.getQuantity() == null || excelVo.getQuantity() <= 0) {
+            throw new RuntimeException("数量必须大于0");
+        }
+
+        // 创建StorePlatformSportsEquipmentFacility对象
+        StorePlatformSportsEquipmentFacility facility = new StorePlatformSportsEquipmentFacility();
+        facility.setStoreId(storeId);
+        facility.setFacilityCategory(excelVo.getFacilityCategory());
+        facility.setFacilityName(excelVo.getFacilityName());
+        facility.setQuantity(excelVo.getQuantity());
+        facility.setBrand(excelVo.getBrand());
+        facility.setDescription(excelVo.getDescription());
+        facility.setDisplayInStoreDetail(excelVo.getDisplayInStoreDetail() != null ? excelVo.getDisplayInStoreDetail() : 0);
+
+        // 保存设施
+        boolean flag = this.save(facility);
+        if (!flag) {
+            throw new RuntimeException("保存设施失败");
+        }
+    }
+
+    /**
+     * 获取单元格值(字符串格式)
+     */
+    private String getCellValueAsString(Cell cell) {
+        if (cell == null) {
+            return null;
+        }
+
+        switch (cell.getCellType()) {
+            case STRING:
+                return cell.getStringCellValue();
+            case NUMERIC:
+                if (DateUtil.isCellDateFormatted(cell)) {
+                    return cell.getDateCellValue().toString();
+                } else {
+                    // 处理数字,避免科学计数法
+                    double numericValue = cell.getNumericCellValue();
+                    if (numericValue == (long) numericValue) {
+                        return String.valueOf((long) numericValue);
+                    } else {
+                        return String.valueOf(numericValue);
+                    }
+                }
+            case BOOLEAN:
+                return String.valueOf(cell.getBooleanCellValue());
+            case FORMULA:
+                return cell.getCellFormula();
+            default:
+                return null;
+        }
+    }
+}

+ 143 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StorePlatformStoreStaffConfigServiceImpl.java

@@ -0,0 +1,143 @@
+package shop.alien.storeplatform.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.stereotype.Service;
+import shop.alien.entity.result.R;
+import shop.alien.storeplatform.entity.StorePlatformStoreStaffConfig;
+import shop.alien.storeplatform.mapper.StorePlatformStoreStaffConfigMapper;
+import shop.alien.storeplatform.service.StorePlatformStoreStaffConfigService;
+
+import java.util.List;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class StorePlatformStoreStaffConfigServiceImpl extends ServiceImpl<StorePlatformStoreStaffConfigMapper, StorePlatformStoreStaffConfig> implements StorePlatformStoreStaffConfigService {
+
+    private final StorePlatformStoreStaffConfigMapper staffConfigMapper;
+
+    @Override
+    public StorePlatformStoreStaffConfig getById(Integer id) {
+        log.info("StorePlatformStoreStaffConfigServiceImpl.getById id={}", id);
+        if (id == null) {
+            throw new RuntimeException("员工ID不能为空");
+        }
+        return super.getById(id);
+    }
+
+    @Override
+    public List<StorePlatformStoreStaffConfig> getListByStoreId(Integer storeId) {
+        log.info("StorePlatformStoreStaffConfigServiceImpl.getListByStoreId storeId={}", storeId);
+        if (storeId == null) {
+            throw new RuntimeException("门店ID不能为空");
+        }
+        LambdaQueryWrapper<StorePlatformStoreStaffConfig> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(StorePlatformStoreStaffConfig::getStoreId, storeId)
+                .eq(StorePlatformStoreStaffConfig::getDeleteFlag, 0)
+                .orderByDesc(StorePlatformStoreStaffConfig::getTopStatus)
+                .orderByAsc(StorePlatformStoreStaffConfig::getId);
+        return this.list(queryWrapper);
+    }
+
+    @Override
+    public R<String> saveStaff(StorePlatformStoreStaffConfig staffConfig) {
+        log.info("StorePlatformStoreStaffConfigServiceImpl.saveStaff staffConfig={}", staffConfig);
+        String validateResult = validateStaffConfig(staffConfig, false);
+        if (validateResult != null) {
+            return R.fail(validateResult);
+        }
+
+        boolean flag = this.save(staffConfig);
+        if (!flag) {
+            return R.fail("新增员工失败");
+        }
+        return R.success("新增员工成功");
+    }
+
+    @Override
+    public R<String> updateStaff(StorePlatformStoreStaffConfig staffConfig) {
+        log.info("StorePlatformStoreStaffConfigServiceImpl.updateStaff staffConfig={}", staffConfig);
+        if (staffConfig == null || staffConfig.getId() == null) {
+            return R.fail("员工信息或员工ID不能为空");
+        }
+        String validateResult = validateStaffConfig(staffConfig, true);
+        if (validateResult != null) {
+            return R.fail(validateResult);
+        }
+
+        boolean flag = this.updateById(staffConfig);
+        if (!flag) {
+            return R.fail("修改员工失败");
+        }
+        return R.success("修改员工成功");
+    }
+
+    @Override
+    public R<String> saveOrUpdateStaff(StorePlatformStoreStaffConfig staffConfig) {
+        log.info("StorePlatformStoreStaffConfigServiceImpl.saveOrUpdateStaff staffConfig={}", staffConfig);
+        if (staffConfig == null) {
+            return R.fail("员工信息不能为空");
+        }
+        if (staffConfig.getId() == null) {
+            return saveStaff(staffConfig);
+        } else {
+            return updateStaff(staffConfig);
+        }
+    }
+
+    @Override
+    public R<String> deleteStaff(Integer id) {
+        log.info("StorePlatformStoreStaffConfigServiceImpl.deleteStaff id={}", id);
+        if (id == null) {
+            return R.fail("员工ID不能为空");
+        }
+        boolean flag = this.removeById(id);
+        if (!flag) {
+            return R.fail("删除员工失败");
+        }
+        return R.success("删除员工成功");
+    }
+
+    @Override
+    public R<String> deleteStaffBatch(List<Integer> ids) {
+        log.info("StorePlatformStoreStaffConfigServiceImpl.deleteStaffBatch ids={}", ids);
+        if (CollectionUtil.isEmpty(ids)) {
+            return R.fail("员工ID列表不能为空");
+        }
+        boolean flag = this.removeByIds(ids);
+        if (!flag) {
+            return R.fail("批量删除员工失败");
+        }
+        return R.success("批量删除员工成功");
+    }
+
+    /**
+     * 校验员工数据
+     */
+    private String validateStaffConfig(StorePlatformStoreStaffConfig staffConfig, boolean isUpdate) {
+        if (staffConfig == null) {
+            return "员工信息不能为空";
+        }
+        if (!isUpdate && staffConfig.getStoreId() == null) {
+            return "门店ID不能为空";
+        }
+        if (StringUtils.isEmpty(staffConfig.getName())) {
+            return "员工姓名不能为空";
+        }
+        if (StringUtils.isEmpty(staffConfig.getStaffPosition())) {
+            return "员工职位不能为空";
+        }
+        if (StringUtils.isEmpty(staffConfig.getStatus())) {
+            staffConfig.setStatus("0");
+        }
+        if (staffConfig.getTopStatus() == null) {
+            staffConfig.setTopStatus(0);
+        }
+        return null;
+    }
+}

+ 13 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/vo/ExcelImportVo.java

@@ -0,0 +1,13 @@
+package shop.alien.storeplatform.vo;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.Data;
+import org.springframework.web.multipart.MultipartFile;
+
+@Data
+@JsonInclude
+public class ExcelImportVo {
+    private MultipartFile file;
+
+    private Integer storeId;
+}

+ 40 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/vo/StorePlatformBathFacilityImportVo.java

@@ -0,0 +1,40 @@
+package shop.alien.storeplatform.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 shop.alien.entity.store.excelVo.util.ExcelImage;
+
+@Data
+@JsonInclude
+@ApiModel(value = "StorePlatformBathFacilityImportVo对象")
+public class StorePlatformBathFacilityImportVo {
+
+    @ApiModelProperty(value = "区域(洗浴区/汗蒸区/休闲区/餐饮区)")
+    private Integer facilityCategory;
+
+    @ApiModelProperty(value = "名称")
+    private String facilityName;
+
+    @ApiModelProperty(value = "图片")
+    @ExcelImage
+    private String img;
+
+    @ApiModelProperty(value = "收费标准(元)")
+    private String chargingStandard;
+
+    @ApiModelProperty(value = "开放时间")
+    private Integer usageTimeType;
+
+    @ApiModelProperty(value = "自定义开始时间")
+    private String usageStartTime;
+
+    @ApiModelProperty(value = "自定义结束时间")
+    private String usageEndTime;
+
+    @ApiModelProperty(value = "状态(展示/隐藏)")
+    private Integer displayInStoreDetail;
+
+}

+ 31 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/vo/StorePlatformSportsEquipmentFacilityImportVo.java

@@ -0,0 +1,31 @@
+package shop.alien.storeplatform.vo;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@JsonInclude
+@ApiModel(value = "StorePlatformSportsEquipmentFacilityImportVo对象")
+public class StorePlatformSportsEquipmentFacilityImportVo {
+
+    @ApiModelProperty(value = "设施分类(1:有氧区, 2:力量区, 3:单功能机械区)")
+    private Integer facilityCategory;
+
+    @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;
+
+}

+ 47 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/vo/StorePlatformStoreMenuImportVo.java

@@ -0,0 +1,47 @@
+package shop.alien.storeplatform.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 shop.alien.entity.store.excelVo.util.ExcelImage;
+
+import java.math.BigDecimal;
+
+@Data
+@JsonInclude
+@ApiModel(value = "StorePlatformStoreMenuImportVo对象")
+public class StorePlatformStoreMenuImportVo {
+    @ApiModelProperty(value = "菜单类型:1-餐食,2-酒水")
+    private Integer dishMenuType;
+
+    @ApiModelProperty(value = "名称")
+    private String dishName;
+
+    @ApiModelProperty(value = "价格")
+    private BigDecimal dishPrice;
+
+    @ApiModelProperty(value = "成本价")
+    private BigDecimal costPrice;
+
+    @ApiModelProperty(value = "品类")
+    private String category;
+
+    @ApiModelProperty(value = "酒精度")
+    private String alcoholVolume;
+
+    @ApiModelProperty(value = "风味")
+    private String flavor;
+
+    @ApiModelProperty(value = "图片")
+    @ExcelImage
+    private String img;
+
+    @ApiModelProperty(value = "描述")
+    private String description;
+
+    @ApiModelProperty(value = "是否推荐, 0:非推荐, 1:推荐")
+    private Integer dishType;
+
+}

BIN
alien-store-platform/src/main/resources/templates/洗浴汗蒸导入模板.xlsx


BIN
alien-store-platform/src/main/resources/templates/美食导入模板.xlsx


BIN
alien-store-platform/src/main/resources/templates/运动健身导入模板.xlsx


BIN
alien-store-platform/src/main/resources/templates/酒水餐食导入模板.xlsx


+ 658 - 0
alien-store-platform/接口文档/OCR识别接口文档.md

@@ -0,0 +1,658 @@
+# OCR 识别接口文档
+
+## 接口概述
+
+本接口提供阿里云 OCR(Optical Character Recognition,光学字符识别)服务,支持多种证件和文档的智能识别,包括身份证、营业执照、食品经营许可证等。
+
+**接口路径**: `POST /ali/ocrRequestUrl`  
+**接口描述**: 调用阿里云 OCR 识别服务,支持批量图片识别  
+**请求方式**: POST  
+**Content-Type**: application/json  
+**登录验证**: ✅ 需要
+
+---
+
+## 请求参数
+
+### 请求体 (JSON)
+
+| 参数名 | 类型 | 必填 | 说明 | 示例值 |
+|--------|------|------|------|--------|
+| imageUrls | String | 是 | 图片URL地址,多个用逗号分隔 | `https://example.com/image1.jpg,https://example.com/image2.jpg` |
+| ocrType | String | 是 | OCR识别类型 | `idCard`, `businessLicense`, `foodLicense` |
+| storeId | String | 否 | 店铺ID(用于记录识别来源) | `103` |
+| userId | String | 否 | 用户ID(用于记录识别来源) | `12345` |
+| storeUserId | String | 否 | 店铺用户ID(用于记录识别来源) | `67890` |
+
+### OCR 类型说明
+
+| ocrType 值 | 识别类型 | 说明 |
+|-----------|----------|------|
+| `idCard` | 身份证识别 | 识别身份证正反面信息 |
+| `businessLicense` | 营业执照识别 | 识别营业执照信息 |
+| `foodLicense` | 食品经营许可证识别 | 识别食品经营许可证信息 |
+
+---
+
+## 请求示例
+
+### 示例 1: 身份证识别(单张)
+
+```http
+POST /ali/ocrRequestUrl
+Content-Type: application/json
+
+{
+    "imageUrls": "https://example.com/idcard-front.jpg",
+    "ocrType": "idCard",
+    "storeId": "103",
+    "userId": "12345",
+    "storeUserId": "67890"
+}
+```
+
+```bash
+curl -X POST "http://localhost:8080/ali/ocrRequestUrl" \
+  -H "Content-Type: application/json" \
+  -H "Authorization: Bearer YOUR_TOKEN" \
+  -d '{
+    "imageUrls": "https://example.com/idcard-front.jpg",
+    "ocrType": "idCard",
+    "storeId": "103"
+  }'
+```
+
+---
+
+### 示例 2: 营业执照识别(批量)
+
+```http
+POST /ali/ocrRequestUrl
+Content-Type: application/json
+
+{
+    "imageUrls": "https://example.com/license1.jpg,https://example.com/license2.jpg",
+    "ocrType": "businessLicense",
+    "storeId": "103",
+    "storeUserId": "67890"
+}
+```
+
+---
+
+### 示例 3: 食品经营许可证识别
+
+```http
+POST /ali/ocrRequestUrl
+Content-Type: application/json
+
+{
+    "imageUrls": "https://example.com/food-license.jpg",
+    "ocrType": "foodLicense",
+    "storeId": "103"
+}
+```
+
+---
+
+## 响应参数
+
+### 成功响应
+
+#### 响应结构
+
+```json
+{
+    "code": 200,
+    "success": true,
+    "data": [
+        {
+            // OCR识别结果(根据不同类型返回不同字段)
+        }
+    ],
+    "msg": "操作成功"
+}
+```
+
+---
+
+### 身份证识别响应字段
+
+```json
+{
+    "code": 200,
+    "success": true,
+    "data": [
+        {
+            "name": "张三",
+            "gender": "男",
+            "nationality": "汉",
+            "birthDate": "19900101",
+            "address": "北京市朝阳区XX街道XX号",
+            "idNumber": "110101199001011234",
+            "issueAuthority": "北京市公安局朝阳分局",
+            "validDate": "2020.01.01-2030.01.01"
+        }
+    ],
+    "msg": "操作成功"
+}
+```
+
+#### 字段说明
+
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| name | String | 姓名 |
+| gender | String | 性别 |
+| nationality | String | 民族 |
+| birthDate | String | 出生日期(格式:yyyyMMdd) |
+| address | String | 地址 |
+| idNumber | String | 身份证号码 |
+| issueAuthority | String | 签发机关(反面) |
+| validDate | String | 有效期限(反面) |
+
+---
+
+### 营业执照识别响应字段
+
+```json
+{
+    "code": 200,
+    "success": true,
+    "data": [
+        {
+            "name": "北京XX科技有限公司",
+            "creditCode": "91110000MA01ABC123",
+            "legalPerson": "李四",
+            "address": "北京市海淀区XX路XX号",
+            "registerDate": "2020-01-01",
+            "registerCapital": "100万元人民币",
+            "businessScope": "技术开发、技术咨询...",
+            "validPeriod": "2020-01-01至2040-01-01",
+            "type": "有限责任公司",
+            "establishDate": "2020-01-01"
+        }
+    ],
+    "msg": "操作成功"
+}
+```
+
+#### 字段说明
+
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| name | String | 企业名称 |
+| creditCode | String | 统一社会信用代码 |
+| legalPerson | String | 法定代表人 |
+| address | String | 注册地址 |
+| registerDate | String | 注册日期 |
+| registerCapital | String | 注册资本 |
+| businessScope | String | 经营范围 |
+| validPeriod | String | 营业期限 |
+| type | String | 企业类型 |
+| establishDate | String | 成立日期 |
+
+---
+
+### 食品经营许可证识别响应字段
+
+```json
+{
+    "code": 200,
+    "success": true,
+    "data": [
+        {
+            "licenseName": "食品经营许可证",
+            "businessLicenseName": "北京XX餐饮有限公司",
+            "businessAddress": "北京市朝阳区XX街XX号",
+            "legalPerson": "王五",
+            "creditCode": "91110000MA01XYZ456",
+            "businessItem": "热食类食品制售",
+            "licenseNumber": "JY11234567890123",
+            "validPeriod": "2020-01-01至2025-01-01",
+            "issueDate": "2020-01-01",
+            "issueAuthority": "北京市朝阳区市场监督管理局"
+        }
+    ],
+    "msg": "操作成功"
+}
+```
+
+#### 字段说明
+
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| licenseName | String | 许可证名称 |
+| businessLicenseName | String | 经营者名称 |
+| businessAddress | String | 经营场所 |
+| legalPerson | String | 法定代表人(负责人) |
+| creditCode | String | 社会信用代码 |
+| businessItem | String | 经营项目 |
+| licenseNumber | String | 许可证编号 |
+| validPeriod | String | 有效期限 |
+| issueDate | String | 发证日期 |
+| issueAuthority | String | 发证机关 |
+
+---
+
+### 失败响应
+
+```json
+{
+    "code": 500,
+    "success": false,
+    "data": null,
+    "msg": "身份证识别失败" // 或其他错误信息
+}
+```
+
+#### 常见错误信息
+
+| 错误信息 | 原因 | 解决方案 |
+|---------|------|---------|
+| OCR识别返回结果为空 | 图片质量差或格式错误 | 上传清晰的图片 |
+| 身份证识别失败 | 图片不是身份证或无法识别 | 检查图片内容 |
+| 营业执照识别失败 | 图片不是营业执照或无法识别 | 检查图片内容 |
+| 图片URL无法访问 | 图片链接失效或网络问题 | 检查图片URL |
+| OCR识别异常 | 系统异常或参数错误 | 检查请求参数 |
+
+---
+
+## 业务流程
+
+### 识别流程图
+
+```
+用户上传图片 
+    ↓
+前端获取图片URL
+    ↓
+调用 /ali/ocrRequestUrl 接口
+    ↓
+根据 ocrType 选择识别策略
+    ↓
+调用阿里云OCR服务
+    ↓
+解析识别结果
+    ↓
+保存识别记录到数据库
+    ↓
+返回识别结果
+```
+
+### 核心业务逻辑
+
+1. **参数接收**: 接收图片URL列表和识别类型
+2. **策略选择**: 根据 ocrType 选择对应的识别策略
+3. **批量识别**: 遍历图片URL列表,逐一调用阿里云OCR
+4. **结果解析**: 解析阿里云返回的JSON数据
+5. **数据存储**: 保存识别结果到 `ocr_image_upload` 表
+6. **结果返回**: 返回识别结果数组
+
+---
+
+## 技术实现
+
+### 技术架构
+
+```
+Controller层 (AliController)
+    ↓
+Service层 (AliApi)
+    ↓
+策略模式 (OcrStrategy)
+    ├── IdCardOcrStrategy (身份证识别)
+    ├── BusinessLicenseOcrStrategy (营业执照识别)
+    └── FoodManageLicenseOcrStrategy (食品许可证识别)
+    ↓
+阿里云OCR API
+```
+
+### 关键类说明
+
+| 类名 | 说明 | 路径 |
+|------|------|------|
+| AliController | 控制器,接收请求 | `shop.alien.store.controller.AliController` |
+| AliApi | 服务类,业务逻辑 | `shop.alien.store.util.ali.AliApi` |
+| OcrStrategy | 策略接口 | `shop.alien.store.util.ali.ocr.OcrStrategy` |
+| AbstractOcrStrategy | 策略抽象类 | `shop.alien.store.util.ali.ocr.AbstractOcrStrategy` |
+| IdCardOcrStrategy | 身份证识别策略 | `shop.alien.store.util.ali.ocr.strategy.IdCardOcrStrategy` |
+| BusinessLicenseOcrStrategy | 营业执照识别策略 | `shop.alien.store.util.ali.ocr.strategy.BusinessLicenseOcrStrategy` |
+| FoodManageLicenseOcrStrategy | 食品许可证识别策略 | `shop.alien.store.util.ali.ocr.strategy.FoodManageLicenseOcrStrategy` |
+
+### 核心代码
+
+#### Controller 层
+
+```java
+@ApiOperation("调用OCR识别接口")
+@PostMapping("/ocrRequestUrl")
+public R ocrRequestUrl(@RequestBody Map<String, String> params) {
+    try {
+        return aliApi.ocrRequestParams(params);
+    } catch (Exception e) {
+        return R.fail(e.getMessage());
+    }
+}
+```
+
+#### Service 层
+
+```java
+public R ocrRequestParams(Map<String, String> params) throws Exception {
+    // 根据 ocrType 获取对应的识别策略
+    OcrStrategy strategy = ocrStrategyFactory.getStrategy(params.get("ocrType"));
+    // 执行识别
+    return strategy.recognizeParams(params);
+}
+```
+
+#### 策略实现(以身份证为例)
+
+```java
+@Override
+public R recognizeParams(Map<String, String> params) throws Exception {
+    String[] imageUrls = params.get("imageUrls").split(",");
+    JSONArray resultArray = new JSONArray();
+    
+    for (String imageUrl : imageUrls) {
+        // 创建阿里云OCR客户端
+        Client client = createOcrClient();
+        
+        // 创建识别请求
+        RecognizeIdcardRequest request = new RecognizeIdcardRequest()
+                .setUrl(imageUrl);
+        
+        // 调用识别接口
+        RecognizeIdcardResponse response = client.recognizeIdcardWithOptions(
+                request, new RuntimeOptions());
+        
+        // 解析结果
+        RecognizeIdcardResponseBody body = response.getBody();
+        JSONObject jsonObject = JSONObject.parseObject(body.getData());
+        
+        // 保存识别记录
+        OcrImageUpload ocrImageUpload = new OcrImageUpload();
+        ocrImageUpload.setImageUrl(imageUrl);
+        ocrImageUpload.setOcrResult(jsonObject.getJSONObject("data").toJSONString());
+        ocrImageUpload.setOcrType(OcrTypeEnum.ID_CARD.getCode());
+        saveOcrImage(ocrImageUpload);
+        
+        // 添加到结果集
+        resultArray.add(jsonObject.getJSONObject("data"));
+    }
+    
+    return R.data(resultArray);
+}
+```
+
+---
+
+## 数据库设计
+
+### ocr_image_upload 表
+
+OCR识别记录表,用于保存每次识别的结果。
+
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| id | INT | 主键ID |
+| user_id | VARCHAR | 用户ID |
+| store_id | VARCHAR | 店铺ID |
+| store_user_id | VARCHAR | 店铺用户ID |
+| image_url | VARCHAR | 图片URL |
+| ocr_result | TEXT | OCR识别结果(JSON格式) |
+| ocr_type | VARCHAR | OCR类型 |
+| create_time | DATETIME | 创建时间 |
+| update_time | DATETIME | 更新时间 |
+
+---
+
+## 配置说明
+
+### 阿里云 OCR 配置
+
+在 `application.yml` 中配置阿里云 OCR 参数:
+
+```yaml
+ali:
+  ocr:
+    accessKeyId: YOUR_ACCESS_KEY_ID
+    accessKeySecret: YOUR_ACCESS_KEY_SECRET
+    endpoint: ocr-api.cn-hangzhou.aliyuncs.com
+```
+
+### 配置项说明
+
+| 配置项 | 说明 | 示例值 |
+|--------|------|--------|
+| accessKeyId | 阿里云访问密钥ID | `LTAI4G...` |
+| accessKeySecret | 阿里云访问密钥Secret | `xxx...` |
+| endpoint | OCR服务端点 | `ocr-api.cn-hangzhou.aliyuncs.com` |
+
+---
+
+## 使用限制
+
+### 图片要求
+
+| 项目 | 要求 |
+|------|------|
+| **格式** | JPG、PNG、BMP、PDF |
+| **大小** | 不超过 4MB |
+| **分辨率** | 建议大于 600x600 像素 |
+| **清晰度** | 图片清晰,无模糊、反光 |
+| **完整性** | 证件信息完整,无遮挡 |
+
+### 调用限制
+
+| 项目 | 限制 |
+|------|------|
+| **QPS限制** | 根据阿里云账户配置 |
+| **并发数** | 建议不超过10个并发请求 |
+| **批量数量** | 单次请求建议不超过5张图片 |
+
+---
+
+## 注意事项
+
+### 1. 图片URL要求
+
+⚠️ **图片URL必须可公网访问**
+
+```java
+// ✅ 正确:公网可访问的URL
+"imageUrls": "https://cdn.example.com/image.jpg"
+
+// ❌ 错误:本地路径
+"imageUrls": "file:///C:/Users/image.jpg"
+
+// ❌ 错误:内网地址
+"imageUrls": "http://192.168.1.100/image.jpg"
+```
+
+### 2. 批量识别建议
+
+⚠️ **批量识别时注意错误处理**
+
+```java
+// 当前实现:遇到错误会中断整个批量
+// 建议:改进为部分失败不影响其他图片识别
+```
+
+### 3. 数据隐私
+
+⚠️ **敏感信息处理**
+
+- 识别结果包含个人身份信息,需妥善保存
+- 建议加密存储敏感字段
+- 遵守数据保护法规
+
+### 4. 成本控制
+
+⚠️ **接口调用成本**
+
+- 阿里云OCR按调用次数计费
+- 建议添加调用频率限制
+- 避免重复识别相同图片
+
+---
+
+## 错误处理
+
+### 异常类型
+
+| 异常类型 | 说明 | 处理方式 |
+|---------|------|---------|
+| TeaException | 阿里云API异常 | 记录日志,返回友好提示 |
+| Exception | 通用异常 | 记录详细日志,返回错误信息 |
+| NullPointerException | 空指针异常 | 参数校验,防止空值 |
+
+### 异常处理代码
+
+```java
+try {
+    // OCR识别逻辑
+} catch (TeaException error) {
+    log.error("OCR识别失败: code={}, message={}", 
+              error.getCode(), error.getMessage());
+    return R.fail("身份证识别失败");
+} catch (Exception error) {
+    log.error("OCR识别异常", error);
+    return R.fail("身份证识别异常");
+}
+```
+
+---
+
+## 性能优化建议
+
+### 1. 图片预处理
+
+```java
+// 建议:上传前压缩图片
+- 控制图片大小在 1MB 以内
+- 分辨率调整到 1200x1200 左右
+- 使用 JPG 格式(压缩率更高)
+```
+
+### 2. 异步处理
+
+```java
+// 建议:批量识别时使用异步
+@Async
+public CompletableFuture<R> recognizeAsync(String imageUrl) {
+    // 异步识别逻辑
+}
+```
+
+### 3. 缓存机制
+
+```java
+// 建议:相同图片不重复识别
+@Cacheable(value = "ocrResult", key = "#imageUrl")
+public R recognizeWithCache(String imageUrl, String ocrType) {
+    // 识别逻辑
+}
+```
+
+---
+
+## 测试建议
+
+### 测试场景
+
+| 测试场景 | 测试目的 | 预期结果 |
+|---------|---------|---------|
+| **正常识别** | 验证识别准确性 | 返回正确的识别结果 |
+| **图片模糊** | 验证容错能力 | 返回友好错误提示 |
+| **批量识别** | 验证批量处理 | 所有图片都能识别 |
+| **无效URL** | 验证参数校验 | 返回错误信息 |
+| **并发请求** | 验证并发安全 | 结果正确,无遗漏 |
+
+### 测试用例
+
+```bash
+# 测试1:身份证识别
+curl -X POST "http://localhost:8080/ali/ocrRequestUrl" \
+  -H "Content-Type: application/json" \
+  -d '{
+    "imageUrls": "https://example.com/idcard.jpg",
+    "ocrType": "idCard",
+    "storeId": "103"
+  }'
+
+# 测试2:营业执照批量识别
+curl -X POST "http://localhost:8080/ali/ocrRequestUrl" \
+  -H "Content-Type: application/json" \
+  -d '{
+    "imageUrls": "https://example.com/license1.jpg,https://example.com/license2.jpg",
+    "ocrType": "businessLicense",
+    "storeId": "103"
+  }'
+```
+
+---
+
+## 相关接口
+
+### 其他 OCR 接口
+
+| 接口路径 | 说明 | 区别 |
+|---------|------|------|
+| GET /ali/ocrRequest | OCR识别方式一 | 通过图片ID识别 |
+| POST /ali/ocrRequestByBase64 | OCR识别方式二 | 通过Base64编码识别 |
+| POST /ali/secondClient | 二手委托人识别 | 专用于二手业务 |
+
+---
+
+## 更新日志
+
+### v1.0 (2025-11-18)
+
+**初始版本**:
+- ✅ 支持身份证识别
+- ✅ 支持营业执照识别
+- ✅ 支持食品经营许可证识别
+- ✅ 支持批量图片识别
+- ✅ 自动保存识别记录
+- ✅ 采用策略模式设计
+
+**开发团队**:lyx
+
+---
+
+### v1.1 (预计)
+
+**计划功能**:
+- 🔜 增加图片缓存机制
+- 🔜 支持异步批量识别
+- 🔜 增加识别结果校验
+- 🔜 优化错误处理逻辑
+- 🔜 增加调用频率限制
+
+---
+
+## 相关文档
+
+- [阿里云OCR API文档](https://help.aliyun.com/document_detail/442275.html)
+- [身份证识别文档](https://help.aliyun.com/document_detail/442322.html)
+- [营业执照识别文档](https://help.aliyun.com/document_detail/442338.html)
+
+---
+
+## 联系方式
+
+| 角色 | 负责内容 | 联系方式 |
+|------|---------|---------|
+| 后端开发 | 接口开发与维护 | - |
+| 技术负责人 | 技术方案审核 | - |
+| 产品经理 | 业务需求对接 | - |
+
+---
+
+**文档版本**: v1.0  
+**最后更新**: 2025-11-28  
+**维护人员**: AI Assistant  
+**接口状态**: ✅ 已上线
+