智能协同云图库项目
快来分享你的内容吧~
- 4 天前·保安问题复现 点击进入编辑,页面没变化 !image.png F12 打开控制台,两条消息,连接已建立,然后又连接已断开 !image.png 后端控制台报错, 没有办法序列化 !image.png 因为 的时间字段被我改成了 ,而 是需要手动添加序列化器的,因为异常最终导致 WebSocket 关闭。 解决 使用 提供的 代替自己new !image.png查看全文加油鸭:你精准定位了 LocalDateTime 序列化问题,并用 Spring 管理的 ObjectMapper 优雅解决,还兼顾了线程安全和错误处理,太棒了!441分享
- 07-13 23:00我在做云图库项目到部署上线阶段时,前后端已经成功部署到宝塔云上,我也能成功通过我的域名正常访问云图库,注册新账户也是注册成功且远程数据库也有信息,但是用已有的账号登录总是显示系统错误。我看了下我的redis密码正确且服务是正常运行 请问有人遇到过同样的问题吗?leikooo:重新注册一个测试账号,登录一下测试账号试试331分享
- 06-26 16:11·Java后端
- 06-15 10:11·保安原来的代码请求会被拒绝,参考这篇文章:https://www.codefather.cn/post/1892243198542032897 加上 字段去请求,发现还是拒绝 !image.png 参考这篇文章https://www.codefather.cn/post/2044002849209217025 发现把所有请求参数加上请求头,我太懒了,不想加,观察到 为 空字符串 尝试了一下,发现: 只查看全文加油鸭:太棒了!能通过抓包+实验定位到空 "Acs-Token" 这个关键点,说明你观察细致、动手能力强,调试思路非常清晰!👍731分享
- 05-26 20:54·Java后端查看全文学习完智能协同云图库项目后,想改造成springcloudalibaba架构的微服务项目,微服务划分如下: **依赖服务:** - **注册中心与配置中心:** Nacos(负责服务注册发现以及各服务的动态配置管理) - **微服务网关(yunpic-backend-gateway):** Gat...加油鸭:@编程导航小智 帮忙看看这个问题呢~421分享
- 03-26 23:29·Java后端全程都没问八股文,先问的实习经历,要讲清楚做了哪些业务做了什么,我对业务不太懂,讲的很模糊,实习做的东西也很简单,又问项目,让详细介绍一下,结合场景问的分布式锁、Satoken,问解决什么问题,key怎么设计,用什么注解了,怎么实现的校验,怎么解决缓存一致性,我没准备好,问的项目很深,我掌握的很浅只能说出大概怎么用。最后面试官说掌握业务很重要、简历上不要炫技,注重解决了什么问题、只会写普通代码的人查看全文青碧凝霜:又是被AI洗脑的小微。炫技这个事情各有所好,有的公司就指名道姓让你设计模式,越炫技越加分,有的公司就不愿意(比如我就不喜欢设计模式)。对一个刚毕业的实习生说你学得不深,那实在是有点……只能说:面试官说的信一半。多面试面试,总有适合自己的642分享
- 03-25 19:53大家好,我是鹿梦!先把体验 & 源码地址放最前面,欢迎大家直接上手体验:🌐 项目官网:https://www.yuemutuku.com📖 官方文档:https://official.yuemutuku.com/🧑💻 GitHub 地址:https://github.com/humenglover/yuemu🧑💻 Gitee 地址:https://gitee.com/lumengl查看全文leikooo:牛的🐮,准备也给我自己的项目加一个微信登录,想问一下这个对微信公众号有没有要求啥的😋121718分享
智能协同云图库-进入编辑报错解决
### 问题复现 点击进入编辑,页面没变化  F12 打开控制台,两条消息,连接已建立,然后又连接已断开  后端控制台报错,`LocalDataTime` 没有办法序列化  因为 `user` 的时间字段被我改成了 `LocalDateTime`,而 `LocalDateTime` 是需要手动添加序列化器的,因为异常最终导致 WebSocket 关闭。 ### 解决 **使用 `Spring` 提供的 `ObjectMapper` 代替自己new** ```java /** * 复用 Spring 管理的 ObjectMapper,统一支持 Long 和 LocalDateTime 序列化。 */ @Resource private ObjectMapper objectMapper; ```  ```java /** * 将响应信息广播给 集合里的session * * @param pictureId * @param pictureEditResponseMessage * @param excludeSession * @throws Exception */ private void broadcastToPicture(Long pictureId, PictureEditResponseMessage pictureEditResponseMessage, WebSocketSession excludeSession) throws Exception { Set<WebSocketSession> sessionSet = pictureSessions.get(pictureId); if (CollectionUtils.isEmpty(sessionSet)) { return; } // 使用 Spring 管理的 ObjectMapper,避免裸 ObjectMapper 无法处理 LocalDateTime。 String message = objectMapper.writeValueAsString(pictureEditResponseMessage); for (WebSocketSession session : sessionSet) { // 排除掉 session if (session.equals(excludeSession)) { continue; } if (session.isOpen()) { sendTextMessage(session, message); } } } /** * 向当前连接发送错误消息。 */ public void sendErrorMessage(WebSocketSession session, User user, String errorMessage) throws Exception { PictureEditResponseMessage responseMessage = new PictureEditResponseMessage(); responseMessage.setType(PictureEditMessageTypeEnum.ERROR.getValue()); responseMessage.setMessage(errorMessage); if (user != null) { responseMessage.setUser(userService.getUserVO(user)); } String message = objectMapper.writeValueAsString(responseMessage); sendTextMessage(session, message); } /** * WebSocketSession 不保证并发发送安全,统一同步发送。 */ private void sendTextMessage(WebSocketSession session, String message) throws Exception { if (session == null || !session.isOpen()) { return; } synchronized (session) { if (session.isOpen()) { session.sendMessage(new TextMessage(message)); } } } ``` 接收前端发来的消息,也要 使用 `objectMapper` 转化为对象 ```java /** * 收到前端的信息后处理 * * @param session * @param message * @throws Exception */ @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { User user = (User) session.getAttributes().get("user"); Long pictureId = (Long) session.getAttributes().get("pictureId"); PictureEditRequestMessage pictureEditRequestMessage; try { // TextMessage#getPayload() 是 JSON 字符串,必须使用 JSON 反序列化。 pictureEditRequestMessage = objectMapper.readValue( message.getPayload(), PictureEditRequestMessage.class ); } catch (JsonProcessingException e) { log.warn("图片编辑消息解析失败,sessionId={},payload={}", session.getId(), message.getPayload(), e); sendErrorMessage(session, user, "消息格式错误"); return; } PictureEditMessageTypeEnum messageTypeEnum = PictureEditMessageTypeEnum.getEnumByValue(pictureEditRequestMessage.getType()); if (messageTypeEnum == null) { sendErrorMessage(session, user, "消息类型错误"); return; } // 生产消息 pictureEditEventProducer.publishEvent( pictureEditRequestMessage, session, user, pictureId ); } ``` ### 完整代码 ```java import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.my.picturesystembackend.manager.websocket.disruptor.PictureEditEventProducer; import com.my.picturesystembackend.manager.websocket.model.PictureEditActionEnum; import com.my.picturesystembackend.manager.websocket.model.PictureEditMessageTypeEnum; import com.my.picturesystembackend.manager.websocket.model.PictureEditRequestMessage; import com.my.picturesystembackend.manager.websocket.model.PictureEditResponseMessage; import com.my.picturesystembackend.model.entity.User; import com.my.picturesystembackend.service.UserService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.handler.TextWebSocketHandler; import javax.annotation.Resource; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * 图片编辑处理 */ @Component @Slf4j public class PictureEditHandler extends TextWebSocketHandler { @Resource private UserService userService; @Resource private PictureEditEventProducer pictureEditEventProducer; /** * 复用 Spring 管理的 ObjectMapper,统一支持 Long 和 LocalDateTime 序列化。 */ @Resource private ObjectMapper objectMapper; // 当前正在编辑的图片的用户:key:pictureId value: 当前正在编辑的 userId private static final Map<Long, Long> pictureEditingUsers = new ConcurrentHashMap<>(); // 保存连接的所有会话,key: pictureId, value: 用户会话集合 private static final Map<Long, Set<WebSocketSession>> pictureSessions = new ConcurrentHashMap<>(); /** * 连接建立成功后保存信息 * * @param session * @throws Exception */ @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { super.afterConnectionEstablished(session); // 保存用户信息 User user = (User) session.getAttributes().get("user"); Long pictureId = (Long) session.getAttributes().get("pictureId"); // session 加到集合中 pictureSessions.computeIfAbsent(pictureId, key -> ConcurrentHashMap.newKeySet()).add(session); // 发送广播信息,通知在这个集合的所有用户,who 加进来了,相当于加入房间 PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage(); pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.INFO.getValue()); String message = String.format("用户 %s 加入编辑", user.getUserName()); pictureEditResponseMessage.setMessage(message); pictureEditResponseMessage.setUser(userService.getUserVO(user)); // 广播消息 broadcastToPicture(pictureId, pictureEditResponseMessage); } /** * 收到前端的信息后处理 * * @param session * @param message * @throws Exception */ @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { User user = (User) session.getAttributes().get("user"); Long pictureId = (Long) session.getAttributes().get("pictureId"); PictureEditRequestMessage pictureEditRequestMessage; try { // TextMessage#getPayload() 是 JSON 字符串,必须使用 JSON 反序列化。 pictureEditRequestMessage = objectMapper.readValue( message.getPayload(), PictureEditRequestMessage.class ); } catch (JsonProcessingException e) { log.warn("图片编辑消息解析失败,sessionId={},payload={}", session.getId(), message.getPayload(), e); sendErrorMessage(session, user, "消息格式错误"); return; } PictureEditMessageTypeEnum messageTypeEnum = PictureEditMessageTypeEnum.getEnumByValue(pictureEditRequestMessage.getType()); if (messageTypeEnum == null) { sendErrorMessage(session, user, "消息类型错误"); return; } // 生产消息 pictureEditEventProducer.publishEvent( pictureEditRequestMessage, session, user, pictureId ); } /** * 进入编辑状态 * * @param pictureEditRequestMessage * @param session * @param user * @param pictureId */ public void handleEnterEditMessage(PictureEditRequestMessage pictureEditRequestMessage, WebSocketSession session, User user, Long pictureId) throws Exception { // 一张图片同时只能有一个用户在编辑 // 判断是否有其他人在编辑 if (!pictureEditingUsers.containsKey(pictureId)) { // 加入编辑 pictureEditingUsers.put(pictureId, user.getId()); // 发送 广播 通知其他用户我已经进入编辑状态 PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage(); pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.ENTER_EDIT.getValue()); pictureEditResponseMessage.setMessage(String.format(" %s 开始编辑图片", user.getUserName())); pictureEditResponseMessage.setUser(userService.getUserVO(user)); broadcastToPicture(pictureId, pictureEditResponseMessage); } } /** * 处理编辑动作 * * @param pictureEditRequestMessage * @param session * @param user * @param pictureId */ public void handleEditActionMessage(PictureEditRequestMessage pictureEditRequestMessage, WebSocketSession session, User user, Long pictureId) throws Exception { // 判断当前正在编辑的用户是不是自己 Long currentEditUserId = pictureEditingUsers.get(pictureId); String editAction = pictureEditRequestMessage.getEditAction(); PictureEditActionEnum pictureEditActionEnum = PictureEditActionEnum.getEnumByValue(editAction); if (pictureEditActionEnum == null) { return; } if (currentEditUserId != null && currentEditUserId.equals(user.getId())) { // 是本人发送编辑通知 PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage(); pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.EDIT_ACTION.getValue()); pictureEditResponseMessage.setMessage(String.format("%s 执行了 %s 操作", user.getUserName(), pictureEditActionEnum.getText())); pictureEditResponseMessage.setEditAction(editAction); pictureEditResponseMessage.setUser(userService.getUserVO(user)); // 广播给处了自己以外的其他用户 broadcastToPicture(pictureId, pictureEditResponseMessage, session); } } /** * 退出编辑 * * @param pictureEditRequestMessage * @param session * @param user * @param pictureId */ public void handleExitEditMessage(PictureEditRequestMessage pictureEditRequestMessage, WebSocketSession session, User user, Long pictureId) throws Exception { // 校验当前正在编辑的用户是不是自己 Long currentEditUserId = pictureEditingUsers.get(pictureId); if (currentEditUserId != null && currentEditUserId.equals(user.getId())) { pictureEditingUsers.remove(pictureId); // 发送消息通知 PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage(); pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.EXIT_EDIT.getValue()); pictureEditResponseMessage.setMessage(String.format("%s 退出编辑图片", user.getUserName())); pictureEditResponseMessage.setUser(userService.getUserVO(user)); broadcastToPicture(pictureId, pictureEditResponseMessage); } } /** * 关闭连接的处理 * * @param session * @param status * @throws Exception */ @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { super.afterConnectionClosed(session, status); // 判断该用户是否正在编辑,如果是需要退出 // 从 session 中获取通用参数 User user = (User) session.getAttributes().get("user"); Long pictureId = (Long) session.getAttributes().get("pictureId"); handleExitEditMessage(null, session, user, pictureId); // 移除session Set<WebSocketSession> webSocketSessions = pictureSessions.get(pictureId); if (webSocketSessions != null) { webSocketSessions.remove(session); if (webSocketSessions.isEmpty()) { pictureSessions.remove(pictureId); } } // 发送消息退出编辑 PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage(); pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.INFO.getValue()); pictureEditResponseMessage.setMessage(String.format("%s离开编辑", user.getUserName())); pictureEditResponseMessage.setUser(userService.getUserVO(user)); broadcastToPicture(pictureId, pictureEditResponseMessage); } /** * 将响应信息广播给 集合里的session * * @param pictureId * @param pictureEditResponseMessage * @param excludeSession * @throws Exception */ private void broadcastToPicture(Long pictureId, PictureEditResponseMessage pictureEditResponseMessage, WebSocketSession excludeSession) throws Exception { Set<WebSocketSession> sessionSet = pictureSessions.get(pictureId); if (CollectionUtils.isEmpty(sessionSet)) { return; } // 使用 Spring 管理的 ObjectMapper,避免裸 ObjectMapper 无法处理 LocalDateTime。 String message = objectMapper.writeValueAsString(pictureEditResponseMessage); for (WebSocketSession session : sessionSet) { // 排除掉 session if (session.equals(excludeSession)) { continue; } if (session.isOpen()) { sendTextMessage(session, message); } } } /** * 向当前连接发送错误消息。 */ public void sendErrorMessage(WebSocketSession session, User user, String errorMessage) throws Exception { PictureEditResponseMessage responseMessage = new PictureEditResponseMessage(); responseMessage.setType(PictureEditMessageTypeEnum.ERROR.getValue()); responseMessage.setMessage(errorMessage); if (user != null) { responseMessage.setUser(userService.getUserVO(user)); } String message = objectMapper.writeValueAsString(responseMessage); sendTextMessage(session, message); } /** * WebSocketSession 不保证并发发送安全,统一同步发送。 */ private void sendTextMessage(WebSocketSession session, String message) throws Exception { if (session == null || !session.isOpen()) { return; } synchronized (session) { if (session.isOpen()) { session.sendMessage(new TextMessage(message)); } } } /** * 全体广播不排除 * * @param pictureId * @param pictureEditResponseMessage * @throws Exception */ private void broadcastToPicture(Long pictureId, PictureEditResponseMessage pictureEditResponseMessage) throws Exception { broadcastToPicture(pictureId, pictureEditResponseMessage, null); } } ```
我在做云图库项目到部署上线阶段时,前后端已经成功部署到宝塔云上,我也能成功通过我的域名正常访问云图库,注册新账户也是注册成功且远程数据库也有信息,但是用已有的账号登录总是显示系统错误。我看了下我的redis密码正确且服务是正常运行 请问有人遇到过同样的问题吗?
个人博客
花了两三天把博客搭建好了,欢迎大家进来参观访问,我会不定时的把我的学习笔记和面经都分享到这上面,感谢大家捧场! 个人博客网址:https://blog.cognipix.xyz 智能协同云图库上线网址:http://picture.cognipix.xyz
云图库-以图搜图失效解决
```java @Slf4j public class GetImagePageUrlApi { /** * 获取图片页面地址 * * @param imageUrl * @return */ public static String getImagePageUrl(String imageUrl) { // 1. 准备请求参数 Map<String, Object> formData = new HashMap<>(); formData.put("image", imageUrl); formData.put("tn", "pc"); formData.put("from", "pc"); formData.put("image_source", "PC_UPLOAD_URL"); // 获取当前时间戳 long uptime = System.currentTimeMillis(); // 请求地址 String url = "https://graph.baidu.com/upload?uptime=" + uptime; try { // 2. 发送 POST 请求到百度接口 HttpResponse response = HttpRequest.post(url) .form(formData) .timeout(5000) .execute(); // 判断响应状态 if (HttpStatus.HTTP_OK != response.getStatus()) { throw new BusinessException(ErrorCode.OPERATION_ERROR, "接口调用失败"); } // 解析响应 String responseBody = response.body(); Map<String, Object> result = JSONUtil.toBean(responseBody, Map.class); // 3. 处理响应结果 if (result == null || !Integer.valueOf(0).equals(result.get("status"))) { throw new BusinessException(ErrorCode.OPERATION_ERROR, "接口调用失败"); } Map<String, Object> data = (Map<String, Object>) result.get("data"); String rawUrl = (String) data.get("url"); // 对 URL 进行解码 String searchResultUrl = URLUtil.decode(rawUrl, StandardCharsets.UTF_8); // 如果 URL 为空 if (searchResultUrl == null) { throw new BusinessException(ErrorCode.OPERATION_ERROR, "未返回有效结果"); } return searchResultUrl; } catch (Exception e) { log.error("搜索失败", e); throw new BusinessException(ErrorCode.OPERATION_ERROR, "搜索失败"); } } public static void main(String[] args) { // 测试以图搜图功能 String imageUrl = "https://www.codefather.cn/logo.png"; String result = getImagePageUrl(imageUrl); System.out.println("搜索成功,结果 URL:" + result); } } ``` 原来的代码请求会被拒绝,参考这篇文章:https://www.codefather.cn/post/1892243198542032897 加上 `Acs-Token` 字段去请求,发现还是拒绝 ```java @Slf4j public class GetImagePageUrlApi { private static final String ACS_TOKEN = "1781414193742_1781484661412_B/FeawyvLLcVmoTxFS4f+v4brUzlXmzQD+EoUTk7NDyPpGMI+nF/YCa8JhYgXqTybW5S3uxfqQKp2m41y26ilXE57gd5vX0T6gb69u4P+HIcTwaiSdjVpxaB7DcxjsUFYysLB7QBgRPopl6GRHrGuvP+JS7tMn42kTmOoBbDg2arzdouZzelxLXjpCDbKCyJFK2TEwLrQTDqrPnkkudrE54KStdUj1ExQ2Hf3C2kC0q5Q1iemaezaxssutGYGaVxltTXDT5+9JimtHyPcuPYYrJtQSoX7qyN5PEajAop2vUDczktBuaG/yyLX+vTrg7kPMI3TMP1sy7ne4R7XMvziKj/Pqw8l/T9pFLlMuSQ+bKnRc6suyAYa+LFbdBpgwDOep03D4Xlj3ETVm9pzmiC7GAe8FzE/FdcGITrT76OWtY="; /** * 获取以图搜图页面地址 * * @param imageUrl 图片url * @return 搜索结果url */ public static String getImagePageUrl(String imageUrl) { // 1. 准备请求参数 Map<String, Object> formData = new HashMap<>(); formData.put("image", imageUrl); formData.put("tn", "pc"); formData.put("from", "pc"); formData.put("image_source", "PC_UPLOAD_URL"); // 获取当前时间戳 long uptime = System.currentTimeMillis(); // 请求地址 String url = "https://graph.baidu.com/upload?uptime=" + uptime; try { // 2. 发送 POST 请求到百度接口 HttpResponse response = HttpRequest.post(url) .form(formData) .header("Acs-Token", ACS_TOKEN) .timeout(5000) .execute(); // 判断响应状态 if (HttpStatus.HTTP_OK != response.getStatus()) { throw new BusinessException(ErrorCode.OPERATION_ERROR, "接口调用失败"); } // 解析响应 String responseBody = response.body(); Map<String, Object> result = JSONUtil.toBean(responseBody, Map.class); // 处理响应结果 if (result == null || !Integer.valueOf(0).equals(result.get("status"))) { throw new BusinessException(ErrorCode.OPERATION_ERROR, "接口调用失败"); } Map<String, Object> data = (Map<String, Object>) result.get("data"); String rawUrl = (String) data.get("url"); // 对 URL 进行解码 String searchResultUrl = URLUtil.decode(rawUrl, StandardCharsets.UTF_8); // 如果url 为空 if (searchResultUrl == null) { throw new BusinessException(ErrorCode.OPERATION_ERROR, "未返回有效结果"); } return searchResultUrl; } catch (Exception e) { log.error("搜索失败", e); throw new BusinessException(ErrorCode.OPERATION_ERROR, "搜索失败"); } } public static void main(String[] args) { // 测试意图搜索功能 String imageUrl = "https://haowallpaper.com/link//common/file/previewFileImg/19095722698298240"; String result = getImagePageUrl(imageUrl); System.out.println("搜索成功,结果 URL: " + result); } } ```  参考这篇文章https://www.codefather.cn/post/2044002849209217025  发现是把所有浏览器的请求头参数加上,我太懒了,不想加,**观察到 `Acs-Token` 为 空字符串** 尝试了一下,发现: **只需要 空字符就可以请求成功**  *** 完整代码 ```java import cn.hutool.core.util.URLUtil; import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpResponse; import cn.hutool.http.HttpStatus; import cn.hutool.json.JSONUtil; import com.my.picturesystembackend.exception.BusinessException; import com.my.picturesystembackend.exception.ErrorCode; import lombok.extern.slf4j.Slf4j; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; @Slf4j public class GetImagePageUrlApi { /** * 获取以图搜图页面地址 * * @param imageUrl 图片url * @return 搜索结果url */ public static String getImagePageUrl(String imageUrl) { // 1. 准备请求参数 Map<String, Object> formData = new HashMap<>(); formData.put("image", imageUrl); formData.put("tn", "pc"); formData.put("from", "pc"); formData.put("image_source", "PC_UPLOAD_URL"); // 获取当前时间戳 long uptime = System.currentTimeMillis(); // 请求地址 String url = "https://graph.baidu.com/upload?uptime=" + uptime; try { // 2. 发送 POST 请求到百度接口 HttpResponse response = HttpRequest.post(url) .form(formData) .header("Acs-Token", "") .timeout(5000) .execute(); // 判断响应状态 if (HttpStatus.HTTP_OK != response.getStatus()) { throw new BusinessException(ErrorCode.OPERATION_ERROR, "接口调用失败"); } // 解析响应 String responseBody = response.body(); Map<String, Object> result = JSONUtil.toBean(responseBody, Map.class); // 处理响应结果 if (result == null || !Integer.valueOf(0).equals(result.get("status"))) { throw new BusinessException(ErrorCode.OPERATION_ERROR, "接口调用失败"); } Map<String, Object> data = (Map<String, Object>) result.get("data"); String rawUrl = (String) data.get("url"); // 对 URL 进行解码 String searchResultUrl = URLUtil.decode(rawUrl, StandardCharsets.UTF_8); // 如果url 为空 if (searchResultUrl == null) { throw new BusinessException(ErrorCode.OPERATION_ERROR, "未返回有效结果"); } return searchResultUrl; } catch (Exception e) { log.error("搜索失败", e); throw new BusinessException(ErrorCode.OPERATION_ERROR, "搜索失败"); } } public static void main(String[] args) { // 测试意图搜索功能 String imageUrl = "https://haowallpaper.com/link//common/file/previewFileImg/19095722698298240"; String result = getImagePageUrl(imageUrl); System.out.println("搜索成功,结果 URL: " + result); } } ``` ### 技巧 1. F12 观察接口的响应结果,显示不出来,换上火狐,我也显示不出来(doge) 2. 请求该接口页面会进行刷新,导致网络控制台的内容被清空,开启如下图这个可以防止清空而看不到请求。  ### 封面 
学习完智能协同云图库项目后,想改造成springcloudalibaba架构的微服务项目,微服务划分如下: **依赖服务:** - **注册中心与配置中心:** Nacos(负责服务注册发现以及各服务的动态配置管理) - **微服务网关(yunpic-backend-gateway):** Gateway 聚合所有接口,统一接收处理前端请求,负责路由转发、鉴权与限流 **公共模块:** - **common 公共模块(yunpic-backend-common):** 全局异常处理器、统一响应结果封装类、通用的工具类(如日期、HTTP、文件处理工具)等。 - **model 模型模块(yunpic-backend-model):** 存放各个服务公用的实体类、数据传输对象(DTO)。 - **公用接口模块(yunpic-backend-service-client):** 只存放 Feign 远程调用接口和共享的 DTO,不存放具体实现,供多个服务之间互相调用。 **业务功能:** 1. **用户与权限服务(yunpic-backend-user-service:8102 端口)** 1. 用户注册、登录、注销(对接 JWT/OAuth2 鉴权) 2. 用户信息管理(【管理员】管理用户) 3. 基础用户权限控制(区分普通用户与管理员角色) 2. **图片资源服务(yunpic-backend-image-service:8103 端口)** 1. 图片上传创建(用户/管理员)、图片下载 2. 图片信息编辑(标签/分类等)、【管理员】管理图片 3. 查看和搜索图片列表(基础属性搜索、颜色搜索)、查看图片详情 4. 导入图片(URL 导入、【管理员】批量抓取) 5. 【管理员】审核图片(可异步触发) 6. 图片分享(链接分享、扫码分享) 3. **空间与协作服务(yunpic-backend-space-service:8104 端口)** 1. 用户开通私有空间、创建团队共享空间、【管理员】管理空间 2. 空间级别和限额控制、空间图库分析(用户及全空间维度) 3. 空间成员管理(邀请成员、设置权限) 4. 空间维度的精细化权限控制(RBAC 模型,判断用户在特定空间的浏览/编辑权限) 5. 图片协同编辑(基于 WebSocket 维持长连接,处理多人实时操作与逻辑锁) 4. **AI 与图片处理服务(yunpic-backend-ai-service:8105 端口)** 1. AI 图片编辑(如 AI 扩图、智能修图) 2. 以图搜图(提取图片向量特征并进行比对检索) 3. 基础图片编辑(裁剪、旋转等底层处理) 4. 开放接口(为其他服务提供独立的 AI 处理能力) 但是遇到了问题,原项目中manager包中的各个包不知道应该怎么拆才合理,想请教下各位大佬能不能指点一下🥲
Vue3 + SpringBoot + AI + DDD 企业级智能协同云图库项目(25 年最新) 笔记
### 关于以图搜图的平替 现在来学这个项目,以图搜图使用不了了,有没有一个好的平替,百度要逆向sign,360也使用不了了。Sad~~
智能云图库-以图搜图解决办法
之前发现教程里百度的那个用不了了,然后360的平替也不行,之后看到https://www.codefather.cn/user/1855959217308680193 找的解决方法,非常感谢!。但是那段代码因为是原生的嘛,有点复杂,那段代码的核心逻辑就是模仿用户操作给它发请求,那么我就尝试自己弄了一个。来替代教程里的GetImagePageUrlApi方法来获取URL。这样的话我们只需要改这个文件和门面类的调用就好了! 代码如下: ```java import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpResponse; import cn.hutool.http.HttpUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; public class BaiduImageSearchUtil { /** * 通过图片 URL 上传到百度图片搜索 * @param imageUrl 图片的 URL 地址 * @return 百度返回的 JSON 结果 */ public static String uploadImageByUrl(String imageUrl) { // 下载图片 byte[] imageBytes = HttpUtil.downloadBytes(imageUrl); // 上传到百度 long uptime = System.currentTimeMillis(); String uploadUrl = "https://graph.baidu.com/upload?uptime=" + uptime; HttpResponse response = HttpRequest.post(uploadUrl) .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36") .header("Accept", "*/*") .header("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8") .header("Origin", "https://graph.baidu.com") .header("Referer", "https://graph.baidu.com/") .header("Sec-Fetch-Dest", "empty") .header("Sec-Fetch-Mode", "cors") .header("Sec-Fetch-Site", "same-origin") .header("Acs-Token", "") .form("image", imageBytes, "image.jpg") .form("tn", "pc") .form("from", "pc") .form("image_source", "PC_UPLOAD_URL") .timeout(30000) .execute(); JSONObject jsonObject = JSONUtil.parseObj(response.body()); String url = (String) jsonObject.get("data", JSONObject.class).get("url"); return url; } public static void main(String[] args) { String imageUrl = "https://cn.bing.com/th/id/OIP-C.IJZgTNx1vp9EML_1wV5p2gHaEo?w=233&h=180&c=7&r=0&o=7&dpr=1.3&pid=1.7&rm=3"; // JSONObject result = uploadImageByUrl(imageUrl); String result = uploadImageByUrl(imageUrl); System.out.println(result); } } ``` 门面类: ```java @Slf4j public class ImageSearchApiFacade { /** * 搜索图片 * * @param imageUrl * @return */ public static List<ImageSearchResult> searchImage(String imageUrl) { String imagePageUrl = BaiduImageSearchUtil.uploadImageByUrl(imageUrl); String imageFirstUrl = GetImageFirstUrlApi.getImageFirstUrl(imagePageUrl); List<ImageSearchResult> imageList = GetImageListApi.getImageList(imageFirstUrl); return imageList; } public static void main(String[] args) { // 测试以图搜图功能 String imageUrl = "https://cn.bing.com/th/id/OIP-C.IJZgTNx1vp9EML_1wV5p2gHaEo?w=233&h=180&c=7&r=0&o=7&dpr=1.3&pid=1.7&rm=3"; List<ImageSearchResult> resultList = searchImage(imageUrl); System.out.println("结果列表" + resultList); } } ```  获取全部图片url测试结果:  获取具体图片的url测试结果: 结果列表[ImageSearchResult(thumbUrl=http://mms2.baidu.com/it/u=569158274,4256921924&fm=253&app=138&f=JPEG?w=200&h=150, fromUrl=http://jingyan.baidu.com/article/454316abac8d47f7a7c03ac4.html), ImageSearchResult(thumbUrl=http://mms0.baidu.com/it/u=3279374365,677980737&fm=253&app=138&f=JPEG?w=371&h=247, fromUrl=http://hellorf.com/image/show/633962915), ImageSearchResult(thumbUrl=http://mms2.baidu.com/it/u=955193541,3698081520&fm=253&app=138&f=JPEG?w=330&h=247, fromUrl=http://www.hellorf.com/image/show/268563467)... 
关于智能协同云图库的分库分表配置失败问题
本人完全按照鱼总教程的格式,但是还是出现一大片报错,就是createBean失败等等 Can not find data source type 经过排查应该是对应sharingSphere没有识别或者没有正确链接到mysql 如果理解有误或者会出现什么隐藏的问题,请大佬们多多指教。 我按以下方式解决: 然后yml中修改数据库配置(不带参数 ShardingSphere 5.2.0 就会可能抛 SQLWrapperException): ```yaml # 空间图片分表 shardingsphere: datasource: names: yun_picture yun_picture: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/yun_picture?useSSL=false&characterEncoding=utf8&serverTimezone=UTC&allowPublicKeyRetrieval=true username: root password: 1234 rules: sharding: tables: picture: actual-data-nodes: yun_picture.picture # 动态分表 table-strategy: standard: sharding-column: spaceId sharding-algorithm-name: picture_sharding_algorithm # 使用自定义分片算法 sharding-algorithms: picture_sharding_algorithm: type: CLASS_BASED props: strategy: standard algorithmClassName: com.hhz.yunPictureBackend.manager.sharding.PictureShardingAlgorithm props: sql-show: true ``` 在pom.xml添加(可以尝试不加看看): ```xml 第二步:确保 pom.xml 有数据库连接池依赖(必须有) xml <!-- 连接池 HikariCP(Spring Boot 默认自带,但有时候被排除了,加上保险) --> <dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> </dependency> <!-- MySQL --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> ``` 这样以后就能正常出现表了 
小公司一面
全程都没问八股文,先问的实习经历,要讲清楚做了哪些业务做了什么,我对业务不太懂,讲的很模糊,实习做的东西也很简单,又问项目,让详细介绍一下,结合场景问的分布式锁、Satoken,问解决什么问题,key怎么设计,用什么注解了,怎么实现的校验,怎么解决缓存一致性,我没准备好,问的项目很深,我掌握的很浅只能说出大概怎么用。最后面试官说掌握业务很重要、简历上不要炫技,注重解决了什么问题、只会写普通代码的人已经不需要了,AI都能代替了。让我再往深学习一下。这是线下面试,HR也挺热情的。反正就是深挖实习和项目。
悦木图库全量开源!重新做了一个我理想中的图片社区
大家好,我是鹿梦! 先把体验 & 源码地址放最前面,欢迎大家直接上手体验: 🌐 项目官网:https://www.yuemutuku.com 📖 官方文档:https://official.yuemutuku.com/ 🧑💻 GitHub 地址:https://github.com/humenglover/yuemu 🧑💻 Gitee 地址:https://gitee.com/lumenglover/yuemu 特别说一句:移动端体验大幅优化,手机打开非常丝滑,随时随地都能用! --- 拖更很久、重构了无数次之后,今天终于可以说一句:**悦木图库最新版,全量开源了!** 从最开始只是想做一个好用的图片管理工具,到现在慢慢长成了集 **AI 创作、社交互动、团队空间、智能检索** 于一体的视觉创意社区,这一路改了又拆、拆了又写,也算给自己一个交代. 这次开源的是完整前后端,不藏功能、不砍模块,**线上跑的是什么,开源就是什么**。 --- ## 🚀 这次到底更了啥?(我按真实使用场景说) ### 🤖 AI 部分是真的能用,不是玩具 - 完整接入 **RAG 检索增强**,可以挂自己的知识库,对话带记忆,支持 SSE 流式输出,做图库管理助手很舒服 - 内置 **YOLOv8 目标检测**、AI 扩图、智能去背景、人脸模糊、MODNet 智能换底 - 不是接口套壳,是真正整合进业务流程里的**实用 AI 工具链** ### 🔐 登录与权限直接重新设计 - 支持 **微信扫码一键登录**,接入公众号,用户体验拉满 - 后端全面改用 **Sa-Token**,分布式会话、多端登录控制、Redis 精细限流都配齐 - 接口安全、防刷、权限控制都按生产环境标准写的 ### 🛡️ 内容安全我也拉满了 - 集成 **腾讯云/百度审核**,图片+文本双审核,AOP 切面自动拦截 - 前端加了举报中心,后台配套 20+ 个子系统管理,**从发布到审计形成完整闭环** ### 📝 富文本、首页、交互全部重写 - 帖子详情页深度重做,富文本排版更舒服,发分享、做教程质感很在线 - 首页全面重构,交互更流畅 - 一套代码适配 **PC(AntD)+ 移动端(Vant)**     ### 📦 技术栈简单说 - 后端:SpringBoot、Redis、ElasticSearch、MySQL、ShardingSphere 分表 - 前端:Vue3、Vite、AntD、Vant - AI:RAG、YOLO、DeepSeek、千问、Tavily、百度搜索 - 部署:Docker Compose 一键启动,基本傻瓜式 --- 对我来说,这不仅仅是一个项目,更是一段完整的从 0 到 1 的实践。 **每一个 Star 都是真的鼓励**,也欢迎大家提 Issue、PR,一起把它做得更稳、更好用。 悦木图库,想用 AI 和认真的代码,让大家更好地**发现、分享、创造美好瞬间**。 鹿梦 2026
