فهرست منبع

Merge remote-tracking branch 'origin/feature/1.8.0-uniapp' into feature/1.8.0-uniapp

sfmind 3 سال پیش
والد
کامیت
7c6e0b09ce
19فایلهای تغییر یافته به همراه813 افزوده شده و 2 حذف شده
  1. 22 0
      sql/ruoyi-vue-pro.sql
  2. 4 0
      yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/ErrorCodeConstants.java
  3. 54 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/AppAddressController.http
  4. 78 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/AppAddressController.java
  5. 0 1
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/package-info.java
  6. 37 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressBaseVO.java
  7. 14 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressCreateReqVO.java
  8. 19 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressRespVO.java
  9. 18 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressUpdateReqVO.java
  10. 32 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/convert/address/AddressConvert.java
  11. 55 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/address/AddressDO.java
  12. 0 1
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/address/package-info.java
  13. 32 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/address/AddressMapper.java
  14. 21 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/enums/AddressTypeEnum.java
  15. 68 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/address/AddressService.java
  16. 144 0
      yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/address/AddressServiceImpl.java
  17. 197 0
      yudao-module-member/yudao-module-member-impl/src/test/java/cn/iocoder/yudao/module/member/service/address/AddressServiceImplTest.java
  18. 1 0
      yudao-module-member/yudao-module-member-impl/src/test/resources/sql/clean.sql
  19. 17 0
      yudao-module-member/yudao-module-member-impl/src/test/resources/sql/create_tables.sql

+ 22 - 0
sql/ruoyi-vue-pro.sql

@@ -1923,6 +1923,28 @@ INSERT INTO `member_user` VALUES (245, 'yunai222', 'http://pic.616pic.com/ys_b_i
 INSERT INTO `member_user` VALUES (246, '', '', 0, '15601691301', '$2a$10$KLvmwoU.bvjU2u/MeWa1iOX2GDRJ2P9YqaCad10bYQCiyOaPexGwW', '127.0.0.1', '127.0.0.1', '2021-10-10 22:36:27', NULL, '2021-10-10 22:36:27', NULL, '2022-02-27 04:14:35', b'0', 1);
 COMMIT;
 
+-- ----------------------------
+-- Table structure for member_address
+-- ----------------------------
+DROP TABLE IF EXISTS `member_address`;
+CREATE TABLE `member_address` (
+  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '收件地址编号',
+  `user_id` bigint NOT NULL COMMENT '用户编号',
+  `name` varchar(10) COLLATE utf8mb4_bin NOT NULL COMMENT '收件人名称',
+  `mobile` varchar(20) COLLATE utf8mb4_bin NOT NULL COMMENT '手机号',
+  `area_code` int(11) NOT NULL COMMENT '地区编码',
+  `detail_address` varchar(250) COLLATE utf8mb4_bin NOT NULL COMMENT '收件详细地址',
+  `type` tinyint(4) NOT NULL COMMENT '地址类型',
+  `creator` varchar(64) DEFAULT '' COMMENT '创建者',
+  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `updater` varchar(64) DEFAULT '' COMMENT '更新者',
+  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '删除状态',
+	`tenant_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '租户编号',
+  PRIMARY KEY (`id`) USING BTREE,
+  KEY `idx_userId` (`user_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='用户收件地址';
+
 -- ----------------------------
 -- Table structure for pay_app
 -- ----------------------------

+ 4 - 0
yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/ErrorCodeConstants.java

@@ -21,4 +21,8 @@ public interface ErrorCodeConstants {
     ErrorCode AUTH_TOKEN_EXPIRED = new ErrorCode(1004003004, "Token 已经过期");
     ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1004003005, "未绑定账号,需要进行绑定");
 
+    // ========== 用户收件地址 1004004000 ==========
+    ErrorCode ADDRESS_NOT_EXISTS = new ErrorCode(1004004000, "用户收件地址不存在");
+    ErrorCode ADDRESS_FORBIDDEN = new ErrorCode(1004004001, "没有该操作权限");
+
 }

+ 54 - 0
yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/AppAddressController.http

@@ -0,0 +1,54 @@
+### 请求 /create 接口 => 成功
+POST {{appApi}}//member/address/create
+Content-Type: application/json
+tenant-id: {{appTenentId}}
+Authorization: Bearer 2510e2e4287346eb8e36353a55e27fd6
+
+{
+  "userId": "245",
+  "name": "yunai",
+  "mobile": "15601691300",
+  "areaCode": "610632",
+  "detailAddress": "芋道源码 233 号 666 室",
+  "type": "1"
+}
+
+### 请求 /update 接口 => 成功
+PUT {{appApi}}//member/address/update
+Content-Type: application/json
+tenant-id: {{appTenentId}}
+Authorization: Bearer 2510e2e4287346eb8e36353a55e27fd6
+
+{
+  "id": "1",
+  "userId": "245",
+  "name": "yunai888",
+  "mobile": "15601691300",
+  "areaCode": "610632",
+  "detailAddress": "芋道源码 233 号 666 室",
+  "type": "1"
+}
+
+### 请求 /delete 接口 => 成功
+DELETE {{appApi}}//member/address/delete?id=2
+Content-Type: application/json
+tenant-id: {{appTenentId}}
+Authorization: Bearer fa4848b001de4eae9faf516c0c8520f8
+
+### 请求 /get 接口 => 成功
+GET {{appApi}}//member/address/get?id=1
+Content-Type: application/json
+tenant-id: {{appTenentId}}
+Authorization: Bearer fa4848b001de4eae9faf516c0c8520f8
+
+### 请求 /get-default 接口 => 成功
+GET {{appApi}}//member/address/get-default
+Content-Type: application/json
+tenant-id: {{appTenentId}}
+Authorization: Bearer fa4848b001de4eae9faf516c0c8520f8
+
+### 请求 /list 接口 => 成功
+GET {{appApi}}//member/address/list
+Content-Type: application/json
+tenant-id: {{appTenentId}}
+Authorization: Bearer fa4848b001de4eae9faf516c0c8520f8

+ 78 - 0
yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/AppAddressController.java

@@ -0,0 +1,78 @@
+package cn.iocoder.yudao.module.member.controller.app.address;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressCreateReqVO;
+import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressPageReqVO;
+import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressRespVO;
+import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressUpdateReqVO;
+import cn.iocoder.yudao.module.member.convert.address.AddressConvert;
+import cn.iocoder.yudao.module.member.dal.dataobject.address.AddressDO;
+import cn.iocoder.yudao.module.member.service.address.AddressService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import java.util.Collection;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+
+@Api(tags = "用户 APP - 用户收件地址")
+@RestController
+@RequestMapping("/member/address")
+@Validated
+public class AppAddressController {
+
+    @Resource
+    private AddressService addressService;
+
+    @PostMapping("/create")
+    @ApiOperation("创建用户收件地址")
+    public CommonResult<Long> createAddress(@Valid @RequestBody AppAddressCreateReqVO createReqVO) {
+        return success(addressService.createAddress(getLoginUserId(), createReqVO));
+    }
+
+    @PutMapping("/update")
+    @ApiOperation("更新用户收件地址")
+    public CommonResult<Boolean> updateAddress(@Valid @RequestBody AppAddressUpdateReqVO updateReqVO) {
+        addressService.updateAddress(getLoginUserId(), updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @ApiOperation("删除用户收件地址")
+    @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
+    public CommonResult<Boolean> deleteAddress(@RequestParam("id") Long id) {
+        addressService.deleteAddress(getLoginUserId(), id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @ApiOperation("获得用户收件地址")
+    @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
+    public CommonResult<AppAddressRespVO> getAddress(@RequestParam("id") Long id) {
+        AddressDO address = addressService.getAddress(getLoginUserId(), id);
+        return success(AddressConvert.INSTANCE.convert(address));
+    }
+
+    @GetMapping("/get-default")
+    @ApiOperation("获得默认的用户收件地址")
+    public CommonResult<AppAddressRespVO> getDefaultUserAddress() {
+        AddressDO address = addressService.getDefaultUserAddress(getLoginUserId());
+        return success(AddressConvert.INSTANCE.convert(address));
+    }
+
+    @GetMapping("/list")
+    @ApiOperation("获得用户收件地址列表")
+    public CommonResult<List<AppAddressRespVO>> getAddressList() {
+        List<AddressDO> list = addressService.getAddressList(getLoginUserId());
+        return success(AddressConvert.INSTANCE.convertList(list));
+    }
+
+}

+ 0 - 1
yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/package-info.java

@@ -1 +0,0 @@
-package cn.iocoder.yudao.module.member.controller.app.address;

+ 37 - 0
yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressBaseVO.java

@@ -0,0 +1,37 @@
+package cn.iocoder.yudao.module.member.controller.app.address.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.annotations.*;
+import javax.validation.constraints.*;
+
+/**
+* 用户收件地址 Base VO,提供给添加、修改、详细的子 VO 使用
+* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
+*/
+@Data
+public class AppAddressBaseVO {
+
+    // TODO @shuaidawang:swagger 注解的 example;其它 VO 类也要补充下
+
+    @ApiModelProperty(value = "收件人名称", required = true)
+    @NotNull(message = "收件人名称不能为空")
+    private String name;
+
+    @ApiModelProperty(value = "手机号", required = true)
+    @NotNull(message = "手机号不能为空")
+    private String mobile;
+
+    @ApiModelProperty(value = "地区编码", required = true)
+    @NotNull(message = "地区编码不能为空")
+    private Integer areaCode;
+
+    @ApiModelProperty(value = "收件详细地址", required = true)
+    @NotNull(message = "收件详细地址不能为空")
+    private String detailAddress;
+
+    @ApiModelProperty(value = "地址类型", required = true) // TODO @shuaidawang:这个是枚举字段,最好说明下对应的枚举类
+    @NotNull(message = "地址类型不能为空")
+    private Integer type;
+
+}

+ 14 - 0
yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressCreateReqVO.java

@@ -0,0 +1,14 @@
+package cn.iocoder.yudao.module.member.controller.app.address.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.annotations.*;
+import javax.validation.constraints.*;
+
+@ApiModel("用户 APP - 用户收件地址创建 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class AppAddressCreateReqVO extends AppAddressBaseVO {
+
+}

+ 19 - 0
yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressRespVO.java

@@ -0,0 +1,19 @@
+package cn.iocoder.yudao.module.member.controller.app.address.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.annotations.*;
+
+@ApiModel("用户 APP - 用户收件地址 Response VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class AppAddressRespVO extends AppAddressBaseVO {
+
+    @ApiModelProperty(value = "编号", required = true)
+    private Long id;
+
+    @ApiModelProperty(value = "创建时间", required = true)
+    private Date createTime;
+
+}

+ 18 - 0
yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/vo/AppAddressUpdateReqVO.java

@@ -0,0 +1,18 @@
+package cn.iocoder.yudao.module.member.controller.app.address.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.annotations.*;
+import javax.validation.constraints.*;
+
+@ApiModel("用户 APP - 用户收件地址更新 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class AppAddressUpdateReqVO extends AppAddressBaseVO {
+
+    @ApiModelProperty(value = "编号", required = true)
+    @NotNull(message = "编号不能为空")
+    private Long id;
+
+}

+ 32 - 0
yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/convert/address/AddressConvert.java

@@ -0,0 +1,32 @@
+package cn.iocoder.yudao.module.member.convert.address;
+
+import java.util.*;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+import cn.iocoder.yudao.module.member.controller.app.address.vo.*;
+import cn.iocoder.yudao.module.member.dal.dataobject.address.AddressDO;
+
+/**
+ * 用户收件地址 Convert
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface AddressConvert {
+
+    AddressConvert INSTANCE = Mappers.getMapper(AddressConvert.class);
+
+    AddressDO convert(AppAddressCreateReqVO bean);
+
+    AddressDO convert(AppAddressUpdateReqVO bean);
+
+    AppAddressRespVO convert(AddressDO bean);
+
+    List<AppAddressRespVO> convertList(List<AddressDO> list);
+
+    PageResult<AppAddressRespVO> convertPage(PageResult<AddressDO> page);
+
+}

+ 55 - 0
yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/address/AddressDO.java

@@ -0,0 +1,55 @@
+package cn.iocoder.yudao.module.member.dal.dataobject.address;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import cn.iocoder.yudao.module.member.enums.AddressTypeEnum;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+/**
+ * 用户收件地址 DO
+ *
+ * @author 芋道源码
+ */
+@TableName("member_address")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class AddressDO extends BaseDO {
+
+    /**
+     * 编号
+     */
+    @TableId
+    private Long id;
+    /**
+     * 用户编号
+     */
+    private Long userId;
+    /**
+     * 收件人名称
+     */
+    private String name;
+    /**
+     * 手机号
+     */
+    private String mobile;
+    /**
+     * 地区编码
+     */
+    private Integer areaCode;
+    /**
+     * 收件详细地址
+     */
+    private String detailAddress;
+    /**
+     * 地址类型
+     *
+     * 枚举 {@link AddressTypeEnum}
+     */
+    private Integer type;
+
+}

+ 0 - 1
yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/address/package-info.java

@@ -1 +0,0 @@
-package cn.iocoder.yudao.module.member.dal.dataobject.address;

+ 32 - 0
yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/address/AddressMapper.java

@@ -0,0 +1,32 @@
+package cn.iocoder.yudao.module.member.dal.mysql.address;
+
+import java.util.*;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.module.member.dal.dataobject.address.AddressDO;
+import org.apache.ibatis.annotations.Mapper;
+import cn.iocoder.yudao.module.member.controller.app.address.vo.*;
+
+/**
+ * 用户收件地址 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface AddressMapper extends BaseMapperX<AddressDO> {
+
+    default PageResult<AddressDO> selectPage(AppAddressPageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<AddressDO>()
+                .eqIfPresent(AddressDO::getUserId, reqVO.getUserId())
+                .likeIfPresent(AddressDO::getName, reqVO.getName())
+                .eqIfPresent(AddressDO::getMobile, reqVO.getMobile())
+                .eqIfPresent(AddressDO::getAreaCode, reqVO.getAreaCode())
+                .eqIfPresent(AddressDO::getDetailAddress, reqVO.getDetailAddress())
+                .eqIfPresent(AddressDO::getType, reqVO.getType())
+                .betweenIfPresent(AddressDO::getCreateTime, reqVO.getBeginCreateTime(), reqVO.getEndCreateTime())
+                .orderByDesc(AddressDO::getId));
+    }
+
+}

+ 21 - 0
yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/enums/AddressTypeEnum.java

@@ -0,0 +1,21 @@
+package cn.iocoder.yudao.module.member.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 用户收件地址的类型枚举
+ */
+@Getter
+@AllArgsConstructor
+public enum AddressTypeEnum {
+
+    DEFAULT(1, "默认收件地址"),
+    NORMAL(2, "普通收件地址"), // 即非默认收件地址
+
+    ;
+
+    private final Integer type;
+    private final String desc;
+
+}

+ 68 - 0
yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/address/AddressService.java

@@ -0,0 +1,68 @@
+package cn.iocoder.yudao.module.member.service.address;
+
+import java.util.*;
+import javax.validation.*;
+import cn.iocoder.yudao.module.member.controller.app.address.vo.*;
+import cn.iocoder.yudao.module.member.dal.dataobject.address.AddressDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+
+/**
+ * 用户收件地址 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface AddressService {
+
+    /**
+     * 创建用户收件地址
+     *
+     *
+     * @param userId 用户编号
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createAddress(Long userId, @Valid AppAddressCreateReqVO createReqVO);
+
+    /**
+     * 更新用户收件地址
+     *
+     * @param userId 用户编号
+     * @param updateReqVO 更新信息
+     */
+    void updateAddress(Long userId, @Valid AppAddressUpdateReqVO updateReqVO);
+
+    /**
+     * 删除用户收件地址
+     *
+     * @param userId 用户编号
+     * @param id 编号
+     */
+    void deleteAddress(Long userId, Long id);
+
+    /**
+     * 获得用户收件地址
+     *
+     * @param id 编号
+     * @return 用户收件地址
+     */
+    AddressDO getAddress(Long id);
+
+    /**
+     * 获得用户收件地址列表
+     *
+     * @param userId 用户编号
+     * @return 用户收件地址列表
+     */
+    List<AddressDO> getAddressList(Long userId);
+    
+    /**
+     * 获得用户收件地址
+     *
+     * @param userId 用户编号
+     * @param id 编号
+     * @return 用户收件地址
+     */
+    AddressDO getAddress(Long userId, Long id);
+
+    AddressDO getDefaultUserAddress(Long userId);
+}

+ 144 - 0
yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/address/AddressServiceImpl.java

@@ -0,0 +1,144 @@
+package cn.iocoder.yudao.module.member.service.address;
+
+import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
+import cn.iocoder.yudao.module.member.enums.AddressTypeEnum;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import org.checkerframework.checker.nullness.Opt;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Nullable;
+import javax.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+
+import java.util.*;
+import cn.iocoder.yudao.module.member.controller.app.address.vo.*;
+import cn.iocoder.yudao.module.member.dal.dataobject.address.AddressDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+
+import cn.iocoder.yudao.module.member.convert.address.AddressConvert;
+import cn.iocoder.yudao.module.member.dal.mysql.address.AddressMapper;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.*;
+
+/**
+ * 用户收件地址 Service 实现类
+ *
+ * @author 芋道源码
+ */
+@Service
+@Validated
+public class AddressServiceImpl implements AddressService {
+
+    @Resource
+    private AddressMapper addressMapper;
+
+    @Override // TODO @shuaidawang:事务要加下哈
+    public Long createAddress(Long userId, AppAddressCreateReqVO createReqVO) {
+        // 如果添加的是默认收件地址,则将原默认地址修改为非默认
+        if (AddressTypeEnum.DEFAULT.getType().equals(createReqVO.getType())) {
+            // TODO @shuaidawang:查询到一个,然后进行 update
+            List<AddressDO> addressDOs = selectListByUserIdAndType(userId, AddressTypeEnum.DEFAULT.getType());
+            if (!CollectionUtils.isEmpty(addressDOs)) {
+                addressDOs.forEach(userAddressDO -> addressMapper.updateById(new AddressDO()
+                        .setId(userAddressDO.getId()).setType(AddressTypeEnum.NORMAL.getType())));
+            }
+        }
+        // 插入
+        AddressDO address = AddressConvert.INSTANCE.convert(createReqVO);
+        address.setUserId(userId);
+        addressMapper.insert(address);
+        // 返回
+        return address.getId();
+    }
+
+    @Override // TODO @shuaidawang:事务要加下哈
+    public void updateAddress(Long userId, AppAddressUpdateReqVO updateReqVO) {
+        // 校验存在,校验是否能够操作 TODO shuaidawang:改成基于 id + userId 查询,以前的做法不太好;
+        check(userId, updateReqVO.getId());
+        // 如果修改的是默认收件地址,则将原默认地址修改为非默认
+        if (AddressTypeEnum.DEFAULT.getType().equals(updateReqVO.getType())) {
+            // TODO @shuaidawang:查询到一个,然后进行 update,需要排除自己哈
+            List<AddressDO> addressDOs = selectListByUserIdAndType(
+                    userId, AddressTypeEnum.DEFAULT.getType());
+            if (!CollectionUtils.isEmpty(addressDOs)) {
+                addressDOs.stream().filter(userAddressDO -> !userAddressDO.getId().equals(updateReqVO.getId())) // 过滤掉更新的收件地址
+                        .forEach(userAddressDO -> addressMapper.updateById(new AddressDO()
+                                .setId(userAddressDO.getId()).setType(AddressTypeEnum.NORMAL.getType())));
+            }
+        }
+        // 更新
+        AddressDO updateObj = AddressConvert.INSTANCE.convert(updateReqVO);
+        updateObj.setUserId(userId); // TODO @shuaidawang:不用加 userId
+        addressMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void deleteAddress(Long userId, Long id) {
+        // 校验存在,校验是否能够操作 TODO shuaidawang:改成基于 id + userId 查询,以前的做法不太好;
+        check(userId, id);
+        // 删除
+        addressMapper.deleteById(id);
+    }
+
+    /**
+     * 校验用户收件地址是不是属于该用户
+     *
+     * @param userId 用户编号
+     * @param userAddressId 用户收件地址
+     */
+    private void check(Long userId, Long userAddressId) {
+        AddressDO addressDO = getAddress(userAddressId);
+        if(null == addressDO){
+            throw exception(ADDRESS_NOT_EXISTS);
+        }
+        if (!addressDO.getUserId().equals(userId)) {
+            throw exception(ADDRESS_FORBIDDEN);
+        }
+    }
+
+    @Override
+    public AddressDO getAddress(Long id) {
+        return addressMapper.selectById(id);
+    }
+
+    @Override
+    public List<AddressDO> getAddressList(Long userId) {
+        return selectListByUserIdAndType(userId, null);
+    }
+
+    @Override
+    public AddressDO getAddress(Long userId, Long id) {
+        AddressDO address = getAddress(id); // TODO shuaidawang:改成基于 id + userId 查询,以前的做法不太好;
+        check(userId, id);
+        return address;
+    }
+
+    /**
+     * 获取默认地址
+     * @param userId
+     * @return
+     */
+    @Override
+    public AddressDO getDefaultUserAddress(Long userId) {
+        // TODO @shuaidawang:查询,都抽到 mapper 中
+        List<AddressDO> addressDOList = selectListByUserIdAndType(userId, AddressTypeEnum.DEFAULT.getType());
+        return addressDOList.stream().findFirst().orElse(null);
+    }
+
+    // TODO @shuaidawang:查询,都抽到 mapper 中
+    /**
+     * 根据类型获取地址列表
+     * @param userId
+     * @param type null则查询全部
+     * @return
+     */
+    public List<AddressDO> selectListByUserIdAndType(Long userId, Integer type) {
+        QueryWrapperX<AddressDO> queryWrapperX = new QueryWrapperX<AddressDO>().eq("user_id", userId)
+                .eqIfPresent("type", type);
+        return addressMapper.selectList(queryWrapperX);
+    }
+
+
+}

+ 197 - 0
yudao-module-member/yudao-module-member-impl/src/test/java/cn/iocoder/yudao/module/member/service/address/AddressServiceImplTest.java

@@ -0,0 +1,197 @@
+package cn.iocoder.yudao.module.member.service.address;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
+import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressCreateReqVO;
+import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressExportReqVO;
+import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressPageReqVO;
+import cn.iocoder.yudao.module.member.controller.app.address.vo.AppAddressUpdateReqVO;
+import cn.iocoder.yudao.module.member.dal.dataobject.address.AddressDO;
+import cn.iocoder.yudao.module.member.dal.mysql.address.AddressMapper;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.annotation.Import;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
+import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
+import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
+import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
+import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.ADDRESS_NOT_EXISTS;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+* {@link AddressServiceImpl} 的单元测试类
+*
+* @author 芋道源码
+*/
+@Import(AddressServiceImpl.class)
+public class AddressServiceImplTest extends BaseDbUnitTest {
+
+    @Resource
+    private AddressServiceImpl addressService;
+
+    @Resource
+    private AddressMapper addressMapper;
+
+    @Test
+    public void testCreateAddress_success() {
+        // 准备参数
+        AppAddressCreateReqVO reqVO = randomPojo(AppAddressCreateReqVO.class);
+
+        // 调用
+        Long addressId = addressService.createAddress(getLoginUserId(), reqVO);
+        // 断言
+        assertNotNull(addressId);
+        // 校验记录的属性是否正确
+        AddressDO address = addressMapper.selectById(addressId);
+        assertPojoEquals(reqVO, address);
+    }
+
+    @Test
+    public void testUpdateAddress_success() {
+        // mock 数据
+        AddressDO dbAddress = randomPojo(AddressDO.class);
+        addressMapper.insert(dbAddress);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        AppAddressUpdateReqVO reqVO = randomPojo(AppAddressUpdateReqVO.class, o -> {
+            o.setId(dbAddress.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        addressService.updateAddress(getLoginUserId(), reqVO);
+        // 校验是否更新正确
+        AddressDO address = addressMapper.selectById(reqVO.getId()); // 获取最新的
+        assertPojoEquals(reqVO, address);
+    }
+
+    @Test
+    public void testUpdateAddress_notExists() {
+        // 准备参数
+        AppAddressUpdateReqVO reqVO = randomPojo(AppAddressUpdateReqVO.class);
+
+        // 调用, 并断言异常
+        assertServiceException(() -> addressService.updateAddress(getLoginUserId(), reqVO), ADDRESS_NOT_EXISTS);
+    }
+
+    @Test
+    public void testDeleteAddress_success() {
+        // mock 数据
+        AddressDO dbAddress = randomPojo(AddressDO.class);
+        addressMapper.insert(dbAddress);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbAddress.getId();
+
+        // 调用
+        addressService.deleteAddress(getLoginUserId(), id);
+       // 校验数据不存在了
+       assertNull(addressMapper.selectById(id));
+    }
+
+    @Test
+    public void testDeleteAddress_notExists() {
+        // 准备参数
+        Long id = randomLongId();
+
+        // 调用, 并断言异常
+        assertServiceException(() -> addressService.deleteAddress(getLoginUserId(), id), ADDRESS_NOT_EXISTS);
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void ins() {
+       // mock 数据
+       AddressDO dbAddress = randomPojo(AddressDO.class, o -> { // 等会查询到
+           o.setUserId(null);
+           o.setName(null);
+           o.setMobile(null);
+           o.setAreaCode(null);
+           o.setDetailAddress(null);
+           o.setType(null);
+           o.setCreateTime(null);
+       });
+       addressMapper.insert(dbAddress);
+       // 测试 userId 不匹配
+       addressMapper.insert(cloneIgnoreId(dbAddress, o -> o.setUserId(null)));
+       // 测试 name 不匹配
+       addressMapper.insert(cloneIgnoreId(dbAddress, o -> o.setName(null)));
+       // 测试 mobile 不匹配
+       addressMapper.insert(cloneIgnoreId(dbAddress, o -> o.setMobile(null)));
+       // 测试 areaCode 不匹配
+       addressMapper.insert(cloneIgnoreId(dbAddress, o -> o.setAreaCode(null)));
+       // 测试 detailAddress 不匹配
+       addressMapper.insert(cloneIgnoreId(dbAddress, o -> o.setDetailAddress(null)));
+       // 测试 type 不匹配
+       addressMapper.insert(cloneIgnoreId(dbAddress, o -> o.setType(null)));
+       // 测试 createTime 不匹配
+       addressMapper.insert(cloneIgnoreId(dbAddress, o -> o.setCreateTime(null)));
+       // 准备参数
+       AppAddressPageReqVO reqVO = new AppAddressPageReqVO();
+       reqVO.setUserId(null);
+       reqVO.setName(null);
+       reqVO.setMobile(null);
+       reqVO.setAreaCode(null);
+       reqVO.setDetailAddress(null);
+       reqVO.setType(null);
+       reqVO.setBeginCreateTime(null);
+       reqVO.setEndCreateTime(null);
+
+       // 调用
+       PageResult<AddressDO> pageResult = addressService.getAddressPage(reqVO);
+       // 断言
+       assertEquals(1, pageResult.getTotal());
+       assertEquals(1, pageResult.getList().size());
+       assertPojoEquals(dbAddress, pageResult.getList().get(0));
+    }
+
+    @Test
+    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    public void testGetAddressList() {
+       // mock 数据
+       AddressDO dbAddress = randomPojo(AddressDO.class, o -> { // 等会查询到
+           o.setUserId(null);
+           o.setName(null);
+           o.setMobile(null);
+           o.setAreaCode(null);
+           o.setDetailAddress(null);
+           o.setType(null);
+           o.setCreateTime(null);
+       });
+       addressMapper.insert(dbAddress);
+       // 测试 userId 不匹配
+       addressMapper.insert(cloneIgnoreId(dbAddress, o -> o.setUserId(null)));
+       // 测试 name 不匹配
+       addressMapper.insert(cloneIgnoreId(dbAddress, o -> o.setName(null)));
+       // 测试 mobile 不匹配
+       addressMapper.insert(cloneIgnoreId(dbAddress, o -> o.setMobile(null)));
+       // 测试 areaCode 不匹配
+       addressMapper.insert(cloneIgnoreId(dbAddress, o -> o.setAreaCode(null)));
+       // 测试 detailAddress 不匹配
+       addressMapper.insert(cloneIgnoreId(dbAddress, o -> o.setDetailAddress(null)));
+       // 测试 type 不匹配
+       addressMapper.insert(cloneIgnoreId(dbAddress, o -> o.setType(null)));
+       // 测试 createTime 不匹配
+       addressMapper.insert(cloneIgnoreId(dbAddress, o -> o.setCreateTime(null)));
+       // 准备参数
+       AppAddressExportReqVO reqVO = new AppAddressExportReqVO();
+       reqVO.setUserId(null);
+       reqVO.setName(null);
+       reqVO.setMobile(null);
+       reqVO.setAreaCode(null);
+       reqVO.setDetailAddress(null);
+       reqVO.setType(null);
+       reqVO.setBeginCreateTime(null);
+       reqVO.setEndCreateTime(null);
+
+       // 调用
+       List<AddressDO> list = addressService.getAddressList(reqVO);
+       // 断言
+       assertEquals(1, list.size());
+       assertPojoEquals(dbAddress, list.get(0));
+    }
+
+}

+ 1 - 0
yudao-module-member/yudao-module-member-impl/src/test/resources/sql/clean.sql

@@ -1 +1,2 @@
 DELETE FROM "member_user";
+DELETE FROM "member_address";

+ 17 - 0
yudao-module-member/yudao-module-member-impl/src/test/resources/sql/create_tables.sql

@@ -30,3 +30,20 @@ CREATE TABLE IF NOT EXISTS "inf_file" (
     PRIMARY KEY ("id")
 ) COMMENT '文件表';
 
+CREATE TABLE IF NOT EXISTS "member_address" (
+    "id" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+    "user_id" bigint(20) NOT NULL,
+    "name" varchar(10) NOT NULL,
+    "mobile" varchar(20) NOT NULL,
+    "area_code" int(11) NOT NULL,
+    "detail_address" varchar(250) NOT NULL,
+    "type" tinyint(4) NOT NULL,
+    "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    "creator" varchar(64) DEFAULT '',
+    "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    "deleted" bit NOT NULL DEFAULT FALSE,
+    "updater" varchar(64) DEFAULT '',
+    "tenant_id" bigint(20) NOT NULL,
+    PRIMARY KEY ("id")
+    ) COMMENT '用户收件地址';
+