qmj 5 дней назад
Родитель
Сommit
1648f2289d
1 измененных файлов с 772 добавлено и 771 удалено
  1. 772 771
      ruoyi-admin/src/main/java/com/ruoyi/app/mendian/PosFoodController.java

+ 772 - 771
ruoyi-admin/src/main/java/com/ruoyi/app/mendian/PosFoodController.java

@@ -1,771 +1,772 @@
-package com.ruoyi.app.mendian;
-
-import com.alibaba.fastjson.JSONArray;
-import com.alibaba.fastjson.JSONObject;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.ruoyi.app.mendian.dto.FoodSearchOutput;
-import com.ruoyi.app.order.OrderInvoiceService;
-import com.ruoyi.app.utils.ImageCompressUtils;
-import com.ruoyi.common.annotation.Anonymous;
-import com.ruoyi.common.annotation.Log;
-import com.ruoyi.common.core.controller.BaseController;
-import com.ruoyi.common.core.domain.AjaxResult;
-import com.ruoyi.common.core.page.TableDataInfo;
-import com.ruoyi.common.enums.BusinessType;
-import com.ruoyi.common.utils.MessageUtils;
-import com.ruoyi.common.utils.StringUtils;
-import com.ruoyi.common.utils.poi.ExcelUtil;
-import com.ruoyi.system.domain.*;
-import com.ruoyi.system.mapper.PosStoreMapper;
-import com.ruoyi.system.service.*;
-import com.ruoyi.system.utils.Auth;
-import com.ruoyi.system.utils.JwtUtil;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
-
-import jakarta.servlet.http.HttpServletResponse;
-
-import java.util.*;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.stream.Collectors;
-
-/**
- * foodController
- *
- * @author ruoyi
- * @date 2023-05-21
- */
-@RestController
-@RequestMapping("/chanting/food")
-public class PosFoodController extends BaseController {
-    @Autowired  //商品
-    private IPosFoodService posFoodService;
-
-    @Autowired  //门店
-    private IPosStoreService posStoreService;
-
-    @Autowired //商品分类
-    private IPosFenleiService posFenleiService;
-
-    @Autowired //用户
-    private IInfoUserService infoUserService;
-    @Autowired //店铺Mapper
-    private PosStoreMapper posStoreMapper;
-    @Autowired
-    private IPosReviewService posReviewService;
-    @Autowired
-    private IOperatingHoursService operatingHoursService;
-    @Autowired
-    private IPosOrderService posOrderService;
-
-    @Autowired
-    private PosStoreEnrichService posStoreEnrichService;
-
-    @Autowired  //规格组
-    private IFoodSpecsService foodSpecsService;
-    @Autowired  //规格值
-    private IFoodSpecsValueService foodSpecsValueService;
-    @Autowired  //商品-规格关联
-    private IFoodSpecRelationService foodSpecRelationService;
-
-    @Autowired //订单电子发票(门店是否能开票)
-    private OrderInvoiceService orderInvoiceService;
-
-    //删除商品
-    @Anonymous
-    @Auth
-    @GetMapping("/delefood")
-    public AjaxResult delefood(@RequestParam String id) {
-        return toAjax(posFoodService.deletePosFoodById(Long.valueOf(id)));
-    }
-
-    //推荐商品
-    @Anonymous
-    @PutMapping("/changerecommend")
-    public AjaxResult changerecommend(@RequestBody PosFood posFood) {
-        return toAjax(posFoodService.saveOrUpdate(posFood));
-    }
-
-    /**
-     * 添加或修改商品
-     */
-    @Anonymous
-    @PostMapping("/setposfood")
-    public AjaxResult setposfood(@RequestBody PosFood posFood) {
-        Boolean org = posFoodService.saveOrUpdate(posFood);
-        if (!org) {
-            return error();
-        }
-        handleFoodSpec(posFood);
-        return success();
-    }
-
-    /**
-     * (h5页面使用)通过分类查询商品列表
-     */
-    @Anonymous
-    @GetMapping("/h5Getidlist")
-    public AjaxResult h5Getidlist(@RequestParam Integer id, @RequestParam(defaultValue = "") String language, @RequestParam(required = false) String stackingUp) {
-        JwtUtil jwtUtil = new JwtUtil();
-//        String ids = jwtUtil.getusid(token);
-//        InfoUser user = infoUserService.getById(ids);
-        QueryWrapper<PosFood> queryWrapper = new QueryWrapper<>();
-        queryWrapper.eq("fl_id", id);
-//        if(!user.getUserType().equals("1")){
-//            queryWrapper.eq("to_examine","1");
-//        }
-        if (stackingUp != null && !"".equals(stackingUp)) {
-            queryWrapper.eq("stacking_up", stackingUp);
-        }
-        String lang = "0";
-        if (!"".equals(language)) {
-            lang = language;
-        }
-        queryWrapper.eq("language", lang);
-        List<PosFood> list = posFoodService.list(queryWrapper);
-        List<Long> __foodIds = list.stream().map(PosFood::getId).collect(Collectors.toList());
-        Map<Long, List<FoodSpecs>> __specMap = loadFoodSpecsMap(__foodIds);
-        JSONArray all = new JSONArray();
-        for (int i = 0; i < list.size(); i++) {
-            JSONObject org = new JSONObject();
-            org.put("id", list.get(i).getId());
-            org.put("fenlei", posFenleiService.getById(list.get(i).getFlId()));
-            org.put("store", posStoreService.getById(list.get(i).getMdid()));
-            org.put("name", list.get(i).getName());
-            org.put("image", list.get(i).getImage());
-            org.put("price", list.get(i).getPrice());
-            org.put("introduce", list.get(i).getIntroduce());
-            org.put("recommend", list.get(i).getRecommend());
-            List<FoodSpecs> __fs = __specMap.getOrDefault(list.get(i).getId(), new ArrayList<>());
-            org.put("foodSpecs", __fs);
-            org.put("foodSku", __fs.isEmpty() ? JSONArray.parseArray(list.get(i).getFoodSku()) : buildFoodSkuArray(__fs));
-            org.put("stackingUp", list.get(i).getStackingUp());
-            org.put("toExamine", list.get(i).getToExamine());
-            all.add(org);
-        }
-        return success(all);
-    }
-
-    /**
-     * 通过分类查询商品列表
-     */
-    @Anonymous
-    @GetMapping("/getidlist")
-    public AjaxResult getidlist(@RequestHeader String token, @RequestParam Integer id, @RequestParam(defaultValue = "") String language, @RequestParam(required = false) String stackingUp, @RequestParam(required = false) String name) {
-        JwtUtil jwtUtil = new JwtUtil();
-        String ids = jwtUtil.getusid(token);
-        InfoUser user = infoUserService.getById(ids);
-        QueryWrapper<PosFood> queryWrapper = new QueryWrapper<>();
-        queryWrapper.eq("fl_id", id);
-        //普通用户显示审核通过的
-        if("0".equals(user.getUserType())){
-            queryWrapper.eq("to_examine", "1");
-        }
-        if (stackingUp != null && !"".equals(stackingUp)) {
-            queryWrapper.eq("stacking_up", stackingUp);
-        }
-        String lang = "3";
-        if (!"".equals(language)) {
-            lang = language;
-        }
-        queryWrapper.eq("language", lang);
-        if (name != null && !"".equals(name)) {
-            queryWrapper.apply(" BINARY name LIKE CONCAT('%', {0}, '%')", name);
-        }
-        List<PosFood> list = posFoodService.list(queryWrapper);
-        List<Long> __foodIds = list.stream().map(PosFood::getId).collect(Collectors.toList());
-        Map<Long, List<FoodSpecs>> __specMap = loadFoodSpecsMap(__foodIds);
-        JSONArray all = new JSONArray();
-        for (int i = 0; i < list.size(); i++) {
-            JSONObject org = new JSONObject();
-            org.put("id", list.get(i).getId());
-            org.put("fenlei", posFenleiService.getById(list.get(i).getFlId()));
-            PosStore store = posStoreService.getById(list.get(i).getMdid());
-            fillStallLocation(store);
-            org.put("store", store);
-            org.put("canInvoice", orderInvoiceService.canInvoice(list.get(i).getMdid()));
-            org.put("name", list.get(i).getName());
-            org.put("image", list.get(i).getImage());
-            org.put("price", list.get(i).getPrice());
-            org.put("introduce", list.get(i).getIntroduce());
-            org.put("recommend", list.get(i).getRecommend());
-            List<FoodSpecs> __fs = __specMap.getOrDefault(list.get(i).getId(), new ArrayList<>());
-            org.put("foodSpecs", __fs);
-            org.put("foodSku", __fs.isEmpty() ? JSONArray.parseArray(list.get(i).getFoodSku()) : buildFoodSkuArray(__fs));
-            org.put("stackingUp", list.get(i).getStackingUp());
-            org.put("toExamine", list.get(i).getToExamine());
-            all.add(org);
-        }
-        return success(all);
-    }
-
-    /**
-     * 查询商品详情
-     */
-    @Anonymous
-    @GetMapping("/getfood")
-    public AjaxResult getfood(@RequestParam Integer id) {
-        JSONObject org = new JSONObject();
-        PosFood posFood = posFoodService.getById(id);
-        List<Long> __foodIds = new ArrayList<>();
-        __foodIds.add(posFood.getId());
-        Map<Long, List<FoodSpecs>> __specMap = loadFoodSpecsMap(__foodIds);
-        List<FoodSpecs> __fs = __specMap.getOrDefault(posFood.getId(), new ArrayList<>());
-        org.put("id", posFood.getId());
-        org.put("fenlei", posFenleiService.getById(posFood.getFlId()));
-        PosStore store = posStoreService.getById(posFood.getMdid());
-        fillStallLocation(store);
-        org.put("store", store);
-        org.put("name", posFood.getName());
-        org.put("image", posFood.getImage());
-        org.put("price", posFood.getPrice());
-        org.put("introduce", posFood.getIntroduce());
-        org.put("recommend", posFood.getRecommend());
-        org.put("stackingUp", posFood.getStackingUp());
-        org.put("foodSpecs", __fs);
-        org.put("foodSku", __fs.isEmpty() ? JSONArray.parseArray(posFood.getFoodSku()) : buildFoodSkuArray(__fs));
-        return success(org);
-    }
-
-    /**
-     * 搜索
-     */
-    @Anonymous
-    @GetMapping("/searchfor")
-    public AjaxResult searchfor(@RequestParam String keyword, @RequestParam(defaultValue = "") String language) {
-        if (keyword.equals("")) {
-            return error(MessageUtils.message("no.tip.lock.keywork"));
-        }
-        JSONArray all = new JSONArray();
-        JSONObject object = new JSONObject();
-        QueryWrapper<PosFood> query = new QueryWrapper<>();
-        String lang = "0";
-        if (!"".equals(language)) {
-            lang = language;
-        }
-        query.eq("language", lang);
-        query.eq("to_examine", "1");
-//        query.like("name",keyword);
-        query.apply("BINARY name LIKE CONCAT('%', {0}, '%')", keyword);
-        List<PosFood> folist = posFoodService.list(query);
-        QueryWrapper<PosStore> wrapper = new QueryWrapper<>();
-        wrapper.eq("off_shelf", "0")
-//                .and(i->i.like("pos_name", keyword).or().like("brief_introduction",keyword));
-                .and(i -> i.apply("BINARY pos_name LIKE CONCAT('%', {0}, '%')", keyword).or().apply("BINARY brief_introduction LIKE CONCAT('%', {0}, '%')", keyword));
-        List<PosStore> stlist = posStoreService.list(wrapper);
-        for (int i = 0; i < folist.size(); i++) {
-            PosStore store = posStoreService.getById(folist.get(i).getMdid());
-            if (store.getOffShelf().equals("0")) {
-                JSONObject org = new JSONObject();
-                org.put("id", folist.get(i).getId());
-                org.put("fenlei", posFenleiService.getById(folist.get(i).getFlId()));
-                org.put("store", posStoreService.getById(folist.get(i).getMdid()));
-                org.put("name", folist.get(i).getName());
-                org.put("image", folist.get(i).getImage());
-                org.put("price", folist.get(i).getPrice());
-                org.put("introduce", folist.get(i).getIntroduce());
-                org.put("recommend", folist.get(i).getRecommend());
-                org.put("stackingUp", folist.get(i).getStackingUp());
-                org.put("foodSku", JSONArray.parseArray(folist.get(i).getFoodSku()));
-                all.add(org);
-            }
-        }
-        object.put("food", all);
-        object.put("store", stlist);
-        return success(object);
-    }
-
-
-    /**
-     * 客户端查询美食
-     *
-     * @param keyword
-     * @param language
-     * @return
-     */
-
-    @Anonymous
-    @GetMapping("/foodSearch")
-    public AjaxResult foodSearch(@RequestParam String keyword, @RequestParam Integer pageNum, @RequestParam Integer pageSize, @RequestParam(defaultValue = "") String language) {
-        if (keyword.isEmpty()) {
-            return error(MessageUtils.message("no.tip.lock.keywork"));
-        }
-
-        // 设置默认分页参数
-        if (pageNum == null || pageNum < 1) {
-            pageNum = 1;
-        }
-        if (pageSize == null || pageSize < 1) {
-            pageSize = 10;
-        }
-
-        String lang = "0";
-        if (!"".equals(language)) {
-            lang = language;
-        }
-        keyword = hanlderKeyword(lang, keyword);
-
-        // 创建分页对象
-        Page<PosStore> page = new Page<>(pageNum, pageSize);
-
-        // 1. 先通过联表查询获取分页的店铺列表(包含总数)
-        IPage<PosStore> storePage = posStoreMapper.selectStoresByFoodKeyword(page, keyword, lang);
-        List<PosStore> stores = storePage.getRecords();
-
-        List<FoodSearchOutput> result = new ArrayList<>();
-        Map<Long, List<PosReview>> reviewMap = Collections.emptyMap();
-        Map<Long, Long> orderCountMap = Collections.emptyMap();
-        List<OperatingHours> hourslist=new ArrayList<>();
-        // 2. 一次性查询所有相关商品(避免循环查询数据库)
-        if (!stores.isEmpty()) {
-            // 提取所有店铺ID
-            List<Long> storeIds = stores.stream()
-                    .map(store -> store.getId().longValue())
-                    .collect(Collectors.toList());
-
-            // 一次性查询所有店铺下匹配关键词的商品
-            QueryWrapper<PosFood> foodQuery = new QueryWrapper<>();
-            foodQuery.in("mdid", storeIds)
-                    .eq("language", lang)
-                    .eq("to_examine", "1")
-                    .apply(" CONCAT(name, ' ') LIKE CONCAT('%', {0}, '%') COLLATE utf8mb4_unicode_ci", keyword);
-
-            List<PosFood> allFoods = posFoodService.list(foodQuery);
-            {
-                List<Long> __foodIds = allFoods.stream().map(PosFood::getId).collect(Collectors.toList());
-                Map<Long, List<FoodSpecs>> __specMap = loadFoodSpecsMap(__foodIds);
-                for (PosFood __f : allFoods) {
-                    __f.setFoodSpecs(__specMap.getOrDefault(__f.getId(), new ArrayList<>()));
-                }
-            }
-
-            // 按店铺ID分组商品
-            Map<Long, List<PosFood>> foodsByStore = allFoods.stream()
-                    .collect(Collectors.groupingBy(PosFood::getMdid));
-            posStoreEnrichService.enrichStoreList(stores);
-            // 3. 为每个店铺设置对应的商品
-            for (PosStore store : stores) {
-                FoodSearchOutput output = new FoodSearchOutput();
-
-                output.setPosStore(store);
-                // 从分组结果中获取该店铺的商品
-                List<PosFood> storeFoods = foodsByStore.getOrDefault(store.getId().longValue(), new ArrayList<>());
-
-                output.setPosFoodList(storeFoods);
-                result.add(output);
-            }
-        }
-
-        // 3. 返回分页结果
-        Map<String, Object> response = new HashMap<>();
-        response.put("list", result);
-        response.put("total", storePage.getTotal());
-        return success(response);
-    }
-
-    //越南语搜索,添加空格,搜索的关键字变成有语意的
-    private String hanlderKeyword(String language, String keyword) {
-        if (language.equals("0")) {
-            if (!keyword.contains(" "))
-                keyword = keyword + " ";
-        }
-        return keyword;
-    }
-
-    /**
-     * 客户端查询门店
-     *
-     * @param keyword
-     * @param language
-     * @return
-     */
-
-    @Anonymous
-    @GetMapping("/storeSearch")
-    public AjaxResult storeSearch(@RequestParam String keyword, @RequestParam Integer pageNum, @RequestParam Integer pageSize, @RequestParam(defaultValue = "") String language) {
-        if (keyword.isEmpty()) {
-            return error(MessageUtils.message("no.tip.lock.keywork"));
-        }
-        QueryWrapper<PosFood> query = new QueryWrapper<>();
-        String lang = "0";
-        if (!"".equals(language)) {
-            lang = language;
-        }
-        keyword = hanlderKeyword(lang, keyword);
-        // 设置默认分页参数
-        if (pageNum == null || pageNum < 1) {
-            pageNum = 1;
-        }
-        if (pageSize == null || pageSize < 1) {
-            pageSize = 10;
-        }
-        Page<PosStore> page = new Page<>(pageNum, pageSize);
-        QueryWrapper<PosStore> storeWrapper = new QueryWrapper<>();
-        String finalKeyword = keyword;
-        storeWrapper.eq("off_shelf", "0")
-//                .and(i->i.like("pos_name", keyword).or().like("brief_introduction",keyword));
-                .and(i -> i.apply(" CONCAT(pos_name,' ') LIKE CONCAT('%', {0}, '%') COLLATE utf8mb4_unicode_ci", finalKeyword).or().apply(" CONCAT(brief_introduction,' ') LIKE CONCAT('%', {0}, '%') COLLATE utf8mb4_unicode_ci", finalKeyword));
-        IPage<PosStore> data = posStoreService.page(page, storeWrapper);
-        Map<String, Object> response = new HashMap<>();
-        List<PosStore> stores=data.getRecords();
-        posStoreEnrichService.enrichStoreList(stores);
-        response.put("list", stores);
-        response.put("total", data.getTotal());
-        return success(response);
-    }
-
-
-
-    @Anonymous
-    @GetMapping("/getFoodPageList")
-    public AjaxResult getFoodPageList(@RequestHeader String token,
-                                      @RequestParam Integer page,
-                                      @RequestParam Integer size,
-                                      @RequestParam Long flId,
-                                      @RequestParam Long mdId,
-                                      @RequestParam(defaultValue = "") String language,
-                                      @RequestParam(defaultValue = "") String name) {
-        JwtUtil jwtUtil = new JwtUtil();
-        String id = jwtUtil.getusid(token);
-        IPage<PosFood> stlist = new Page<>(page, size);
-        LambdaQueryWrapper<PosFood> queryWrapper = new LambdaQueryWrapper<>();
-        queryWrapper.eq(PosFood::getFlId, flId);
-        queryWrapper.eq(PosFood::getMdid, mdId);
-
-        // language 不为 null 且不为空字符串时才加条件
-        if (language != null && !"".equals(language)) {
-            queryWrapper.eq(PosFood::getLanguage, language);
-        }
-
-        // name 不为 null 且不为空字符串时模糊查询
-        if (name != null && !"".equals(name)) {
-            queryWrapper.like(PosFood::getName, name);
-        }
-
-        IPage<PosFood> list = posFoodService.page(stlist, queryWrapper);
-        return success(list);
-    }
-
-    /**
-     * C端扫码:获取摊位商品列表(摊位码扫码入口)
-     */
-    @Anonymous
-    @GetMapping("/stallFoodList")
-    public AjaxResult stallFoodList(@RequestParam Long storeId,
-                                    @RequestParam(required = false) Long flId,
-                                    @RequestParam Integer page,
-                                    @RequestParam Integer size,
-                                    @RequestParam(required = false) Long tableId) {
-        IPage<PosFood> foodPage = new Page<>(page, size);
-        QueryWrapper<PosFood> queryWrapper = new QueryWrapper<>();
-        queryWrapper.eq("mdid", storeId);
-        queryWrapper.eq("to_examine", "1");
-        queryWrapper.eq("stacking_up", "1");
-        if (flId != null) {
-            queryWrapper.eq("fl_id", flId);
-        }
-        IPage<PosFood> result = posFoodService.page(foodPage, queryWrapper);
-        List<Long> __foodIds = result.getRecords().stream().map(PosFood::getId).collect(Collectors.toList());
-        Map<Long, List<FoodSpecs>> __specMap = loadFoodSpecsMap(__foodIds);
-        for (PosFood __f : result.getRecords()) {
-            __f.setFoodSpecs(__specMap.getOrDefault(__f.getId(), new ArrayList<>()));
-        }
-        return success(result);
-    }
-
-    /**
-     * 查询商品列表
-     */
-    @Anonymous
-    @GetMapping("/getfoodlist")
-    public AjaxResult getfoodlist(@RequestParam Integer page,
-                                  @RequestParam Integer size,
-                                  @RequestParam(defaultValue = "") String language,
-                                  @RequestParam(defaultValue = "") String keyword) {
-        IPage<PosFood> food = new Page<>(page, size);
-        QueryWrapper<PosFood> queryWrapper = new QueryWrapper<>();
-        String lang = "0";
-        if (!"".equals(language)) {
-            lang = language;
-        }
-        queryWrapper.eq("language", lang);
-        queryWrapper.eq("to_examine", "1");
-        if (!"".equals(keyword)) {
-            queryWrapper.like("name", keyword);
-        }
-        IPage<PosFood> list = posFoodService.page(food, queryWrapper);
-        List<PosFood> foodlist = list.getRecords();
-        JSONArray all = new JSONArray();
-        for (int i = 0; i < foodlist.size(); i++) {
-            PosStore store = posStoreService.getById(foodlist.get(i).getMdid());
-            if (store.getOffShelf().equals("0")) {
-                JSONObject org = new JSONObject();
-                org.put("id", foodlist.get(i).getId());
-                org.put("fenlei", posFenleiService.getById(foodlist.get(i).getFlId()));
-                org.put("store", posStoreService.getById(foodlist.get(i).getMdid()));
-                org.put("name", foodlist.get(i).getName());
-                org.put("image", foodlist.get(i).getImage());
-                org.put("price", foodlist.get(i).getPrice());
-                org.put("introduce", foodlist.get(i).getIntroduce());
-                org.put("recommend", foodlist.get(i).getRecommend());
-                org.put("stackingUp", foodlist.get(i).getStackingUp());
-                org.put("foodSku", JSONArray.parseArray(foodlist.get(i).getFoodSku()));
-                all.add(org);
-            }
-        }
-        return success(all);
-    }
-
-    /**
-     * 查询food列表
-     */
-
-    @PreAuthorize("@ss.hasPermi('chanting:food:list')")
-    @GetMapping("/list")
-    public TableDataInfo list(PosFood posFood) {
-        startPage();
-        List<PosFood> list = posFoodService.selectPosFoodList(posFood);
-        return getDataTable(list);
-    }
-
-    /**
-     * 导出food列表
-     */
-    @PreAuthorize("@ss.hasPermi('chanting:food:export')")
-    @Log(title = "food", businessType = BusinessType.EXPORT)
-    @PostMapping("/export")
-    public void export(HttpServletResponse response, PosFood posFood) {
-        List<PosFood> list = posFoodService.selectPosFoodList(posFood);
-        ExcelUtil<PosFood> util = new ExcelUtil<PosFood>(PosFood.class);
-        util.exportExcel(response, list, MessageUtils.message("no.export.excel.food"));
-    }
-
-    /**
-     * 获取food详细信息
-     */
-    @Anonymous
-    @GetMapping(value = "/{id}")
-    public AjaxResult getInfo(@PathVariable("id") Long id) {
-        return success(posFoodService.selectPosFoodById(id));
-    }
-
-    /**
-     * 新增food
-     */
-    @PreAuthorize("@ss.hasPermi('chanting:food:add')")
-    @Log(title = "food", businessType = BusinessType.INSERT)
-    @PostMapping
-    public AjaxResult add(@RequestBody PosFood posFood) {
-        int rows = posFoodService.insertPosFood(posFood);
-        if (rows > 0) {
-            handleFoodSpec(posFood);
-        }
-        return toAjax(rows);
-    }
-
-    /**
-     * 修改food
-     */
-    @PreAuthorize("@ss.hasPermi('chanting:food:edit')")
-    @Log(title = "food", businessType = BusinessType.UPDATE)
-    @PutMapping
-    public AjaxResult edit(@RequestBody PosFood posFood) {
-        int rows = posFoodService.updatePosFood(posFood);
-        if (rows > 0) {
-            handleFoodSpec(posFood);
-        }
-        return toAjax(rows);
-    }
-
-    /**
-     * 删除food
-     */
-    @PreAuthorize("@ss.hasPermi('chanting:food:remove')")
-    @Log(title = "food", businessType = BusinessType.DELETE)
-    @DeleteMapping("/{ids}")
-    public AjaxResult remove(@PathVariable Long[] ids) {
-        return toAjax(posFoodService.deletePosFoodByIds(ids));
-    }
-
-    /**
-     * 压缩图片店铺的商品图片
-     */
-    @Anonymous
-    @GetMapping("/compressFoodImage")
-    public AjaxResult compressFoodImage(@RequestParam Long mdId, @RequestParam String idsStr) {
-        LambdaQueryWrapper<PosFood> queryWrapper = new LambdaQueryWrapper<>();
-        queryWrapper.eq(PosFood::getMdid, mdId);
-        List<Long> ids= new ArrayList<>();
-        if(!StringUtils.isEmpty(idsStr)){
-            ids = Arrays.stream(idsStr.split(","))
-                    .map(Long::valueOf)
-                    .collect(Collectors.toList());
-        }
-        queryWrapper.in(!ids.isEmpty(), PosFood::getId, ids);
-        List<PosFood> list = posFoodService.list(queryWrapper);
-        AtomicInteger count = new AtomicInteger();
-        list.forEach(food -> {
-            String compressImage = ImageCompressUtils.compressImageByWebPath(food.getImage());
-            if (!compressImage.equals(food.getImage())) {
-                food.setImage(compressImage);
-                PosFood posFood = new PosFood();
-                posFood.setId(food.getId());
-                posFood.setImage(compressImage);
-                posFoodService.saveOrUpdate(posFood);
-                count.getAndIncrement();
-                System.out.println("food成功压缩的图片数量:"+count.get());
-                logger.info("food成功压缩的图片数量:"+count.get());
-            }
-        });
-        return success("成功压缩的 :"+count.get());
-    }
-
-    /**
-     * 压缩所有的商品图片
-     */
-    @Anonymous
-    @GetMapping("/compressFoodImageAll")
-    public AjaxResult compressFoodImage() {
-        LambdaQueryWrapper<PosFood> queryWrapper = new LambdaQueryWrapper<>();
-        List<PosFood> list = posFoodService.list(queryWrapper);
-        AtomicInteger count = new AtomicInteger();
-        list.forEach(food -> {
-            String compressImage = ImageCompressUtils.compressImageByWebPath(food.getImage());
-            if (!compressImage.equals(food.getImage())) {
-                food.setImage(compressImage);
-                PosFood posFood = new PosFood();
-                posFood.setId(food.getId());
-                posFood.setImage(compressImage);
-                posFoodService.saveOrUpdate(posFood);
-                count.getAndIncrement();
-                System.out.println("food成功压缩的图片数量:"+count.get());
-                logger.info("food成功压缩的图片数量:"+count.get());
-            }
-        });
-        return success("成功压缩的图片数量:"+count.get());
-    }
-
-    /**
-     * 当门店是摊位时,用夜市的经纬度覆盖摊位的经纬度
-     */
-    /**
-     * 保存商品规格:sku JSON 写 food_sku(落库) -> 删除旧关联 -> 按 foodSpecs 建立新关联
-     */
-    private void handleFoodSpec(PosFood posFood) {
-        if (posFood == null || posFood.getId() == null) {
-            return;
-        }
-        JSONArray sku = posFood.getSku();
-        if (sku != null) {
-            PosFood upd = new PosFood();
-            upd.setId(posFood.getId());
-            upd.setFoodSku(sku.toString());
-            posFoodService.updateById(upd);
-            posFood.setFoodSku(sku.toString());
-        }
-        foodSpecRelationService.remove(new LambdaQueryWrapper<FoodSpecRelation>()
-                .eq(FoodSpecRelation::getFoodId, posFood.getId()));
-        List<FoodSpecs> foodSpecs = posFood.getFoodSpecs();
-        if (foodSpecs != null && !foodSpecs.isEmpty()) {
-            List<FoodSpecRelation> rels = foodSpecs.stream()
-                    .filter(sp -> sp.getId() != null && sp.getId() > 0)
-                    .map(sp -> {
-                        FoodSpecRelation r = new FoodSpecRelation();
-                        r.setFoodId(posFood.getId());
-                        r.setSpecsId(sp.getId());
-                        return r;
-                    }).collect(Collectors.toList());
-            if (!rels.isEmpty()) {
-                foodSpecRelationService.saveBatch(rels);
-            }
-        }
-    }
-
-    /**
-     * 批量查询商品规格(避免 N+1):关联 -> 启用规格组 -> 启用规格值 -> 内存分组
-     * @return foodId -> 规格组列表(含启用规格值)
-     */
-    private Map<Long, List<FoodSpecs>> loadFoodSpecsMap(List<Long> foodIds) {
-        Map<Long, List<FoodSpecs>> result = new HashMap<>();
-        if (foodIds == null || foodIds.isEmpty()) {
-            return result;
-        }
-        List<FoodSpecRelation> rels = foodSpecRelationService.list(new LambdaQueryWrapper<FoodSpecRelation>()
-                .in(FoodSpecRelation::getFoodId, foodIds));
-        if (rels.isEmpty()) {
-            return result;
-        }
-        Map<Long, List<Long>> foodToSpecIds = rels.stream()
-                .collect(Collectors.groupingBy(FoodSpecRelation::getFoodId,
-                        Collectors.mapping(FoodSpecRelation::getSpecsId, Collectors.toList())));
-        List<Long> specsIds = rels.stream().map(FoodSpecRelation::getSpecsId).distinct().collect(Collectors.toList());
-        List<FoodSpecs> specsList = foodSpecsService.list(new LambdaQueryWrapper<FoodSpecs>()
-                .in(FoodSpecs::getId, specsIds)
-                .eq(FoodSpecs::getIsOpen, true)
-                .eq(FoodSpecs::getIsDelete, false)
-                .orderByAsc(FoodSpecs::getSort));
-        if (specsList.isEmpty()) {
-            return result;
-        }
-        List<Long> enabledSpecIds = specsList.stream().map(FoodSpecs::getId).collect(Collectors.toList());
-        List<FoodSpecsValue> values = foodSpecsValueService.list(new LambdaQueryWrapper<FoodSpecsValue>()
-                .in(FoodSpecsValue::getParentId, enabledSpecIds)
-                .eq(FoodSpecsValue::getIsOpen, true)
-                .orderByAsc(FoodSpecsValue::getId));
-        Map<Long, List<FoodSpecsValue>> valueMap = values.stream()
-                .collect(Collectors.groupingBy(FoodSpecsValue::getParentId));
-        Map<Long, FoodSpecs> specsMap = specsList.stream().collect(Collectors.toMap(FoodSpecs::getId, sp -> sp));
-        for (Map.Entry<Long, List<Long>> entry : foodToSpecIds.entrySet()) {
-            List<FoodSpecs> fsList = new ArrayList<>();
-            for (Long sid : entry.getValue()) {
-                FoodSpecs src = specsMap.get(sid);
-                if (src != null) {
-                    FoodSpecs copy = new FoodSpecs();
-                    copy.setId(src.getId());
-                    copy.setTitle(src.getTitle());
-                    copy.setType(src.getType());
-                    copy.setState(src.getState());
-                    copy.setLanguage(src.getLanguage());
-                    copy.setRemark(src.getRemark());
-                    copy.setMdId(src.getMdId());
-                    copy.setSort(src.getSort());
-                    copy.setIsOpen(src.getIsOpen());
-                    copy.setIsDelete(src.getIsDelete());
-                    copy.setObjects(valueMap.getOrDefault(src.getId(), new ArrayList<>()));
-                    fsList.add(copy);
-                }
-            }
-            result.put(entry.getKey(), fsList);
-        }
-        return result;
-    }
-
-    /**
-     * 由结构化规格构建兼容的 food_sku JSON 数组(明细字段名 foodSpecsItems)
-     */
-    private JSONArray buildFoodSkuArray(List<FoodSpecs> fsList) {
-        JSONArray skuArr = new JSONArray();
-        if (fsList == null || fsList.isEmpty()) {
-            return skuArr;
-        }
-        for (FoodSpecs s : fsList) {
-            skuArr.add(JSONObject.toJSON(s));
-        }
-        return skuArr;
-    }
-
-    private void fillStallLocation(PosStore store) {
-        if (store != null && store.getIsStall() != null && store.getIsStall() == 1
-                && store.getNightMarketId() != null) {
-            InfoUser nightMarket = infoUserService.getById(store.getNightMarketId());
-            if (nightMarket != null) {
-                store.setLongitude(nightMarket.getLongitude());
-                store.setLatitude(nightMarket.getLatitude());
-            }
-        }
-    }
-}
+package com.ruoyi.app.mendian;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.app.mendian.dto.FoodSearchOutput;
+import com.ruoyi.app.order.OrderInvoiceService;
+import com.ruoyi.app.utils.ImageCompressUtils;
+import com.ruoyi.common.annotation.Anonymous;
+import com.ruoyi.common.annotation.Log;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.page.TableDataInfo;
+import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.common.utils.MessageUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.system.domain.*;
+import com.ruoyi.system.mapper.PosStoreMapper;
+import com.ruoyi.system.service.*;
+import com.ruoyi.system.utils.Auth;
+import com.ruoyi.system.utils.JwtUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import jakarta.servlet.http.HttpServletResponse;
+
+import java.util.*;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
+
+/**
+ * foodController
+ *
+ * @author ruoyi
+ * @date 2023-05-21
+ */
+@RestController
+@RequestMapping("/chanting/food")
+public class PosFoodController extends BaseController {
+    @Autowired  //商品
+    private IPosFoodService posFoodService;
+
+    @Autowired  //门店
+    private IPosStoreService posStoreService;
+
+    @Autowired //商品分类
+    private IPosFenleiService posFenleiService;
+
+    @Autowired //用户
+    private IInfoUserService infoUserService;
+    @Autowired //店铺Mapper
+    private PosStoreMapper posStoreMapper;
+    @Autowired
+    private IPosReviewService posReviewService;
+    @Autowired
+    private IOperatingHoursService operatingHoursService;
+    @Autowired
+    private IPosOrderService posOrderService;
+
+    @Autowired
+    private PosStoreEnrichService posStoreEnrichService;
+
+    @Autowired  //规格组
+    private IFoodSpecsService foodSpecsService;
+    @Autowired  //规格值
+    private IFoodSpecsValueService foodSpecsValueService;
+    @Autowired  //商品-规格关联
+    private IFoodSpecRelationService foodSpecRelationService;
+
+    @Autowired //订单电子发票(门店是否能开票)
+    private OrderInvoiceService orderInvoiceService;
+
+    //删除商品
+    @Anonymous
+    @Auth
+    @GetMapping("/delefood")
+    public AjaxResult delefood(@RequestParam String id) {
+        return toAjax(posFoodService.deletePosFoodById(Long.valueOf(id)));
+    }
+
+    //推荐商品
+    @Anonymous
+    @PutMapping("/changerecommend")
+    public AjaxResult changerecommend(@RequestBody PosFood posFood) {
+        return toAjax(posFoodService.saveOrUpdate(posFood));
+    }
+
+    /**
+     * 添加或修改商品
+     */
+    @Anonymous
+    @PostMapping("/setposfood")
+    public AjaxResult setposfood(@RequestBody PosFood posFood) {
+        Boolean org = posFoodService.saveOrUpdate(posFood);
+        if (!org) {
+            return error();
+        }
+        handleFoodSpec(posFood);
+        return success();
+    }
+
+    /**
+     * (h5页面使用)通过分类查询商品列表
+     */
+    @Anonymous
+    @GetMapping("/h5Getidlist")
+    public AjaxResult h5Getidlist(@RequestParam Integer id, @RequestParam(defaultValue = "") String language, @RequestParam(required = false) String stackingUp) {
+        JwtUtil jwtUtil = new JwtUtil();
+//        String ids = jwtUtil.getusid(token);
+//        InfoUser user = infoUserService.getById(ids);
+        QueryWrapper<PosFood> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq("fl_id", id);
+//        if(!user.getUserType().equals("1")){
+//            queryWrapper.eq("to_examine","1");
+//        }
+        if (stackingUp != null && !"".equals(stackingUp)) {
+            queryWrapper.eq("stacking_up", stackingUp);
+        }
+        String lang = "0";
+        if (!"".equals(language)) {
+            lang = language;
+        }
+        queryWrapper.eq("language", lang);
+        List<PosFood> list = posFoodService.list(queryWrapper);
+        List<Long> __foodIds = list.stream().map(PosFood::getId).collect(Collectors.toList());
+        Map<Long, List<FoodSpecs>> __specMap = loadFoodSpecsMap(__foodIds);
+        JSONArray all = new JSONArray();
+        for (int i = 0; i < list.size(); i++) {
+            JSONObject org = new JSONObject();
+            org.put("id", list.get(i).getId());
+            org.put("fenlei", posFenleiService.getById(list.get(i).getFlId()));
+            org.put("store", posStoreService.getById(list.get(i).getMdid()));
+            org.put("name", list.get(i).getName());
+            org.put("image", list.get(i).getImage());
+            org.put("price", list.get(i).getPrice());
+            org.put("introduce", list.get(i).getIntroduce());
+            org.put("recommend", list.get(i).getRecommend());
+            List<FoodSpecs> __fs = __specMap.getOrDefault(list.get(i).getId(), new ArrayList<>());
+            org.put("foodSpecs", __fs);
+            org.put("foodSku", __fs.isEmpty() ? JSONArray.parseArray(list.get(i).getFoodSku()) : buildFoodSkuArray(__fs));
+            org.put("stackingUp", list.get(i).getStackingUp());
+            org.put("toExamine", list.get(i).getToExamine());
+            all.add(org);
+        }
+        return success(all);
+    }
+
+    /**
+     * 通过分类查询商品列表
+     */
+    @Anonymous
+    @GetMapping("/getidlist")
+    public AjaxResult getidlist(@RequestHeader String token, @RequestParam Integer id, @RequestParam(defaultValue = "") String language, @RequestParam(required = false) String stackingUp, @RequestParam(required = false) String name) {
+        JwtUtil jwtUtil = new JwtUtil();
+        String ids = jwtUtil.getusid(token);
+        InfoUser user = infoUserService.getById(ids);
+        QueryWrapper<PosFood> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq("fl_id", id);
+        //普通用户显示审核通过的
+        if("0".equals(user.getUserType())){
+            queryWrapper.eq("to_examine", "1");
+        }
+        if (stackingUp != null && !"".equals(stackingUp)) {
+            queryWrapper.eq("stacking_up", stackingUp);
+        }
+        String lang = "3";
+        if (!"".equals(language)) {
+            lang = language;
+        }
+        queryWrapper.eq("language", lang);
+        if (name != null && !"".equals(name)) {
+            queryWrapper.apply(" BINARY name LIKE CONCAT('%', {0}, '%')", name);
+        }
+        List<PosFood> list = posFoodService.list(queryWrapper);
+        List<Long> __foodIds = list.stream().map(PosFood::getId).collect(Collectors.toList());
+        Map<Long, List<FoodSpecs>> __specMap = loadFoodSpecsMap(__foodIds);
+        JSONArray all = new JSONArray();
+        for (int i = 0; i < list.size(); i++) {
+            JSONObject org = new JSONObject();
+            org.put("id", list.get(i).getId());
+            org.put("fenlei", posFenleiService.getById(list.get(i).getFlId()));
+            PosStore store = posStoreService.getById(list.get(i).getMdid());
+            fillStallLocation(store);
+            org.put("store", store);
+            org.put("canInvoice", orderInvoiceService.canInvoice(list.get(i).getMdid()));
+            org.put("name", list.get(i).getName());
+            org.put("image", list.get(i).getImage());
+            org.put("price", list.get(i).getPrice());
+            org.put("introduce", list.get(i).getIntroduce());
+            org.put("recommend", list.get(i).getRecommend());
+            List<FoodSpecs> __fs = __specMap.getOrDefault(list.get(i).getId(), new ArrayList<>());
+            org.put("foodSpecs", __fs);
+            org.put("foodSku", __fs.isEmpty() ? JSONArray.parseArray(list.get(i).getFoodSku()) : buildFoodSkuArray(__fs));
+            org.put("stackingUp", list.get(i).getStackingUp());
+            org.put("toExamine", list.get(i).getToExamine());
+            all.add(org);
+        }
+        return success(all);
+    }
+
+    /**
+     * 查询商品详情
+     */
+    @Anonymous
+    @GetMapping("/getfood")
+    public AjaxResult getfood(@RequestParam Integer id) {
+        JSONObject org = new JSONObject();
+        PosFood posFood = posFoodService.getById(id);
+        List<Long> __foodIds = new ArrayList<>();
+        __foodIds.add(posFood.getId());
+        Map<Long, List<FoodSpecs>> __specMap = loadFoodSpecsMap(__foodIds);
+        List<FoodSpecs> __fs = __specMap.getOrDefault(posFood.getId(), new ArrayList<>());
+        org.put("id", posFood.getId());
+        org.put("fenlei", posFenleiService.getById(posFood.getFlId()));
+        PosStore store = posStoreService.getById(posFood.getMdid());
+        fillStallLocation(store);
+        org.put("store", store);
+        org.put("canInvoice", orderInvoiceService.canInvoice(posFood.getMdid()));

+        org.put("name", posFood.getName());
+        org.put("image", posFood.getImage());
+        org.put("price", posFood.getPrice());
+        org.put("introduce", posFood.getIntroduce());
+        org.put("recommend", posFood.getRecommend());
+        org.put("stackingUp", posFood.getStackingUp());
+        org.put("foodSpecs", __fs);
+        org.put("foodSku", __fs.isEmpty() ? JSONArray.parseArray(posFood.getFoodSku()) : buildFoodSkuArray(__fs));
+        return success(org);
+    }
+
+    /**
+     * 搜索
+     */
+    @Anonymous
+    @GetMapping("/searchfor")
+    public AjaxResult searchfor(@RequestParam String keyword, @RequestParam(defaultValue = "") String language) {
+        if (keyword.equals("")) {
+            return error(MessageUtils.message("no.tip.lock.keywork"));
+        }
+        JSONArray all = new JSONArray();
+        JSONObject object = new JSONObject();
+        QueryWrapper<PosFood> query = new QueryWrapper<>();
+        String lang = "0";
+        if (!"".equals(language)) {
+            lang = language;
+        }
+        query.eq("language", lang);
+        query.eq("to_examine", "1");
+//        query.like("name",keyword);
+        query.apply("BINARY name LIKE CONCAT('%', {0}, '%')", keyword);
+        List<PosFood> folist = posFoodService.list(query);
+        QueryWrapper<PosStore> wrapper = new QueryWrapper<>();
+        wrapper.eq("off_shelf", "0")
+//                .and(i->i.like("pos_name", keyword).or().like("brief_introduction",keyword));
+                .and(i -> i.apply("BINARY pos_name LIKE CONCAT('%', {0}, '%')", keyword).or().apply("BINARY brief_introduction LIKE CONCAT('%', {0}, '%')", keyword));
+        List<PosStore> stlist = posStoreService.list(wrapper);
+        for (int i = 0; i < folist.size(); i++) {
+            PosStore store = posStoreService.getById(folist.get(i).getMdid());
+            if (store.getOffShelf().equals("0")) {
+                JSONObject org = new JSONObject();
+                org.put("id", folist.get(i).getId());
+                org.put("fenlei", posFenleiService.getById(folist.get(i).getFlId()));
+                org.put("store", posStoreService.getById(folist.get(i).getMdid()));
+                org.put("name", folist.get(i).getName());
+                org.put("image", folist.get(i).getImage());
+                org.put("price", folist.get(i).getPrice());
+                org.put("introduce", folist.get(i).getIntroduce());
+                org.put("recommend", folist.get(i).getRecommend());
+                org.put("stackingUp", folist.get(i).getStackingUp());
+                org.put("foodSku", JSONArray.parseArray(folist.get(i).getFoodSku()));
+                all.add(org);
+            }
+        }
+        object.put("food", all);
+        object.put("store", stlist);
+        return success(object);
+    }
+
+
+    /**
+     * 客户端查询美食
+     *
+     * @param keyword
+     * @param language
+     * @return
+     */
+
+    @Anonymous
+    @GetMapping("/foodSearch")
+    public AjaxResult foodSearch(@RequestParam String keyword, @RequestParam Integer pageNum, @RequestParam Integer pageSize, @RequestParam(defaultValue = "") String language) {
+        if (keyword.isEmpty()) {
+            return error(MessageUtils.message("no.tip.lock.keywork"));
+        }
+
+        // 设置默认分页参数
+        if (pageNum == null || pageNum < 1) {
+            pageNum = 1;
+        }
+        if (pageSize == null || pageSize < 1) {
+            pageSize = 10;
+        }
+
+        String lang = "0";
+        if (!"".equals(language)) {
+            lang = language;
+        }
+        keyword = hanlderKeyword(lang, keyword);
+
+        // 创建分页对象
+        Page<PosStore> page = new Page<>(pageNum, pageSize);
+
+        // 1. 先通过联表查询获取分页的店铺列表(包含总数)
+        IPage<PosStore> storePage = posStoreMapper.selectStoresByFoodKeyword(page, keyword, lang);
+        List<PosStore> stores = storePage.getRecords();
+
+        List<FoodSearchOutput> result = new ArrayList<>();
+        Map<Long, List<PosReview>> reviewMap = Collections.emptyMap();
+        Map<Long, Long> orderCountMap = Collections.emptyMap();
+        List<OperatingHours> hourslist=new ArrayList<>();
+        // 2. 一次性查询所有相关商品(避免循环查询数据库)
+        if (!stores.isEmpty()) {
+            // 提取所有店铺ID
+            List<Long> storeIds = stores.stream()
+                    .map(store -> store.getId().longValue())
+                    .collect(Collectors.toList());
+
+            // 一次性查询所有店铺下匹配关键词的商品
+            QueryWrapper<PosFood> foodQuery = new QueryWrapper<>();
+            foodQuery.in("mdid", storeIds)
+                    .eq("language", lang)
+                    .eq("to_examine", "1")
+                    .apply(" CONCAT(name, ' ') LIKE CONCAT('%', {0}, '%') COLLATE utf8mb4_unicode_ci", keyword);
+
+            List<PosFood> allFoods = posFoodService.list(foodQuery);
+            {
+                List<Long> __foodIds = allFoods.stream().map(PosFood::getId).collect(Collectors.toList());
+                Map<Long, List<FoodSpecs>> __specMap = loadFoodSpecsMap(__foodIds);
+                for (PosFood __f : allFoods) {
+                    __f.setFoodSpecs(__specMap.getOrDefault(__f.getId(), new ArrayList<>()));
+                }
+            }
+
+            // 按店铺ID分组商品
+            Map<Long, List<PosFood>> foodsByStore = allFoods.stream()
+                    .collect(Collectors.groupingBy(PosFood::getMdid));
+            posStoreEnrichService.enrichStoreList(stores);
+            // 3. 为每个店铺设置对应的商品
+            for (PosStore store : stores) {
+                FoodSearchOutput output = new FoodSearchOutput();
+
+                output.setPosStore(store);
+                // 从分组结果中获取该店铺的商品
+                List<PosFood> storeFoods = foodsByStore.getOrDefault(store.getId().longValue(), new ArrayList<>());
+
+                output.setPosFoodList(storeFoods);
+                result.add(output);
+            }
+        }
+
+        // 3. 返回分页结果
+        Map<String, Object> response = new HashMap<>();
+        response.put("list", result);
+        response.put("total", storePage.getTotal());
+        return success(response);
+    }
+
+    //越南语搜索,添加空格,搜索的关键字变成有语意的
+    private String hanlderKeyword(String language, String keyword) {
+        if (language.equals("0")) {
+            if (!keyword.contains(" "))
+                keyword = keyword + " ";
+        }
+        return keyword;
+    }
+
+    /**
+     * 客户端查询门店
+     *
+     * @param keyword
+     * @param language
+     * @return
+     */
+
+    @Anonymous
+    @GetMapping("/storeSearch")
+    public AjaxResult storeSearch(@RequestParam String keyword, @RequestParam Integer pageNum, @RequestParam Integer pageSize, @RequestParam(defaultValue = "") String language) {
+        if (keyword.isEmpty()) {
+            return error(MessageUtils.message("no.tip.lock.keywork"));
+        }
+        QueryWrapper<PosFood> query = new QueryWrapper<>();
+        String lang = "0";
+        if (!"".equals(language)) {
+            lang = language;
+        }
+        keyword = hanlderKeyword(lang, keyword);
+        // 设置默认分页参数
+        if (pageNum == null || pageNum < 1) {
+            pageNum = 1;
+        }
+        if (pageSize == null || pageSize < 1) {
+            pageSize = 10;
+        }
+        Page<PosStore> page = new Page<>(pageNum, pageSize);
+        QueryWrapper<PosStore> storeWrapper = new QueryWrapper<>();
+        String finalKeyword = keyword;
+        storeWrapper.eq("off_shelf", "0")
+//                .and(i->i.like("pos_name", keyword).or().like("brief_introduction",keyword));
+                .and(i -> i.apply(" CONCAT(pos_name,' ') LIKE CONCAT('%', {0}, '%') COLLATE utf8mb4_unicode_ci", finalKeyword).or().apply(" CONCAT(brief_introduction,' ') LIKE CONCAT('%', {0}, '%') COLLATE utf8mb4_unicode_ci", finalKeyword));
+        IPage<PosStore> data = posStoreService.page(page, storeWrapper);
+        Map<String, Object> response = new HashMap<>();
+        List<PosStore> stores=data.getRecords();
+        posStoreEnrichService.enrichStoreList(stores);
+        response.put("list", stores);
+        response.put("total", data.getTotal());
+        return success(response);
+    }
+
+
+
+    @Anonymous
+    @GetMapping("/getFoodPageList")
+    public AjaxResult getFoodPageList(@RequestHeader String token,
+                                      @RequestParam Integer page,
+                                      @RequestParam Integer size,
+                                      @RequestParam Long flId,
+                                      @RequestParam Long mdId,
+                                      @RequestParam(defaultValue = "") String language,
+                                      @RequestParam(defaultValue = "") String name) {
+        JwtUtil jwtUtil = new JwtUtil();
+        String id = jwtUtil.getusid(token);
+        IPage<PosFood> stlist = new Page<>(page, size);
+        LambdaQueryWrapper<PosFood> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(PosFood::getFlId, flId);
+        queryWrapper.eq(PosFood::getMdid, mdId);
+
+        // language 不为 null 且不为空字符串时才加条件
+        if (language != null && !"".equals(language)) {
+            queryWrapper.eq(PosFood::getLanguage, language);
+        }
+
+        // name 不为 null 且不为空字符串时模糊查询
+        if (name != null && !"".equals(name)) {
+            queryWrapper.like(PosFood::getName, name);
+        }
+
+        IPage<PosFood> list = posFoodService.page(stlist, queryWrapper);
+        return success(list);
+    }
+
+    /**
+     * C端扫码:获取摊位商品列表(摊位码扫码入口)
+     */
+    @Anonymous
+    @GetMapping("/stallFoodList")
+    public AjaxResult stallFoodList(@RequestParam Long storeId,
+                                    @RequestParam(required = false) Long flId,
+                                    @RequestParam Integer page,
+                                    @RequestParam Integer size,
+                                    @RequestParam(required = false) Long tableId) {
+        IPage<PosFood> foodPage = new Page<>(page, size);
+        QueryWrapper<PosFood> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq("mdid", storeId);
+        queryWrapper.eq("to_examine", "1");
+        queryWrapper.eq("stacking_up", "1");
+        if (flId != null) {
+            queryWrapper.eq("fl_id", flId);
+        }
+        IPage<PosFood> result = posFoodService.page(foodPage, queryWrapper);
+        List<Long> __foodIds = result.getRecords().stream().map(PosFood::getId).collect(Collectors.toList());
+        Map<Long, List<FoodSpecs>> __specMap = loadFoodSpecsMap(__foodIds);
+        for (PosFood __f : result.getRecords()) {
+            __f.setFoodSpecs(__specMap.getOrDefault(__f.getId(), new ArrayList<>()));
+        }
+        return success(result);
+    }
+
+    /**
+     * 查询商品列表
+     */
+    @Anonymous
+    @GetMapping("/getfoodlist")
+    public AjaxResult getfoodlist(@RequestParam Integer page,
+                                  @RequestParam Integer size,
+                                  @RequestParam(defaultValue = "") String language,
+                                  @RequestParam(defaultValue = "") String keyword) {
+        IPage<PosFood> food = new Page<>(page, size);
+        QueryWrapper<PosFood> queryWrapper = new QueryWrapper<>();
+        String lang = "0";
+        if (!"".equals(language)) {
+            lang = language;
+        }
+        queryWrapper.eq("language", lang);
+        queryWrapper.eq("to_examine", "1");
+        if (!"".equals(keyword)) {
+            queryWrapper.like("name", keyword);
+        }
+        IPage<PosFood> list = posFoodService.page(food, queryWrapper);
+        List<PosFood> foodlist = list.getRecords();
+        JSONArray all = new JSONArray();
+        for (int i = 0; i < foodlist.size(); i++) {
+            PosStore store = posStoreService.getById(foodlist.get(i).getMdid());
+            if (store.getOffShelf().equals("0")) {
+                JSONObject org = new JSONObject();
+                org.put("id", foodlist.get(i).getId());
+                org.put("fenlei", posFenleiService.getById(foodlist.get(i).getFlId()));
+                org.put("store", posStoreService.getById(foodlist.get(i).getMdid()));
+                org.put("name", foodlist.get(i).getName());
+                org.put("image", foodlist.get(i).getImage());
+                org.put("price", foodlist.get(i).getPrice());
+                org.put("introduce", foodlist.get(i).getIntroduce());
+                org.put("recommend", foodlist.get(i).getRecommend());
+                org.put("stackingUp", foodlist.get(i).getStackingUp());
+                org.put("foodSku", JSONArray.parseArray(foodlist.get(i).getFoodSku()));
+                all.add(org);
+            }
+        }
+        return success(all);
+    }
+
+    /**
+     * 查询food列表
+     */
+
+    @PreAuthorize("@ss.hasPermi('chanting:food:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(PosFood posFood) {
+        startPage();
+        List<PosFood> list = posFoodService.selectPosFoodList(posFood);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出food列表
+     */
+    @PreAuthorize("@ss.hasPermi('chanting:food:export')")
+    @Log(title = "food", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(HttpServletResponse response, PosFood posFood) {
+        List<PosFood> list = posFoodService.selectPosFoodList(posFood);
+        ExcelUtil<PosFood> util = new ExcelUtil<PosFood>(PosFood.class);
+        util.exportExcel(response, list, MessageUtils.message("no.export.excel.food"));
+    }
+
+    /**
+     * 获取food详细信息
+     */
+    @Anonymous
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id) {
+        return success(posFoodService.selectPosFoodById(id));
+    }
+
+    /**
+     * 新增food
+     */
+    @PreAuthorize("@ss.hasPermi('chanting:food:add')")
+    @Log(title = "food", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody PosFood posFood) {
+        int rows = posFoodService.insertPosFood(posFood);
+        if (rows > 0) {
+            handleFoodSpec(posFood);
+        }
+        return toAjax(rows);
+    }
+
+    /**
+     * 修改food
+     */
+    @PreAuthorize("@ss.hasPermi('chanting:food:edit')")
+    @Log(title = "food", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody PosFood posFood) {
+        int rows = posFoodService.updatePosFood(posFood);
+        if (rows > 0) {
+            handleFoodSpec(posFood);
+        }
+        return toAjax(rows);
+    }
+
+    /**
+     * 删除food
+     */
+    @PreAuthorize("@ss.hasPermi('chanting:food:remove')")
+    @Log(title = "food", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids) {
+        return toAjax(posFoodService.deletePosFoodByIds(ids));
+    }
+
+    /**
+     * 压缩图片店铺的商品图片
+     */
+    @Anonymous
+    @GetMapping("/compressFoodImage")
+    public AjaxResult compressFoodImage(@RequestParam Long mdId, @RequestParam String idsStr) {
+        LambdaQueryWrapper<PosFood> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(PosFood::getMdid, mdId);
+        List<Long> ids= new ArrayList<>();
+        if(!StringUtils.isEmpty(idsStr)){
+            ids = Arrays.stream(idsStr.split(","))
+                    .map(Long::valueOf)
+                    .collect(Collectors.toList());
+        }
+        queryWrapper.in(!ids.isEmpty(), PosFood::getId, ids);
+        List<PosFood> list = posFoodService.list(queryWrapper);
+        AtomicInteger count = new AtomicInteger();
+        list.forEach(food -> {
+            String compressImage = ImageCompressUtils.compressImageByWebPath(food.getImage());
+            if (!compressImage.equals(food.getImage())) {
+                food.setImage(compressImage);
+                PosFood posFood = new PosFood();
+                posFood.setId(food.getId());
+                posFood.setImage(compressImage);
+                posFoodService.saveOrUpdate(posFood);
+                count.getAndIncrement();
+                System.out.println("food成功压缩的图片数量:"+count.get());
+                logger.info("food成功压缩的图片数量:"+count.get());
+            }
+        });
+        return success("成功压缩的 :"+count.get());
+    }
+
+    /**
+     * 压缩所有的商品图片
+     */
+    @Anonymous
+    @GetMapping("/compressFoodImageAll")
+    public AjaxResult compressFoodImage() {
+        LambdaQueryWrapper<PosFood> queryWrapper = new LambdaQueryWrapper<>();
+        List<PosFood> list = posFoodService.list(queryWrapper);
+        AtomicInteger count = new AtomicInteger();
+        list.forEach(food -> {
+            String compressImage = ImageCompressUtils.compressImageByWebPath(food.getImage());
+            if (!compressImage.equals(food.getImage())) {
+                food.setImage(compressImage);
+                PosFood posFood = new PosFood();
+                posFood.setId(food.getId());
+                posFood.setImage(compressImage);
+                posFoodService.saveOrUpdate(posFood);
+                count.getAndIncrement();
+                System.out.println("food成功压缩的图片数量:"+count.get());
+                logger.info("food成功压缩的图片数量:"+count.get());
+            }
+        });
+        return success("成功压缩的图片数量:"+count.get());
+    }
+
+    /**
+     * 当门店是摊位时,用夜市的经纬度覆盖摊位的经纬度
+     */
+    /**
+     * 保存商品规格:sku JSON 写 food_sku(落库) -> 删除旧关联 -> 按 foodSpecs 建立新关联
+     */
+    private void handleFoodSpec(PosFood posFood) {
+        if (posFood == null || posFood.getId() == null) {
+            return;
+        }
+        JSONArray sku = posFood.getSku();
+        if (sku != null) {
+            PosFood upd = new PosFood();
+            upd.setId(posFood.getId());
+            upd.setFoodSku(sku.toString());
+            posFoodService.updateById(upd);
+            posFood.setFoodSku(sku.toString());
+        }
+        foodSpecRelationService.remove(new LambdaQueryWrapper<FoodSpecRelation>()
+                .eq(FoodSpecRelation::getFoodId, posFood.getId()));
+        List<FoodSpecs> foodSpecs = posFood.getFoodSpecs();
+        if (foodSpecs != null && !foodSpecs.isEmpty()) {
+            List<FoodSpecRelation> rels = foodSpecs.stream()
+                    .filter(sp -> sp.getId() != null && sp.getId() > 0)
+                    .map(sp -> {
+                        FoodSpecRelation r = new FoodSpecRelation();
+                        r.setFoodId(posFood.getId());
+                        r.setSpecsId(sp.getId());
+                        return r;
+                    }).collect(Collectors.toList());
+            if (!rels.isEmpty()) {
+                foodSpecRelationService.saveBatch(rels);
+            }
+        }
+    }
+
+    /**
+     * 批量查询商品规格(避免 N+1):关联 -> 启用规格组 -> 启用规格值 -> 内存分组
+     * @return foodId -> 规格组列表(含启用规格值)
+     */
+    private Map<Long, List<FoodSpecs>> loadFoodSpecsMap(List<Long> foodIds) {
+        Map<Long, List<FoodSpecs>> result = new HashMap<>();
+        if (foodIds == null || foodIds.isEmpty()) {
+            return result;
+        }
+        List<FoodSpecRelation> rels = foodSpecRelationService.list(new LambdaQueryWrapper<FoodSpecRelation>()
+                .in(FoodSpecRelation::getFoodId, foodIds));
+        if (rels.isEmpty()) {
+            return result;
+        }
+        Map<Long, List<Long>> foodToSpecIds = rels.stream()
+                .collect(Collectors.groupingBy(FoodSpecRelation::getFoodId,
+                        Collectors.mapping(FoodSpecRelation::getSpecsId, Collectors.toList())));
+        List<Long> specsIds = rels.stream().map(FoodSpecRelation::getSpecsId).distinct().collect(Collectors.toList());
+        List<FoodSpecs> specsList = foodSpecsService.list(new LambdaQueryWrapper<FoodSpecs>()
+                .in(FoodSpecs::getId, specsIds)
+                .eq(FoodSpecs::getIsOpen, true)
+                .eq(FoodSpecs::getIsDelete, false)
+                .orderByAsc(FoodSpecs::getSort));
+        if (specsList.isEmpty()) {
+            return result;
+        }
+        List<Long> enabledSpecIds = specsList.stream().map(FoodSpecs::getId).collect(Collectors.toList());
+        List<FoodSpecsValue> values = foodSpecsValueService.list(new LambdaQueryWrapper<FoodSpecsValue>()
+                .in(FoodSpecsValue::getParentId, enabledSpecIds)
+                .eq(FoodSpecsValue::getIsOpen, true)
+                .orderByAsc(FoodSpecsValue::getId));
+        Map<Long, List<FoodSpecsValue>> valueMap = values.stream()
+                .collect(Collectors.groupingBy(FoodSpecsValue::getParentId));
+        Map<Long, FoodSpecs> specsMap = specsList.stream().collect(Collectors.toMap(FoodSpecs::getId, sp -> sp));
+        for (Map.Entry<Long, List<Long>> entry : foodToSpecIds.entrySet()) {
+            List<FoodSpecs> fsList = new ArrayList<>();
+            for (Long sid : entry.getValue()) {
+                FoodSpecs src = specsMap.get(sid);
+                if (src != null) {
+                    FoodSpecs copy = new FoodSpecs();
+                    copy.setId(src.getId());
+                    copy.setTitle(src.getTitle());
+                    copy.setType(src.getType());
+                    copy.setState(src.getState());
+                    copy.setLanguage(src.getLanguage());
+                    copy.setRemark(src.getRemark());
+                    copy.setMdId(src.getMdId());
+                    copy.setSort(src.getSort());
+                    copy.setIsOpen(src.getIsOpen());
+                    copy.setIsDelete(src.getIsDelete());
+                    copy.setObjects(valueMap.getOrDefault(src.getId(), new ArrayList<>()));
+                    fsList.add(copy);
+                }
+            }
+            result.put(entry.getKey(), fsList);
+        }
+        return result;
+    }
+
+    /**
+     * 由结构化规格构建兼容的 food_sku JSON 数组(明细字段名 foodSpecsItems)
+     */
+    private JSONArray buildFoodSkuArray(List<FoodSpecs> fsList) {
+        JSONArray skuArr = new JSONArray();
+        if (fsList == null || fsList.isEmpty()) {
+            return skuArr;
+        }
+        for (FoodSpecs s : fsList) {
+            skuArr.add(JSONObject.toJSON(s));
+        }
+        return skuArr;
+    }
+
+    private void fillStallLocation(PosStore store) {
+        if (store != null && store.getIsStall() != null && store.getIsStall() == 1
+                && store.getNightMarketId() != null) {
+            InfoUser nightMarket = infoUserService.getById(store.getNightMarketId());
+            if (nightMarket != null) {
+                store.setLongitude(nightMarket.getLongitude());
+                store.setLatitude(nightMarket.getLatitude());
+            }
+        }
+    }
+}