fix(food-encyclopedia): 后台食物百科列表/编辑页接入并修复图片URL双前缀

- 新增 FoodEncyclopediaController 及 ToolFoodAdminService,提供 /api/admin/tool/food/* CRUD
- ToolFoodAdminServiceImpl 在保存前 clearPrefix 并正则修复历史脏数据中的多层 host 前缀
- 前端 list.vue/edit.vue 修复二次解包导致 listData.list 渲染崩溃
- edit.vue 加载详情时兜底归一化 image 字段,处理 https://host//https://host//crmebimage/... 形式
- content.js 注册 foodManager / foodEdit 路由

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
msh-agent
2026-05-03 01:08:34 +08:00
parent d7c870ced7
commit a3b609e70a
7 changed files with 1013 additions and 0 deletions

View File

@@ -0,0 +1,134 @@
package com.zbkj.service.service.impl.tool;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.pagehelper.PageHelper;
import com.zbkj.common.model.tool.V2Food;
import com.zbkj.common.request.PageParamRequest;
import com.zbkj.service.dao.tool.V2FoodDao;
import com.zbkj.service.service.SystemAttachmentService;
import com.zbkj.service.service.tool.ToolFoodAdminService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
/**
* 食物百科管理后台服务实现类
* +----------------------------------------------------------------------
* | 提供后台 CRUD 操作,图片维护等功能
* +----------------------------------------------------------------------
*/
@Slf4j
@Service
public class ToolFoodAdminServiceImpl implements ToolFoodAdminService {
@Resource
private V2FoodDao v2FoodDao;
@Resource
private SystemAttachmentService systemAttachmentService;
@Override
public List<V2Food> getAdminList(String keyword, String category, String status,
PageParamRequest pageParamRequest) {
PageHelper.startPage(pageParamRequest.getPage(), pageParamRequest.getLimit());
LambdaQueryWrapper<V2Food> wrapper = new LambdaQueryWrapper<>();
// 关键词搜索(名称或别名)
if (StrUtil.isNotBlank(keyword)) {
wrapper.and(w -> w.like(V2Food::getName, keyword)
.or().like(V2Food::getAlias, keyword));
}
// 分类筛选
if (StrUtil.isNotBlank(category)) {
wrapper.eq(V2Food::getCategory, category);
}
// 状态筛选
if (StrUtil.isNotBlank(status)) {
wrapper.eq(V2Food::getStatus, status);
}
wrapper.orderByDesc(V2Food::getSortOrder)
.orderByDesc(V2Food::getUpdatedAt);
return v2FoodDao.selectList(wrapper);
}
@Override
public V2Food getById(Long id) {
return v2FoodDao.selectById(id);
}
@Override
public boolean create(V2Food food) {
Date now = new Date();
food.setImage(normalizeImage(food.getImage()));
food.setCreatedAt(now);
food.setUpdatedAt(now);
if (food.getStatus() == null) {
food.setStatus("active");
}
if (food.getSortOrder() == null) {
food.setSortOrder(0);
}
if (food.getViewCount() == null) {
food.setViewCount(0);
}
return v2FoodDao.insert(food) > 0;
}
@Override
public boolean updateFood(V2Food food) {
food.setImage(normalizeImage(food.getImage()));
food.setUpdatedAt(new Date());
return v2FoodDao.updateById(food) > 0;
}
/**
* 规范化图片字段:
* 1. 先 clearPrefix 剥掉单层 CDN 前缀
* 2. 再用正则去掉残留的「http(s)://xxx/」段,修复历史脏数据中的双前缀
*/
private String normalizeImage(String image) {
if (StrUtil.isBlank(image)) {
return image;
}
String cleaned = systemAttachmentService.clearPrefix(image);
// 处理历史脏数据:形如 "https://.../crmebimage/..." 或 "https://.../https://.../crmebimage/..."
// 去除 "crmebimage/" 之前的所有 "http(s)://...//?" 段
return cleaned.replaceAll("^(https?://[^/]+/+)+(?=crmebimage/)", "");
}
@Override
public boolean deleteById(Long id) {
return v2FoodDao.deleteById(id) > 0;
}
@Override
public boolean changeStatus(Long id, String status) {
V2Food food = new V2Food();
food.setFoodId(id);
food.setStatus(status);
food.setUpdatedAt(new Date());
return v2FoodDao.updateById(food) > 0;
}
@Override
public List<String> getAllCategories() {
LambdaQueryWrapper<V2Food> wrapper = new LambdaQueryWrapper<>();
wrapper.select(V2Food::getCategory)
.isNotNull(V2Food::getCategory)
.ne(V2Food::getCategory, "")
.groupBy(V2Food::getCategory);
return v2FoodDao.selectList(wrapper).stream()
.map(V2Food::getCategory)
.filter(StrUtil::isNotBlank)
.distinct()
.sorted()
.collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,51 @@
package com.zbkj.service.service.tool;
import com.zbkj.common.model.tool.V2Food;
import com.zbkj.common.request.PageParamRequest;
import java.util.List;
/**
* 食物百科管理后台服务接口
* +----------------------------------------------------------------------
* | 提供后台 CRUD 操作,图片维护等功能
* +----------------------------------------------------------------------
*/
public interface ToolFoodAdminService {
/**
* 后台分页列表(支持关键词、分类、状态筛选)
*/
List<V2Food> getAdminList(String keyword, String category, String status,
PageParamRequest pageParamRequest);
/**
* 根据ID获取食物<E9A39F><E789A9><EFBFBD>
*/
V2Food getById(Long id);
/**
* 新增食物
*/
boolean create(V2Food food);
/**
* 修改食物
*/
boolean updateFood(V2Food food);
/**
* 删除食物
*/
boolean deleteById(Long id);
/**
* 修改状态(上架/下架)
*/
boolean changeStatus(Long id, String status);
/**
* 获取所有分类列表
*/
List<String> getAllCategories();
}