Просмотр исходного кода

feat: add UserAddress module (entity+mapper+service+controller+region)

Add user address management with region cascade API.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
liaoxg 2 месяцев назад
Родитель
Сommit
0388460ed6

+ 198 - 0
cfc-backend/src/main/java/com/etotem/cfc/controller/RegionController.java

@@ -0,0 +1,198 @@
+package com.etotem.cfc.controller;
+
+import com.etotem.cfc.common.Result;
+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 java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@RestController
+@RequestMapping("/api/region")
+public class RegionController {
+
+    private static final List<Map<String, Object>> ALL_REGIONS = new ArrayList<>();
+
+    static {
+        // Provinces
+        addRegion(1L, 0L, "北京市", 1);
+        addRegion(2L, 0L, "上海市", 1);
+        addRegion(3L, 0L, "天津市", 1);
+        addRegion(4L, 0L, "重庆市", 1);
+        addRegion(5L, 0L, "广东省", 1);
+        addRegion(6L, 0L, "浙江省", 1);
+        addRegion(7L, 0L, "江苏省", 1);
+        addRegion(8L, 0L, "山东省", 1);
+        addRegion(9L, 0L, "河南省", 1);
+        addRegion(10L, 0L, "四川省", 1);
+        addRegion(11L, 0L, "湖北省", 1);
+        addRegion(12L, 0L, "湖南省", 1);
+        addRegion(13L, 0L, "河北省", 1);
+        addRegion(14L, 0L, "福建省", 1);
+        addRegion(15L, 0L, "安徽省", 1);
+
+        // Beijing districts
+        addRegion(101L, 1L, "朝阳区", 2);
+        addRegion(102L, 1L, "海淀区", 2);
+        addRegion(103L, 1L, "西城区", 2);
+        addRegion(104L, 1L, "东城区", 2);
+        addRegion(105L, 1L, "丰台区", 2);
+        addRegion(106L, 1L, "通州区", 2);
+        addRegion(107L, 1L, "大兴区", 2);
+        addRegion(108L, 1L, "昌平区", 2);
+        addRegion(109L, 1L, "顺义区", 2);
+        addRegion(110L, 1L, "房山区", 2);
+
+        // Shanghai districts
+        addRegion(201L, 2L, "浦东新区", 2);
+        addRegion(202L, 2L, "黄浦区", 2);
+        addRegion(203L, 2L, "徐汇区", 2);
+        addRegion(204L, 2L, "长宁区", 2);
+        addRegion(205L, 2L, "静安区", 2);
+        addRegion(206L, 2L, "普陀区", 2);
+        addRegion(207L, 2L, "虹口区", 2);
+        addRegion(208L, 2L, "杨浦区", 2);
+        addRegion(209L, 2L, "闵行区", 2);
+        addRegion(210L, 2L, "宝山区", 2);
+
+        // Guangzhou (Guangdong)
+        addRegion(501L, 5L, "广州市", 2);
+        addRegion(502L, 5L, "深圳市", 2);
+        addRegion(503L, 5L, "珠海市", 2);
+        addRegion(504L, 5L, "佛山市", 2);
+        addRegion(505L, 5L, "东莞市", 2);
+        addRegion(506L, 5L, "中山市", 2);
+
+        // Hangzhou (Zhejiang)
+        addRegion(601L, 6L, "杭州市", 2);
+        addRegion(602L, 6L, "宁波市", 2);
+        addRegion(603L, 6L, "温州市", 2);
+        addRegion(604L, 6L, "嘉兴市", 2);
+
+        // Nanjing (Jiangsu)
+        addRegion(701L, 7L, "南京市", 2);
+        addRegion(702L, 7L, "苏州市", 2);
+        addRegion(703L, 7L, "无锡市", 2);
+        addRegion(704L, 7L, "常州市", 2);
+
+        // Chengdu (Sichuan)
+        addRegion(1001L, 10L, "成都市", 2);
+        addRegion(1002L, 10L, "绵阳市", 2);
+        addRegion(1003L, 10L, "德阳市", 2);
+
+        // Wuhan (Hubei)
+        addRegion(1101L, 11L, "武汉市", 2);
+        addRegion(1102L, 11L, "宜昌市", 2);
+
+        // Changsha (Hunan)
+        addRegion(1201L, 12L, "长沙市", 2);
+        addRegion(1202L, 12L, "株洲市", 2);
+
+        // Jinan (Shandong)
+        addRegion(801L, 8L, "济南市", 2);
+        addRegion(802L, 8L, "青岛市", 2);
+        addRegion(803L, 8L, "烟台市", 2);
+
+        // Fuzhou (Fujian)
+        addRegion(1401L, 14L, "福州市", 2);
+        addRegion(1402L, 14L, "厦门市", 2);
+
+        // Hefei (Anhui)
+        addRegion(1501L, 15L, "合肥市", 2);
+
+        // Some districts for major cities
+        addRegion(10101L, 101L, "望京街道", 3);
+        addRegion(10102L, 101L, "双井街道", 3);
+        addRegion(10103L, 101L, "三里屯街道", 3);
+        addRegion(10201L, 102L, "中关村街道", 3);
+        addRegion(10202L, 102L, "五道口街道", 3);
+        addRegion(10203L, 102L, "清华园街道", 3);
+        addRegion(20101L, 201L, "陆家嘴街道", 3);
+        addRegion(20102L, 201L, "张江镇", 3);
+        addRegion(50101L, 501L, "天河区", 3);
+        addRegion(50102L, 501L, "越秀区", 3);
+        addRegion(50103L, 501L, "海珠区", 3);
+        addRegion(50201L, 502L, "南山区", 3);
+        addRegion(50202L, 502L, "福田区", 3);
+        addRegion(50203L, 502L, "宝安区", 3);
+        addRegion(60101L, 601L, "西湖区", 3);
+        addRegion(60102L, 601L, "上城区", 3);
+        addRegion(70101L, 701L, "鼓楼区", 3);
+        addRegion(70102L, 701L, "玄武区", 3);
+        addRegion(100101L, 1001L, "武侯区", 3);
+        addRegion(100102L, 1001L, "锦江区", 3);
+        addRegion(110101L, 1101L, "武昌区", 3);
+        addRegion(110102L, 1101L, "江汉区", 3);
+    }
+
+    private static void addRegion(Long id, Long parentId, String name, int level) {
+        Map<String, Object> region = new HashMap<>();
+        region.put("id", id);
+        region.put("parentId", parentId);
+        region.put("name", name);
+        region.put("level", level);
+        ALL_REGIONS.add(region);
+    }
+
+    @PostMapping("/provinces")
+    public Result<List<Map<String, Object>>> provinces() {
+        List<Map<String, Object>> result = ALL_REGIONS.stream()
+            .filter(r -> (int) r.get("level") == 1)
+            .map(r -> {
+                Map<String, Object> m = new HashMap<>();
+                m.put("id", r.get("id"));
+                m.put("name", r.get("name"));
+                return m;
+            })
+            .collect(Collectors.toList());
+        return Result.success(result);
+    }
+
+    @PostMapping("/cities")
+    public Result<List<Map<String, Object>>> cities(@RequestBody Map<String, Object> params) {
+        Object parentId = params.get("parentId");
+        if (parentId == null) {
+            return Result.error("parentId不能为空");
+        }
+        Long pid = Long.parseLong(parentId.toString());
+        List<Map<String, Object>> result = ALL_REGIONS.stream()
+            .filter(r -> (int) r.get("level") == 2 && ((Long) r.get("parentId")).equals(pid))
+            .map(r -> {
+                Map<String, Object> m = new HashMap<>();
+                m.put("id", r.get("id"));
+                m.put("name", r.get("name"));
+                return m;
+            })
+            .collect(Collectors.toList());
+        return Result.success(result);
+    }
+
+    @PostMapping("/districts")
+    public Result<List<Map<String, Object>>> districts(@RequestBody Map<String, Object> params) {
+        Object parentId = params.get("parentId");
+        if (parentId == null) {
+            return Result.error("parentId不能为空");
+        }
+        Long pid = Long.parseLong(parentId.toString());
+        List<Map<String, Object>> result = ALL_REGIONS.stream()
+            .filter(r -> (int) r.get("level") == 3 && ((Long) r.get("parentId")).equals(pid))
+            .map(r -> {
+                Map<String, Object> m = new HashMap<>();
+                m.put("id", r.get("id"));
+                m.put("name", r.get("name"));
+                return m;
+            })
+            .collect(Collectors.toList());
+        return Result.success(result);
+    }
+
+    @PostMapping("/streets")
+    public Result<List<Map<String, Object>>> streets(@RequestBody Map<String, Object> params) {
+        return districts(params);
+    }
+}

+ 61 - 0
cfc-backend/src/main/java/com/etotem/cfc/controller/UserAddressController.java

@@ -0,0 +1,61 @@
+package com.etotem.cfc.controller;
+
+import com.etotem.cfc.common.Result;
+import com.etotem.cfc.entity.UserAddress;
+import com.etotem.cfc.service.UserAddressService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+import java.util.Map;
+
+@Tag(name = "用户地址接口")
+@RestController
+@RequestMapping("/api/user/address")
+public class UserAddressController {
+
+    @Resource
+    private UserAddressService userAddressService;
+
+    @PostMapping("/list")
+    @Operation(summary = "获取用户地址列表")
+    public Result<List<UserAddress>> list(HttpServletRequest request) {
+        Long userId = (Long) request.getAttribute("userId");
+        return Result.success(userAddressService.list(userId));
+    }
+
+    @PostMapping("/delete")
+    @Operation(summary = "删除地址")
+    public Result<String> delete(HttpServletRequest request, @RequestBody Map<String, Long> params) {
+        Long userId = (Long) request.getAttribute("userId");
+        Long id = params.get("id");
+        if (id == null) {
+            return Result.error("地址ID不能为空");
+        }
+        try {
+            userAddressService.delete(id, userId);
+            return Result.success("已删除");
+        } catch (RuntimeException e) {
+            return Result.error(e.getMessage());
+        }
+    }
+
+    @PostMapping("/setDefault")
+    @Operation(summary = "设置默认地址")
+    public Result<String> setDefault(HttpServletRequest request, @RequestBody Map<String, Long> params) {
+        Long userId = (Long) request.getAttribute("userId");
+        Long id = params.get("id");
+        if (id == null) {
+            return Result.error("地址ID不能为空");
+        }
+        try {
+            userAddressService.setDefault(id, userId);
+            return Result.success("已设为默认");
+        } catch (RuntimeException e) {
+            return Result.error(e.getMessage());
+        }
+    }
+}

+ 80 - 0
cfc-backend/src/main/java/com/etotem/cfc/controller/user/UserAddressController.java

@@ -0,0 +1,80 @@
+package com.etotem.cfc.controller.user;
+
+import com.etotem.cfc.common.Result;
+import com.etotem.cfc.entity.UserAddress;
+import com.etotem.cfc.service.UserAddressService;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestAttribute;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import java.util.List;
+import java.util.Map;
+
+@RestController
+@RequestMapping("/api/user/address")
+public class UserAddressController {
+
+    @Resource
+    private UserAddressService userAddressService;
+
+    @PostMapping("/list")
+    public Result<List<UserAddress>> list(@RequestAttribute("userId") Long userId) {
+        return Result.success(userAddressService.list(userId));
+    }
+
+    @PostMapping("/get")
+    public Result<UserAddress> get(@RequestBody Map<String, Object> params,
+                                   @RequestAttribute("userId") Long userId) {
+        Long id = Long.parseLong(params.get("id").toString());
+        UserAddress address = userAddressService.get(id, userId);
+        if (address == null) {
+            return Result.error("地址不存在");
+        }
+        return Result.success(address);
+    }
+
+    @PostMapping("/create")
+    public Result<String> create(@RequestBody UserAddress address,
+                                 @RequestAttribute("userId") Long userId) {
+        userAddressService.create(address, userId);
+        return Result.success("创建成功");
+    }
+
+    @PostMapping("/update")
+    public Result<String> update(@RequestBody UserAddress address,
+                                 @RequestAttribute("userId") Long userId) {
+        try {
+            userAddressService.update(address, userId);
+            return Result.success("更新成功");
+        } catch (RuntimeException e) {
+            return Result.error(e.getMessage());
+        }
+    }
+
+    @PostMapping("/delete")
+    public Result<String> delete(@RequestBody Map<String, Object> params,
+                                 @RequestAttribute("userId") Long userId) {
+        Long id = Long.parseLong(params.get("id").toString());
+        try {
+            userAddressService.delete(id, userId);
+            return Result.success("删除成功");
+        } catch (RuntimeException e) {
+            return Result.error(e.getMessage());
+        }
+    }
+
+    @PostMapping("/setDefault")
+    public Result<String> setDefault(@RequestBody Map<String, Object> params,
+                                     @RequestAttribute("userId") Long userId) {
+        Long id = Long.parseLong(params.get("id").toString());
+        try {
+            userAddressService.setDefault(id, userId);
+            return Result.success("设置成功");
+        } catch (RuntimeException e) {
+            return Result.error(e.getMessage());
+        }
+    }
+}

+ 28 - 0
cfc-backend/src/main/java/com/etotem/cfc/entity/UserAddress.java

@@ -0,0 +1,28 @@
+package com.etotem.cfc.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+@Data
+@TableName("user_address")
+public class UserAddress implements Serializable {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    private Long userId;
+    private String receiverName;
+    private String phone;
+    private String province;
+    private String city;
+    private String district;
+    private String street;
+    private Integer isDefault;
+    private Date createdAt;
+    private Date updatedAt;
+}

+ 9 - 0
cfc-backend/src/main/java/com/etotem/cfc/mapper/UserAddressMapper.java

@@ -0,0 +1,9 @@
+package com.etotem.cfc.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.etotem.cfc.entity.UserAddress;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface UserAddressMapper extends BaseMapper<UserAddress> {
+}

+ 94 - 0
cfc-backend/src/main/java/com/etotem/cfc/service/UserAddressService.java

@@ -0,0 +1,94 @@
+package com.etotem.cfc.service;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.etotem.cfc.entity.UserAddress;
+import com.etotem.cfc.mapper.UserAddressMapper;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.Resource;
+import java.util.Date;
+import java.util.List;
+
+@Service
+public class UserAddressService {
+
+    @Resource
+    private UserAddressMapper userAddressMapper;
+
+    public List<UserAddress> list(Long userId) {
+        LambdaQueryWrapper<UserAddress> wrapper = new LambdaQueryWrapper<UserAddress>()
+            .eq(UserAddress::getUserId, userId)
+            .orderByDesc(UserAddress::getIsDefault)
+            .orderByDesc(UserAddress::getUpdatedAt);
+        return userAddressMapper.selectList(wrapper);
+    }
+
+    public UserAddress get(Long id, Long userId) {
+        LambdaQueryWrapper<UserAddress> wrapper = new LambdaQueryWrapper<UserAddress>()
+            .eq(UserAddress::getId, id)
+            .eq(UserAddress::getUserId, userId);
+        return userAddressMapper.selectOne(wrapper);
+    }
+
+    @Transactional
+    public void create(UserAddress address, Long userId) {
+        address.setUserId(userId);
+        address.setCreatedAt(new Date());
+        address.setUpdatedAt(new Date());
+        if (address.getIsDefault() == null) {
+            address.setIsDefault(0);
+        }
+        if (address.getIsDefault() == 1) {
+            clearDefault(userId);
+        }
+        userAddressMapper.insert(address);
+    }
+
+    @Transactional
+    public void update(UserAddress address, Long userId) {
+        UserAddress existing = get(address.getId(), userId);
+        if (existing == null) {
+            throw new RuntimeException("地址不存在");
+        }
+        address.setUserId(userId);
+        address.setUpdatedAt(new Date());
+        if (address.getIsDefault() == 1) {
+            clearDefault(userId);
+        }
+        userAddressMapper.updateById(address);
+    }
+
+    @Transactional
+    public void delete(Long id, Long userId) {
+        UserAddress existing = get(id, userId);
+        if (existing == null) {
+            throw new RuntimeException("地址不存在");
+        }
+        userAddressMapper.deleteById(id);
+    }
+
+    @Transactional
+    public void setDefault(Long id, Long userId) {
+        UserAddress existing = get(id, userId);
+        if (existing == null) {
+            throw new RuntimeException("地址不存在");
+        }
+        clearDefault(userId);
+        existing.setIsDefault(1);
+        existing.setUpdatedAt(new Date());
+        userAddressMapper.updateById(existing);
+    }
+
+    private void clearDefault(Long userId) {
+        UserAddress defaultAddr = userAddressMapper.selectOne(
+            new LambdaQueryWrapper<UserAddress>()
+                .eq(UserAddress::getUserId, userId)
+                .eq(UserAddress::getIsDefault, 1));
+        if (defaultAddr != null) {
+            defaultAddr.setIsDefault(0);
+            defaultAddr.setUpdatedAt(new Date());
+            userAddressMapper.updateById(defaultAddr);
+        }
+    }
+}