| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245 |
- package com.ruoyi.app.mendian;
- import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
- import com.baomidou.mybatisplus.core.metadata.IPage;
- import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
- import com.ruoyi.common.annotation.Anonymous;
- import com.ruoyi.common.core.controller.BaseController;
- import com.ruoyi.common.core.domain.AjaxResult;
- import com.ruoyi.system.domain.FoodSpecs;
- import com.ruoyi.system.domain.FoodSpecsValue;
- import com.ruoyi.system.service.IFoodSpecsService;
- import com.ruoyi.system.service.IFoodSpecsValueService;
- import com.ruoyi.system.utils.Auth;
- import com.ruoyi.system.utils.JwtUtil;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.transaction.annotation.Transactional;
- import org.springframework.web.bind.annotation.*;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.Map;
- import java.util.stream.Collectors;
- /**
- * 商品规格管理 Controller(规格组 + 级联规格值)
- * 门店级可复用规格模板
- *
- * @author ruoyi
- * @date 2026-07-15
- */
- @RestController
- @RequestMapping("/chanting/foodSpec")
- public class FoodSpecController extends BaseController
- {
- @Autowired
- private IFoodSpecsService foodSpecsService;
- @Autowired
- private IFoodSpecsValueService foodSpecsValueService;
- /**
- * 规格组分页列表(每条带 foodSpecsItems)
- */
- @Anonymous
- @Auth
- @GetMapping("/foodSpecPageList")
- public AjaxResult foodSpecPageList(@RequestParam(defaultValue = "1") Integer pageNum,
- @RequestParam(defaultValue = "10") Integer pageSize,
- @RequestParam Long mdId,
- @RequestParam(required = false) String language) {
- Page<FoodSpecs> page = new Page<>(pageNum, pageSize);
- LambdaQueryWrapper<FoodSpecs> qw = new LambdaQueryWrapper<>();
- qw.eq(FoodSpecs::getMdId, mdId);
- qw.eq(FoodSpecs::getIsDelete, false);
- if (language != null && !"".equals(language)) {
- qw.eq(FoodSpecs::getLanguage, language);
- }
- qw.orderByAsc(FoodSpecs::getSort).orderByDesc(FoodSpecs::getId);
- IPage<FoodSpecs> result = foodSpecsService.page(page, qw);
- fillItems(result.getRecords(), false);
- return success(result);
- }
- /**
- * 保存规格组(含级联规格值):id<=0 新增,否则更新
- */
- @Anonymous
- @Auth
- @PostMapping("/saveFoodSpec")
- @Transactional(rollbackFor = Exception.class)
- public AjaxResult saveFoodSpec(@RequestBody List<FoodSpecs> list) {
- if (list == null || list.isEmpty()) {
- return error();
- }
- for (FoodSpecs s : list) {
- if (s.getIsDelete() == null) {
- s.setIsDelete(false);
- }
- if (s.getIsOpen() == null) {
- s.setIsOpen(true);
- }
- saveOrUpdateSpecs(s);
- // 级联规格值(全量替换:先删前端未保留的,再保存/新增)
- if (s.getObjects() != null) {
- removeDeletedValues(s);
- for (FoodSpecsValue v : s.getObjects()) {
- v.setParentId(s.getId());
- if (v.getIsOpen() == null) {
- v.setIsOpen(true);
- }
- saveOrUpdateValue(v);
- }
- }
- }
- return success();
- }
- private void saveOrUpdateSpecs(FoodSpecs s) {
- if (s.getId() == null || s.getId() <= 0) {
- s.setId(null);
- foodSpecsService.save(s);
- } else {
- foodSpecsService.updateById(s);
- }
- }
- private void saveOrUpdateValue(FoodSpecsValue v) {
- if (v.getId() == null || v.getId() <= 0) {
- v.setId(null);
- foodSpecsValueService.save(v);
- } else {
- foodSpecsValueService.updateById(v);
- }
- }
- /**
- * 规格值全量替换:删除该规格组下 DB 有、但前端 objects 未传(id>0 集合外)的规格值(物理删除)。
- * 仅对已存在规格组(id>0)生效;新增规格组无历史数据,无需 diff。
- */
- private void removeDeletedValues(FoodSpecs s) {
- if (s.getId() == null || s.getId() <= 0) {
- return;
- }
- // 前端保留的规格值 id(id>0 的;id<=0 是新增,不参与 diff)
- List<Long> keepIds = s.getObjects().stream()
- .map(FoodSpecsValue::getId)
- .filter(id -> id != null && id > 0)
- .collect(Collectors.toList());
- // DB 中该规格组的全部规格值 id
- List<Long> dbIds = foodSpecsValueService.list(
- new LambdaQueryWrapper<FoodSpecsValue>()
- .eq(FoodSpecsValue::getParentId, s.getId())
- .select(FoodSpecsValue::getId)
- ).stream().map(FoodSpecsValue::getId).collect(Collectors.toList());
- // DB 有但前端没传 → 物理删除
- List<Long> toRemove = dbIds.stream()
- .filter(id -> !keepIds.contains(id))
- .collect(Collectors.toList());
- if (!toRemove.isEmpty()) {
- foodSpecsValueService.removeByIds(toRemove);
- }
- }
- /**
- * 规格详情(带全部规格值)
- */
- @Anonymous
- @Auth
- @GetMapping("/getSpecs")
- public AjaxResult getSpecs(@RequestParam Long id) {
- FoodSpecs s = foodSpecsService.getById(id);
- if (s != null) {
- List<FoodSpecs> list = new ArrayList<>();
- list.add(s);
- fillItems(list, false);
- }
- return success(s);
- }
- /**
- * 软删除规格组(is_delete=1)
- */
- @Anonymous
- @Auth
- @GetMapping("/deleteFoodSpec")
- public AjaxResult deleteFoodSpec(@RequestParam Long id) {
- FoodSpecs s = new FoodSpecs();
- s.setId(id);
- s.setIsDelete(true);
- return toAjax(foodSpecsService.updateById(s));
- }
- /**
- * 商品编辑页:本门店可用规格(is_open=1 & is_delete=0,仅含 is_open=1 的规格值)
- */
- @Anonymous
- @Auth
- @GetMapping("/getAvailableSpecsList")
- public AjaxResult getAvailableSpecsList(@RequestParam Long mdId,
- @RequestParam(required = false) String language) {
- LambdaQueryWrapper<FoodSpecs> qw = new LambdaQueryWrapper<>();
- qw.eq(FoodSpecs::getMdId, mdId);
- qw.eq(FoodSpecs::getIsOpen, true);
- qw.eq(FoodSpecs::getIsDelete, false);
- if (language != null && !"".equals(language)) {
- qw.eq(FoodSpecs::getLanguage, language);
- }
- qw.orderByAsc(FoodSpecs::getSort).orderByDesc(FoodSpecs::getId);
- List<FoodSpecs> list = foodSpecsService.list(qw);
- fillItems(list, true);
- return success(list);
- }
- /**
- * 启停规格组
- */
- @Anonymous
- @Auth
- @GetMapping("/changeOpen")
- public AjaxResult changeOpen(@RequestParam Long id, @RequestParam Boolean isOpen) {
- FoodSpecs s = new FoodSpecs();
- s.setId(id);
- s.setIsOpen(isOpen);
- return toAjax(foodSpecsService.updateById(s));
- }
- /**
- * 启停规格值
- */
- @Anonymous
- @Auth
- @GetMapping("/changeSpecValueOpen")
- public AjaxResult changeSpecValueOpen(@RequestParam Long id, @RequestParam Boolean isOpen) {
- FoodSpecsValue v = new FoodSpecsValue();
- v.setId(id);
- v.setIsOpen(isOpen);
- return toAjax(foodSpecsValueService.updateById(v));
- }
- /**
- * 批量填充规格值到规格组(避免 N+1)
- *
- * @param onlyOpen true=只取 is_open=1 的规格值(用于商品可用规格)
- */
- private void fillItems(List<FoodSpecs> list, boolean onlyOpen) {
- if (list == null || list.isEmpty()) {
- return;
- }
- List<Long> ids = list.stream().map(FoodSpecs::getId).collect(Collectors.toList());
- LambdaQueryWrapper<FoodSpecsValue> qw = new LambdaQueryWrapper<FoodSpecsValue>()
- .in(FoodSpecsValue::getParentId, ids)
- .orderByAsc(FoodSpecsValue::getId);
- if (onlyOpen) {
- qw.eq(FoodSpecsValue::getIsOpen, true);
- }
- List<FoodSpecsValue> values = foodSpecsValueService.list(qw);
- Map<Long, List<FoodSpecsValue>> map = values.stream()
- .collect(Collectors.groupingBy(FoodSpecsValue::getParentId));
- for (FoodSpecs s : list) {
- s.setObjects(map.getOrDefault(s.getId(), new ArrayList<>()));
- }
- }
- }
|