Procházet zdrojové kódy

feat: 完成愿望CRUD接口及前后端API集成

后端新增:
- Wish实体、WishMapper、WishService/WishServiceImpl
- WishController: 创建/列表/详情/删除/点赞API
- FileUploadController: 图片上传API
- WishingTreeController: 新增许愿树详情接口
- MobileMvcConfig: 静态资源配置支持上传文件访问

前端改造:
- api/wish.ts: 对接真实愿望API,mock/real可切换
- api/upload.ts: FormData真实上传替换DataURL mock
- WishDetailView: 删除愿望传递userId

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
tanlie před 1 měsícem
rodič
revize
d9197ea2f3
18 změnil soubory, kde provedl 558 přidání a 32 odebrání
  1. 45 0
      wishing-platform/platform-entity/platform-entity-wishing/src/main/java/cn/qinys/platform/entity/wishing/Wish.java
  2. 9 9
      wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/config/MobileMvcConfig.java
  3. 63 0
      wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/controller/FileUploadController.java
  4. 66 0
      wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/controller/WishController.java
  5. 10 4
      wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/controller/WishingTreeController.java
  6. 9 0
      wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/mapper/WishMapper.java
  7. 33 0
      wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/req/WishCreateReq.java
  8. 40 0
      wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/resp/WishDetailResp.java
  9. 14 0
      wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/resp/WishListResp.java
  10. 20 0
      wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/service/WishService.java
  11. 152 0
      wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/service/WishServiceImpl.java
  12. 5 0
      wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/service/WishTreeService.java
  13. 19 0
      wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/service/WishTreeServiceImpl.java
  14. 25 5
      wishing-tree-h5/src/api/upload.ts
  15. 46 12
      wishing-tree-h5/src/api/wish.ts
  16. 1 1
      wishing-tree-h5/src/views/WishDetailView.vue
  17. 1 1
      wishing-tree-h5/vite.config.ts
  18. binární
      wishing-tree-h5/wish.zip

+ 45 - 0
wishing-platform/platform-entity/platform-entity-wishing/src/main/java/cn/qinys/platform/entity/wishing/Wish.java

@@ -0,0 +1,45 @@
+package cn.qinys.platform.entity.wishing;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("wishing_wish")
+public class Wish extends BaseEntity {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    private Long treeId;
+
+    private String treeName;
+
+    private String userId;
+
+    private String content;
+
+    /** 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 */
+    private String tags;
+
+    private Integer likes = 0;
+
+    /** 0=active, 1=fulfilled */
+    private Integer status = 0;
+}

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

@@ -1,19 +1,19 @@
 package cn.qinys.platform.config;
 
-
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
-
-/**
- * Description:
- *
- * @author tanlie
- * Date: 2024/3/4 15:44
- */
 @Configuration
 public class MobileMvcConfig implements WebMvcConfigurer {
 
+    @Value("${wishing.upload.path:./uploads}")
+    private String uploadPath;
 
-
+    @Override
+    public void addResourceHandlers(ResourceHandlerRegistry registry) {
+        registry.addResourceHandler("/uploads/**")
+                .addResourceLocations("file:" + uploadPath + "/");
+    }
 }

+ 63 - 0
wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/controller/FileUploadController.java

@@ -0,0 +1,63 @@
+package cn.qinys.platform.mobile.controller;
+
+import cn.qinys.platform.base.response.Result;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+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;
+
+    @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);
+    }
+}

+ 66 - 0
wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/controller/WishController.java

@@ -0,0 +1,66 @@
+package cn.qinys.platform.mobile.controller;
+
+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 jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/dgapi/mobile/wish")
+public class WishController {
+
+    @Resource
+    private WishService wishService;
+
+    /** 创建愿望 */
+    @PostMapping("/create")
+    public Result<WishDetailResp> create(@RequestBody @Valid WishCreateReq req,
+                                         @RequestHeader(value = "X-User-Id", defaultValue = "anonymous") String userId) {
+        WishDetailResp resp = wishService.create(req, userId);
+        return new Result<>(resp);
+    }
+
+    /** 许愿树下的公开愿望列表 */
+    @GetMapping("/tree/{treeId}")
+    public Result<WishListResp> listByTree(@PathVariable Long treeId,
+                                           @RequestParam(defaultValue = "1") Integer page,
+                                           @RequestParam(defaultValue = "20") Integer pageSize) {
+        WishListResp resp = wishService.listByTree(treeId, page, pageSize);
+        return new Result<>(resp);
+    }
+
+    /** 我的愿望列表 */
+    @GetMapping("/my")
+    public Result<WishListResp> myList(@RequestHeader(value = "X-User-Id", defaultValue = "anonymous") String userId,
+                                       @RequestParam(defaultValue = "1") Integer page,
+                                       @RequestParam(defaultValue = "20") Integer pageSize) {
+        WishListResp resp = wishService.listMyWishes(userId, page, pageSize);
+        return new Result<>(resp);
+    }
+
+    /** 愿望详情 */
+    @GetMapping("/{id}")
+    public Result<WishDetailResp> detail(@PathVariable Long id) {
+        WishDetailResp resp = wishService.getDetail(id);
+        return resp != null ? new Result<>(resp) : new Result<>(404, "愿望不存在");
+    }
+
+    /** 删除愿望 */
+    @DeleteMapping("/{id}")
+    public Result<Boolean> delete(@PathVariable Long id,
+                                  @RequestHeader(value = "X-User-Id", defaultValue = "anonymous") String userId) {
+        boolean ok = wishService.delete(id, userId);
+        return ok ? new Result<>(true) : new Result<>(404, "删除失败,愿望不存在或无权操作");
+    }
+
+    /** 点赞愿望 */
+    @PostMapping("/{id}/like")
+    public Result<Integer> like(@PathVariable Long id) {
+        int count = wishService.like(id);
+        return new Result<>(count);
+    }
+}

+ 10 - 4
wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/controller/WishingTreeController.java

@@ -6,10 +6,7 @@ import cn.qinys.platform.mobile.resp.WishingTreeListResp;
 import cn.qinys.platform.mobile.service.WishTreeService;
 import jakarta.annotation.Resource;
 import jakarta.validation.Valid;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 
 import java.util.List;
 
@@ -33,4 +30,13 @@ public class WishingTreeController {
         List<WishingTreeListResp> list = wishTreeService.listNearbyTrees(req);
         return new Result<>(list);
     }
+
+    /**
+     * 许愿树详情
+     */
+    @GetMapping("/{id}")
+    public Result<WishingTreeListResp> detail(@PathVariable Long id) {
+        WishingTreeListResp resp = wishTreeService.getTreeDetail(id);
+        return resp != null ? new Result<>(resp) : new Result<>(404, "许愿树不存在");
+    }
 }

+ 9 - 0
wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/mapper/WishMapper.java

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

+ 33 - 0
wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/req/WishCreateReq.java

@@ -0,0 +1,33 @@
+package cn.qinys.platform.mobile.req;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.List;
+
+@Data
+public class WishCreateReq implements Serializable {
+
+    @NotNull(message = "treeId can not be null")
+    private Long treeId;
+
+    @NotBlank(message = "content can not be blank")
+    private String content;
+
+    private List<String> images;
+
+    @NotNull(message = "lng can not be null")
+    private BigDecimal lng;
+
+    @NotNull(message = "lat can not be null")
+    private BigDecimal lat;
+
+    private String address;
+
+    private Boolean isPublic = true;
+
+    private List<String> tags;
+}

+ 40 - 0
wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/resp/WishDetailResp.java

@@ -0,0 +1,40 @@
+package cn.qinys.platform.mobile.resp;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Data
+public class WishDetailResp implements Serializable {
+
+    private Long id;
+
+    private Long treeId;
+
+    private String treeName;
+
+    private String userId;
+
+    private String content;
+
+    private List<String> images;
+
+    private BigDecimal lng;
+
+    private BigDecimal lat;
+
+    private String address;
+
+    private Boolean isPublic;
+
+    private List<String> tags;
+
+    private Integer likes;
+
+    private Integer status;
+
+    private LocalDateTime createdAt;
+}

+ 14 - 0
wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/resp/WishListResp.java

@@ -0,0 +1,14 @@
+package cn.qinys.platform.mobile.resp;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+public class WishListResp implements Serializable {
+
+    private List<WishDetailResp> list;
+
+    private Long total;
+}

+ 20 - 0
wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/service/WishService.java

@@ -0,0 +1,20 @@
+package cn.qinys.platform.mobile.service;
+
+import cn.qinys.platform.mobile.req.WishCreateReq;
+import cn.qinys.platform.mobile.resp.WishDetailResp;
+import cn.qinys.platform.mobile.resp.WishListResp;
+
+public interface WishService {
+
+    WishListResp listByTree(Long treeId, Integer page, Integer pageSize);
+
+    WishListResp listMyWishes(String userId, Integer page, Integer pageSize);
+
+    WishDetailResp getDetail(Long id);
+
+    WishDetailResp create(WishCreateReq req, String userId);
+
+    boolean delete(Long id, String userId);
+
+    int like(Long id);
+}

+ 152 - 0
wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/service/WishServiceImpl.java

@@ -0,0 +1,152 @@
+package cn.qinys.platform.mobile.service;
+
+import cn.qinys.platform.entity.wishing.Wish;
+import cn.qinys.platform.entity.wishing.WishingTree;
+import cn.qinys.platform.mobile.mapper.WishMapper;
+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 com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@Slf4j
+@Service
+public class WishServiceImpl implements WishService {
+
+    @Resource
+    private WishMapper wishMapper;
+
+    @Resource
+    private WishingTreeMapper wishingTreeMapper;
+
+    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
+    @Override
+    public WishListResp listByTree(Long treeId, Integer page, Integer pageSize) {
+        Page<Wish> 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)
+        );
+
+        WishListResp resp = new WishListResp();
+        resp.setList(mpPage.getRecords().stream().map(this::toDetailResp).toList());
+        resp.setTotal(mpPage.getTotal());
+        return resp;
+    }
+
+    @Override
+    public WishListResp listMyWishes(String userId, Integer page, Integer pageSize) {
+        Page<Wish> mpPage = wishMapper.selectPage(
+                new Page<>(page, pageSize),
+                new LambdaQueryWrapper<Wish>()
+                        .eq(Wish::getUserId, userId)
+                        .orderByDesc(Wish::getCreatedAt)
+        );
+
+        WishListResp resp = new WishListResp();
+        resp.setList(mpPage.getRecords().stream().map(this::toDetailResp).toList());
+        resp.setTotal(mpPage.getTotal());
+        return resp;
+    }
+
+    @Override
+    public WishDetailResp getDetail(Long id) {
+        Wish wish = wishMapper.selectById(id);
+        return wish != null ? toDetailResp(wish) : null;
+    }
+
+    @Override
+    public WishDetailResp create(WishCreateReq req, String userId) {
+        WishingTree tree = wishingTreeMapper.selectById(req.getTreeId());
+
+        Wish wish = new Wish();
+        wish.setTreeId(req.getTreeId());
+        wish.setTreeName(tree != null ? tree.getName() : "");
+        wish.setUserId(userId);
+        wish.setContent(req.getContent());
+        wish.setImages(toJson(req.getImages()));
+        wish.setLongitude(req.getLng());
+        wish.setLatitude(req.getLat());
+        wish.setAddress(req.getAddress());
+        wish.setIsPublic(req.getIsPublic() != null && req.getIsPublic() ? 1 : 0);
+        wish.setTags(toJson(req.getTags()));
+
+        wishMapper.insert(wish);
+        return toDetailResp(wish);
+    }
+
+    @Override
+    public boolean delete(Long id, String userId) {
+        Wish wish = wishMapper.selectById(id);
+        if (wish == null || !wish.getUserId().equals(userId)) {
+            return false;
+        }
+        wishMapper.deleteById(id);
+        return true;
+    }
+
+    @Override
+    public int like(Long id) {
+        Wish wish = wishMapper.selectById(id);
+        if (wish == null) {
+            return 0;
+        }
+        wish.setLikes(wish.getLikes() + 1);
+        wishMapper.updateById(wish);
+        return wish.getLikes();
+    }
+
+    private WishDetailResp toDetailResp(Wish wish) {
+        WishDetailResp resp = new WishDetailResp();
+        resp.setId(wish.getId());
+        resp.setTreeId(wish.getTreeId());
+        resp.setTreeName(wish.getTreeName());
+        resp.setUserId(wish.getUserId());
+        resp.setContent(wish.getContent());
+        resp.setImages(parseJsonList(wish.getImages()));
+        resp.setLng(wish.getLongitude());
+        resp.setLat(wish.getLatitude());
+        resp.setAddress(wish.getAddress());
+        resp.setIsPublic(wish.getIsPublic() != null && wish.getIsPublic() == 1);
+        resp.setTags(parseJsonList(wish.getTags()));
+        resp.setLikes(wish.getLikes());
+        resp.setStatus(wish.getStatus());
+        resp.setCreatedAt(wish.getCreatedAt());
+        return resp;
+    }
+
+    private String toJson(List<String> list) {
+        if (list == null || list.isEmpty()) return "[]";
+        try {
+            return OBJECT_MAPPER.writeValueAsString(list);
+        } catch (JsonProcessingException e) {
+            log.error("toJson error", e);
+            return "[]";
+        }
+    }
+
+    private List<String> parseJsonList(String json) {
+        if (json == null || json.isBlank()) return new ArrayList<>();
+        try {
+            return OBJECT_MAPPER.readValue(json, new TypeReference<List<String>>() {});
+        } catch (Exception e) {
+            log.error("parseJsonList error", e);
+            return new ArrayList<>();
+        }
+    }
+}

+ 5 - 0
wishing-platform/platform-service/platform-service-mobile/src/main/java/cn/qinys/platform/mobile/service/WishTreeService.java

@@ -22,4 +22,9 @@ public interface WishTreeService {
      * @return 许愿树列表(含距离、是否在范围内)
      */
     List<WishingTreeListResp> listNearbyTrees(WishingTreeListReq req);
+
+    /**
+     * 查询许愿树详情
+     */
+    WishingTreeListResp getTreeDetail(Long id);
 }

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

@@ -70,6 +70,25 @@ public class WishTreeServiceImpl implements WishTreeService {
                 .collect(Collectors.toList());
     }
 
+    @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;
+    }
+
     /** Haversine 公式计算两点间距离(米) */
     private int haversine(double lng1, double lat1, double lng2, double lat2) {
         double dLat = Math.toRadians(lat2 - lat1);

+ 25 - 5
wishing-tree-h5/src/api/upload.ts

@@ -1,8 +1,28 @@
-// Mock upload — 将图片转为 DataURL 当作"已上传的URL"
+import { showToast } from 'vant'
+
+const USE_MOCK = false
+
 export async function uploadImage(file: File): Promise<string> {
-  return new Promise((resolve) => {
-    const reader = new FileReader()
-    reader.onload = () => resolve(reader.result as string)
-    reader.readAsDataURL(file)
+  if (USE_MOCK) {
+    return new Promise((resolve) => {
+      const reader = new FileReader()
+      reader.onload = () => resolve(reader.result as string)
+      reader.readAsDataURL(file)
+    })
+  }
+
+  const formData = new FormData()
+  formData.append('file', file)
+
+  const baseUrl = import.meta.env.VITE_API_BASE || '/dgapi'
+  const res = await fetch(`${baseUrl}/mobile/upload/image`, {
+    method: 'POST',
+    body: formData,
   })
+  const json = await res.json()
+  if (json.code !== 200) {
+    showToast(json.msg || '上传失败')
+    throw new Error(json.msg || '上传失败')
+  }
+  return json.data.url
 }

+ 46 - 12
wishing-tree-h5/src/api/wish.ts

@@ -7,27 +7,61 @@ import {
   likeWish as likeWishMock,
 } from '@/mock/wish'
 import type { Wish } from '@/mock/data'
+import { request } from './request'
 
-export function fetchTreeWishes(treeId: number, page?: number, pageSize?: number) {
-  return getWishesByTree(treeId, page, pageSize)
+const USE_MOCK = false
+
+export async function fetchTreeWishes(treeId: number, page = 1, pageSize = 20) {
+  if (USE_MOCK) return getWishesByTree(treeId, page, pageSize)
+
+  const res = await request<{ list: Wish[]; total: number }>(
+    `/mobile/wish/tree/${treeId}?page=${page}&pageSize=${pageSize}`,
+  )
+  return { list: res.data.list, total: res.data.total }
 }
 
-export function fetchMyWishes(userId: string, page?: number, pageSize?: number) {
-  return getMyWishesMock(userId, page, pageSize)
+export async function fetchMyWishes(userId: string, page = 1, pageSize = 20) {
+  if (USE_MOCK) return getMyWishesMock(userId, page, pageSize)
+
+  const res = await request<{ list: Wish[]; total: number }>(
+    `/mobile/wish/my?page=${page}&pageSize=${pageSize}`,
+    { headers: { 'X-User-Id': userId } },
+  )
+  return { list: res.data.list, total: res.data.total }
 }
 
-export function fetchWishDetail(id: number) {
-  return getWishById(id)
+export async function fetchWishDetail(id: number) {
+  if (USE_MOCK) return getWishById(id)
+
+  const res = await request<Wish>(`/mobile/wish/${id}`)
+  return res.data
 }
 
-export function submitWish(data: Omit<Wish, 'id' | 'status' | 'likes' | 'createdAt'>) {
-  return createWishMock(data)
+export async function submitWish(data: Omit<Wish, 'id' | 'status' | 'likes' | 'createdAt'>) {
+  if (USE_MOCK) return createWishMock(data)
+
+  const { userId, ...body } = data as any
+  const res = await request<Wish>('/mobile/wish/create', {
+    method: 'POST',
+    body: JSON.stringify(body),
+    headers: { 'X-User-Id': userId || 'anonymous' },
+  })
+  return res.data
 }
 
-export function removeWish(id: number) {
-  return deleteWishMock(id)
+export async function removeWish(id: number, userId?: string) {
+  if (USE_MOCK) return deleteWishMock(id)
+
+  const res = await request<boolean>(`/mobile/wish/${id}`, {
+    method: 'DELETE',
+    headers: userId ? { 'X-User-Id': userId } : undefined,
+  })
+  return res.data
 }
 
-export function toggleLikeWish(id: number) {
-  return likeWishMock(id)
+export async function toggleLikeWish(id: number) {
+  if (USE_MOCK) return likeWishMock(id)
+
+  const res = await request<number>(`/mobile/wish/${id}/like`, { method: 'POST' })
+  return res.data
 }

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

@@ -92,7 +92,7 @@ async function handleLike() {
 async function handleDelete() {
   if (!wish.value) return
   await showConfirmDialog({ title: '确定删除这个愿望吗?', message: '删除后不可恢复' })
-  await removeWish(wish.value.id)
+  await removeWish(wish.value.id, userStore.phone)
   showSuccessToast('愿望已删除')
   router.replace('/my-wishes')
 }

+ 1 - 1
wishing-tree-h5/vite.config.ts

@@ -1,7 +1,7 @@
 import { defineConfig } from 'vite'
 import vue from '@vitejs/plugin-vue'
 import Components from 'unplugin-vue-components/vite'
-import { VantResolver } from '@van/tauto-import-resolver'
+import { VantResolver } from '@vant/auto-import-resolver'
 import { fileURLToPath, URL } from 'node:url'
 
 export default defineConfig({

binární
wishing-tree-h5/wish.zip