Forráskód Böngészése

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

lyx 1 hete
szülő
commit
44c11deffa

+ 103 - 0
alien-entity/src/main/java/shop/alien/entity/store/DrinkInfo.java

@@ -0,0 +1,103 @@
+package shop.alien.entity.store;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 二期-酒水餐食信息
+ *
+ * @author ssk
+ * @since 2025-05-14
+ */
+@Data
+@JsonInclude
+@TableName("drink_info")
+@ApiModel(value = "DrinkInfo对象", description = "酒水餐食信息")
+public class DrinkInfo  {
+
+    @ApiModelProperty(value = "主键")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "类型(枚举值:酒水/餐食)")
+    @TableField("type")
+    private String type;
+
+    @ApiModelProperty(value = "名称")
+    @TableField("name")
+    private String name;
+
+    @ApiModelProperty(value = "售价")
+    @TableField("price")
+    private BigDecimal price;
+
+    @ApiModelProperty(value = "成本价")
+    @TableField("cost_price")
+    private BigDecimal costPrice;
+
+    @ApiModelProperty(value = "酒精度")
+    @TableField("alcohol_volume")
+    private BigDecimal alcoholVolume;
+
+    @ApiModelProperty(value = "图片")
+    @TableField("pic_url")
+    private String picUrl;
+
+    @ApiModelProperty(value = "分类")
+    @TableField("category")
+    private String category;
+
+    @ApiModelProperty(value = "口味")
+    @TableField("flavor")
+    private String flavor;
+
+    @ApiModelProperty(value = "描述")
+    @TableField("description")
+    private String description;
+
+    @ApiModelProperty(value = "排序")
+    @TableField("sort")
+    private Integer sort;
+
+    @ApiModelProperty(value = "是否推荐 0:否 1:是")
+    @TableField("is_recommended")
+    private Integer isRecommended;
+
+    @ApiModelProperty(value = "状态 0:下架 1:上架")
+    @TableField("status")
+    private Integer status;
+
+    @ApiModelProperty(value = "门店ID")
+    @TableField("store_id")
+    private Integer storeId;
+
+    @ApiModelProperty(value = "创建人ID")
+    @TableField("create_user_id")
+    private Integer createUserId;
+
+    @ApiModelProperty(value = "创建时间")
+    @TableField(value = "create_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createTime;
+
+    @ApiModelProperty(value = "修改人ID")
+    @TableField("update_user_id")
+    private Integer updateUserId;
+
+    @ApiModelProperty(value = "修改时间")
+    @TableField(value = "update_time", fill = FieldFill.UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updateTime;
+
+    @ApiModelProperty(value = "是否删除 0:未删除 1:已删除")
+    @TableField("is_deleted")
+    @TableLogic
+    private Integer isDeleted;
+}

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

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

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

@@ -86,6 +86,7 @@ public class StoreInfo {
 
     @ApiModelProperty(value = "到期时间")
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @TableField("expiration_time")
     private Date expirationTime;
 
     @ApiModelProperty(value = "门店坐标")

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

@@ -0,0 +1,82 @@
+package shop.alien.entity.store.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 酒水餐食信息表 VO
+ *
+ * @author ssk
+ * @since 2025-06-13
+ */
+@Data
+@ApiModel(value = "DrinkInfoVo", description = "酒水餐食信息表视图对象")
+public class DrinkInfoVo {
+
+    @ApiModelProperty(value = "主键ID")
+    private Integer id;
+
+    @ApiModelProperty(value = "类型(枚举值:酒水/餐食)")
+    private String type;
+
+    @ApiModelProperty(value = "商品名称")
+    private String name;
+
+    @ApiModelProperty(value = "商品价格")
+    private BigDecimal price;
+
+    @ApiModelProperty(value = "商品成本价")
+    private BigDecimal costPrice;
+
+    @ApiModelProperty(value = "商品分类")
+    private String category;
+
+    @ApiModelProperty(value = "酒精度数")
+    private BigDecimal alcoholVolume;
+
+    @ApiModelProperty(value = "商品图片")
+    private String picUrl;
+
+    @ApiModelProperty(value = "商品描述")
+    private String description;
+
+    @ApiModelProperty(value = "商品口味")
+    private String flavor;
+
+    @ApiModelProperty(value = "门店ID")
+    private Integer storeId;
+
+    @ApiModelProperty(value = "门店名称")
+    private String storeName;
+
+    @ApiModelProperty(value = "是否推荐(0:否, 1:是)")
+    private Integer isRecommended;
+
+    @ApiModelProperty(value = "状态(0:下架, 1:上架)")
+    private Integer status;
+
+    @ApiModelProperty(value = "排序值")
+    private Integer sort;
+
+    @ApiModelProperty(value = "是否删除(0:否, 1:是)")
+    private Integer isDeleted;
+
+    @ApiModelProperty(value = "创建人ID")
+    private Integer createUserId;
+
+    @ApiModelProperty(value = "创建时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createTime;
+
+    @ApiModelProperty(value = "更新人ID")
+    private Integer updateUserId;
+
+    @ApiModelProperty(value = "更新时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updateTime;
+}

+ 14 - 0
alien-entity/src/main/java/shop/alien/mapper/DrinkInfoMapper.java

@@ -0,0 +1,14 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import shop.alien.entity.store.DrinkInfo;
+
+/**
+ * 酒水餐食信息表 Mapper 接口
+ *
+ * @author ssk
+ * @since 2025-06-13
+ */
+public interface DrinkInfoMapper extends BaseMapper<DrinkInfo> {
+
+}

+ 34 - 0
alien-entity/src/main/resources/mapper/DrinkInfoMapper.xml

@@ -0,0 +1,34 @@
+<?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.DrinkInfoMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="shop.alien.entity.store.DrinkInfo">
+        <id column="id" property="id" />
+        <result column="type" property="type" />
+        <result column="name" property="name" />
+        <result column="price" property="price" />
+        <result column="cost_price" property="costPrice" />
+        <result column="category" property="category" />
+        <result column="alcohol_volume" property="alcoholVolume" />
+        <result column="pic_url" property="picUrl" />
+        <result column="description" property="description" />
+        <result column="flavor" property="flavor" />
+        <result column="store_id" property="storeId" />
+        <result column="is_recommended" property="isRecommended" />
+        <result column="status" property="status" />
+        <result column="sort" property="sort" />
+        <result column="is_deleted" property="isDeleted" />
+        <result column="create_user_id" property="createUserId" />
+        <result column="create_time" property="createTime" />
+        <result column="update_user_id" property="updateUserId" />
+        <result column="update_time" property="updateTime" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, type, name, price, cost_price, category, alcohol_volume, pic_url, description, flavor, store_id, 
+        is_recommended, status, sort, is_deleted, create_user_id, create_time, update_user_id, update_time
+    </sql>
+
+</mapper>

+ 1 - 1
alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerConsultationOrderServiceImpl.java

@@ -1715,7 +1715,7 @@ public class LawyerConsultationOrderServiceImpl extends ServiceImpl<LawyerConsul
             LifeNotice lifeNotice = new LifeNotice();
             lifeNotice.setSenderId(SYSTEM_SENDER_ID);
             lifeNotice.setBusinessId(order.getId());
-            lifeNotice.setTitle("退款申请通知");
+            lifeNotice.setTitle("申请退款通知");
 
             // 获取申请人接收ID
             String receiverId = getClientReceiverId(clientUserId);

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

@@ -177,7 +177,7 @@ public class OrderExpirationServiceImpl implements OrderExpirationService, Comma
                     //退款到账通知
                     LifeNotice lifeNotice2 = buildUserLifeNotice(order, "退款到账通知", "您的编号为" + orderNo + "的订单,订单金额已原路返还至您的支付渠道,请查收。");
                     WebSocketVo webSocketVo2 = buildWebSocketVo(lifeNotice2);
-                    lifeNotice2.setCreatedTime(DateUtils.calcMinute(lifeNotice2.getCreatedTime(), 2));
+                    lifeNotice2.setCreatedTime(DateUtils.calcMinute(new Date(), 2));
 
                     lifeNoticeMapper.insert(lifeNotice2);
                     webSocketProcess.sendMessage(lifeNotice2.getReceiverId(), JSONObject.from(webSocketVo2).toJSONString());
@@ -187,6 +187,7 @@ public class OrderExpirationServiceImpl implements OrderExpirationService, Comma
                     LawyerConsultationOrder update = new LawyerConsultationOrder();
                     update.setId(order.getId());
                     update.setOrderStatus(5);
+                    update.setApplyRefundTime(new Date());
                     orderMapper.updateById( update);
                 }
             }

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

@@ -214,10 +214,10 @@ public class StorePlatformLoginServiceImpl extends ServiceImpl<StoreUserMapper,
 
             StoreInfo storeInfo = storeInfoMapper.selectById(storeId);
             if (storeInfo != null) {
-                if (storeInfo.getExpirationTime().compareTo(new Date()) < 0) {
+                if (storeInfo.getExpirationTime() == null || storeInfo.getExpirationTime().compareTo(new Date()) < 0) {
                     jsonObject.put("expirationTime", 0);
                 }
-                if (storeInfo.getFoodLicenceExpirationTime().compareTo(new Date()) < 0) {
+                if (storeInfo.getFoodLicenceExpirationTime() == null || storeInfo.getFoodLicenceExpirationTime().compareTo(new Date()) < 0) {
                     jsonObject.put("foodLicenceExpirationTime", 0);
                 }
                 if (storeInfo.getEntertainmentLicenceExpirationTime() != null && storeInfo.getEntertainmentLicenceExpirationTime().compareTo(new Date()) < 0) {

+ 190 - 0
alien-store/src/main/java/shop/alien/store/controller/DrinkInfoController.java

@@ -0,0 +1,190 @@
+package shop.alien.store.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.DrinkInfo;
+import shop.alien.entity.store.vo.DrinkInfoVo;
+import shop.alien.store.service.DrinkInfoService;
+
+import java.util.List;
+
+/**
+ * 酒水餐食信息表Controller
+ *
+ * @author ssk
+ * @since 2025-06-13
+ */
+@Slf4j
+@Api(tags = {"酒水餐食管理"})
+@ApiSort(1)
+@CrossOrigin
+@RestController
+@RequestMapping("/store/drink")
+@RequiredArgsConstructor
+public class DrinkInfoController {
+
+    private final DrinkInfoService drinkInfoService;
+
+    @ApiOperation("分页查询酒水餐食列表")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页码", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "size", value = "页容", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "type", value = "类型(枚举值:酒水/餐食)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "category", value = "分类", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "name", value = "名称关键词", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "createUserId", value = "创建人ID(只查询自己创建的)", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/page")
+    public R<IPage<DrinkInfoVo>> getDrinkInfoPage(
+            @RequestParam("page") int page,
+            @RequestParam("size") int size,
+            @RequestParam(value = "storeId", required = false) Integer storeId,
+            @RequestParam(value = "type", required = false) String type,
+            @RequestParam(value = "category", required = false) String category,
+            @RequestParam(value = "name", required = false) String name,
+            @RequestParam(value = "createUserId", required = false) Integer createUserId) {
+        log.info("DrinkInfoController.getDrinkInfoPage?page={}, size={}, storeId={}, type={}, category={}, name={}, createUserId={}", 
+                page, size, storeId, type, category, name, createUserId);
+        IPage<DrinkInfoVo> drinkInfoPage = drinkInfoService.getDrinkInfoPage(page, size, storeId, type, category, name, createUserId);
+        return R.data(drinkInfoPage);
+    }
+
+    @ApiOperation("根据ID查询酒水餐食详情")
+    @ApiOperationSupport(order = 2)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "Integer", paramType = "path", required = true)
+    })
+    @GetMapping("/{id}")
+    public R<DrinkInfoVo> getDrinkInfoById(@PathVariable("id") Integer id) {
+        log.info("DrinkInfoController.getDrinkInfoById?id={}", id);
+        DrinkInfoVo drinkInfoVo = drinkInfoService.getDrinkInfoById(id);
+        if (drinkInfoVo == null) {
+            return R.fail("未找到该酒水餐食信息");
+        }
+        return R.data(drinkInfoVo);
+    }
+
+    @ApiOperation("新增酒水餐食")
+    @ApiOperationSupport(order = 3)
+    @PostMapping
+    public R<String> saveDrinkInfo(@RequestBody DrinkInfo drinkInfo) {
+        log.info("DrinkInfoController.saveDrinkInfo?drinkInfo={}", drinkInfo);
+        boolean result = drinkInfoService.saveDrinkInfo(drinkInfo);
+        if (result) {
+            return R.success("新增成功");
+        }
+        return R.fail("新增失败");
+    }
+
+    @ApiOperation("修改酒水餐食")
+    @ApiOperationSupport(order = 4)
+    @PutMapping
+    public R<String> updateDrinkInfo(@RequestBody DrinkInfo drinkInfo) {
+        log.info("DrinkInfoController.updateDrinkInfo?drinkInfo={}", drinkInfo);
+        boolean result = drinkInfoService.updateDrinkInfo(drinkInfo);
+        if (result) {
+            return R.success("修改成功");
+        }
+        return R.fail("修改失败");
+    }
+
+    @ApiOperation("删除酒水餐食")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "Integer", paramType = "path", required = true)
+    })
+    @DeleteMapping("/{id}")
+    public R<String> deleteDrinkInfo(@PathVariable("id") Integer id) {
+        log.info("DrinkInfoController.deleteDrinkInfo?id={}", id);
+        boolean result = drinkInfoService.deleteDrinkInfo(id);
+        if (result) {
+            return R.success("删除成功");
+        }
+        return R.fail("删除失败");
+    }
+
+    @ApiOperation("设置酒水餐食推荐")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "Integer", paramType = "path", required = true),
+            @ApiImplicitParam(name = "isRecommended", value = "是否推荐(0:否, 1:是)", dataType = "Integer", paramType = "query", required = true)
+    })
+    @PutMapping("/{id}/recommended")
+    public R<String> updateIsRecommended(
+            @PathVariable("id") Integer id,
+            @RequestParam("isRecommended") Integer isRecommended) {
+        log.info("DrinkInfoController.updateIsRecommended?id={}, isRecommended={}", id, isRecommended);
+        boolean result = drinkInfoService.updateIsRecommended(id, isRecommended);
+        if (result) {
+            return R.success("操作成功");
+        }
+        return R.fail("操作失败");
+    }
+
+    @ApiOperation("更新酒水餐食排序")
+    @ApiOperationSupport(order = 8)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "Integer", paramType = "path", required = true),
+            @ApiImplicitParam(name = "sort", value = "排序值", dataType = "Integer", paramType = "query", required = true)
+    })
+    @PutMapping("/{id}/sort")
+    public R<String> updateSort(
+            @PathVariable("id") Integer id,
+            @RequestParam("sort") Integer sort) {
+        log.info("DrinkInfoController.updateSort?id={}, sort={}", id, sort);
+        boolean result = drinkInfoService.updateSort(id, sort);
+        if (result) {
+            return R.success("操作成功");
+        }
+        return R.fail("操作失败");
+    }
+
+    @ApiOperation("根据门店ID查询所有酒水餐食")
+    @ApiOperationSupport(order = 9)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "type", value = "类型(枚举值:酒水/餐食)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/listByStoreId")
+    public R<List<DrinkInfoVo>> getDrinkInfoListByStoreId(
+            @RequestParam("storeId") Integer storeId,
+            @RequestParam(value = "type", required = false) String type) {
+        log.info("DrinkInfoController.getDrinkInfoListByStoreId?storeId={}, type={}", storeId, type);
+        List<DrinkInfoVo> drinkInfoList = drinkInfoService.getDrinkInfoListByStoreId(storeId, type);
+        return R.data(drinkInfoList);
+    }
+
+    @ApiOperation("查询推荐酒水餐食列表")
+    @ApiOperationSupport(order = 10)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "createUserId", value = "创建人ID(只查询自己创建的)", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/recommended")
+    public R<List<DrinkInfoVo>> getRecommendedList(
+            @RequestParam(value = "storeId", required = false) Integer storeId,
+            @RequestParam(value = "createUserId", required = false) Integer createUserId) {
+        log.info("DrinkInfoController.getRecommendedList?storeId={}, createUserId={}", storeId, createUserId);
+        List<DrinkInfoVo> drinkInfoList = drinkInfoService.getRecommendedList(storeId, createUserId);
+        return R.data(drinkInfoList);
+    }
+
+    @ApiOperation("批量更新酒水餐食排序")
+    @ApiOperationSupport(order = 11)
+    @PostMapping("/batchSort")
+    public R<String> batchUpdateSort(@RequestBody List<DrinkInfo> drinkInfoList) {
+        log.info("DrinkInfoController.batchUpdateSort?size={}", drinkInfoList.size());
+        boolean result = drinkInfoService.batchUpdateSort(drinkInfoList);
+        if (result) {
+            return R.success("批量更新排序成功");
+        }
+        return R.fail("批量更新排序失败");
+    }
+
+}

+ 141 - 0
alien-store/src/main/java/shop/alien/store/service/DrinkInfoService.java

@@ -0,0 +1,141 @@
+package shop.alien.store.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.store.DrinkInfo;
+import shop.alien.entity.store.vo.DrinkInfoVo;
+
+import java.util.List;
+
+/**
+ * 酒水餐食信息表 服务类
+ *
+ * @author ssk
+ * @since 2025-06-13
+ */
+public interface DrinkInfoService extends IService<DrinkInfo> {
+
+    /**
+     * 分页查询酒水餐食列表
+     *
+     * @param page         页码
+     * @param size         页容
+     * @param storeId      门店ID
+     * @param type         类型(枚举值:酒水/餐食)
+     * @param category     分类
+     * @param name         名称关键词
+     * @param createUserId 创建人ID(只查询指定用户创建的)
+     * @return IPage<DrinkInfoVo>
+     */
+    IPage<DrinkInfoVo> getDrinkInfoPage(int page, int size, Integer storeId, String type, String category, String name, Integer createUserId);
+
+    /**
+     * 根据ID查询酒水餐食详情
+     *
+     * @param id 主键ID
+     * @return DrinkInfoVo
+     */
+    DrinkInfoVo getDrinkInfoById(Integer id);
+
+    /**
+     * 新增酒水餐食
+     *
+     * @param drinkInfo 酒水餐食信息
+     * @return boolean
+     */
+    boolean saveDrinkInfo(DrinkInfo drinkInfo);
+
+    /**
+     * 修改酒水餐食
+     *
+     * @param drinkInfo 酒水餐食信息
+     * @return boolean
+     */
+    boolean updateDrinkInfo(DrinkInfo drinkInfo);
+
+    /**
+     * 删除酒水餐食(软删除)
+     *
+     * @param id 主键ID
+     * @return boolean
+     */
+    boolean deleteDrinkInfo(Integer id);
+
+    /**
+     * 上下架酒水餐食
+     *
+     * @param id     主键ID
+     * @param status 状态(0:下架, 1:上架)
+     * @return boolean
+     */
+    boolean updateStatus(Integer id, Integer status);
+
+    /**
+     * 设置酒水餐食推荐
+     *
+     * @param id            主键ID
+     * @param isRecommended 是否推荐(0:否, 1:是)
+     * @return boolean
+     */
+    boolean updateIsRecommended(Integer id, Integer isRecommended);
+
+    /**
+     * 更新酒水餐食排序
+     *
+     * @param id   主键ID
+     * @param sort 排序值
+     * @return boolean
+     */
+    boolean updateSort(Integer id, Integer sort);
+
+    /**
+     * 根据门店ID查询所有酒水餐食
+     *
+     * @param storeId 门店ID
+     * @param type    类型(枚举值:酒水/餐食)
+     * @return List<DrinkInfoVo>
+     */
+    List<DrinkInfoVo> getDrinkInfoListByStoreId(Integer storeId, String type);
+
+    /**
+     * 查询推荐酒水餐食列表
+     *
+     * @param storeId      门店ID
+     * @param createUserId 创建人ID(只查询指定用户创建的)
+     * @return List<DrinkInfoVo>
+     */
+    List<DrinkInfoVo> getRecommendedList(Integer storeId, Integer createUserId);
+
+    /**
+     * 批量更新酒水餐食排序
+     *
+     * @param drinkInfoList 酒水餐食列表(包含id和sort字段)
+     * @return boolean
+     */
+    boolean batchUpdateSort(List<DrinkInfo> drinkInfoList);
+
+    /**
+     * 获取酒水餐食类型统计
+     *
+     * @param storeId 门店ID
+     * @return Map<String, Long> 类型统计信息
+     */
+    java.util.Map<String, Long> getStatistics(Integer storeId);
+
+    /**
+     * 批量删除酒水餐食
+     *
+     * @param ids 主键ID列表
+     * @return boolean
+     */
+    boolean batchDelete(List<Integer> ids);
+
+    /**
+     * 批量上下架
+     *
+     * @param ids    主键ID列表
+     * @param status 状态(0:下架, 1:上架)
+     * @return boolean
+     */
+    boolean batchUpdateStatus(List<Integer> ids, Integer status);
+}

+ 79 - 57
alien-store/src/main/java/shop/alien/store/service/StoreInfoService.java

@@ -138,6 +138,13 @@ public interface StoreInfoService extends IService<StoreInfo> {
     StoreInfoVo editStoreInfo(StoreInfoDto storeInfoDto);
 
     /**
+     * 新中台web端修改门店及门店用户
+     *
+     * @return ResponseEntity
+     */
+    StoreInfoVo editNewStoreInfo(StoreInfoDto storeInfoDto);
+
+    /**
      * web端删除门店及门店用户
      *
      * @return ResponseEntity
@@ -177,6 +184,11 @@ public interface StoreInfoService extends IService<StoreInfo> {
     StoreInfoVo getStoreDetail(String storeId, String userId, String jingdu, String weidu);
 
     /**
+     * 中台web端查询门店明细
+     */
+    StoreInfoVo getNewStoreDetail(String storeId);
+
+    /**
      * web端审批结果
      */
     void approveStoreInfo(String storeId, Integer approvalStatus, String reason);
@@ -291,6 +303,52 @@ public interface StoreInfoService extends IService<StoreInfo> {
     int foodLicenceType(int id);
 
     /**
+     * 店铺娱乐经营许可证
+     * @param storeImg
+     * @return
+     */
+    int uploadEntertainmentLicence(StoreImg storeImg);
+
+    /**
+     * 获取店铺娱乐经营许可证状态以及店铺状态为审核中合同图片list
+     * @param id
+     * @return
+     */
+    Map<String,Object> getStoreEntertainmentLicenceStatus(int id);
+
+    /**
+     * 娱乐经营许可证审核通过后 图片类型变为审核通过后 img_type26
+     *
+     */
+    int entertainmentLicenceType(int id);
+
+    /**
+     * 获取门店代金券
+     */
+    List<LifeCouponVo> getStoreCouponList(String storeId);
+
+
+    /**
+     * 获取活动banner图
+     */
+    List<StoreImg> getBannerUrl (String storeId);
+    /**
+     * 获取活动banner图
+     */
+
+    List<StoreImg> getBannerUrlInfo(String storeId, Integer businessId);
+
+
+    /**
+     * web-分页查询店铺信息
+     *
+
+     * @return IPage<StoreInfoVo>
+     */
+    List<StoreInfoVo> getMoreRecommendedStores(Double lon , Double lat, String businessSection, String businessTypes, String businessClassify);
+
+
+    /**
      * 根据门店及图片地址查询最新OCR识别数据
      *
      * @param imageUrl 图片URL
@@ -298,9 +356,6 @@ public interface StoreInfoService extends IService<StoreInfo> {
      */
     Map<String, Object> getStoreOcrData(String imageUrl, String merchantName);
 
-    void aiApproveStoreInfo(AiApproveStoreInfo aiApproveStoreInfo);
-
-
     /**
      * 查询四种类型店铺(酒吧、ktv、洗浴汗蒸、按摩足浴)并按距离筛选
      *
@@ -316,7 +371,10 @@ public interface StoreInfoService extends IService<StoreInfo> {
      */
     IPage<StoreInfoVo> getSpecialTypeStoresByDistance(Double lon, Double lat, Double distance, Integer sortType, Integer businessType, Integer categoryId, int pageNum, int pageSize);
 
-
+    /**
+     * web端查询门店明细
+     */
+    StoreInfoVo getClientStoreDetail(String storeId, String userId, String jingdu, String weidu);
 
     /**
      * 获取休闲娱乐分类数据(主分类和子分类)
@@ -337,6 +395,14 @@ public interface StoreInfoService extends IService<StoreInfo> {
      */
     List<StoreDictionary> getAllBusinessSection(String businessSection);
 
+    /**
+     * 获取门店下三级分类结构
+     * 根据门店ID查询该门店的经营板块、经营种类和分类的三级结构
+     *
+     * @param storeId 门店ID
+     * @return StoreThreeLevelStructureVo 包含门店信息和三级分类树形结构
+     */
+    StoreThreeLevelStructureVo getStoreThreeLevelStructure(Integer storeId);
 
     /**
      * 你可能还喜欢(推荐店铺)
@@ -352,9 +418,16 @@ public interface StoreInfoService extends IService<StoreInfo> {
      */
     List<StoreInfoVo> getRecommendedStores(String businessSection, String businessTypes, String businessClassify, Double lon, Double lat);
 
+    /**
+     * 获取三级分类数据
+     * 根据二级分类ID获取该分类下的三级分类
+     *
+     * @param parentId 二级分类ID
+     * @return List<StoreDictionaryVo> 三级分类列表
+     */
+    List<StoreDictionaryVo> getBusinessClassifyData(Integer parentId);
 
-
-
+    void aiApproveStoreInfo(AiApproveStoreInfo aiApproveStoreInfo);
 
 
     /**
@@ -370,55 +443,4 @@ public interface StoreInfoService extends IService<StoreInfo> {
      * @return R<IPage<StoreInfoVo>> 分页的门店信息列表
      */
     IPage<StoreInfoVo> getLifeServicesByDistance(Double lon, Double lat, Double distance, Integer sortType, Integer businessType, Integer categoryId, int pageNum, int pageSize);
-
-    /**
-     * 获取活动banner图
-     */
-    List<StoreImg> getBannerUrl (String storeId);
-    /**
-     * 获取活动详情banner图
-     */
-
-    List<StoreImg> getBannerUrlInfo(String storeId, Integer businessId);
-
-    /**
-     * web-分页查询店铺信息
-     *
-
-     * @return IPage<StoreInfoVo>
-     */
-    List<StoreInfoVo> getMoreRecommendedStores(Double lon , Double lat, String businessSection, String businessTypes, String businessClassify);
-
-
-    /**
-     * web端查询门店明细
-     */
-    StoreInfoVo getClientStoreDetail(String storeId, String userId, String jingdu, String weidu);
-
-    /**
-     * 获取门店代金券
-     */
-    List<LifeCouponVo> getStoreCouponList(String storeId);
-
-    /**
-     * 中台web端查询门店明细
-     */
-    StoreInfoVo getNewStoreDetail(String storeId);
-
-    /**
-     * 新中台web端修改门店及门店用户
-     *
-     * @return ResponseEntity
-     */
-    StoreInfoVo editNewStoreInfo(StoreInfoDto storeInfoDto);
-
-
-    /**
-     * 根据business_classify字段获取字典表数据实现UI图中的功能
-     *
-     * @param parentId 父分类ID(可选),如果提供则只返回该父分类下的子分类;如果为null则返回所有子分类
-     * @return List<StoreDictionaryVo> 分类列表
-     */
-    List<StoreDictionaryVo> getBusinessClassifyData(Integer parentId);
-
 }

+ 260 - 0
alien-store/src/main/java/shop/alien/store/service/impl/DrinkInfoServiceImpl.java

@@ -0,0 +1,260 @@
+package shop.alien.store.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import shop.alien.entity.store.DrinkInfo;
+import shop.alien.entity.store.vo.DrinkInfoVo;
+import shop.alien.mapper.DrinkInfoMapper;
+import shop.alien.store.service.DrinkInfoService;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 酒水餐食信息表 服务实现类
+ *
+ * @author ssk
+ * @since 2025-06-13
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+@Transactional
+public class DrinkInfoServiceImpl extends ServiceImpl<DrinkInfoMapper, DrinkInfo> implements DrinkInfoService {
+
+    private final DrinkInfoMapper drinkInfoMapper;
+
+    @Override
+    public IPage<DrinkInfoVo> getDrinkInfoPage(int page, int size, Integer storeId, String type, String category, String name, Integer createUserId) {
+        LambdaQueryWrapper<DrinkInfo> queryWrapper = new LambdaQueryWrapper<>();
+        // 软删除条件
+        queryWrapper.eq(DrinkInfo::getIsDeleted, 0);
+        // 筛选条件
+        if (createUserId != null) {
+            queryWrapper.eq(DrinkInfo::getCreateUserId, createUserId);
+        }
+        if (storeId != null) {
+            queryWrapper.eq(DrinkInfo::getStoreId, storeId);
+        }
+        if (type != null && !type.isEmpty()) {
+            queryWrapper.eq(DrinkInfo::getType, type);
+        }
+        if (category != null && !category.isEmpty()) {
+            queryWrapper.eq(DrinkInfo::getCategory, category);
+        }
+        if (name != null && !name.isEmpty()) {
+            queryWrapper.like(DrinkInfo::getName, name);
+        }
+        // 按排序和创建时间倒序
+        queryWrapper.orderByAsc(DrinkInfo::getSort);
+        queryWrapper.orderByDesc(DrinkInfo::getCreateTime);
+
+        IPage<DrinkInfo> drinkInfoPage = drinkInfoMapper.selectPage(new Page<>(page, size), queryWrapper);
+        // 转换为Vo
+        IPage<DrinkInfoVo> drinkInfoVoPage = new Page<>(page, size);
+        drinkInfoVoPage.setTotal(drinkInfoPage.getTotal());
+        List<DrinkInfoVo> drinkInfoVoList = drinkInfoPage.getRecords().stream()
+                .map(this::convertToVo)
+                .collect(Collectors.toList());
+        drinkInfoVoPage.setRecords(drinkInfoVoList);
+
+        return drinkInfoVoPage;
+    }
+
+    @Override
+    public DrinkInfoVo getDrinkInfoById(Integer id) {
+        DrinkInfo drinkInfo = drinkInfoMapper.selectById(id);
+        if (drinkInfo == null || drinkInfo.getIsDeleted() == 1) {
+            return null;
+        }
+        return convertToVo(drinkInfo);
+    }
+
+    @Override
+    public boolean saveDrinkInfo(DrinkInfo drinkInfo) {
+        // 设置默认值
+        drinkInfo.setIsDeleted(0);
+        drinkInfo.setStatus(1); // 默认上架
+        drinkInfo.setSort(0); // 默认排序
+        return this.save(drinkInfo);
+    }
+
+    @Override
+    public boolean updateDrinkInfo(DrinkInfo drinkInfo) {
+        return this.updateById(drinkInfo);
+    }
+
+    @Override
+    public boolean deleteDrinkInfo(Integer id) {
+        LambdaUpdateWrapper<DrinkInfo> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.eq(DrinkInfo::getId, id)
+                .set(DrinkInfo::getIsDeleted, 1);
+        return this.update(updateWrapper);
+    }
+
+    @Override
+    public boolean updateStatus(Integer id, Integer status) {
+        LambdaUpdateWrapper<DrinkInfo> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.eq(DrinkInfo::getId, id)
+                .eq(DrinkInfo::getIsDeleted, 0)
+                .set(DrinkInfo::getStatus, status);
+        return this.update(updateWrapper);
+    }
+
+    @Override
+    public boolean updateIsRecommended(Integer id, Integer isRecommended) {
+        LambdaUpdateWrapper<DrinkInfo> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.eq(DrinkInfo::getId, id)
+                .eq(DrinkInfo::getIsDeleted, 0)
+                .set(DrinkInfo::getIsRecommended, isRecommended);
+        return this.update(updateWrapper);
+    }
+
+    @Override
+    public boolean updateSort(Integer id, Integer sort) {
+        LambdaUpdateWrapper<DrinkInfo> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.eq(DrinkInfo::getId, id)
+                .eq(DrinkInfo::getIsDeleted, 0)
+                .set(DrinkInfo::getSort, sort);
+        return this.update(updateWrapper);
+    }
+
+    @Override
+    public List<DrinkInfoVo> getDrinkInfoListByStoreId(Integer storeId, String type) {
+        LambdaQueryWrapper<DrinkInfo> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(DrinkInfo::getStoreId, storeId)
+                .eq(DrinkInfo::getIsDeleted, 0)
+                .eq(DrinkInfo::getStatus, 1); // 只查询上架的
+        if (type != null && !type.isEmpty()) {
+            queryWrapper.eq(DrinkInfo::getType, type);
+        }
+        queryWrapper.orderByAsc(DrinkInfo::getSort);
+        queryWrapper.orderByDesc(DrinkInfo::getCreateTime);
+
+        List<DrinkInfo> drinkInfoList = drinkInfoMapper.selectList(queryWrapper);
+        return drinkInfoList.stream()
+                .map(this::convertToVo)
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public List<DrinkInfoVo> getRecommendedList(Integer storeId, Integer createUserId) {
+        LambdaQueryWrapper<DrinkInfo> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(DrinkInfo::getIsRecommended, 1)
+                .eq(DrinkInfo::getIsDeleted, 0)
+                .eq(DrinkInfo::getStatus, 1); // 只查询上架的推荐商品
+        
+        // 只查询指定用户创建的商品
+        if (createUserId != null) {
+            queryWrapper.eq(DrinkInfo::getCreateUserId, createUserId);
+        }
+        
+        if (storeId != null) {
+            // 查询指定门店的商品,或者store_id为null的公共商品
+            queryWrapper.and(wrapper -> wrapper.eq(DrinkInfo::getStoreId, storeId));
+        }
+        queryWrapper.orderByAsc(DrinkInfo::getSort);
+        queryWrapper.orderByDesc(DrinkInfo::getCreateTime);
+
+        List<DrinkInfo> drinkInfoList = drinkInfoMapper.selectList(queryWrapper);
+        return drinkInfoList.stream()
+                .map(this::convertToVo)
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public boolean batchUpdateSort(List<DrinkInfo> drinkInfoList) {
+        if (drinkInfoList == null || drinkInfoList.isEmpty()) {
+            return false;
+        }
+        try {
+            for (DrinkInfo drinkInfo : drinkInfoList) {
+                if (drinkInfo.getId() != null && drinkInfo.getSort() != null) {
+                    LambdaUpdateWrapper<DrinkInfo> updateWrapper = new LambdaUpdateWrapper<>();
+                    updateWrapper.eq(DrinkInfo::getId, drinkInfo.getId())
+                            .eq(DrinkInfo::getIsDeleted, 0)
+                            .set(DrinkInfo::getSort, drinkInfo.getSort());
+                    this.update(updateWrapper);
+                }
+            }
+            return true;
+        } catch (Exception e) {
+            log.error("批量更新排序失败", e);
+            return false;
+        }
+    }
+
+    @Override
+    public java.util.Map<String, Long> getStatistics(Integer storeId) {
+        LambdaQueryWrapper<DrinkInfo> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(DrinkInfo::getIsDeleted, 0)
+                .eq(DrinkInfo::getStatus, 1); // 只统计上架的
+        if (storeId != null) {
+            queryWrapper.eq(DrinkInfo::getStoreId, storeId);
+        }
+
+        List<DrinkInfo> allList = drinkInfoMapper.selectList(queryWrapper);
+        
+        java.util.Map<String, Long> statistics = new java.util.HashMap<>();
+        statistics.put("total", (long) allList.size());
+        statistics.put("drink", allList.stream().filter(d -> "酒水".equals(d.getType())).count());
+        statistics.put("food", allList.stream().filter(d -> "餐食".equals(d.getType())).count());
+        statistics.put("recommended", allList.stream().filter(d -> d.getIsRecommended() == 1).count());
+        
+        return statistics;
+    }
+
+    @Override
+    public boolean batchDelete(List<Integer> ids) {
+        if (ids == null || ids.isEmpty()) {
+            return false;
+        }
+        try {
+            for (Integer id : ids) {
+                deleteDrinkInfo(id);
+            }
+            return true;
+        } catch (Exception e) {
+            log.error("批量删除失败", e);
+            return false;
+        }
+    }
+
+    @Override
+    public boolean batchUpdateStatus(List<Integer> ids, Integer status) {
+        if (ids == null || ids.isEmpty()) {
+            return false;
+        }
+        try {
+            for (Integer id : ids) {
+                updateStatus(id, status);
+            }
+            return true;
+        } catch (Exception e) {
+            log.error("批量更新状态失败", e);
+            return false;
+        }
+    }
+
+    /**
+     * 将DrinkInfo转换为DrinkInfoVo
+     *
+     * @param drinkInfo DrinkInfo对象
+     * @return DrinkInfoVo对象
+     */
+    private DrinkInfoVo convertToVo(DrinkInfo drinkInfo) {
+        DrinkInfoVo drinkInfoVo = new DrinkInfoVo();
+        BeanUtils.copyProperties(drinkInfo, drinkInfoVo);
+        // 确保图片字段正确映射
+        drinkInfoVo.setPicUrl(drinkInfo.getPicUrl());
+        return drinkInfoVo;
+    }
+}

+ 448 - 9
alien-store/src/main/java/shop/alien/store/service/impl/StoreInfoServiceImpl.java

@@ -381,6 +381,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
         } else if (CommonConstant.FOOD_LICENCE_NOT_EXPIRED_STATUS.equals(foodLicenceWhetherExpiredStatus)) {//经营许可证筛选状态 2 查询食品经营许可证未到期 距离到期时间大于30天以上
             queryWrapper.gt("a.food_licence_expiration_time", nowDay.plusDays(31));
         }
+        //查出所有店铺
         IPage<StoreInfoVo> storeInfoVoPage = storeInfoMapper.getStoreInfoVoPage(iPage, queryWrapper);
         List<StoreInfoVo> records = storeInfoVoPage.getRecords();
         if (!records.isEmpty()) {
@@ -860,6 +861,77 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
         if (null == storeInfo.getCommissionRate()) {
             storeInfo.setCommissionRate("3");
         }
+
+        // 处理食品经营许可证OCR数据
+        if (StringUtils.isNotEmpty(storeInfoDto.getFoodLicenceUrl())) {
+            // 查询食品经营许可证OCR识别记录
+            OcrImageUpload foodLicenceOcr = ocrImageUploadMapper.selectOne(
+                    new LambdaQueryWrapper<OcrImageUpload>()
+                            .eq(OcrImageUpload::getImageUrl, storeInfoDto.getFoodLicenceUrl())
+                            .eq(OcrImageUpload::getOcrType, OcrTypeEnum.FOOD_MANAGE_LICENSE.getCode())
+                            .orderByDesc(OcrImageUpload::getCreateTime)
+                            .last("limit 1")
+            );
+            if (foodLicenceOcr != null && StringUtils.isNotEmpty(foodLicenceOcr.getOcrResult())) {
+                try {
+                    com.alibaba.fastjson2.JSONObject ocrResult = com.alibaba.fastjson2.JSONObject.parseObject(foodLicenceOcr.getOcrResult());
+                    // 食品经营许可证OCR字段:validToDate(优先)、standardizedValidToDate
+                    Date expirationTime = parseFoodLicenceExpirationDate(ocrResult);
+                    if (expirationTime != null) {
+                        storeInfo.setFoodLicenceExpirationTime(expirationTime);
+                    }
+                    // 设置食品经营许可证状态为"待审核"(字典值2)
+                    storeInfo.setFoodLicenceStatus(2);
+                    storeInfo.setUpdateFoodLicenceTime(new Date());
+                    log.info("食品经营许可证OCR数据解析成功,到期时间:{}", expirationTime);
+                } catch (Exception e) {
+                    log.error("解析食品经营许可证OCR数据失败", e);
+                    // 解析失败时仍设置状态为待审核
+                    storeInfo.setFoodLicenceStatus(2);
+                    storeInfo.setUpdateFoodLicenceTime(new Date());
+                }
+            } else {
+                // 没有OCR记录时,设置状态为待审核
+                storeInfo.setFoodLicenceStatus(2);
+                storeInfo.setUpdateFoodLicenceTime(new Date());
+            }
+        }
+
+        // 处理娱乐经营许可证OCR数据(复用营业执照OCR类型,数据库中ocr_type存的是BUSINESS_LICENSE)
+        if (StringUtils.isNotEmpty(storeInfoDto.getEntertainmentLicenceUrl())) {
+            // 查询娱乐经营许可证OCR识别记录
+            OcrImageUpload entertainmentLicenceOcr = ocrImageUploadMapper.selectOne(
+                    new LambdaQueryWrapper<OcrImageUpload>()
+                            .eq(OcrImageUpload::getImageUrl, storeInfoDto.getEntertainmentLicenceUrl())
+                            .eq(OcrImageUpload::getOcrType, OcrTypeEnum.BUSINESS_LICENSE.getCode())
+                            .orderByDesc(OcrImageUpload::getCreateTime)
+                            .last("limit 1")
+            );
+            if (entertainmentLicenceOcr != null && StringUtils.isNotEmpty(entertainmentLicenceOcr.getOcrResult())) {
+                try {
+                    com.alibaba.fastjson2.JSONObject ocrResult = com.alibaba.fastjson2.JSONObject.parseObject(entertainmentLicenceOcr.getOcrResult());
+                    // 娱乐经营许可证OCR字段(复用营业执照类型):validToDate(优先,格式"20220903")、validPeriod(格式"2020年09月04日至2022年09月03日")
+                    Date expirationTime = parseEntertainmentLicenceExpirationDate(ocrResult);
+                    if (expirationTime != null) {
+                        storeInfo.setEntertainmentLicenceExpirationTime(expirationTime);
+                    }
+                    // 设置娱乐经营许可证状态为"待审核"(字典值2)
+                    storeInfo.setEntertainmentLicenceStatus(2);
+                    storeInfo.setUpdateEntertainmentLicenceTime(new Date());
+                    log.info("娱乐经营许可证OCR数据解析成功,到期时间:{}", expirationTime);
+                } catch (Exception e) {
+                    log.error("解析娱乐经营许可证OCR数据失败", e);
+                    // 解析失败时仍设置状态为待审核
+                    storeInfo.setEntertainmentLicenceStatus(2);
+                    storeInfo.setUpdateEntertainmentLicenceTime(new Date());
+                }
+            } else {
+                // 没有OCR记录时,设置状态为待审核
+                storeInfo.setEntertainmentLicenceStatus(2);
+                storeInfo.setUpdateEntertainmentLicenceTime(new Date());
+            }
+        }
+
         storeInfoMapper.insert(storeInfo);
         result.setId(storeInfo.getId());
         if (StringUtils.isNotEmpty(storeInfoDto.getStorePositionLongitude()) && StringUtils.isNotEmpty(storeInfoDto.getStorePositionLatitude())) {
@@ -1061,7 +1133,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
         storeInfo.setBusinessSectionName(businessSectionName.getDictDetail());
         storeInfo.setBusinessTypes(String.join(",", businessTypes));
         storeInfo.setBusinessTypesName(String.join(",", businessTypeNames));
-
+        
         //处理分类信息
         List<String> businessClassify = storeInfoDto.getBusinessClassify();
         if (!CollectionUtils.isEmpty(businessClassify)) {
@@ -1092,7 +1164,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
             storeInfo.setBusinessClassify(String.join(",", businessClassify));
             storeInfo.setBusinessClassifyName(String.join(",", businessClassifyNames));
         }
-
+        
         //处理一下行政区域信息
         EssentialCityCode essentialCityCode1 = essentialCityCodeMapper.selectOne(new LambdaQueryWrapper<EssentialCityCode>().eq(EssentialCityCode::getAreaCode, storeInfo.getAdministrativeRegionProvinceAdcode()));
         storeInfo.setAdministrativeRegionProvinceName(essentialCityCode1.getAreaName());
@@ -1931,6 +2003,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
         queryWrapper.eq("a.delete_flag", 0);
         queryWrapper.eq("a.logout_flag", 0);
         queryWrapper.ne("a.store_status", 0);
+        queryWrapper.eq("a.business_section", 1);
 
         queryWrapper.ge("a.food_licence_expiration_time", new Date());
 
@@ -2130,12 +2203,18 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
     }
 
     /**
-     * 根据过期时间修改状态
+     * 检查并更新过期记录
+     * 将过期时间已到且业务状态为0的店铺状态更新为99(过期状态)
+     *
+     * @return 更新的记录数
      */
-    public void checkAndUpdateExpiredRecords() {
-        LambdaUpdateWrapper<StoreInfo> queryWrapper = new LambdaUpdateWrapper<>();
-        queryWrapper.isNotNull(StoreInfo::getExpirationTime).lt(StoreInfo::getExpirationTime, LocalDateTime.now()).eq(StoreInfo::getBusinessStatus, 0).set(StoreInfo::getBusinessStatus, 99);
-        this.update(queryWrapper);
+    public int checkAndUpdateExpiredRecords() {
+        LambdaUpdateWrapper<StoreInfo> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.isNotNull(StoreInfo::getExpirationTime)
+                .lt(StoreInfo::getExpirationTime, LocalDateTime.now())
+                .eq(StoreInfo::getBusinessStatus, 0)
+                .set(StoreInfo::getStoreStatus, 0);
+        return storeInfoMapper.update(null, updateWrapper);
     }
 
     @Override
@@ -2538,6 +2617,31 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
         storeInfo.setFoodLicenceStatus(2);
         storeInfo.setId(storeImg.getStoreId());
         storeInfo.setUpdateFoodLicenceTime(new Date());
+
+        // 从OCR识别记录中获取食品经营许可证到期时间
+        if (StringUtils.isNotEmpty(storeImg.getImgUrl())) {
+            OcrImageUpload foodLicenceOcr = ocrImageUploadMapper.selectOne(
+                    new LambdaQueryWrapper<OcrImageUpload>()
+                            .eq(OcrImageUpload::getImageUrl, storeImg.getImgUrl())
+                            .eq(OcrImageUpload::getOcrType, OcrTypeEnum.FOOD_MANAGE_LICENSE.getCode())
+                            .orderByDesc(OcrImageUpload::getCreateTime)
+                            .last("limit 1")
+            );
+            if (foodLicenceOcr != null && StringUtils.isNotEmpty(foodLicenceOcr.getOcrResult())) {
+                try {
+                    com.alibaba.fastjson2.JSONObject ocrResult = com.alibaba.fastjson2.JSONObject.parseObject(foodLicenceOcr.getOcrResult());
+                    // 解析食品经营许可证到期时间(优先validToDate,其次standardizedValidToDate)
+                    Date expirationTime = parseFoodLicenceExpirationDate(ocrResult);
+                    if (expirationTime != null) {
+                        storeInfo.setFoodLicenceExpirationTime(expirationTime);
+                        log.info("食品经营许可证OCR数据解析成功,门店ID:{},到期时间:{}", storeImg.getStoreId(), expirationTime);
+                    }
+                } catch (Exception e) {
+                    log.error("解析食品经营许可证OCR数据失败,门店ID:{}", storeImg.getStoreId(), e);
+                }
+            }
+        }
+
         return storeInfoMapper.updateById(storeInfo);
     }
 
@@ -4299,7 +4403,228 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
         // SQL已经实现了距离过滤和排序,直接返回结果
         return storeInfoVoList;
     }
+    @Override
+    public int uploadEntertainmentLicence(StoreImg storeImg) {
+        storeImgMapper.delete(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getImgType, 24).eq(StoreImg::getStoreId, storeImg.getStoreId()));
+        storeImg.setImgType(24);
+        storeImg.setImgDescription("娱乐经营许可证审核通过前图片");
+        storeImgMapper.insert(storeImg);
+
+        //更新店铺
+        StoreInfo storeInfo = new StoreInfo();
+        storeInfo.setEntertainmentLicenceStatus(2);
+        storeInfo.setId(storeImg.getStoreId());
+        storeInfo.setUpdateEntertainmentLicenceTime(new Date());
+
+        // 从OCR识别记录中获取娱乐经营许可证到期时间(复用营业执照OCR类型,数据库中ocr_type存的是BUSINESS_LICENSE)
+        if (StringUtils.isNotEmpty(storeImg.getImgUrl())) {
+            OcrImageUpload entertainmentLicenceOcr = ocrImageUploadMapper.selectOne(
+                    new LambdaQueryWrapper<OcrImageUpload>()
+                            .eq(OcrImageUpload::getImageUrl, storeImg.getImgUrl())
+                            .eq(OcrImageUpload::getOcrType, OcrTypeEnum.BUSINESS_LICENSE.getCode())
+                            .orderByDesc(OcrImageUpload::getCreateTime)
+                            .last("limit 1")
+            );
+            if (entertainmentLicenceOcr != null && StringUtils.isNotEmpty(entertainmentLicenceOcr.getOcrResult())) {
+                try {
+                    com.alibaba.fastjson2.JSONObject ocrResult = com.alibaba.fastjson2.JSONObject.parseObject(entertainmentLicenceOcr.getOcrResult());
+                    // 解析娱乐经营许可证到期时间(优先validToDate,其次validPeriod)
+                    Date expirationTime = parseEntertainmentLicenceExpirationDate(ocrResult);
+                    if (expirationTime != null) {
+                        storeInfo.setEntertainmentLicenceExpirationTime(expirationTime);
+                        log.info("娱乐经营许可证OCR数据解析成功,门店ID:{},到期时间:{}", storeImg.getStoreId(), expirationTime);
+                    }
+                } catch (Exception e) {
+                    log.error("解析娱乐经营许可证OCR数据失败,门店ID:{}", storeImg.getStoreId(), e);
+                }
+            }
+        }
+
+        return storeInfoMapper.updateById(storeInfo);
+    }
+
+    @Override
+    public Map<String, Object> getStoreEntertainmentLicenceStatus(int id) {
+        Map<String, Object> map = new HashMap<>();
+        StoreInfo storeInfo = storeInfoMapper.selectOne(new LambdaQueryWrapper<StoreInfo>().eq(StoreInfo::getId, id));
+        //审核通过给前台反显未提交
+        if (storeInfo.getEntertainmentLicenceStatus() == 1) {
+            map.put("entertainmentLicenceStatus", 0);
+        } else {
+            map.put("entertainmentLicenceStatus", storeInfo.getEntertainmentLicenceStatus());
+        }
+        //娱乐经营许可证照片列表
+        List<StoreImg> storeImgList = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, id).eq(StoreImg::getImgType, 24));
+        if (!CollectionUtils.isEmpty(storeImgList)) {
+            map.put("entertainmentLicenceImgList", storeImgList);
+        } else {
+            map.put("entertainmentLicenceImgList", "");
+        }
+        if (storeInfo.getEntertainmentLicenceReason() != null) {
+            map.put("entertainmentLicenceReason", storeInfo.getEntertainmentLicenceReason());
+        } else {
+            map.put("entertainmentLicenceReason", "");
+        }
+        return map;
+    }
+
+    @Override
+    public int entertainmentLicenceType(int id) {
+        //删除原娱乐经营许可证照片
+        LambdaUpdateWrapper<StoreImg> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
+        lambdaUpdateWrapper.eq(StoreImg::getStoreId, id);
+        lambdaUpdateWrapper.eq(StoreImg::getImgType, 26);
+        lambdaUpdateWrapper.set(StoreImg::getDeleteFlag, 1);
+        storeImgMapper.update(null, lambdaUpdateWrapper);
+        //修改娱乐经营许可证类型为审核通过类型
+        List<StoreImg> storeImgList = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, id).eq(StoreImg::getImgType, 24));
+        List<Integer> imgList = storeImgList.stream().map(StoreImg::getId).collect(Collectors.toList());
+        LambdaUpdateWrapper<StoreImg> imgLambdaUpdateWrapper = new LambdaUpdateWrapper();
+        imgLambdaUpdateWrapper.in(StoreImg::getId, imgList).set(StoreImg::getImgType, 26).set(StoreImg::getImgDescription, "娱乐经营许可证审核通过图片");
+        int num = storeImgMapper.update(null, imgLambdaUpdateWrapper);
+        return num;
+    }
+
+    /**
+     * 解析食品经营许可证OCR结果中的到期时间
+     * 食品经营许可证OCR返回字段:validToDate、standardizedValidToDate
+     *
+     * @param ocrResult OCR识别结果JSON对象
+     * @return 到期日期,如果解析失败则返回null
+     */
+    private Date parseFoodLicenceExpirationDate(com.alibaba.fastjson2.JSONObject ocrResult) {
+        if (ocrResult == null) {
+            return null;
+        }
+
+        // 优先使用validToDate字段(格式可能为空或yyyyMMdd)
+        String validToDate = ocrResult.getString("validToDate");
+        if (StringUtils.isNotEmpty(validToDate)) {
+            Date date = parseDateString(validToDate);
+            if (date != null) {
+                return date;
+            }
+        }
+
+        // 其次使用standardizedValidToDate字段
+        String standardizedValidToDate = ocrResult.getString("standardizedValidToDate");
+        if (StringUtils.isNotEmpty(standardizedValidToDate)) {
+            Date date = parseDateString(standardizedValidToDate);
+            if (date != null) {
+                return date;
+            }
+        }
+
+        log.warn("食品经营许可证OCR结果中未找到有效的到期时间,validToDate={},standardizedValidToDate={}", validToDate, standardizedValidToDate);
+        return null;
+    }
 
+    /**
+     * 解析娱乐经营许可证OCR结果中的到期时间(复用营业执照OCR类型)
+     * 娱乐经营许可证OCR返回字段:validToDate(格式"20220903")、validPeriod(格式"2020年09月04日至2022年09月03日")
+     *
+     * @param ocrResult OCR识别结果JSON对象
+     * @return 到期日期,如果解析失败则返回null
+     */
+    private Date parseEntertainmentLicenceExpirationDate(com.alibaba.fastjson2.JSONObject ocrResult) {
+        if (ocrResult == null) {
+            return null;
+        }
+
+        // 优先使用validToDate字段(格式为yyyyMMdd,如"20220903")
+        String validToDate = ocrResult.getString("validToDate");
+        if (StringUtils.isNotEmpty(validToDate)) {
+            Date date = parseDateString(validToDate);
+            if (date != null) {
+                return date;
+            }
+        }
+
+        // 其次使用validPeriod字段(格式为"2020年09月04日至2022年09月03日")
+        String validPeriod = ocrResult.getString("validPeriod");
+        if (StringUtils.isNotEmpty(validPeriod)) {
+            Date date = parseValidPeriodEndDate(validPeriod);
+            if (date != null) {
+                return date;
+            }
+        }
+
+        log.warn("娱乐经营许可证OCR结果中未找到有效的到期时间,validToDate={},validPeriod={}", validToDate, validPeriod);
+        return null;
+    }
+
+    /**
+     * 解析日期字符串
+     * 支持的格式:yyyyMMdd、yyyy-MM-dd、yyyy/MM/dd、yyyy年MM月dd日
+     *
+     * @param dateStr 日期字符串
+     * @return 日期对象,如果解析失败则返回null
+     */
+    private Date parseDateString(String dateStr) {
+        if (StringUtils.isEmpty(dateStr)) {
+            return null;
+        }
+
+        // 处理"长期"或"永久"情况
+        if (dateStr.contains("长期") || dateStr.contains("永久")) {
+            return null;
+        }
+
+        String normalizedDateStr = dateStr.trim();
+
+        // 如果包含中文年月日,转换格式
+        if (normalizedDateStr.contains("年")) {
+            normalizedDateStr = normalizedDateStr.replaceAll("[年月]", "-").replaceAll("日", "").trim();
+        }
+
+        // 尝试多种日期格式解析
+        String[] dateFormats = {"yyyyMMdd", "yyyy-MM-dd", "yyyy/MM/dd"};
+        for (String format : dateFormats) {
+            try {
+                SimpleDateFormat sdf = new SimpleDateFormat(format);
+                sdf.setLenient(false);
+                return sdf.parse(normalizedDateStr);
+            } catch (Exception ignored) {
+                // 尝试下一个格式
+            }
+        }
+
+        log.warn("无法解析日期字符串: {}", dateStr);
+        return null;
+    }
+
+    /**
+     * 解析有效期字符串,获取结束日期
+     * 支持的格式:
+     * - "2020年09月04日至2022年09月03日"
+     * - "2020-01-01至2025-12-31"
+     * - "长期" 或 "永久" 返回null(表示长期有效)
+     *
+     * @param validPeriod 有效期字符串
+     * @return 结束日期,如果解析失败或为长期有效则返回null
+     */
+    private Date parseValidPeriodEndDate(String validPeriod) {
+        if (StringUtils.isEmpty(validPeriod)) {
+            return null;
+        }
+
+        // 处理"长期"或"永久"情况
+        if (validPeriod.contains("长期") || validPeriod.contains("永久")) {
+            return null;
+        }
+
+        String endDateStr = validPeriod;
+
+        // 如果包含"至",取后半部分作为结束日期
+        if (validPeriod.contains("至")) {
+            String[] parts = validPeriod.split("至");
+            if (parts.length >= 2) {
+                endDateStr = parts[1].trim();
+            }
+        }
+
+        return parseDateString(endDateStr);
+    }
 
     @Override
     public StoreInfoVo getClientStoreDetail(String storeId, String userId, String jingdu, String weidu) {
@@ -4827,13 +5152,127 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
             // 不设置subDataList,返回扁平化列表
             result.add(vo);
         }
+        return result;
+    }
+
+    @Override
+    public StoreThreeLevelStructureVo getStoreThreeLevelStructure(Integer storeId) {
+        // 1. 根据门店ID查询门店信息
+        StoreInfo storeInfo = storeInfoMapper.selectById(storeId);
+        if (storeInfo == null) {
+            log.warn("门店不存在,storeId={}", storeId);
+            StoreThreeLevelStructureVo vo = new StoreThreeLevelStructureVo();
+            vo.setStoreId(storeId);
+            vo.setStoreName(null);
+            vo.setThreeLevelStructure(new ArrayList<>());
+            return vo;
+        }
+
+        // 2. 获取门店表里的三级分类信息
+        Integer businessSection = storeInfo.getBusinessSection(); // 一级:经营板块ID
+        String businessTypes = storeInfo.getBusinessTypes(); // 二级:经营种类IDs(逗号分隔,fine_food类型)
+        String businessClassify = storeInfo.getBusinessClassify(); // 三级:分类IDs(逗号分隔)
+
+        // 3. 查询一级分类(business_section - 经营板块)
+        // business_section存储的是dictId(字符串),需要转换为String
+        List<StoreDictionary> allDicts = new ArrayList<>();
+        StoreDictionary firstLevelDict = null;
+        if (businessSection != null) {
+            firstLevelDict = storeDictionaryMapper.selectOne(
+                    new LambdaQueryWrapper<StoreDictionary>()
+                            .eq(StoreDictionary::getDictId, String.valueOf(businessSection))
+                            .eq(StoreDictionary::getTypeName, "business_section")
+                            .eq(StoreDictionary::getDeleteFlag, 0)
+            );
+            if (firstLevelDict != null) {
+                allDicts.add(firstLevelDict);
+                log.debug("查询到一级分类: id={}, dictId={}, dictDetail={}",
+                        firstLevelDict.getId(), firstLevelDict.getDictId(), firstLevelDict.getDictDetail());
+            } else {
+                log.warn("未查询到一级分类,businessSection={}", businessSection);
+            }
+        }
 
+        // 4. 查询二级分类(business_type - 根据门店表里的business_types字段,存储的是dictId)
+        List<StoreDictionary> secondLevelDicts = new ArrayList<>();
+        if (StringUtils.isNotEmpty(businessTypes) && firstLevelDict != null) {
+            // 解析二级分类dictId列表(business_types存储的是dictId字符串)
+            String[] typeDictIds = businessTypes.split(",");
+            List<String> typeDictIdList = new ArrayList<>();
+            for (String typeDictId : typeDictIds) {
+                if (StringUtils.isNotEmpty(typeDictId.trim())) {
+                    typeDictIdList.add(typeDictId.trim());
+                }
+            }
 
+            // 查询二级分类,需要满足:
+            // 1. dict_id 在门店配置的经营种类dictId列表中
+            // 2. type_name = "business_type"
+            // 3. parent_id = 一级分类的id(不是dictId)
+            if (!typeDictIdList.isEmpty() && firstLevelDict != null) {
+                LambdaQueryWrapper<StoreDictionary> secondLevelWrapper = new LambdaQueryWrapper<>();
+                secondLevelWrapper.in(StoreDictionary::getDictId, typeDictIdList);
+                secondLevelWrapper.eq(StoreDictionary::getTypeName, "business_type");
+                secondLevelWrapper.eq(StoreDictionary::getParentId, firstLevelDict.getId());
+                secondLevelWrapper.eq(StoreDictionary::getDeleteFlag, 0);
+//                secondLevelWrapper.orderByAsc(StoreDictionary::getSortId);
+                secondLevelDicts = storeDictionaryMapper.selectList(secondLevelWrapper);
+                allDicts.addAll(secondLevelDicts);
+                log.debug("查询到二级分类数量: {}, dictIds={}", secondLevelDicts.size(), typeDictIdList);
+            } else {
+                log.warn("无法查询二级分类: typeDictIdList为空或一级分类不存在, typeDictIdList={}, firstLevelDict={}",
+                        typeDictIdList, firstLevelDict != null ? firstLevelDict.getId() : null);
+            }
+        }
 
+        // 5. 查询三级分类(business_classify - 根据门店表里的business_classify字段,存储的是dictId)
+        if (!secondLevelDicts.isEmpty() && StringUtils.isNotEmpty(businessClassify)) {
+            // 解析三级分类dictId列表(business_classify存储的是dictId字符串)
+            String[] classifyDictIds = businessClassify.split(",");
+            List<String> classifyDictIdList = new ArrayList<>();
+            for (String classifyDictId : classifyDictIds) {
+                if (StringUtils.isNotEmpty(classifyDictId.trim())) {
+                    classifyDictIdList.add(classifyDictId.trim());
+                }
+            }
 
-        return result;
-    }
+            // 查询三级分类,需要满足:
+            // 1. dict_id 在门店配置的分类dictId列表中
+            // 2. parent_id 在二级分类的id列表中(不是dictId)
+            // 3. type_name = "business_classify"
+            if (!classifyDictIdList.isEmpty() && !secondLevelDicts.isEmpty()) {
+                Set<Integer> secondLevelIds = secondLevelDicts.stream()
+                        .map(StoreDictionary::getId)
+                        .collect(Collectors.toSet());
 
+                List<StoreDictionary> thirdLevelDicts = storeDictionaryMapper.selectList(
+                        new LambdaQueryWrapper<StoreDictionary>()
+                                .in(StoreDictionary::getDictId, classifyDictIdList)
+                                .in(StoreDictionary::getParentId, secondLevelIds)
+                                .eq(StoreDictionary::getTypeName, "business_classify")
+                                .eq(StoreDictionary::getDeleteFlag, 0)
+//                                .orderByAsc(StoreDictionary::getSortId)
+                );
+                allDicts.addAll(thirdLevelDicts);
+                log.debug("查询到三级分类数量: {}, dictIds={}, parentIds={}",
+                        thirdLevelDicts.size(), classifyDictIdList, secondLevelIds);
+            } else {
+                log.warn("无法查询三级分类: classifyDictIdList为空或二级分类为空, classifyDictIdList={}, secondLevelDicts.size={}",
+                        classifyDictIdList, secondLevelDicts.size());
+            }
+        }
+
+        // 6. 构建树形结构(根据parent_id关系组装)
+        List<StoreDictionary> treeStructure = buildTreeOptimized(allDicts);
 
+        // 7. 构建返回对象
+        StoreThreeLevelStructureVo vo = new StoreThreeLevelStructureVo();
+        vo.setStoreId(storeInfo.getId());
+        vo.setStoreName(storeInfo.getStoreName());
+        vo.setThreeLevelStructure(treeStructure);
+
+        return vo;
     }
 
+}
+

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

@@ -179,9 +179,9 @@ public class StoreStaffConfigServiceImpl implements StoreStaffConfigService {
             queryWrapper.eq("store_id", storeId);
         }
         // 如果状态不为空,则进行精确匹配查询
-        if (StringUtils.isNotEmpty(status)) {
-            queryWrapper.eq("status", status);
-        }
+//        if (StringUtils.isNotEmpty(status)) {
+//            queryWrapper.eq("status", status);
+//        }
         // 只查询未删除的记录
         queryWrapper.eq("delete_flag", 0);
         queryWrapper.orderByDesc("created_time");