Procházet zdrojové kódy

feat: 集成许愿树详情接口、修复图片展示、优化标签布局等

- 许愿树详情接口改为 POST /detail,传入 lng/lat 获取距离
- 详情页和许愿页先等待定位完成再请求接口
- 上传图片带上 Authorization token 头
- 修复愿望图片不展示问题:移除 lazy-load、修复 ImageUploader 响应式
- 标签换行增加上下间距,使用 flex gap 布局
- 新增 TreeDetailReq/TreeDetailResp、AttachmentService 等后端接口
- 重命名 Wish → UserWish, WishController → UserWishController

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
tanlie před 3 týdny
rodič
revize
a8400c01c4
30 změnil soubory, kde provedl 361 přidání a 144 odebrání
  1. 15 5
      wishing-platform/platform-entity/platform-entity-wishing/src/main/java/cn/qinys/platform/entity/wishing/UserWish.java
  2. binární
      wishing-platform/platform-entity/platform-entity-wishing/target/classes/cn/qinys/platform/entity/wishing/UserWish.class
  3. 1 1
      wishing-platform/platform-entity/platform-entity-wishing/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
  4. 1 1
      wishing-platform/platform-entity/platform-entity-wishing/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
  5. binární
      wishing-platform/platform-entity/platform-entity-wishing/target/platform-entity-wishing-1.0.0-SNAPSHOT.jar
  6. 17 4
      wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/config/MobileMvcConfig.java
  7. 8 40
      wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/controller/FileUploadController.java
  8. 7 5
      wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/controller/TreeController.java
  9. 4 4
      wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/controller/UserWishController.java
  10. 3 2
      wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/mapper/UserWishMapper.java
  11. 6 1
      wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/mapper/WishingTreeMapper.java
  12. 25 0
      wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/req/TreeDetailReq.java
  13. 39 0
      wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/resp/TreeDetailResp.java
  14. 24 0
      wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/service/AttachmentService.java
  15. 1 1
      wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/service/UserWishService.java
  16. 3 1
      wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/service/WishTreeService.java
  17. 98 0
      wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/service/impl/AttachmentServiceImpl.java
  18. 20 21
      wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/service/impl/UserWishServiceImpl.java
  19. 17 19
      wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/service/impl/WishTreeServiceImpl.java
  20. 21 0
      wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/properties/SystemProperties.java
  21. 3 0
      wishing-platform/platform-service/platform-service-mobile/src/main/resources/application-local.yml
  22. 5 2
      wishing-tree-h5/src/api/tree.ts
  23. 8 0
      wishing-tree-h5/src/api/upload.ts
  24. 2 8
      wishing-tree-h5/src/components/ImageUploader.vue
  25. 1 1
      wishing-tree-h5/src/components/TreeCard.vue
  26. 4 2
      wishing-tree-h5/src/components/WishCard.vue
  27. 2 0
      wishing-tree-h5/src/mock/data.ts
  28. 7 23
      wishing-tree-h5/src/views/MakeWishView.vue
  29. 19 2
      wishing-tree-h5/src/views/TreeDetailView.vue
  30. 0 1
      wishing-tree-h5/src/views/WishDetailView.vue

+ 15 - 5
wishing-platform/platform-entity/platform-entity-wishing/src/main/java/cn/qinys/platform/entity/wishing/Wish.java → wishing-platform/platform-entity/platform-entity-wishing/src/main/java/cn/qinys/platform/entity/wishing/UserWish.java

@@ -10,8 +10,8 @@ import java.math.BigDecimal;
 
 @Data
 @EqualsAndHashCode(callSuper = true)
-@TableName("wishing_wish")
-public class Wish extends BaseEntity {
+@TableName("user_wish")
+public class UserWish extends BaseEntity {
 
     @TableId(type = IdType.AUTO)
     private Long id;
@@ -24,22 +24,32 @@ public class Wish extends BaseEntity {
 
     private String content;
 
-    /** JSON array of image URLs */
+    /**
+     * JSON array of image URLs
+     */
     private String images;
 
+
     private BigDecimal longitude;
 
+
     private BigDecimal latitude;
 
+
     private String address;
 
+
     private Integer isPublic;
 
-    /** JSON array of tag strings */
+    /**
+     * JSON array of tag strings
+     */
     private String tags;
 
     private Integer likes = 0;
 
-    /** 0=active, 1=fulfilled */
+    /**
+     * 0=active, 1=fulfilled
+     */
     private Integer status = 0;
 }

binární
wishing-platform/platform-entity/platform-entity-wishing/target/classes/cn/qinys/platform/entity/wishing/Wish.class → wishing-platform/platform-entity/platform-entity-wishing/target/classes/cn/qinys/platform/entity/wishing/UserWish.class


+ 1 - 1
wishing-platform/platform-entity/platform-entity-wishing/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst

@@ -1,4 +1,4 @@
 cn\qinys\platform\entity\wishing\BaseEntity.class
 cn\qinys\platform\entity\wishing\WishingTree.class
-cn\qinys\platform\entity\wishing\Wish.class
+cn\qinys\platform\entity\wishing\UserWish.class
 cn\qinys\platform\entity\wishing\WishingUser.class

+ 1 - 1
wishing-platform/platform-entity/platform-entity-wishing/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst

@@ -1,4 +1,4 @@
 G:\许愿树\wishing-platform\platform-entity\platform-entity-wishing\src\main\java\cn\qinys\platform\entity\wishing\BaseEntity.java
-G:\许愿树\wishing-platform\platform-entity\platform-entity-wishing\src\main\java\cn\qinys\platform\entity\wishing\Wish.java
+G:\许愿树\wishing-platform\platform-entity\platform-entity-wishing\src\main\java\cn\qinys\platform\entity\wishing\UserWish.java
 G:\许愿树\wishing-platform\platform-entity\platform-entity-wishing\src\main\java\cn\qinys\platform\entity\wishing\WishingTree.java
 G:\许愿树\wishing-platform\platform-entity\platform-entity-wishing\src\main\java\cn\qinys\platform\entity\wishing\WishingUser.java

binární
wishing-platform/platform-entity/platform-entity-wishing/target/platform-entity-wishing-1.0.0-SNAPSHOT.jar


+ 17 - 4
wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/config/MobileMvcConfig.java

@@ -1,5 +1,7 @@
 package cn.qinys.platform.config;
 
+import cn.qinys.platform.properties.SystemProperties;
+import jakarta.annotation.Resource;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
@@ -8,12 +10,23 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 @Configuration
 public class MobileMvcConfig implements WebMvcConfigurer {
 
-    @Value("${wishing.upload.path:./uploads}")
-    private String uploadPath;
+    @Resource
+    SystemProperties systemProperties;
 
+
+    /**
+     * 添加静态资源文件,外部可以直接访问地址
+     *
+     * @param registry
+     */
     @Override
     public void addResourceHandlers(ResourceHandlerRegistry registry) {
-        registry.addResourceHandler("/uploads/**")
-                .addResourceLocations("file:" + uploadPath + "/");
+        registry.addResourceHandler("/dgapi/mobile/ext/file/**")
+                .addResourceLocations(
+                        "file:" + systemProperties.getFileDirectory(),
+                        "file:" + systemProperties.getTempDirectory(),
+                        "file:" + systemProperties.getRichDirectory()
+                );
     }
+
 }

+ 8 - 40
wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/controller/FileUploadController.java

@@ -1,6 +1,9 @@
 package cn.qinys.platform.mobile.controller;
 
+import cn.qinys.platform.base.dto.AttachmentDTO;
 import cn.qinys.platform.base.response.Result;
+import cn.qinys.platform.mobile.service.AttachmentService;
+import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.web.bind.annotation.PostMapping;
@@ -9,55 +12,20 @@ import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.multipart.MultipartFile;
 
-import java.io.File;
-import java.io.IOException;
-import java.time.LocalDateTime;
-import java.time.format.DateTimeFormatter;
-import java.util.HashMap;
 import java.util.Map;
-import java.util.UUID;
 
 @Slf4j
 @RestController
 @RequestMapping("/dgapi/mobile/upload")
 public class FileUploadController {
 
-    @Value("${wishing.upload.path:./uploads}")
-    private String uploadPath;
+    @Resource
+    private AttachmentService attachmentService;
 
-    @Value("${wishing.upload.url-prefix:/uploads}")
-    private String urlPrefix;
 
     @PostMapping("/image")
-    public Result<Map<String, String>> uploadImage(@RequestParam("file") MultipartFile file) {
-        if (file.isEmpty()) {
-            return new Result<>(400, "文件为空");
-        }
-
-        String originalName = file.getOriginalFilename();
-        String suffix = "";
-        if (originalName != null) {
-            int i = originalName.lastIndexOf('.');
-            if (i > 0) suffix = originalName.substring(i);
-        }
-
-        String ym = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMM")) + "/";
-        String fileName = UUID.randomUUID().toString().replace("-", "") + suffix;
-        File dest = new File(uploadPath + File.separator + ym + File.separator + fileName);
-
-        try {
-            if (!dest.getParentFile().exists()) {
-                dest.getParentFile().mkdirs();
-            }
-            file.transferTo(dest);
-        } catch (IOException e) {
-            log.error("upload error", e);
-            return new Result<>(500, "上传失败");
-        }
-
-        String url = urlPrefix + "/" + ym + fileName;
-        Map<String, String> data = new HashMap<>();
-        data.put("url", url);
-        return new Result<>(data);
+    public Result<AttachmentDTO> uploadImage(@RequestParam("file") MultipartFile file) {
+        AttachmentDTO attachmentDTO = attachmentService.uploadFileAttachment(file);
+        return new Result<>(attachmentDTO);
     }
 }

+ 7 - 5
wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/controller/WishingTreeController.java → wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/controller/TreeController.java

@@ -1,7 +1,9 @@
 package cn.qinys.platform.mobile.controller;
 
 import cn.qinys.platform.base.response.Result;
+import cn.qinys.platform.mobile.req.TreeDetailReq;
 import cn.qinys.platform.mobile.req.WishingTreeListReq;
+import cn.qinys.platform.mobile.resp.TreeDetailResp;
 import cn.qinys.platform.mobile.resp.WishingTreeListResp;
 import cn.qinys.platform.mobile.service.WishTreeService;
 import jakarta.annotation.Resource;
@@ -17,7 +19,7 @@ import java.util.List;
  **/
 @RestController
 @RequestMapping("/dgapi/mobile/wishingtree")
-public class WishingTreeController {
+public class TreeController {
 
     @Resource
     private WishTreeService wishTreeService;
@@ -34,9 +36,9 @@ public class WishingTreeController {
     /**
      * 许愿树详情
      */
-    @GetMapping("/{id}")
-    public Result<WishingTreeListResp> detail(@PathVariable Long id) {
-        WishingTreeListResp resp = wishTreeService.getTreeDetail(id);
-        return resp != null ? new Result<>(resp) : new Result<>(404, "许愿树不存在");
+    @PostMapping("/detail")
+    public Result<TreeDetailResp> detail(@RequestBody @Valid TreeDetailReq req) {
+        TreeDetailResp resp = wishTreeService.getTreeDetail(req);
+        return new Result<>(resp);
     }
 }

+ 4 - 4
wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/controller/WishController.java → wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/controller/UserWishController.java

@@ -4,17 +4,17 @@ import cn.qinys.platform.base.response.Result;
 import cn.qinys.platform.mobile.req.WishCreateReq;
 import cn.qinys.platform.mobile.resp.WishDetailResp;
 import cn.qinys.platform.mobile.resp.WishListResp;
-import cn.qinys.platform.mobile.service.WishService;
+import cn.qinys.platform.mobile.service.UserWishService;
 import jakarta.annotation.Resource;
 import jakarta.validation.Valid;
 import org.springframework.web.bind.annotation.*;
 
 @RestController
 @RequestMapping("/dgapi/mobile/wish")
-public class WishController {
+public class UserWishController {
 
     @Resource
-    private WishService wishService;
+    private UserWishService wishService;
 
     /** 创建愿望 */
     @PostMapping("/create")
@@ -44,7 +44,7 @@ public class WishController {
 
     /** 愿望详情 */
     @GetMapping("/{id}")
-    public Result<WishDetailResp> detail(@PathVariable Long id) {
+    public Result<WishDetailResp> detail(@RequestBody Long id) {
         WishDetailResp resp = wishService.getDetail(id);
         return resp != null ? new Result<>(resp) : new Result<>(404, "愿望不存在");
     }

+ 3 - 2
wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/mapper/WishMapper.java → wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/mapper/UserWishMapper.java

@@ -1,9 +1,10 @@
 package cn.qinys.platform.mobile.mapper;
 
-import cn.qinys.platform.entity.wishing.Wish;
+import cn.qinys.platform.entity.wishing.UserWish;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import org.apache.ibatis.annotations.Mapper;
 
 @Mapper
-public interface WishMapper extends BaseMapper<Wish> {
+public interface UserWishMapper extends BaseMapper<UserWish> {
+
 }

+ 6 - 1
wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/mapper/WishingTreeMapper.java

@@ -1,8 +1,12 @@
 package cn.qinys.platform.mobile.mapper;
 
 import cn.qinys.platform.entity.wishing.WishingTree;
+import cn.qinys.platform.mobile.resp.TreeDetailResp;
+import cn.qinys.platform.mobile.resp.WishDetailResp;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
 
 /**
  * @author lie tan
@@ -13,5 +17,6 @@ import org.apache.ibatis.annotations.Mapper;
 public interface WishingTreeMapper extends BaseMapper<WishingTree> {
 
 
-
+    @Select("SELECT * FROM wishing_tree WHERE id = #{id}")
+    TreeDetailResp selectDetailById(@Param("id") Long id);
 }

+ 25 - 0
wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/req/TreeDetailReq.java

@@ -0,0 +1,25 @@
+package cn.qinys.platform.mobile.req;
+
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * @author lie tan
+ * @description 许愿树列表响应
+ * @date 2026-05-23 13:47
+ **/
+@Data
+public class TreeDetailReq implements Serializable {
+
+    @NotNull(message = "id can not be null")
+    private Long id;
+
+    @NotNull(message = "lng can not be null")
+    private BigDecimal lng;
+
+    @NotNull(message = "lat can not be null")
+    private BigDecimal lat;
+}

+ 39 - 0
wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/resp/TreeDetailResp.java

@@ -0,0 +1,39 @@
+package cn.qinys.platform.mobile.resp;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * @author lie tan
+ * @description 许愿树列表响应
+ * @date 2026-05-23 13:47
+ **/
+@Data
+public class TreeDetailResp implements Serializable {
+
+    private Long id;
+
+    private String name;
+
+    private String description;
+
+    private BigDecimal longitude;
+
+    private BigDecimal latitude;
+
+    private String address;
+
+    private Integer radius;
+
+    private String coverImage;
+
+    private Integer totalWishes;
+
+    /** 距离(米) */
+    private Integer distance;
+
+    /** 是否在可许愿范围内 */
+    private Boolean isInRange;
+}

+ 24 - 0
wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/service/AttachmentService.java

@@ -0,0 +1,24 @@
+package cn.qinys.platform.mobile.service;
+
+
+import cn.qinys.platform.base.dto.AttachmentDTO;
+import org.springframework.web.multipart.MultipartFile;
+
+/**
+ * code is far away from bug with the animal protecting
+ *
+ * @author v-lishiquan.gx@chinatelecom.cn
+ * @description
+ * @date 2020-04-29
+ */
+public interface AttachmentService {
+    /**
+     * 上传文件(存在临时路径)
+     *
+     * @param file 文件
+     * @return 内容
+     */
+    AttachmentDTO uploadFileAttachment(MultipartFile file);
+
+
+}

+ 1 - 1
wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/service/WishService.java → wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/service/UserWishService.java

@@ -4,7 +4,7 @@ import cn.qinys.platform.mobile.req.WishCreateReq;
 import cn.qinys.platform.mobile.resp.WishDetailResp;
 import cn.qinys.platform.mobile.resp.WishListResp;
 
-public interface WishService {
+public interface UserWishService {
 
     WishListResp listByTree(Long treeId, Integer page, Integer pageSize);
 

+ 3 - 1
wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/service/WishTreeService.java

@@ -1,6 +1,8 @@
 package cn.qinys.platform.mobile.service;
 
+import cn.qinys.platform.mobile.req.TreeDetailReq;
 import cn.qinys.platform.mobile.req.WishingTreeListReq;
+import cn.qinys.platform.mobile.resp.TreeDetailResp;
 import cn.qinys.platform.mobile.resp.WishingTreeListResp;
 
 import java.util.List;
@@ -26,5 +28,5 @@ public interface WishTreeService {
     /**
      * 查询许愿树详情
      */
-    WishingTreeListResp getTreeDetail(Long id);
+    TreeDetailResp getTreeDetail(TreeDetailReq req);
 }

+ 98 - 0
wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/service/impl/AttachmentServiceImpl.java

@@ -0,0 +1,98 @@
+package cn.qinys.platform.mobile.service.impl;
+
+import cn.qinys.platform.base.constants.BaseConstants;
+import cn.qinys.platform.base.dto.AttachmentDTO;
+import cn.qinys.platform.base.exceptions.BizException;
+import cn.qinys.platform.base.utils.SnowflakeIdWorker;
+import cn.qinys.platform.mobile.service.AttachmentService;
+import cn.qinys.platform.properties.SystemProperties;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.IOException;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Objects;
+
+/**
+ * @author lie tan
+ * @description
+ * @date 2026-06-07 13:30
+ **/
+
+@Slf4j
+@Service
+public class AttachmentServiceImpl implements AttachmentService {
+
+    @Resource
+    SystemProperties systemProperties;
+    @Resource
+    SnowflakeIdWorker snowflakeIdWorker;
+
+
+    @Override
+    public AttachmentDTO uploadFileAttachment(MultipartFile file) {
+        return this.uploadAttach(file, systemProperties.getBaseUrl());
+    }
+
+
+    /**
+     * 上传文件并返回数据
+     *
+     * @param multipartFile 上传的文件
+     * @param urlPrefix     url的前缀
+     * @return 数据
+     */
+    private AttachmentDTO uploadAttach(MultipartFile multipartFile, String urlPrefix) {
+        // 获取时间
+        String ym = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMM")) + "/";
+        AttachmentDTO dto = new AttachmentDTO();
+        try {
+            //获取文件后缀名
+            String originalFileName = multipartFile.getOriginalFilename();
+            if (Objects.isNull(originalFileName)) {
+                return dto;
+            }
+            // 验证上传文件后缀
+            String suffix = this.getFileSuffix(originalFileName);
+            //生成文件名
+            String id = snowflakeIdWorker.nextIdStr();
+            String fileName = id + suffix;
+            File tmpFile = new File(systemProperties.getFileDirectory() + ym + File.separator + fileName);
+            //转存文件
+            if (!tmpFile.getParentFile().exists()) {
+                tmpFile.getParentFile().mkdirs();
+            }
+            multipartFile.transferTo(tmpFile.getAbsoluteFile());
+            dto.setId(id);
+            dto.setName(originalFileName);
+            dto.setVirtualName(ym + fileName);
+            dto.setUrl(urlPrefix + ym + fileName);
+            dto.setSuffix(suffix);
+            dto.setStorageType(BaseConstants.StorageType.LOCAL.getCode());
+        } catch (IOException e) {
+            log.error("上传文件异常:[{}]", e.getMessage());
+            e.printStackTrace();
+            throw new BizException("上传文件异常");
+        }
+        return dto;
+    }
+
+    /**
+     * 获取文件后缀
+     *
+     * @param fileName 文件名
+     * @return 后缀
+     */
+    private String getFileSuffix(String fileName) {
+        int lastIndex = fileName.lastIndexOf('.');
+        String suffix = "";
+        if (lastIndex > -1 && lastIndex < (fileName.length() - 1)) {
+            suffix = "." + fileName.substring(lastIndex + 1);
+        }
+        return suffix;
+    }
+}

+ 20 - 21
wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/service/impl/WishServiceImpl.java → wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/service/impl/UserWishServiceImpl.java

@@ -1,13 +1,12 @@
 package cn.qinys.platform.mobile.service.impl;
-
-import cn.qinys.platform.entity.wishing.Wish;
+import cn.qinys.platform.entity.wishing.UserWish;
 import cn.qinys.platform.entity.wishing.WishingTree;
-import cn.qinys.platform.mobile.mapper.WishMapper;
+import cn.qinys.platform.mobile.mapper.UserWishMapper;
 import cn.qinys.platform.mobile.mapper.WishingTreeMapper;
 import cn.qinys.platform.mobile.req.WishCreateReq;
 import cn.qinys.platform.mobile.resp.WishDetailResp;
 import cn.qinys.platform.mobile.resp.WishListResp;
-import cn.qinys.platform.mobile.service.WishService;
+import cn.qinys.platform.mobile.service.UserWishService;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.fasterxml.jackson.core.JsonProcessingException;
@@ -22,10 +21,10 @@ import java.util.List;
 
 @Slf4j
 @Service
-public class WishServiceImpl implements WishService {
+public class UserWishServiceImpl implements UserWishService {
 
     @Resource
-    private WishMapper wishMapper;
+    private UserWishMapper wishMapper;
 
     @Resource
     private WishingTreeMapper wishingTreeMapper;
@@ -34,13 +33,13 @@ public class WishServiceImpl implements WishService {
 
     @Override
     public WishListResp listByTree(Long treeId, Integer page, Integer pageSize) {
-        Page<Wish> mpPage = wishMapper.selectPage(
+        Page<UserWish> mpPage = wishMapper.selectPage(
                 new Page<>(page, pageSize),
-                new LambdaQueryWrapper<Wish>()
-                        .eq(Wish::getTreeId, treeId)
-                        .eq(Wish::getIsPublic, 1)
-                        .eq(Wish::getStatus, 0)
-                        .orderByDesc(Wish::getCreatedAt)
+                new LambdaQueryWrapper<UserWish>()
+                        .eq(UserWish::getTreeId, treeId)
+                        .eq(UserWish::getIsPublic, 1)
+                        .eq(UserWish::getStatus, 0)
+                        .orderByDesc(UserWish::getCreatedAt)
         );
 
         WishListResp resp = new WishListResp();
@@ -51,11 +50,11 @@ public class WishServiceImpl implements WishService {
 
     @Override
     public WishListResp listMyWishes(String userId, Integer page, Integer pageSize) {
-        Page<Wish> mpPage = wishMapper.selectPage(
+        Page<UserWish> mpPage = wishMapper.selectPage(
                 new Page<>(page, pageSize),
-                new LambdaQueryWrapper<Wish>()
-                        .eq(Wish::getUserId, userId)
-                        .orderByDesc(Wish::getCreatedAt)
+                new LambdaQueryWrapper<UserWish>()
+                        .eq(UserWish::getUserId, userId)
+                        .orderByDesc(UserWish::getCreatedAt)
         );
 
         WishListResp resp = new WishListResp();
@@ -66,7 +65,7 @@ public class WishServiceImpl implements WishService {
 
     @Override
     public WishDetailResp getDetail(Long id) {
-        Wish wish = wishMapper.selectById(id);
+        UserWish wish = wishMapper.selectById(id);
         return wish != null ? toDetailResp(wish) : null;
     }
 
@@ -74,7 +73,7 @@ public class WishServiceImpl implements WishService {
     public WishDetailResp create(WishCreateReq req, String userId) {
         WishingTree tree = wishingTreeMapper.selectById(req.getTreeId());
 
-        Wish wish = new Wish();
+        UserWish wish = new UserWish();
         wish.setTreeId(req.getTreeId());
         wish.setTreeName(tree != null ? tree.getName() : "");
         wish.setUserId(userId);
@@ -92,7 +91,7 @@ public class WishServiceImpl implements WishService {
 
     @Override
     public boolean delete(Long id, String userId) {
-        Wish wish = wishMapper.selectById(id);
+        UserWish wish = wishMapper.selectById(id);
         if (wish == null || !wish.getUserId().equals(userId)) {
             return false;
         }
@@ -102,7 +101,7 @@ public class WishServiceImpl implements WishService {
 
     @Override
     public int like(Long id) {
-        Wish wish = wishMapper.selectById(id);
+        UserWish wish = wishMapper.selectById(id);
         if (wish == null) {
             return 0;
         }
@@ -111,7 +110,7 @@ public class WishServiceImpl implements WishService {
         return wish.getLikes();
     }
 
-    private WishDetailResp toDetailResp(Wish wish) {
+    private WishDetailResp toDetailResp(UserWish wish) {
         WishDetailResp resp = new WishDetailResp();
         resp.setId(wish.getId());
         resp.setTreeId(wish.getTreeId());

+ 17 - 19
wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/service/impl/WishTreeServiceImpl.java

@@ -2,7 +2,9 @@ package cn.qinys.platform.mobile.service.impl;
 
 import cn.qinys.platform.entity.wishing.WishingTree;
 import cn.qinys.platform.mobile.mapper.WishingTreeMapper;
+import cn.qinys.platform.mobile.req.TreeDetailReq;
 import cn.qinys.platform.mobile.req.WishingTreeListReq;
+import cn.qinys.platform.mobile.resp.TreeDetailResp;
 import cn.qinys.platform.mobile.resp.WishingTreeListResp;
 import cn.qinys.platform.mobile.service.WishTreeService;
 import cn.qinys.platform.mobile.util.CoordUtil;
@@ -26,7 +28,7 @@ import java.util.stream.Collectors;
 public class WishTreeServiceImpl implements WishTreeService {
 
     @Resource
-    private WishingTreeMapper wishingTreeMapper;
+    private WishingTreeMapper treeMapper;
 
     private static final int EARTH_RADIUS = 6371000; // 地球半径(米)
 
@@ -35,7 +37,7 @@ public class WishTreeServiceImpl implements WishTreeService {
 
 
         // 查询所有启用的许愿树
-        List<WishingTree> trees = wishingTreeMapper.selectList(
+        List<WishingTree> trees = treeMapper.selectList(
                 new LambdaQueryWrapper<WishingTree>()
                         .eq(WishingTree::getIsActive, 1)
         );
@@ -74,25 +76,21 @@ public class WishTreeServiceImpl implements WishTreeService {
     }
 
     @Override
-    public WishingTreeListResp getTreeDetail(Long id) {
-        WishingTree tree = wishingTreeMapper.selectById(id);
-        if (tree == null) {
-            return null;
-        }
-        WishingTreeListResp resp = new WishingTreeListResp();
-        resp.setId(tree.getId());
-        resp.setName(tree.getName());
-        resp.setDescription(tree.getDescription());
-        resp.setLongitude(tree.getLongitude());
-        resp.setLatitude(tree.getLatitude());
-        resp.setAddress(tree.getAddress());
-        resp.setRadius(tree.getRadius());
-        resp.setCoverImage(tree.getCoverImage());
-        resp.setTotalWishes(tree.getTotalWishes());
-        return resp;
+    public TreeDetailResp getTreeDetail(TreeDetailReq req) {
+        TreeDetailResp tree = treeMapper.selectDetailById(req.getId());
+        // 用户 GPS 是 WGS-84,DB 存的是 GCJ-02,统一转为 GCJ-02 计算距离
+        double[] gcj = CoordUtil.wgs84ToGcj02(req.getLng().doubleValue(), req.getLat().doubleValue());
+        int dist = haversine(gcj[0], gcj[1],
+                tree.getLongitude().doubleValue(), tree.getLatitude().doubleValue()
+        );
+        tree.setDistance(dist);
+        tree.setIsInRange(dist <= (tree.getRadius() != null ? tree.getRadius() : 100));
+        return tree;
     }
 
-    /** Haversine 公式计算两点间距离(米) */
+    /**
+     * Haversine 公式计算两点间距离(米)
+     */
     private int haversine(double lng1, double lat1, double lng2, double lat2) {
         double dLat = Math.toRadians(lat2 - lat1);
         double dLng = Math.toRadians(lng2 - lng1);

+ 21 - 0
wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/properties/SystemProperties.java

@@ -0,0 +1,21 @@
+package cn.qinys.platform.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author lie tan
+ * @description
+ * @date 2026-06-07 13:33
+ **/
+@Component
+@ConfigurationProperties(prefix = "system")
+@Data
+public class SystemProperties {
+    private String baseUrl;
+    private String fileDirectory;
+    private String richDirectory;
+    private String tempDirectory;
+
+}

+ 3 - 0
wishing-platform/platform-service/platform-service-mobile/src/main/resources/application-local.yml

@@ -36,3 +36,6 @@ mybatis-plus:
       logic-delete-value: 1
       logic-not-delete-value: 0
 
+system:
+  base-url: https://wish.qinys.cn/dgapi/mobile/ext/file/
+  file-directory: D:\\xinchan\\server\\file\

+ 5 - 2
wishing-tree-h5/src/api/tree.ts

@@ -16,9 +16,12 @@ export async function fetchNearbyTrees(lng: number, lat: number, maxDistance = 1
   }))
 }
 
-export async function fetchTreeDetail(id: number) {
+export async function fetchTreeDetail(id: number, lng?: number, lat?: number) {
   if (USE_MOCK) return getTreeById(id)
 
-  const res = await request<any>(`/mobile/wishingtree/${id}`)
+  const res = await request<any>('/mobile/wishingtree/detail', {
+    method: 'POST',
+    body: JSON.stringify({ id, lng: lng ?? 0, lat: lat ?? 0 }),
+  })
   return res.data
 }

+ 8 - 0
wishing-tree-h5/src/api/upload.ts

@@ -1,4 +1,5 @@
 import { showToast } from 'vant'
+import { useUserStore } from '@/stores/user'
 
 const USE_MOCK = false
 
@@ -14,9 +15,16 @@ export async function uploadImage(file: File): Promise<string> {
   const formData = new FormData()
   formData.append('file', file)
 
+  const userStore = useUserStore()
+  const headers: Record<string, string> = {}
+  if (userStore.token) {
+    headers['Authorization'] = `Bearer ${userStore.token}`
+  }
+
   const baseUrl = import.meta.env.VITE_API_BASE || '/dgapi'
   const res = await fetch(`${baseUrl}/mobile/upload/image`, {
     method: 'POST',
+    headers,
     body: formData,
   })
   const json = await res.json()

+ 2 - 8
wishing-tree-h5/src/components/ImageUploader.vue

@@ -33,7 +33,7 @@
 </template>
 
 <script setup lang="ts">
-import { ref } from 'vue'
+import { ref, computed } from 'vue'
 import { showToast } from 'vant'
 import { takePhoto, pickFromAlbum } from '@/utils/camera'
 import { uploadImage } from '@/api/upload'
@@ -53,13 +53,7 @@ const actions = [
   { name: 'album', description: '从相册选择' },
 ]
 
-const images = computedWithEmit()
-
-function computedWithEmit() {
-  return new Proxy(props.modelValue, {
-    get: (_, key) => props.modelValue[key as any],
-  })
-}
+const images = computed(() => props.modelValue)
 
 function removeImage(i: number) {
   const arr = [...props.modelValue]

+ 1 - 1
wishing-tree-h5/src/components/TreeCard.vue

@@ -13,7 +13,7 @@
       <div class="tree-meta">
         <span class="tree-distance">
           <van-tag :type="tree.isInRange ? 'success' : 'warning'" round size="medium">
-            {{ tree.isInRange ? '可许愿' : formatDistance(tree.distance) }}
+            {{ tree.isInRange ? `可许愿 · ${formatDistance(tree.distance)}` : formatDistance(tree.distance) }}
           </van-tag>
         </span>
         <span class="tree-wish-count">💬 {{ tree.totalWishes }} 个愿望</span>

+ 4 - 2
wishing-tree-h5/src/components/WishCard.vue

@@ -10,14 +10,13 @@
         height="80"
         fit="cover"
         radius="4"
-        lazy-load
       />
       <span v-if="wish.images.length > 3" class="more-images">
         +{{ wish.images.length - 3 }}
       </span>
     </div>
     <div class="wish-tags" v-if="wish.tags?.length">
-      <van-tag v-for="t in wish.tags" :key="t" plain type="primary" size="medium" style="margin-right: 4px">
+      <van-tag v-for="t in wish.tags" :key="t" plain type="primary" size="medium">
         {{ t }}
       </van-tag>
     </div>
@@ -91,6 +90,9 @@ function formatTime(s: string) {
   color: #999;
 }
 .wish-tags {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 6px 4px;
   margin-bottom: 10px;
 }
 .wish-footer {

+ 2 - 0
wishing-tree-h5/src/mock/data.ts

@@ -9,6 +9,8 @@ export interface WishingTree {
   coverImage: string
   totalWishes: number
   isActive: boolean
+  distance?: number
+  isInRange?: boolean
 }
 
 export interface Wish {

+ 7 - 23
wishing-tree-h5/src/views/MakeWishView.vue

@@ -5,8 +5,8 @@
     <div class="page-body">
       <div class="tree-info" v-if="tree">
         <p class="for-tree">🌳 {{ tree.name }}</p>
-        <van-tag :type="isInRange ? 'success' : 'warning'" round>
-          {{ isInRange ? `距离 ${fmtDist(distance)} · 在范围内` : `距离 ${fmtDist(distance)} · 太远了` }}
+        <van-tag v-if="tree.distance != null" :type="isInRange ? 'success' : 'warning'" round>
+          {{ isInRange ? `距离 ${fmtDist(tree.distance)} · 在范围内` : `距离 ${fmtDist(tree.distance)} · 太远了` }}
         </van-tag>
       </div>
 
@@ -88,17 +88,7 @@ function fmtDist(m: number) {
   return m >= 1000 ? (m / 1000).toFixed(1) + 'km' : m + 'm'
 }
 
-const distance = computed(() => {
-  if (!tree.value || !locationStore.lng) return Infinity
-  return Math.round(
-    haversine(locationStore.lng, locationStore.lat!, tree.value.longitude, tree.value.latitude)
-  )
-})
-
-const isInRange = computed(() => {
-  if (!tree.value) return false
-  return distance.value <= (tree.value.radius || 100)
-})
+const isInRange = computed(() => tree.value?.isInRange ?? false)
 
 async function submitWish() {
   if (!content.value.trim()) {
@@ -132,19 +122,13 @@ async function submitWish() {
   }
 }
 
-function haversine(lng1: number, lat1: number, lng2: number, lat2: number) {
-  const R = 6371000
-  const toRad = (d: number) => (d * Math.PI) / 180
-  const dLat = toRad(lat2 - lat1)
-  const dLng = toRad(lng2 - lng1)
-  const a = Math.sin(dLat / 2) ** 2 + Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.sin(dLng / 2) ** 2
-  return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
-}
-
 onMounted(async () => {
   const id = Number(route.query.treeId)
   if (id) {
-    tree.value = await fetchTreeDetail(id)
+    if (!locationStore.located) {
+      await locationStore.refreshLocation()
+    }
+    tree.value = await fetchTreeDetail(id, locationStore.lng ?? undefined, locationStore.lat ?? undefined)
   }
 })
 </script>

+ 19 - 2
wishing-tree-h5/src/views/TreeDetailView.vue

@@ -14,6 +14,11 @@
         <p class="tree-desc">{{ tree.description }}</p>
         <div class="tree-stats">
           <span>💬 {{ tree.totalWishes }} 个愿望</span>
+          <span v-if="tree.distance != null" class="tree-dist">
+            <van-tag :type="tree.isInRange ? 'success' : 'warning'" round size="medium">
+              {{ tree.isInRange ? '可许愿' : '' }} {{ fmtDist(tree.distance) }}
+            </van-tag>
+          </span>
         </div>
         <van-button
           type="primary"
@@ -79,13 +84,20 @@ function goMakeWish() {
   router.push({ path: '/make-wish', query: { treeId: route.params.id } })
 }
 
+function fmtDist(m: number) {
+  return m >= 1000 ? (m / 1000).toFixed(1) + 'km' : m + 'm'
+}
+
 function goWish(id: number) {
   router.push(`/wish/${id}`)
 }
 
 onMounted(async () => {
   const id = Number(route.params.id)
-  tree.value = await fetchTreeDetail(id)
+  if (!locationStore.located) {
+    await locationStore.refreshLocation()
+  }
+  tree.value = await fetchTreeDetail(id, locationStore.lng ?? undefined, locationStore.lat ?? undefined)
   loadWishes()
 })
 </script>
@@ -140,9 +152,14 @@ onMounted(async () => {
   margin-bottom: 12px;
 }
 .tree-stats {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
   margin-bottom: 16px;
   font-size: 13px;
-  color: #07c160;
+}
+.tree-dist {
+  margin-left: 8px;
 }
 .loading {
   margin: 100px auto;

+ 0 - 1
wishing-tree-h5/src/views/WishDetailView.vue

@@ -15,7 +15,6 @@
           width="100%"
           fit="contain"
           radius="8"
-          lazy-load
           style="margin-bottom: 8px"
         />
       </div>