初始化order和pay模块
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
package com.tashow.cloud.member;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* 项目的启动类
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class MemberServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(MemberServerApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.tashow.cloud.member.address;
|
||||
|
||||
import com.tashow.cloud.common.pojo.CommonResult;
|
||||
import com.tashow.cloud.memberapi.api.address.MemberAddressApi;
|
||||
import com.tashow.cloud.memberapi.api.address.dto.MemberAddressRespDTO;
|
||||
import com.tashow.cloud.member.convert.address.AddressConvert;
|
||||
import com.tashow.cloud.member.service.address.AddressService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static com.tashow.cloud.common.pojo.CommonResult.success;
|
||||
|
||||
|
||||
/**
|
||||
* 用户收件地址 API 实现类
|
||||
*/
|
||||
@RestController // 提供 RESTful API 接口,给 Feign 调用
|
||||
@Validated
|
||||
public class MemberAddressApiImpl implements MemberAddressApi {
|
||||
|
||||
@Resource
|
||||
private AddressService addressService;
|
||||
|
||||
@Override
|
||||
public CommonResult<MemberAddressRespDTO> getAddress(Long id, Long userId) {
|
||||
return success(AddressConvert.INSTANCE.convert02(addressService.getAddress(userId, id)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<MemberAddressRespDTO> getDefaultAddress(Long userId) {
|
||||
return success(AddressConvert.INSTANCE.convert02(addressService.getDefaultUserAddress(userId)));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.tashow.cloud.member.controller.admin.address;
|
||||
|
||||
import com.tashow.cloud.common.pojo.CommonResult;
|
||||
import com.tashow.cloud.member.controller.admin.address.vo.AddressRespVO;
|
||||
import com.tashow.cloud.member.convert.address.AddressConvert;
|
||||
import com.tashow.cloud.member.dal.dataobject.address.MemberAddressDO;
|
||||
import com.tashow.cloud.member.service.address.AddressService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.tashow.cloud.common.pojo.CommonResult.success;
|
||||
|
||||
|
||||
// 管理后台 - 用户收件地址
|
||||
@RestController
|
||||
@RequestMapping("/member/address")
|
||||
@Validated
|
||||
public class AddressController {
|
||||
|
||||
@Resource
|
||||
private AddressService addressService;
|
||||
|
||||
@GetMapping("/list")
|
||||
// 获得用户收件地址列表
|
||||
// userId: 用户编号,必填
|
||||
@PreAuthorize("@ss.hasPermission('member:user:query')")
|
||||
public CommonResult<List<AddressRespVO>> getAddressList(@RequestParam("userId") Long userId) {
|
||||
List<MemberAddressDO> list = addressService.getAddressList(userId);
|
||||
return success(AddressConvert.INSTANCE.convertList2(list));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package com.tashow.cloud.member.controller.admin.address;
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.tashow.cloud.member.controller.admin.address.vo;
|
||||
|
||||
import lombok.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
|
||||
/**
|
||||
* 用户收件地址 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
|
||||
*/
|
||||
@Data
|
||||
public class AddressBaseVO {
|
||||
|
||||
// 收件人名称,必填,示例:张三
|
||||
@NotNull(message = "收件人名称不能为空")
|
||||
private String name;
|
||||
|
||||
// 手机号,必填
|
||||
@NotNull(message = "手机号不能为空")
|
||||
private String mobile;
|
||||
|
||||
// 地区编码,必填,示例:15716
|
||||
@NotNull(message = "地区编码不能为空")
|
||||
private Long areaId;
|
||||
|
||||
// 收件详细地址,必填
|
||||
@NotNull(message = "收件详细地址不能为空")
|
||||
private String detailAddress;
|
||||
|
||||
// 是否默认,必填,示例:2
|
||||
@NotNull(message = "是否默认不能为空")
|
||||
private Boolean defaultStatus;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.tashow.cloud.member.controller.admin.address.vo;
|
||||
|
||||
import lombok.*;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
// 管理后台 - 用户收件地址 Response VO
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class AddressRespVO extends AddressBaseVO {
|
||||
|
||||
// 收件地址编号,必填,示例:7380
|
||||
private Long id;
|
||||
|
||||
// 创建时间,必填
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.tashow.cloud.member.controller.admin.user;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.tashow.cloud.common.pojo.CommonResult;
|
||||
import com.tashow.cloud.common.pojo.PageResult;
|
||||
import com.tashow.cloud.member.controller.admin.user.vo.MemberUserPageReqVO;
|
||||
import com.tashow.cloud.member.controller.admin.user.vo.MemberUserRespVO;
|
||||
import com.tashow.cloud.member.controller.admin.user.vo.MemberUserUpdateReqVO;
|
||||
import com.tashow.cloud.member.convert.user.MemberUserConvert;
|
||||
import com.tashow.cloud.member.dal.dataobject.user.MemberUserDO;
|
||||
import com.tashow.cloud.member.service.user.MemberUserService;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.tashow.cloud.common.pojo.CommonResult.success;
|
||||
|
||||
|
||||
// 管理后台 - 会员用户
|
||||
@RestController
|
||||
@RequestMapping("/member/user")
|
||||
@Validated
|
||||
public class MemberUserController {
|
||||
|
||||
@Resource
|
||||
private MemberUserService memberUserService;
|
||||
@Resource
|
||||
|
||||
@PutMapping("/update")
|
||||
// 更新会员用户
|
||||
@PreAuthorize("@ss.hasPermission('member:user:update')")
|
||||
public CommonResult<Boolean> updateUser(@Valid @RequestBody MemberUserUpdateReqVO updateReqVO) {
|
||||
memberUserService.updateUser(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/get")
|
||||
// 获得会员用户
|
||||
// id: 编号,必填,示例:1024
|
||||
@PreAuthorize("@ss.hasPermission('member:user:query')")
|
||||
public CommonResult<MemberUserRespVO> getUser(@RequestParam("id") Long id) {
|
||||
MemberUserDO user = memberUserService.getUser(id);
|
||||
return success(MemberUserConvert.INSTANCE.convert03(user));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
// 获得会员用户分页
|
||||
@PreAuthorize("@ss.hasPermission('member:user:query')")
|
||||
public CommonResult<PageResult<MemberUserRespVO>> getUserPage(@Valid MemberUserPageReqVO pageVO) {
|
||||
PageResult<MemberUserDO> pageResult = memberUserService.getUserPage(pageVO);
|
||||
if (CollUtil.isEmpty(pageResult.getList())) {
|
||||
return success(PageResult.empty());
|
||||
}
|
||||
|
||||
// 处理用户标签返显
|
||||
Set<Long> tagIds = pageResult.getList().stream()
|
||||
.map(MemberUserDO::getTagIds)
|
||||
.filter(Objects::nonNull)
|
||||
.flatMap(Collection::stream)
|
||||
.collect(Collectors.toSet());
|
||||
return success(MemberUserConvert.INSTANCE.convertPage(pageResult));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.tashow.cloud.member.controller.admin.user.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.URL;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import static com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
|
||||
|
||||
|
||||
/**
|
||||
* 会员用户 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
|
||||
*/
|
||||
@Data
|
||||
public class MemberUserBaseVO {
|
||||
|
||||
// 手机号,必填,示例:15601691300
|
||||
@NotNull(message = "手机号不能为空")
|
||||
private String mobile;
|
||||
|
||||
// 状态,必填,示例:2
|
||||
@NotNull(message = "状态不能为空")
|
||||
private Byte status;
|
||||
|
||||
// 用户昵称,必填,示例:李四
|
||||
@NotNull(message = "用户昵称不能为空")
|
||||
private String nickname;
|
||||
|
||||
// 头像,必填,示例:https://www.iocoder.cn/x.png
|
||||
@URL(message = "头像必须是 URL 格式")
|
||||
private String avatar;
|
||||
|
||||
// 用户昵称,示例:李四
|
||||
private String name;
|
||||
|
||||
// 用户性别,示例:1
|
||||
private Integer sex;
|
||||
|
||||
// 所在地编号,示例:4371
|
||||
private Long areaId;
|
||||
|
||||
// 所在地全程,示例:上海上海市普陀区
|
||||
private String areaName;
|
||||
|
||||
// 出生日期,示例:2023-03-12
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
||||
private LocalDateTime birthday;
|
||||
|
||||
// 会员备注,示例:我是小备注
|
||||
private String mark;
|
||||
|
||||
// 会员标签,示例:[1, 2]
|
||||
private List<Long> tagIds;
|
||||
|
||||
// 会员等级编号,示例:1
|
||||
private Long levelId;
|
||||
|
||||
// 用户分组编号,示例:1
|
||||
private Long groupId;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.tashow.cloud.member.controller.admin.user.vo;
|
||||
|
||||
import com.tashow.cloud.common.pojo.PageParam;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import static com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
|
||||
// 管理后台 - 会员用户分页 Request VO
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class MemberUserPageReqVO extends PageParam {
|
||||
|
||||
// 手机号,示例:15601691300
|
||||
private String mobile;
|
||||
|
||||
// 用户昵称,示例:李四
|
||||
private String nickname;
|
||||
|
||||
// 最后登录时间
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] loginDate;
|
||||
|
||||
// 创建时间
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
// 会员标签编号列表,示例:[1, 2]
|
||||
private List<Long> tagIds;
|
||||
|
||||
// 会员等级编号,示例:1
|
||||
private Long levelId;
|
||||
|
||||
// 用户分组编号,示例:1
|
||||
private Long groupId;
|
||||
|
||||
// TODO 芋艿:注册用户类型;
|
||||
|
||||
// TODO 芋艿:登录用户类型;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.tashow.cloud.member.controller.admin.user.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
// 管理后台 - 会员用户 Response VO
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class MemberUserRespVO extends MemberUserBaseVO {
|
||||
|
||||
// 编号,必填,示例:23788
|
||||
private Long id;
|
||||
|
||||
// 注册 IP,必填,示例:127.0.0.1
|
||||
private String registerIp;
|
||||
|
||||
// 最后登录IP,必填,示例:127.0.0.1
|
||||
private String loginIp;
|
||||
|
||||
// 最后登录时间,必填
|
||||
private LocalDateTime loginDate;
|
||||
|
||||
// 创建时间,必填
|
||||
private LocalDateTime createTime;
|
||||
|
||||
// ========== 其它信息 ==========
|
||||
|
||||
// 积分,必填,示例:100
|
||||
private Integer point;
|
||||
|
||||
// 总积分,必填,示例:2000
|
||||
private Integer totalPoint;
|
||||
|
||||
// 会员标签,示例:[红色, 快乐]
|
||||
private List<String> tagNames;
|
||||
|
||||
// 会员等级,示例:黄金会员
|
||||
private String levelName;
|
||||
|
||||
// 用户分组,示例:购物达人
|
||||
private String groupName;
|
||||
|
||||
// 用户经验值,必填,示例:200
|
||||
private Integer experience;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.tashow.cloud.member.controller.admin.user.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
// 管理后台 - 用户修改等级 Request VO
|
||||
@Data
|
||||
@ToString(callSuper = true)
|
||||
public class MemberUserUpdateLevelReqVO {
|
||||
|
||||
// 用户编号,必填,示例:23788
|
||||
@NotNull(message = "用户编号不能为空")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 取消用户等级时,值为空
|
||||
*/
|
||||
// 用户等级编号,示例:1
|
||||
private Long levelId;
|
||||
|
||||
// 修改原因,必填,示例:推广需要
|
||||
@NotBlank(message = "修改原因不能为空")
|
||||
private String reason;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.tashow.cloud.member.controller.admin.user.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
// 管理后台 - 用户修改积分 Request VO
|
||||
@Data
|
||||
@ToString(callSuper = true)
|
||||
public class MemberUserUpdatePointReqVO {
|
||||
|
||||
// 用户编号,必填,示例:23788
|
||||
@NotNull(message = "用户编号不能为空")
|
||||
private Long id;
|
||||
|
||||
// 变动积分,正数为增加,负数为减少,必填,示例:100
|
||||
@NotNull(message = "变动积分不能为空")
|
||||
private Integer point;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.tashow.cloud.member.controller.admin.user.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
// 管理后台 - 会员用户更新 Request VO
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class MemberUserUpdateReqVO extends MemberUserBaseVO {
|
||||
|
||||
// 编号,必填,示例:23788
|
||||
@NotNull(message = "编号不能为空")
|
||||
private Long id;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
### 请求 /create 接口 => 成功
|
||||
POST {{appApi}}//member/address/create
|
||||
Content-Type: application/json
|
||||
tenant-id: {{appTenantId}}
|
||||
Authorization: Bearer {{appToken}}
|
||||
|
||||
{
|
||||
"name": "yunai",
|
||||
"mobile": "15601691300",
|
||||
"areaId": "610632",
|
||||
"postCode": "200000",
|
||||
"detailAddress": "芋道源码 233 号 666 室",
|
||||
"defaulted": true
|
||||
}
|
||||
|
||||
### 请求 /update 接口 => 成功
|
||||
PUT {{appApi}}//member/address/update
|
||||
Content-Type: application/json
|
||||
tenant-id: {{appTenantId}}
|
||||
Authorization: Bearer {{appToken}}
|
||||
|
||||
{
|
||||
"id": "1",
|
||||
"name": "yunai888",
|
||||
"mobile": "15601691300",
|
||||
"areaId": "610632",
|
||||
"postCode": "200000",
|
||||
"detailAddress": "芋道源码 233 号 666 室",
|
||||
"defaulted": false
|
||||
}
|
||||
|
||||
### 请求 /delete 接口 => 成功
|
||||
DELETE {{appApi}}//member/address/delete?id=2
|
||||
Content-Type: application/json
|
||||
tenant-id: {{appTenantId}}
|
||||
Authorization: Bearer {{appToken}}
|
||||
|
||||
### 请求 /get 接口 => 成功
|
||||
GET {{appApi}}//member/address/get?id=1
|
||||
Content-Type: application/json
|
||||
tenant-id: {{appTenantId}}
|
||||
Authorization: Bearer {{appToken}}
|
||||
|
||||
### 请求 /get-default 接口 => 成功
|
||||
GET {{appApi}}//member/address/get-default
|
||||
Content-Type: application/json
|
||||
tenant-id: {{appTenantId}}
|
||||
Authorization: Bearer {{appToken}}
|
||||
|
||||
### 请求 /list 接口 => 成功
|
||||
GET {{appApi}}//member/address/list
|
||||
Content-Type: application/json
|
||||
tenant-id: {{appTenantId}}
|
||||
Authorization: Bearer {{appToken}}
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.tashow.cloud.member.controller.app.address;
|
||||
|
||||
import com.tashow.cloud.common.pojo.CommonResult;
|
||||
import com.tashow.cloud.member.controller.app.address.vo.AppAddressCreateReqVO;
|
||||
import com.tashow.cloud.member.controller.app.address.vo.AppAddressRespVO;
|
||||
import com.tashow.cloud.member.controller.app.address.vo.AppAddressUpdateReqVO;
|
||||
import com.tashow.cloud.member.convert.address.AddressConvert;
|
||||
import com.tashow.cloud.member.dal.dataobject.address.MemberAddressDO;
|
||||
import com.tashow.cloud.member.service.address.AddressService;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.tashow.cloud.common.pojo.CommonResult.success;
|
||||
import static com.tashow.cloud.security.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
|
||||
/**
|
||||
* 用户 APP - 用户收件地址
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/member/address")
|
||||
@Validated
|
||||
public class AppAddressController {
|
||||
|
||||
@Resource
|
||||
private AddressService addressService;
|
||||
|
||||
/**
|
||||
* 创建用户收件地址
|
||||
* @param createReqVO
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/create")
|
||||
public CommonResult<Long> createAddress(@Valid @RequestBody AppAddressCreateReqVO createReqVO) {
|
||||
return success(addressService.createAddress(getLoginUserId(), createReqVO));
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户收件地址
|
||||
* @param updateReqVO
|
||||
* @return
|
||||
*/
|
||||
@PutMapping("/update")
|
||||
public CommonResult<Boolean> updateAddress(@Valid @RequestBody AppAddressUpdateReqVO updateReqVO) {
|
||||
addressService.updateAddress(getLoginUserId(), updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户收件地址
|
||||
* @param id 编号
|
||||
* @return
|
||||
*/
|
||||
@DeleteMapping("/delete")
|
||||
public CommonResult<Boolean> deleteAddress(@RequestParam("id") Long id) {
|
||||
addressService.deleteAddress(getLoginUserId(), id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得用户收件地址
|
||||
* @param id 编号
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/get")
|
||||
public CommonResult<AppAddressRespVO> getAddress(@RequestParam("id") Long id) {
|
||||
MemberAddressDO address = addressService.getAddress(getLoginUserId(), id);
|
||||
return success(AddressConvert.INSTANCE.convert(address));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得默认的用户收件地址
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/get-default")
|
||||
public CommonResult<AppAddressRespVO> getDefaultUserAddress() {
|
||||
MemberAddressDO address = addressService.getDefaultUserAddress(getLoginUserId());
|
||||
return success(AddressConvert.INSTANCE.convert(address));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得用户收件地址列表
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public CommonResult<List<AppAddressRespVO>> getAddressList() {
|
||||
List<MemberAddressDO> list = addressService.getAddressList(getLoginUserId());
|
||||
return success(AddressConvert.INSTANCE.convertList(list));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.tashow.cloud.member.controller.app.address.vo;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 用户收件地址 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||
*/
|
||||
@Data
|
||||
public class AppAddressBaseVO {
|
||||
|
||||
//收件人名称
|
||||
@NotNull(message = "收件人名称不能为空")
|
||||
private String name;
|
||||
|
||||
//手机号
|
||||
@NotNull(message = "手机号不能为空")
|
||||
private String mobile;
|
||||
|
||||
//地区编号
|
||||
@NotNull(message = "地区编号不能为空")
|
||||
private Long areaId;
|
||||
|
||||
//收件详细地址
|
||||
@NotNull(message = "收件详细地址不能为空")
|
||||
private String detailAddress;
|
||||
|
||||
//是否默认地址
|
||||
@NotNull(message = "是否默认地址不能为空")
|
||||
private Boolean defaultStatus;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.tashow.cloud.member.controller.app.address.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* 用户 APP - 用户收件地址创建 Request VO
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class AppAddressCreateReqVO extends AppAddressBaseVO {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.tashow.cloud.member.controller.app.address.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* 用户 APP - 用户收件地址 Response VO
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class AppAddressRespVO extends AppAddressBaseVO {
|
||||
|
||||
//编号
|
||||
private Long id;
|
||||
|
||||
//地区名字
|
||||
private String areaName;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.tashow.cloud.member.controller.app.address.vo;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* 用户 APP - 用户收件地址更新 Request VO
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class AppAddressUpdateReqVO extends AppAddressBaseVO {
|
||||
|
||||
//编号
|
||||
@NotNull(message = "编号不能为空")
|
||||
private Long id;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
### 请求 /login 接口 => 成功
|
||||
POST {{appApi}}/member/auth/login
|
||||
Content-Type: application/json
|
||||
tenant-id: {{appTenantId}}
|
||||
|
||||
{
|
||||
"mobile": "15601691388",
|
||||
"password": "admin123"
|
||||
}
|
||||
|
||||
### 请求 /send-sms-code 接口 => 成功
|
||||
POST {{appApi}}/member/auth/send-sms-code
|
||||
Content-Type: application/json
|
||||
tenant-id: {{appTenantId}}
|
||||
|
||||
{
|
||||
"mobile": "15601691388",
|
||||
"scene": 1
|
||||
}
|
||||
|
||||
### 请求 /sms-login 接口 => 成功
|
||||
POST {{appApi}}/member/auth/sms-login
|
||||
Content-Type: application/json
|
||||
tenant-id: {{appTenantId}}
|
||||
terminal: 30
|
||||
|
||||
{
|
||||
"mobile": "15601691388",
|
||||
"code": 9999
|
||||
}
|
||||
|
||||
### 请求 /social-login 接口 => 成功
|
||||
POST {{appApi}}/member/auth/social-login
|
||||
Content-Type: application/json
|
||||
tenant-id: {{appTenantId}}
|
||||
|
||||
{
|
||||
"type": 34,
|
||||
"code": "0e1oc9000CTjFQ1oim200bhtb61oc90g",
|
||||
"state": "default"
|
||||
}
|
||||
|
||||
### 请求 /weixin-mini-app-login 接口 => 成功
|
||||
POST {{appApi}}/member/auth/weixin-mini-app-login
|
||||
Content-Type: application/json
|
||||
tenant-id: {{appTenantId}}
|
||||
|
||||
{
|
||||
"phoneCode": "618e6412e0c728f5b8fc7164497463d0158a923c9e7fd86af8bba393b9decbc5",
|
||||
"loginCode": "001frTkl21JUf94VGxol2hSlff1frTkR"
|
||||
}
|
||||
|
||||
### 请求 /logout 接口 => 成功
|
||||
POST {{appApi}}/member/auth/logout
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer c1b76bdaf2c146c581caa4d7fd81ee66
|
||||
tenant-id: {{appTenantId}}
|
||||
|
||||
### 请求 /auth/refresh-token 接口 => 成功
|
||||
POST {{appApi}}/member/auth/refresh-token?refreshToken=bc43d929094849a28b3a69f6e6940d70
|
||||
Content-Type: application/json
|
||||
tenant-id: {{appTenantId}}
|
||||
|
||||
### 请求 /auth/create-weixin-jsapi-signature 接口 => 成功
|
||||
POST {{appApi}}/member/auth/create-weixin-jsapi-signature?url=http://www.iocoder.cn
|
||||
Authorization: Bearer {{appToken}}
|
||||
tenant-id: {{appTenantId}}
|
||||
@@ -0,0 +1,169 @@
|
||||
package com.tashow.cloud.member.controller.app.auth;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.tashow.cloud.common.enums.UserTypeEnum;
|
||||
import com.tashow.cloud.common.pojo.CommonResult;
|
||||
import com.tashow.cloud.member.controller.app.auth.vo.*;
|
||||
import com.tashow.cloud.member.convert.auth.AuthConvert;
|
||||
import com.tashow.cloud.member.service.auth.MemberAuthService;
|
||||
import com.tashow.cloud.security.security.config.SecurityProperties;
|
||||
import com.tashow.cloud.security.security.core.util.SecurityFrameworkUtils;
|
||||
import com.tashow.cloud.systemapi.api.social.SocialClientApi;
|
||||
import com.tashow.cloud.systemapi.api.social.dto.SocialWxJsapiSignatureRespDTO;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import static com.tashow.cloud.common.pojo.CommonResult.success;
|
||||
import static com.tashow.cloud.security.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
|
||||
/**
|
||||
* 用户 APP - 认证
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/member/auth")
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class AppAuthController {
|
||||
|
||||
@Resource
|
||||
private MemberAuthService authService;
|
||||
|
||||
@Resource
|
||||
private SocialClientApi socialClientApi;
|
||||
|
||||
@Resource
|
||||
private SecurityProperties securityProperties;
|
||||
|
||||
/**
|
||||
* 使用手机 + 密码登录
|
||||
* @param reqVO
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/login")
|
||||
@PermitAll
|
||||
public CommonResult<AppAuthLoginRespVO> login(@RequestBody @Valid AppAuthLoginReqVO reqVO) {
|
||||
return success(authService.login(reqVO));
|
||||
}
|
||||
|
||||
/**
|
||||
* 登出系统
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/logout")
|
||||
@PermitAll
|
||||
public CommonResult<Boolean> logout(HttpServletRequest request) {
|
||||
String token = SecurityFrameworkUtils.obtainAuthorization(request,
|
||||
securityProperties.getTokenHeader(), securityProperties.getTokenParameter());
|
||||
if (StrUtil.isNotBlank(token)) {
|
||||
authService.logout(token);
|
||||
}
|
||||
return success(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新令牌
|
||||
* @param refreshToken 刷新令牌
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/refresh-token")
|
||||
@PermitAll
|
||||
public CommonResult<AppAuthLoginRespVO> refreshToken(@RequestParam("refreshToken") String refreshToken) {
|
||||
return success(authService.refreshToken(refreshToken));
|
||||
}
|
||||
|
||||
// ========== 短信登录相关 ==========
|
||||
|
||||
/**
|
||||
* 使用手机 + 验证码登录
|
||||
* @param reqVO
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/sms-login")
|
||||
@PermitAll
|
||||
public CommonResult<AppAuthLoginRespVO> smsLogin(@RequestBody @Valid AppAuthSmsLoginReqVO reqVO) {
|
||||
return success(authService.smsLogin(reqVO));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 发送手机验证码
|
||||
* @param reqVO
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/send-sms-code")
|
||||
@PermitAll
|
||||
public CommonResult<Boolean> sendSmsCode(@RequestBody @Valid AppAuthSmsSendReqVO reqVO) {
|
||||
authService.sendSmsCode(getLoginUserId(), reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验手机验证码
|
||||
* @param reqVO
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/validate-sms-code")
|
||||
@PermitAll
|
||||
public CommonResult<Boolean> validateSmsCode(@RequestBody @Valid AppAuthSmsValidateReqVO reqVO) {
|
||||
authService.validateSmsCode(getLoginUserId(), reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
// ========== 社交登录相关 ==========
|
||||
|
||||
/**
|
||||
* 社交授权的跳转
|
||||
* @param type 社交类型
|
||||
* @param redirectUri 回调路径
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/social-auth-redirect")
|
||||
@PermitAll
|
||||
public CommonResult<String> socialAuthRedirect(@RequestParam("type") Integer type,
|
||||
@RequestParam("redirectUri") String redirectUri) {
|
||||
return success(authService.getSocialAuthorizeUrl(type, redirectUri));
|
||||
}
|
||||
|
||||
/**
|
||||
* 社交快捷登录,使用 code 授权码
|
||||
* @param reqVO
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/social-login")
|
||||
@PermitAll
|
||||
public CommonResult<AppAuthLoginRespVO> socialLogin(@RequestBody @Valid AppAuthSocialLoginReqVO reqVO) {
|
||||
return success(authService.socialLogin(reqVO));
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信小程序的一键登录
|
||||
* @param reqVO
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/weixin-mini-app-login")
|
||||
@PermitAll
|
||||
public CommonResult<AppAuthLoginRespVO> weixinMiniAppLogin(@RequestBody @Valid AppAuthWeixinMiniAppLoginReqVO reqVO) {
|
||||
return success(authService.weixinMiniAppLogin(reqVO));
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建微信 JS SDK 初始化所需的签名
|
||||
* @param url
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/create-weixin-jsapi-signature")
|
||||
@PermitAll
|
||||
public CommonResult<SocialWxJsapiSignatureRespDTO> createWeixinMpJsapiSignature(@RequestParam("url") String url) {
|
||||
SocialWxJsapiSignatureRespDTO signature = socialClientApi.createWxMpJsapiSignature(
|
||||
UserTypeEnum.MEMBER.getValue(), url).getCheckedData();
|
||||
return success(AuthConvert.INSTANCE.convert(signature));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.tashow.cloud.member.controller.app.auth.vo;
|
||||
|
||||
import com.tashow.cloud.common.validation.InEnum;
|
||||
import com.tashow.cloud.common.validation.Mobile;
|
||||
import com.tashow.cloud.systemapi.enums.sms.SmsSceneEnum;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
/**
|
||||
* 用户 APP - 校验验证码 Request VO
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class AppAuthCheckCodeReqVO {
|
||||
|
||||
//手机号
|
||||
@NotBlank(message = "手机号不能为空")
|
||||
@Mobile
|
||||
private String mobile;
|
||||
|
||||
//手机验证码
|
||||
@NotBlank(message = "手机验证码不能为空")
|
||||
@Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位")
|
||||
@Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
|
||||
private String code;
|
||||
|
||||
//发送场景,对应 SmsSceneEnum 枚举
|
||||
@NotNull(message = "发送场景不能为空")
|
||||
@InEnum(SmsSceneEnum.class)
|
||||
private Integer scene;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.tashow.cloud.member.controller.app.auth.vo;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.tashow.cloud.common.validation.InEnum;
|
||||
import com.tashow.cloud.common.validation.Mobile;
|
||||
import com.tashow.cloud.systemapi.enums.social.SocialTypeEnum;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
/**
|
||||
* 用户 APP - 手机 + 密码登录 Request VO,如果登录并绑定社交用户,需要传递 social 开头的参数
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class AppAuthLoginReqVO {
|
||||
|
||||
//手机号
|
||||
@NotEmpty(message = "手机号不能为空")
|
||||
@Mobile
|
||||
private String mobile;
|
||||
|
||||
//密码
|
||||
@NotEmpty(message = "密码不能为空")
|
||||
@Length(min = 4, max = 16, message = "密码长度为 4-16 位")
|
||||
private String password;
|
||||
|
||||
// ========== 绑定社交登录时,需要传递如下参数 ==========
|
||||
|
||||
//社交平台的类型,参见 SocialTypeEnum 枚举值
|
||||
@InEnum(SocialTypeEnum.class)
|
||||
private Integer socialType;
|
||||
|
||||
//授权码
|
||||
private String socialCode;
|
||||
|
||||
//state
|
||||
private String socialState;
|
||||
|
||||
@AssertTrue(message = "授权码不能为空")
|
||||
public boolean isSocialCodeValid() {
|
||||
return socialType == null || StrUtil.isNotEmpty(socialCode);
|
||||
}
|
||||
|
||||
@AssertTrue(message = "授权 state 不能为空")
|
||||
public boolean isSocialState() {
|
||||
return socialType == null || StrUtil.isNotEmpty(socialState);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.tashow.cloud.member.controller.app.auth.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户 APP - 登录 Response VO
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class AppAuthLoginRespVO {
|
||||
|
||||
//用户编号
|
||||
private Long userId;
|
||||
|
||||
//访问令牌
|
||||
private String accessToken;
|
||||
|
||||
//刷新令牌
|
||||
private String refreshToken;
|
||||
|
||||
//过期时间
|
||||
private LocalDateTime expiresTime;
|
||||
|
||||
/**
|
||||
* 社交用户
|
||||
* 仅社交登录、社交绑定时会返回
|
||||
* 为什么需要返回?微信公众号、微信小程序支付需要传递 openid 给支付接口
|
||||
*/
|
||||
private String openid;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.tashow.cloud.member.controller.app.auth.vo;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.tashow.cloud.common.validation.InEnum;
|
||||
import com.tashow.cloud.common.validation.Mobile;
|
||||
import com.tashow.cloud.systemapi.enums.social.SocialTypeEnum;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
/**
|
||||
* 用户 APP - 手机 + 验证码登录 Request VO,如果登录并绑定社交用户,需要传递 social 开头的参数
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class AppAuthSmsLoginReqVO {
|
||||
|
||||
//手机号
|
||||
@NotEmpty(message = "手机号不能为空")
|
||||
@Mobile
|
||||
private String mobile;
|
||||
|
||||
//手机验证码
|
||||
@NotEmpty(message = "手机验证码不能为空")
|
||||
@Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位")
|
||||
@Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
|
||||
private String code;
|
||||
|
||||
// ========== 绑定社交登录时,需要传递如下参数 ==========
|
||||
|
||||
//社交平台的类型,参见 SocialTypeEnum 枚举值
|
||||
@InEnum(SocialTypeEnum.class)
|
||||
private Integer socialType;
|
||||
|
||||
//授权码
|
||||
private String socialCode;
|
||||
|
||||
//state
|
||||
private String socialState;
|
||||
|
||||
@AssertTrue(message = "授权码不能为空")
|
||||
public boolean isSocialCodeValid() {
|
||||
return socialType == null || StrUtil.isNotEmpty(socialCode);
|
||||
}
|
||||
|
||||
@AssertTrue(message = "授权 state 不能为空")
|
||||
public boolean isSocialState() {
|
||||
return socialType == null || StrUtil.isNotEmpty(socialState);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.tashow.cloud.member.controller.app.auth.vo;
|
||||
|
||||
import com.tashow.cloud.common.validation.InEnum;
|
||||
import com.tashow.cloud.common.validation.Mobile;
|
||||
import com.tashow.cloud.systemapi.enums.sms.SmsSceneEnum;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 用户 APP - 发送手机验证码 Request VO
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class AppAuthSmsSendReqVO {
|
||||
|
||||
//手机号
|
||||
@Mobile
|
||||
private String mobile;
|
||||
|
||||
//发送场景,对应 SmsSceneEnum 枚举
|
||||
@NotNull(message = "发送场景不能为空")
|
||||
@InEnum(SmsSceneEnum.class)
|
||||
private Integer scene;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.tashow.cloud.member.controller.app.auth.vo;
|
||||
|
||||
import com.tashow.cloud.common.validation.InEnum;
|
||||
import com.tashow.cloud.common.validation.Mobile;
|
||||
import com.tashow.cloud.systemapi.enums.sms.SmsSceneEnum;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
//用户 APP - 校验手机验证码 Request VO
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class AppAuthSmsValidateReqVO {
|
||||
|
||||
//手机号
|
||||
@Mobile
|
||||
private String mobile;
|
||||
|
||||
//发送场景,对应 SmsSceneEnum 枚举
|
||||
@NotNull(message = "发送场景不能为空")
|
||||
@InEnum(SmsSceneEnum.class)
|
||||
private Integer scene;
|
||||
|
||||
//手机验证码
|
||||
@NotEmpty(message = "手机验证码不能为空")
|
||||
@Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位")
|
||||
@Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
|
||||
private String code;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.tashow.cloud.member.controller.app.auth.vo;
|
||||
|
||||
import com.tashow.cloud.common.validation.InEnum;
|
||||
import com.tashow.cloud.systemapi.enums.social.SocialTypeEnum;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 用户 APP - 社交快捷登录 Request VO,使用 code 授权码
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class AppAuthSocialLoginReqVO {
|
||||
|
||||
//社交平台的类型,参见 SocialTypeEnum 枚举值
|
||||
@InEnum(SocialTypeEnum.class)
|
||||
@NotNull(message = "社交平台的类型不能为空")
|
||||
private Integer type;
|
||||
|
||||
//授权码
|
||||
@NotEmpty(message = "授权码不能为空")
|
||||
private String code;
|
||||
|
||||
//state
|
||||
@NotEmpty(message = "state 不能为空")
|
||||
private String state;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.tashow.cloud.member.controller.app.auth.vo;
|
||||
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 用户 APP - 微信小程序手机登录 Request VO
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class AppAuthWeixinMiniAppLoginReqVO {
|
||||
|
||||
/**
|
||||
* 手机 code,小程序通过 wx.getPhoneNumber 方法获得
|
||||
*/
|
||||
@NotEmpty(message = "手机 code 不能为空")
|
||||
private String phoneCode;
|
||||
|
||||
/**
|
||||
* 登录 code,小程序通过 wx.login 方法获得
|
||||
*/
|
||||
@NotEmpty(message = "登录 code 不能为空")
|
||||
private String loginCode;
|
||||
|
||||
/**
|
||||
* state
|
||||
*/
|
||||
@NotEmpty(message = "state 不能为空")
|
||||
private String state;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.tashow.cloud.member.controller.app.auth.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 用户 APP - 微信公众号 JSAPI 签名 Response VO
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class AuthWeixinJsapiSignatureRespVO {
|
||||
|
||||
//微信公众号的 appId
|
||||
private String appId;
|
||||
|
||||
//匿名串
|
||||
private String nonceStr;
|
||||
|
||||
//时间戳
|
||||
private Long timestamp;
|
||||
|
||||
//URL
|
||||
private String url;
|
||||
|
||||
//签名
|
||||
private String signature;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
### 请求 /member/user/profile/get 接口 => 没有权限
|
||||
GET {{appApi}}/member/user/get
|
||||
Authorization: Bearer test245
|
||||
tenant-id: {{appTenantId}}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.tashow.cloud.member.controller.app.user;
|
||||
|
||||
import com.tashow.cloud.common.pojo.CommonResult;
|
||||
import com.tashow.cloud.member.controller.app.user.vo.*;
|
||||
import com.tashow.cloud.member.convert.user.MemberUserConvert;
|
||||
import com.tashow.cloud.member.dal.dataobject.user.MemberUserDO;
|
||||
import com.tashow.cloud.member.service.user.MemberUserService;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import static com.tashow.cloud.common.pojo.CommonResult.success;
|
||||
import static com.tashow.cloud.security.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
|
||||
// 用户 APP - 用户个人中心
|
||||
@RestController
|
||||
@RequestMapping("/member/user")
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class AppMemberUserController {
|
||||
|
||||
@Resource
|
||||
private MemberUserService userService;
|
||||
|
||||
@GetMapping("/get")
|
||||
// 获得基本信息
|
||||
public CommonResult<AppMemberUserInfoRespVO> getUserInfo() {
|
||||
MemberUserDO user = userService.getUser(getLoginUserId());
|
||||
return success(MemberUserConvert.INSTANCE.convert(user));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
// 修改基本信息
|
||||
public CommonResult<Boolean> updateUser(@RequestBody @Valid AppMemberUserUpdateReqVO reqVO) {
|
||||
userService.updateUser(getLoginUserId(), reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/update-mobile")
|
||||
// 修改用户手机
|
||||
public CommonResult<Boolean> updateUserMobile(@RequestBody @Valid AppMemberUserUpdateMobileReqVO reqVO) {
|
||||
userService.updateUserMobile(getLoginUserId(), reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/update-mobile-by-weixin")
|
||||
// 基于微信小程序的授权码,修改用户手机
|
||||
public CommonResult<Boolean> updateUserMobileByWeixin(@RequestBody @Valid AppMemberUserUpdateMobileByWeixinReqVO reqVO) {
|
||||
userService.updateUserMobileByWeixin(getLoginUserId(), reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/update-password")
|
||||
// 修改用户密码
|
||||
// 用户修改密码时使用
|
||||
public CommonResult<Boolean> updateUserPassword(@RequestBody @Valid AppMemberUserUpdatePasswordReqVO reqVO) {
|
||||
userService.updateUserPassword(getLoginUserId(), reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/reset-password")
|
||||
// 重置密码
|
||||
// 用户忘记密码时使用
|
||||
@PermitAll
|
||||
public CommonResult<Boolean> resetUserPassword(@RequestBody @Valid AppMemberUserResetPasswordReqVO reqVO) {
|
||||
userService.resetUserPassword(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.tashow.cloud.member.controller.app.user.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 用户 APP - 用户个人信息 Response VO
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AppMemberUserInfoRespVO {
|
||||
|
||||
//用户编号
|
||||
private Long id;
|
||||
|
||||
//用户昵称
|
||||
private String nickname;
|
||||
//用户头像
|
||||
private String avatar;
|
||||
//用户手机号
|
||||
private String mobile;
|
||||
//用户性别
|
||||
private Integer sex;
|
||||
//积分
|
||||
private Integer point;
|
||||
//经验值
|
||||
private Integer experience;
|
||||
/// 用户等级
|
||||
private Level level;
|
||||
//是否成为推广员
|
||||
private Boolean brokerageEnabled;
|
||||
|
||||
/**
|
||||
* 用户 App - 会员等级
|
||||
*/
|
||||
@Data
|
||||
public static class Level {
|
||||
//等级编号
|
||||
private Long id;
|
||||
//等级名称
|
||||
private String name;
|
||||
// 等级
|
||||
private Integer level;
|
||||
//等级图标
|
||||
private String icon;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.tashow.cloud.member.controller.app.user.vo;
|
||||
|
||||
|
||||
import com.tashow.cloud.common.validation.Mobile;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
// 用户 APP - 重置密码 Request VO
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class AppMemberUserResetPasswordReqVO {
|
||||
|
||||
// 新密码, 必需, 示例: buzhidao
|
||||
@NotEmpty(message = "新密码不能为空")
|
||||
@Length(min = 4, max = 16, message = "密码长度为 4-16 位")
|
||||
private String password;
|
||||
|
||||
// 手机验证码, 必需, 示例: 1024
|
||||
@NotEmpty(message = "手机验证码不能为空")
|
||||
@Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位")
|
||||
@Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
|
||||
private String code;
|
||||
|
||||
// 手机号, 必需, 示例: 15878962356
|
||||
@NotBlank(message = "手机号不能为空")
|
||||
@Mobile
|
||||
private String mobile;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.tashow.cloud.member.controller.app.user.vo;
|
||||
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.Data;
|
||||
|
||||
// 用户 APP - 基于微信小程序的授权码,修改手机 Request VO
|
||||
@Data
|
||||
public class AppMemberUserUpdateMobileByWeixinReqVO {
|
||||
|
||||
// 手机 code,小程序通过 wx.getPhoneNumber 方法获得, 必需, 示例: hello
|
||||
@NotEmpty(message = "手机 code 不能为空")
|
||||
private String code;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.tashow.cloud.member.controller.app.user.vo;
|
||||
|
||||
import com.tashow.cloud.common.validation.Mobile;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
// 用户 APP - 修改手机 Request VO
|
||||
@Data
|
||||
public class AppMemberUserUpdateMobileReqVO {
|
||||
|
||||
// 手机验证码, 必需, 示例: 1024
|
||||
@NotEmpty(message = "手机验证码不能为空")
|
||||
@Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位")
|
||||
@Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
|
||||
private String code;
|
||||
|
||||
// 手机号, 必需, 示例: 15823654487
|
||||
@NotBlank(message = "手机号不能为空")
|
||||
@Length(min = 8, max = 11, message = "手机号码长度为 8-11 位")
|
||||
@Mobile
|
||||
private String mobile;
|
||||
|
||||
// 原手机验证码, 示例: 1024
|
||||
@Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位")
|
||||
@Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
|
||||
private String oldCode;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.tashow.cloud.member.controller.app.user.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
|
||||
// 用户 APP - 修改密码 Request VO
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class AppMemberUserUpdatePasswordReqVO {
|
||||
|
||||
// 新密码, 必需, 示例: buzhidao
|
||||
@NotEmpty(message = "新密码不能为空")
|
||||
@Length(min = 4, max = 16, message = "密码长度为 4-16 位")
|
||||
private String password;
|
||||
|
||||
// 手机验证码, 必需, 示例: 1024
|
||||
@NotEmpty(message = "手机验证码不能为空")
|
||||
@Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位")
|
||||
@Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
|
||||
private String code;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.tashow.cloud.member.controller.app.user.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.URL;
|
||||
|
||||
// 用户 App - 会员用户更新 Request VO
|
||||
@Data
|
||||
public class AppMemberUserUpdateReqVO {
|
||||
|
||||
// 用户昵称, 必需, 示例: 李四
|
||||
private String nickname;
|
||||
|
||||
// 头像, 必需, 示例: https://www.iocoder.cn/x.png
|
||||
@URL(message = "头像必须是 URL 格式")
|
||||
private String avatar;
|
||||
|
||||
// 性别, 必需, 示例: 1
|
||||
private Integer sex;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* 提供 RESTful API 给前端:
|
||||
* 1. admin 包:提供给管理后台 yudao-ui-admin 前端项目
|
||||
* 2. app 包:提供给用户 APP yudao-ui-app 前端项目,它的 Controller 和 VO 都要添加 App 前缀,用于和管理后台进行区分
|
||||
*/
|
||||
package com.tashow.cloud.member.controller;
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.tashow.cloud.member.convert.address;
|
||||
|
||||
import com.tashow.cloud.common.util.ip.AreaUtils;
|
||||
import com.tashow.cloud.memberapi.api.address.dto.MemberAddressRespDTO;
|
||||
import com.tashow.cloud.member.controller.admin.address.vo.AddressRespVO;
|
||||
import com.tashow.cloud.member.controller.app.address.vo.AppAddressCreateReqVO;
|
||||
import com.tashow.cloud.member.controller.app.address.vo.AppAddressRespVO;
|
||||
import com.tashow.cloud.member.controller.app.address.vo.AppAddressUpdateReqVO;
|
||||
import com.tashow.cloud.member.dal.dataobject.address.MemberAddressDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.Named;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 用户收件地址 Convert
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Mapper
|
||||
public interface AddressConvert {
|
||||
|
||||
AddressConvert INSTANCE = Mappers.getMapper(AddressConvert.class);
|
||||
|
||||
MemberAddressDO convert(AppAddressCreateReqVO bean);
|
||||
|
||||
MemberAddressDO convert(AppAddressUpdateReqVO bean);
|
||||
|
||||
@Mapping(source = "areaId", target = "areaName", qualifiedByName = "convertAreaIdToAreaName")
|
||||
AppAddressRespVO convert(MemberAddressDO bean);
|
||||
|
||||
List<AppAddressRespVO> convertList(List<MemberAddressDO> list);
|
||||
|
||||
MemberAddressRespDTO convert02(MemberAddressDO bean);
|
||||
|
||||
@Named("convertAreaIdToAreaName")
|
||||
default String convertAreaIdToAreaName(Integer areaId) {
|
||||
return AreaUtils.format(areaId);
|
||||
}
|
||||
|
||||
List<AddressRespVO> convertList2(List<MemberAddressDO> list);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.tashow.cloud.member.convert.auth;
|
||||
|
||||
import com.tashow.cloud.member.controller.app.auth.vo.*;
|
||||
import com.tashow.cloud.member.controller.app.user.vo.AppMemberUserResetPasswordReqVO;
|
||||
import com.tashow.cloud.systemapi.api.oauth2.dto.OAuth2AccessTokenRespDTO;
|
||||
import com.tashow.cloud.systemapi.api.sms.dto.code.SmsCodeSendReqDTO;
|
||||
import com.tashow.cloud.systemapi.api.sms.dto.code.SmsCodeUseReqDTO;
|
||||
import com.tashow.cloud.systemapi.api.sms.dto.code.SmsCodeValidateReqDTO;
|
||||
import com.tashow.cloud.systemapi.api.social.dto.SocialUserBindReqDTO;
|
||||
import com.tashow.cloud.systemapi.api.social.dto.SocialUserUnbindReqDTO;
|
||||
import com.tashow.cloud.systemapi.api.social.dto.SocialWxJsapiSignatureRespDTO;
|
||||
import com.tashow.cloud.systemapi.enums.sms.SmsSceneEnum;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper
|
||||
public interface AuthConvert {
|
||||
|
||||
AuthConvert INSTANCE = Mappers.getMapper(AuthConvert.class);
|
||||
|
||||
SocialUserBindReqDTO convert(Long userId, Integer userType, AppAuthSocialLoginReqVO reqVO);
|
||||
SocialUserUnbindReqDTO convert(Long userId, Integer userType);
|
||||
|
||||
SmsCodeSendReqDTO convert(AppAuthSmsSendReqVO reqVO);
|
||||
SmsCodeUseReqDTO convert(AppMemberUserResetPasswordReqVO reqVO, SmsSceneEnum scene, String usedIp);
|
||||
SmsCodeUseReqDTO convert(AppAuthSmsLoginReqVO reqVO, Integer scene, String usedIp);
|
||||
|
||||
AppAuthLoginRespVO convert(OAuth2AccessTokenRespDTO bean, String openid);
|
||||
|
||||
SmsCodeValidateReqDTO convert(AppAuthSmsValidateReqVO bean);
|
||||
|
||||
SocialWxJsapiSignatureRespDTO convert(SocialWxJsapiSignatureRespDTO bean);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* 提供 POJO 类的实体转换
|
||||
*
|
||||
* 目前使用 MapStruct 框架
|
||||
*/
|
||||
package com.tashow.cloud.member.convert;
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.tashow.cloud.member.convert.user;
|
||||
|
||||
import com.tashow.cloud.common.pojo.PageResult;
|
||||
import com.tashow.cloud.memberapi.api.user.dto.MemberUserRespDTO;
|
||||
import com.tashow.cloud.member.controller.admin.user.vo.MemberUserRespVO;
|
||||
import com.tashow.cloud.member.controller.admin.user.vo.MemberUserUpdateReqVO;
|
||||
import com.tashow.cloud.member.controller.app.user.vo.AppMemberUserInfoRespVO;
|
||||
import com.tashow.cloud.member.convert.address.AddressConvert;
|
||||
import com.tashow.cloud.member.dal.dataobject.user.MemberUserDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.Mappings;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Mapper(uses = {AddressConvert.class})
|
||||
public interface MemberUserConvert {
|
||||
|
||||
MemberUserConvert INSTANCE = Mappers.getMapper(MemberUserConvert.class);
|
||||
|
||||
AppMemberUserInfoRespVO convert(MemberUserDO bean);
|
||||
|
||||
|
||||
@Mappings({
|
||||
@Mapping(source = "bean.id", target = "id"),
|
||||
})
|
||||
|
||||
MemberUserRespDTO convert2(MemberUserDO bean);
|
||||
|
||||
List<MemberUserRespDTO> convertList2(List<MemberUserDO> list);
|
||||
|
||||
MemberUserDO convert(MemberUserUpdateReqVO bean);
|
||||
|
||||
PageResult<MemberUserRespVO> convertPage(PageResult<MemberUserDO> page);
|
||||
|
||||
@Mapping(source = "areaId", target = "areaName", qualifiedByName = "convertAreaIdToAreaName")
|
||||
MemberUserRespVO convert03(MemberUserDO bean);
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao>
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.tashow.cloud.member.dal.dataobject.address;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* 用户收件地址 DO
|
||||
*
|
||||
*/
|
||||
@TableName("member_address")
|
||||
@KeySequence("member_address_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class MemberAddressDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 用户编号
|
||||
*/
|
||||
private Long userId;
|
||||
/**
|
||||
* 收件人名称
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
private String mobile;
|
||||
/**
|
||||
* 地区编号
|
||||
*/
|
||||
private Long areaId;
|
||||
/**
|
||||
* 收件详细地址
|
||||
*/
|
||||
private String detailAddress;
|
||||
/**
|
||||
* 是否默认
|
||||
*
|
||||
* true - 默认收件地址
|
||||
*/
|
||||
private Boolean defaultStatus;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package com.tashow.cloud.member.dal.dataobject.user;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.tashow.cloud.common.enums.CommonStatusEnum;
|
||||
import com.tashow.cloud.common.enums.TerminalEnum;
|
||||
import com.tashow.cloud.mybatis.mybatis.core.type.LongListTypeHandler;
|
||||
import com.tashow.cloud.systemapi.enums.common.SexEnum;
|
||||
import com.tashow.cloud.tenant.core.db.TenantBaseDO;
|
||||
import lombok.*;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 会员用户 DO
|
||||
*
|
||||
* uk_mobile 索引:基于 {@link #mobile} 字段
|
||||
*
|
||||
*/
|
||||
@TableName(value = "member_user", autoResultMap = true)
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class MemberUserDO extends TenantBaseDO {
|
||||
|
||||
// ========== 账号信息 ==========
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 手机
|
||||
*/
|
||||
private String mobile;
|
||||
/**
|
||||
* 加密后的密码
|
||||
*
|
||||
* 因为目前使用 {@link BCryptPasswordEncoder} 加密器,所以无需自己处理 salt 盐
|
||||
*/
|
||||
private String password;
|
||||
/**
|
||||
* 帐号状态
|
||||
*
|
||||
* 枚举 {@link CommonStatusEnum}
|
||||
*/
|
||||
private Integer status;
|
||||
/**
|
||||
* 注册 IP
|
||||
*/
|
||||
private String registerIp;
|
||||
/**
|
||||
* 注册终端
|
||||
* 枚举 {@link TerminalEnum}
|
||||
*/
|
||||
private Integer registerTerminal;
|
||||
/**
|
||||
* 最后登录IP
|
||||
*/
|
||||
private String loginIp;
|
||||
/**
|
||||
* 最后登录时间
|
||||
*/
|
||||
private LocalDateTime loginDate;
|
||||
|
||||
// ========== 基础信息 ==========
|
||||
|
||||
/**
|
||||
* 用户昵称
|
||||
*/
|
||||
private String nickname;
|
||||
/**
|
||||
* 用户头像
|
||||
*/
|
||||
private String avatar;
|
||||
|
||||
/**
|
||||
* 真实名字
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 性别
|
||||
*
|
||||
* 枚举 {@link SexEnum}
|
||||
*/
|
||||
private Integer sex;
|
||||
/**
|
||||
* 出生日期
|
||||
*/
|
||||
private LocalDateTime birthday;
|
||||
/**
|
||||
* 所在地
|
||||
*
|
||||
* 关联 {@link Area#getId()} 字段
|
||||
*/
|
||||
private Integer areaId;
|
||||
/**
|
||||
* 用户备注
|
||||
*/
|
||||
private String mark;
|
||||
|
||||
// ========== 其它信息 ==========
|
||||
|
||||
/**
|
||||
* 积分
|
||||
*/
|
||||
private Integer point;
|
||||
// TODO 疯狂:增加一个 totalPoint;个人信息接口要返回
|
||||
|
||||
/**
|
||||
* 会员标签列表,以逗号分隔
|
||||
*/
|
||||
@TableField(typeHandler = LongListTypeHandler.class)
|
||||
private List<Long> tagIds;
|
||||
|
||||
/**
|
||||
* 会员级别编号
|
||||
*
|
||||
* 关联 {@link MemberLevelDO#getId()} 字段
|
||||
*/
|
||||
private Long levelId;
|
||||
/**
|
||||
* 会员经验
|
||||
*/
|
||||
private Integer experience;
|
||||
/**
|
||||
* 用户分组编号
|
||||
*
|
||||
* 关联 {@link MemberGroupDO#getId()} 字段
|
||||
*/
|
||||
private Long groupId;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.tashow.cloud.member.dal.mysql.address;
|
||||
|
||||
import com.tashow.cloud.member.dal.dataobject.address.MemberAddressDO;
|
||||
import com.tashow.cloud.mybatis.mybatis.core.mapper.BaseMapperX;
|
||||
import com.tashow.cloud.mybatis.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface MemberAddressMapper extends BaseMapperX<MemberAddressDO> {
|
||||
|
||||
default MemberAddressDO selectByIdAndUserId(Long id, Long userId) {
|
||||
return selectOne(MemberAddressDO::getId, id, MemberAddressDO::getUserId, userId);
|
||||
}
|
||||
|
||||
default List<MemberAddressDO> selectListByUserIdAndDefaulted(Long userId, Boolean defaulted) {
|
||||
return selectList(new LambdaQueryWrapperX<MemberAddressDO>().eq(MemberAddressDO::getUserId, userId)
|
||||
.eqIfPresent(MemberAddressDO::getDefaultStatus, defaulted));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package com.tashow.cloud.member.dal.mysql.user;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.tashow.cloud.common.pojo.PageResult;
|
||||
import com.tashow.cloud.member.controller.admin.user.vo.MemberUserPageReqVO;
|
||||
import com.tashow.cloud.member.dal.dataobject.user.MemberUserDO;
|
||||
import com.tashow.cloud.mybatis.mybatis.core.mapper.BaseMapperX;
|
||||
import com.tashow.cloud.mybatis.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 会员 User Mapper
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Mapper
|
||||
public interface MemberUserMapper extends BaseMapperX<MemberUserDO> {
|
||||
|
||||
default MemberUserDO selectByMobile(String mobile) {
|
||||
return selectOne(MemberUserDO::getMobile, mobile);
|
||||
}
|
||||
|
||||
default List<MemberUserDO> selectListByNicknameLike(String nickname) {
|
||||
return selectList(new LambdaQueryWrapperX<MemberUserDO>()
|
||||
.likeIfPresent(MemberUserDO::getNickname, nickname));
|
||||
}
|
||||
|
||||
default PageResult<MemberUserDO> selectPage(MemberUserPageReqVO reqVO) {
|
||||
// 处理 tagIds 过滤条件
|
||||
String tagIdSql = "";
|
||||
if (CollUtil.isNotEmpty(reqVO.getTagIds())) {
|
||||
tagIdSql = reqVO.getTagIds().stream()
|
||||
.map(tagId -> "FIND_IN_SET(" + tagId + ", tag_ids)")
|
||||
.collect(Collectors.joining(" OR "));
|
||||
}
|
||||
// 分页查询
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<MemberUserDO>()
|
||||
.likeIfPresent(MemberUserDO::getMobile, reqVO.getMobile())
|
||||
.betweenIfPresent(MemberUserDO::getLoginDate, reqVO.getLoginDate())
|
||||
.likeIfPresent(MemberUserDO::getNickname, reqVO.getNickname())
|
||||
.betweenIfPresent(MemberUserDO::getCreateTime, reqVO.getCreateTime())
|
||||
.eqIfPresent(MemberUserDO::getLevelId, reqVO.getLevelId())
|
||||
.eqIfPresent(MemberUserDO::getGroupId, reqVO.getGroupId())
|
||||
.apply(StrUtil.isNotEmpty(tagIdSql), tagIdSql)
|
||||
.orderByDesc(MemberUserDO::getId));
|
||||
}
|
||||
|
||||
default Long selectCountByGroupId(Long groupId) {
|
||||
return selectCount(MemberUserDO::getGroupId, groupId);
|
||||
}
|
||||
|
||||
default Long selectCountByLevelId(Long levelId) {
|
||||
return selectCount(MemberUserDO::getLevelId, levelId);
|
||||
}
|
||||
|
||||
default Long selectCountByTagId(Long tagId) {
|
||||
return selectCount(new LambdaQueryWrapperX<MemberUserDO>()
|
||||
.apply("FIND_IN_SET({0}, tag_ids)", tagId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户积分(增加)
|
||||
*
|
||||
* @param id 用户编号
|
||||
* @param incrCount 增加积分(正数)
|
||||
*/
|
||||
default void updatePointIncr(Long id, Integer incrCount) {
|
||||
Assert.isTrue(incrCount > 0);
|
||||
LambdaUpdateWrapper<MemberUserDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<MemberUserDO>()
|
||||
.setSql(" point = point + " + incrCount)
|
||||
.eq(MemberUserDO::getId, id);
|
||||
update(null, lambdaUpdateWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户积分(减少)
|
||||
*
|
||||
* @param id 用户编号
|
||||
* @param incrCount 增加积分(负数)
|
||||
* @return 更新行数
|
||||
*/
|
||||
default int updatePointDecr(Long id, Integer incrCount) {
|
||||
Assert.isTrue(incrCount < 0);
|
||||
LambdaUpdateWrapper<MemberUserDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<MemberUserDO>()
|
||||
.setSql(" point = point + " + incrCount) // 负数,所以使用 + 号
|
||||
.eq(MemberUserDO::getId, id);
|
||||
return update(null, lambdaUpdateWrapper);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* DAL = Data Access Layer 数据访问层
|
||||
* 1. data object:数据对象
|
||||
* 2. redis:Redis 的 CRUD 操作
|
||||
* 3. mysql:MySQL 的 CRUD 操作
|
||||
*
|
||||
* 其中,MySQL 的表以 member_ 作为前缀
|
||||
*/
|
||||
package com.tashow.cloud.member.dal;
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 占位,后续有类后,可以删除,避免 package 无法提交到 Git 上
|
||||
*/
|
||||
package com.tashow.cloud.member.dal.redis;
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* 属于 member 模块的 framework 封装
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
package com.tashow.cloud.member.framework;
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.tashow.cloud.member.framework.rpc.config;
|
||||
|
||||
import com.tashow.cloud.systemapi.api.logger.LoginLogApi;
|
||||
import com.tashow.cloud.systemapi.api.sms.SmsCodeApi;
|
||||
import com.tashow.cloud.systemapi.api.social.SocialClientApi;
|
||||
import com.tashow.cloud.systemapi.api.social.SocialUserApi;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableFeignClients(clients = {SmsCodeApi.class, LoginLogApi.class, SocialUserApi.class, SocialClientApi.class})
|
||||
public class RpcConfiguration {
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 占位
|
||||
*/
|
||||
package com.tashow.cloud.member.framework.rpc;
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.tashow.cloud.member.framework.security.config;
|
||||
|
||||
import com.tashow.cloud.memberapi.enums.ApiConstants;
|
||||
import com.tashow.cloud.security.security.config.AuthorizeRequestsCustomizer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
|
||||
|
||||
/**
|
||||
* Member 模块的 Security 配置
|
||||
*/
|
||||
@Configuration("memberSecurityConfiguration")
|
||||
public class SecurityConfiguration {
|
||||
|
||||
@Bean("memberAuthorizeRequestsCustomizer")
|
||||
public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() {
|
||||
return new AuthorizeRequestsCustomizer() {
|
||||
|
||||
@Override
|
||||
public void customize(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry registry) {
|
||||
// Swagger 接口文档
|
||||
registry.requestMatchers("/v3/api-docs/**").permitAll()
|
||||
.requestMatchers("/webjars/**").permitAll()
|
||||
.requestMatchers("/swagger-ui").permitAll()
|
||||
.requestMatchers("/swagger-ui/**").permitAll();
|
||||
// Spring Boot Actuator 的安全配置
|
||||
registry.requestMatchers("/actuator").permitAll()
|
||||
.requestMatchers("/actuator/**").permitAll();
|
||||
// Druid 监控
|
||||
registry.requestMatchers("/druid/**").permitAll();
|
||||
// RPC 服务的安全配置
|
||||
registry.requestMatchers(ApiConstants.PREFIX + "/**").permitAll();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 占位
|
||||
*/
|
||||
package com.tashow.cloud.member.framework.security.core;
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 消息队列的消费者
|
||||
*/
|
||||
package com.tashow.cloud.member.mq.consumer;
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 消息队列的消息
|
||||
*/
|
||||
package com.tashow.cloud.member.mq.message;
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 消息队列的生产者
|
||||
*/
|
||||
package com.tashow.cloud.member.mq.producer;
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.tashow.cloud.member.mq.producer.user;
|
||||
|
||||
import com.tashow.cloud.memberapi.message.user.MemberUserCreateMessage;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 会员用户 Producer
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class MemberUserProducer {
|
||||
|
||||
@Resource
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
/**
|
||||
* 发送 {@link MemberUserCreateMessage} 消息
|
||||
*
|
||||
* @param userId 用户编号
|
||||
*/
|
||||
public void sendUserCreateMessage(Long userId) {
|
||||
applicationContext.publishEvent(new MemberUserCreateMessage().setUserId(userId));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* member 模块,我们放会员业务。
|
||||
* 例如说:会员中心等等
|
||||
*
|
||||
* 1. Controller URL:以 /member/ 开头,避免和其它 Module 冲突
|
||||
* 2. DataObject 表名:以 member_ 开头,方便在数据库中区分
|
||||
*/
|
||||
package com.tashow.cloud.member;
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.tashow.cloud.member.service.address;
|
||||
|
||||
import com.tashow.cloud.member.controller.app.address.vo.AppAddressCreateReqVO;
|
||||
import com.tashow.cloud.member.controller.app.address.vo.AppAddressUpdateReqVO;
|
||||
import com.tashow.cloud.member.dal.dataobject.address.MemberAddressDO;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 用户收件地址 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 用户收件地址
|
||||
*/
|
||||
MemberAddressDO getAddress(Long userId, Long id);
|
||||
|
||||
/**
|
||||
* 获得用户收件地址列表
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @return 用户收件地址列表
|
||||
*/
|
||||
List<MemberAddressDO> getAddressList(Long userId);
|
||||
|
||||
/**
|
||||
* 获得用户默认的收件地址
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @return 用户收件地址
|
||||
*/
|
||||
MemberAddressDO getDefaultUserAddress(Long userId);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.tashow.cloud.member.service.address;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.tashow.cloud.member.controller.app.address.vo.AppAddressCreateReqVO;
|
||||
import com.tashow.cloud.member.controller.app.address.vo.AppAddressUpdateReqVO;
|
||||
import com.tashow.cloud.member.convert.address.AddressConvert;
|
||||
import com.tashow.cloud.member.dal.dataobject.address.MemberAddressDO;
|
||||
import com.tashow.cloud.member.dal.mysql.address.MemberAddressMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
import static com.tashow.cloud.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static com.tashow.cloud.memberapi.enums.ErrorCodeConstants.ADDRESS_NOT_EXISTS;
|
||||
|
||||
/**
|
||||
* 用户收件地址 Service 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class AddressServiceImpl implements AddressService {
|
||||
|
||||
@Resource
|
||||
private MemberAddressMapper memberAddressMapper;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createAddress(Long userId, AppAddressCreateReqVO createReqVO) {
|
||||
// 如果添加的是默认收件地址,则将原默认地址修改为非默认
|
||||
if (Boolean.TRUE.equals(createReqVO.getDefaultStatus())) {
|
||||
List<MemberAddressDO> addresses = memberAddressMapper.selectListByUserIdAndDefaulted(userId, true);
|
||||
addresses.forEach(address -> memberAddressMapper.updateById(new MemberAddressDO().setId(address.getId()).setDefaultStatus(false)));
|
||||
}
|
||||
|
||||
// 插入
|
||||
MemberAddressDO address = AddressConvert.INSTANCE.convert(createReqVO);
|
||||
address.setUserId(userId);
|
||||
memberAddressMapper.insert(address);
|
||||
// 返回
|
||||
return address.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateAddress(Long userId, AppAddressUpdateReqVO updateReqVO) {
|
||||
// 校验存在,校验是否能够操作
|
||||
validAddressExists(userId, updateReqVO.getId());
|
||||
|
||||
// 如果修改的是默认收件地址,则将原默认地址修改为非默认
|
||||
if (Boolean.TRUE.equals(updateReqVO.getDefaultStatus())) {
|
||||
List<MemberAddressDO> addresses = memberAddressMapper.selectListByUserIdAndDefaulted(userId, true);
|
||||
addresses.stream().filter(u -> !u.getId().equals(updateReqVO.getId())) // 排除自己
|
||||
.forEach(address -> memberAddressMapper.updateById(new MemberAddressDO().setId(address.getId()).setDefaultStatus(false)));
|
||||
}
|
||||
|
||||
// 更新
|
||||
MemberAddressDO updateObj = AddressConvert.INSTANCE.convert(updateReqVO);
|
||||
memberAddressMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAddress(Long userId, Long id) {
|
||||
// 校验存在,校验是否能够操作
|
||||
validAddressExists(userId, id);
|
||||
// 删除
|
||||
memberAddressMapper.deleteById(id);
|
||||
}
|
||||
|
||||
private void validAddressExists(Long userId, Long id) {
|
||||
MemberAddressDO addressDO = getAddress(userId, id);
|
||||
if (addressDO == null) {
|
||||
throw exception(ADDRESS_NOT_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemberAddressDO getAddress(Long userId, Long id) {
|
||||
return memberAddressMapper.selectByIdAndUserId(id, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MemberAddressDO> getAddressList(Long userId) {
|
||||
return memberAddressMapper.selectListByUserIdAndDefaulted(userId, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemberAddressDO getDefaultUserAddress(Long userId) {
|
||||
List<MemberAddressDO> addresses = memberAddressMapper.selectListByUserIdAndDefaulted(userId, true);
|
||||
return CollUtil.getFirst(addresses);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.tashow.cloud.member.service.auth;
|
||||
|
||||
|
||||
import com.tashow.cloud.member.controller.app.auth.vo.*;
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
/**
|
||||
* 会员的认证 Service 接口
|
||||
*
|
||||
* 提供用户的账号密码登录、token 的校验等认证相关的功能
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface MemberAuthService {
|
||||
|
||||
/**
|
||||
* 手机 + 密码登录
|
||||
*
|
||||
* @param reqVO 登录信息
|
||||
* @return 登录结果
|
||||
*/
|
||||
AppAuthLoginRespVO login(@Valid AppAuthLoginReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 基于 token 退出登录
|
||||
*
|
||||
* @param token token
|
||||
*/
|
||||
void logout(String token);
|
||||
|
||||
/**
|
||||
* 手机 + 验证码登陆
|
||||
*
|
||||
* @param reqVO 登陆信息
|
||||
* @return 登录结果
|
||||
*/
|
||||
AppAuthLoginRespVO smsLogin(@Valid AppAuthSmsLoginReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 社交登录,使用 code 授权码
|
||||
*
|
||||
* @param reqVO 登录信息
|
||||
* @return 登录结果
|
||||
*/
|
||||
AppAuthLoginRespVO socialLogin(@Valid AppAuthSocialLoginReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 微信小程序的一键登录
|
||||
*
|
||||
* @param reqVO 登录信息
|
||||
* @return 登录结果
|
||||
*/
|
||||
AppAuthLoginRespVO weixinMiniAppLogin(AppAuthWeixinMiniAppLoginReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 获得社交认证 URL
|
||||
*
|
||||
* @param type 社交平台类型
|
||||
* @param redirectUri 跳转地址
|
||||
* @return 认证 URL
|
||||
*/
|
||||
String getSocialAuthorizeUrl(Integer type, String redirectUri);
|
||||
|
||||
/**
|
||||
* 给用户发送短信验证码
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param reqVO 发送信息
|
||||
*/
|
||||
void sendSmsCode(Long userId, AppAuthSmsSendReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 校验短信验证码是否正确
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param reqVO 校验信息
|
||||
*/
|
||||
void validateSmsCode(Long userId, AppAuthSmsValidateReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 刷新访问令牌
|
||||
*
|
||||
* @param refreshToken 刷新令牌
|
||||
* @return 登录结果
|
||||
*/
|
||||
AppAuthLoginRespVO refreshToken(String refreshToken);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
package com.tashow.cloud.member.service.auth;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import com.tashow.cloud.common.enums.CommonStatusEnum;
|
||||
import com.tashow.cloud.common.enums.TerminalEnum;
|
||||
import com.tashow.cloud.common.enums.UserTypeEnum;
|
||||
import com.tashow.cloud.common.util.monitor.TracerUtils;
|
||||
import com.tashow.cloud.common.util.servlet.ServletUtils;
|
||||
import com.tashow.cloud.member.controller.app.auth.vo.*;
|
||||
import com.tashow.cloud.member.convert.auth.AuthConvert;
|
||||
import com.tashow.cloud.member.dal.dataobject.user.MemberUserDO;
|
||||
import com.tashow.cloud.member.service.user.MemberUserService;
|
||||
import com.tashow.cloud.systemapi.api.logger.LoginLogApi;
|
||||
import com.tashow.cloud.systemapi.api.logger.dto.LoginLogCreateReqDTO;
|
||||
import com.tashow.cloud.systemapi.api.oauth2.OAuth2TokenApi;
|
||||
import com.tashow.cloud.systemapi.api.oauth2.dto.OAuth2AccessTokenCreateReqDTO;
|
||||
import com.tashow.cloud.systemapi.api.oauth2.dto.OAuth2AccessTokenRespDTO;
|
||||
import com.tashow.cloud.systemapi.api.sms.SmsCodeApi;
|
||||
import com.tashow.cloud.systemapi.api.social.SocialClientApi;
|
||||
import com.tashow.cloud.systemapi.api.social.SocialUserApi;
|
||||
import com.tashow.cloud.systemapi.api.social.dto.SocialUserBindReqDTO;
|
||||
import com.tashow.cloud.systemapi.api.social.dto.SocialUserRespDTO;
|
||||
import com.tashow.cloud.systemapi.api.social.dto.SocialWxPhoneNumberInfoRespDTO;
|
||||
import com.tashow.cloud.systemapi.enums.logger.LoginLogTypeEnum;
|
||||
import com.tashow.cloud.systemapi.enums.logger.LoginResultEnum;
|
||||
import com.tashow.cloud.systemapi.enums.oauth2.OAuth2ClientConstants;
|
||||
import com.tashow.cloud.systemapi.enums.sms.SmsSceneEnum;
|
||||
import com.tashow.cloud.systemapi.enums.social.SocialTypeEnum;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import static com.tashow.cloud.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static com.tashow.cloud.common.util.servlet.ServletUtils.getClientIP;
|
||||
import static com.tashow.cloud.memberapi.enums.ErrorCodeConstants.AUTH_MOBILE_USED;
|
||||
import static com.tashow.cloud.memberapi.enums.ErrorCodeConstants.AUTH_SOCIAL_USER_NOT_FOUND;
|
||||
import static com.tashow.cloud.systemapi.enums.ErrorCodeConstants.*;
|
||||
import static com.tashow.cloud.web.web.core.util.WebFrameworkUtils.getTerminal;
|
||||
|
||||
/**
|
||||
* 会员的认证 Service 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class MemberAuthServiceImpl implements MemberAuthService {
|
||||
|
||||
@Resource
|
||||
private MemberUserService userService;
|
||||
@Resource
|
||||
private SmsCodeApi smsCodeApi;
|
||||
@Resource
|
||||
private LoginLogApi loginLogApi;
|
||||
@Resource
|
||||
private SocialUserApi socialUserApi;
|
||||
@Resource
|
||||
private SocialClientApi socialClientApi;
|
||||
@Resource
|
||||
private OAuth2TokenApi oauth2TokenApi;
|
||||
|
||||
@Override
|
||||
public AppAuthLoginRespVO login(AppAuthLoginReqVO reqVO) {
|
||||
// 使用手机 + 密码,进行登录。
|
||||
MemberUserDO user = login0(reqVO.getMobile(), reqVO.getPassword());
|
||||
|
||||
// 如果 socialType 非空,说明需要绑定社交用户
|
||||
String openid = null;
|
||||
if (reqVO.getSocialType() != null) {
|
||||
openid = socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
|
||||
reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState())).getCheckedData();
|
||||
}
|
||||
|
||||
// 创建 Token 令牌,记录登录日志
|
||||
return createTokenAfterLoginSuccess(user, reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE, openid);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public AppAuthLoginRespVO smsLogin(AppAuthSmsLoginReqVO reqVO) {
|
||||
// 校验验证码
|
||||
String userIp = getClientIP();
|
||||
smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.MEMBER_LOGIN.getScene(), userIp)).checkError();
|
||||
|
||||
// 获得获得注册用户
|
||||
MemberUserDO user = userService.createUserIfAbsent(reqVO.getMobile(), userIp, getTerminal());
|
||||
Assert.notNull(user, "获取用户失败,结果为空");
|
||||
|
||||
// 校验是否禁用
|
||||
if (CommonStatusEnum.isDisable(user.getStatus())) {
|
||||
createLoginLog(user.getId(), reqVO.getMobile(), LoginLogTypeEnum.LOGIN_SMS, LoginResultEnum.USER_DISABLED);
|
||||
throw exception(AUTH_LOGIN_USER_DISABLED);
|
||||
}
|
||||
|
||||
// 如果 socialType 非空,说明需要绑定社交用户
|
||||
String openid = null;
|
||||
if (reqVO.getSocialType() != null) {
|
||||
openid = socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
|
||||
reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState())).getCheckedData();
|
||||
}
|
||||
|
||||
// 创建 Token 令牌,记录登录日志
|
||||
return createTokenAfterLoginSuccess(user, reqVO.getMobile(), LoginLogTypeEnum.LOGIN_SMS, openid);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public AppAuthLoginRespVO socialLogin(AppAuthSocialLoginReqVO reqVO) {
|
||||
// 使用 code 授权码,进行登录。然后,获得到绑定的用户编号
|
||||
SocialUserRespDTO socialUser = socialUserApi.getSocialUserByCode(UserTypeEnum.MEMBER.getValue(), reqVO.getType(),
|
||||
reqVO.getCode(), reqVO.getState()).getCheckedData();
|
||||
if (socialUser == null) {
|
||||
throw exception(AUTH_SOCIAL_USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
// 情况一:已绑定,直接读取用户信息
|
||||
MemberUserDO user;
|
||||
if (socialUser.getUserId() != null) {
|
||||
user = userService.getUser(socialUser.getUserId());
|
||||
// 情况二:未绑定,注册用户 + 绑定用户
|
||||
} else {
|
||||
user = userService.createUser(socialUser.getNickname(), socialUser.getAvatar(), getClientIP(), getTerminal());
|
||||
socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
|
||||
reqVO.getType(), reqVO.getCode(), reqVO.getState())).checkError();
|
||||
}
|
||||
if (user == null) {
|
||||
throw exception(USER_NOT_EXISTS);
|
||||
}
|
||||
|
||||
// 创建 Token 令牌,记录登录日志
|
||||
return createTokenAfterLoginSuccess(user, user.getMobile(), LoginLogTypeEnum.LOGIN_SOCIAL, socialUser.getOpenid());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppAuthLoginRespVO weixinMiniAppLogin(AppAuthWeixinMiniAppLoginReqVO reqVO) {
|
||||
// 获得对应的手机号信息
|
||||
SocialWxPhoneNumberInfoRespDTO phoneNumberInfo = socialClientApi.getWxMaPhoneNumberInfo(
|
||||
UserTypeEnum.MEMBER.getValue(), reqVO.getPhoneCode()).getCheckedData();
|
||||
Assert.notNull(phoneNumberInfo, "获得手机信息失败,结果为空");
|
||||
|
||||
// 获得获得注册用户
|
||||
MemberUserDO user = userService.createUserIfAbsent(phoneNumberInfo.getPurePhoneNumber(),
|
||||
getClientIP(), TerminalEnum.WECHAT_MINI_PROGRAM.getTerminal());
|
||||
Assert.notNull(user, "获取用户失败,结果为空");
|
||||
|
||||
// 绑定社交用户
|
||||
String openid = socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
|
||||
SocialTypeEnum.WECHAT_MINI_APP.getType(), reqVO.getLoginCode(), reqVO.getState())).getCheckedData();
|
||||
|
||||
// 创建 Token 令牌,记录登录日志
|
||||
return createTokenAfterLoginSuccess(user, user.getMobile(), LoginLogTypeEnum.LOGIN_SOCIAL, openid);
|
||||
}
|
||||
|
||||
private AppAuthLoginRespVO createTokenAfterLoginSuccess(MemberUserDO user, String mobile,
|
||||
LoginLogTypeEnum logType, String openid) {
|
||||
// 插入登陆日志
|
||||
createLoginLog(user.getId(), mobile, logType, LoginResultEnum.SUCCESS);
|
||||
// 创建 Token 令牌
|
||||
OAuth2AccessTokenRespDTO accessTokenRespDTO = oauth2TokenApi.createAccessToken(new OAuth2AccessTokenCreateReqDTO()
|
||||
.setUserId(user.getId()).setUserType(getUserType().getValue())
|
||||
.setClientId(OAuth2ClientConstants.CLIENT_ID_DEFAULT)).getCheckedData();
|
||||
// 构建返回结果
|
||||
return AuthConvert.INSTANCE.convert(accessTokenRespDTO, openid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSocialAuthorizeUrl(Integer type, String redirectUri) {
|
||||
return socialClientApi.getAuthorizeUrl(type, UserTypeEnum.MEMBER.getValue(), redirectUri).getCheckedData();
|
||||
}
|
||||
|
||||
private MemberUserDO login0(String mobile, String password) {
|
||||
final LoginLogTypeEnum logTypeEnum = LoginLogTypeEnum.LOGIN_MOBILE;
|
||||
// 校验账号是否存在
|
||||
MemberUserDO user = userService.getUserByMobile(mobile);
|
||||
if (user == null) {
|
||||
createLoginLog(null, mobile, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS);
|
||||
throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
|
||||
}
|
||||
if (!userService.isPasswordMatch(password, user.getPassword())) {
|
||||
createLoginLog(user.getId(), mobile, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS);
|
||||
throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
|
||||
}
|
||||
// 校验是否禁用
|
||||
if (CommonStatusEnum.isDisable(user.getStatus())) {
|
||||
createLoginLog(user.getId(), mobile, logTypeEnum, LoginResultEnum.USER_DISABLED);
|
||||
throw exception(AUTH_LOGIN_USER_DISABLED);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
private void createLoginLog(Long userId, String mobile, LoginLogTypeEnum logType, LoginResultEnum loginResult) {
|
||||
// 插入登录日志
|
||||
LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO();
|
||||
reqDTO.setLogType(logType.getType());
|
||||
reqDTO.setTraceId(TracerUtils.getTraceId());
|
||||
reqDTO.setUserId(userId);
|
||||
reqDTO.setUserType(getUserType().getValue());
|
||||
reqDTO.setUsername(mobile);
|
||||
reqDTO.setUserAgent(ServletUtils.getUserAgent());
|
||||
reqDTO.setUserIp(getClientIP());
|
||||
reqDTO.setResult(loginResult.getResult());
|
||||
loginLogApi.createLoginLog(reqDTO).checkError();
|
||||
// 更新最后登录时间
|
||||
if (userId != null && Objects.equals(LoginResultEnum.SUCCESS.getResult(), loginResult.getResult())) {
|
||||
userService.updateUserLogin(userId, getClientIP());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logout(String token) {
|
||||
// 删除访问令牌
|
||||
OAuth2AccessTokenRespDTO accessTokenRespDTO = oauth2TokenApi.removeAccessToken(token).getCheckedData();
|
||||
if (accessTokenRespDTO == null) {
|
||||
return;
|
||||
}
|
||||
// 删除成功,则记录登出日志
|
||||
createLogoutLog(accessTokenRespDTO.getUserId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendSmsCode(Long userId, AppAuthSmsSendReqVO reqVO) {
|
||||
// 情况 1:如果是修改手机场景,需要校验新手机号是否已经注册,说明不能使用该手机了
|
||||
if (Objects.equals(reqVO.getScene(), SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene())) {
|
||||
MemberUserDO user = userService.getUserByMobile(reqVO.getMobile());
|
||||
if (user != null && !Objects.equals(user.getId(), userId)) {
|
||||
throw exception(AUTH_MOBILE_USED);
|
||||
}
|
||||
}
|
||||
// 情况 2:如果是重置密码场景,需要校验手机号是存在的
|
||||
if (Objects.equals(reqVO.getScene(), SmsSceneEnum.MEMBER_RESET_PASSWORD.getScene())) {
|
||||
MemberUserDO user = userService.getUserByMobile(reqVO.getMobile());
|
||||
if (user == null) {
|
||||
throw exception(USER_MOBILE_NOT_EXISTS);
|
||||
}
|
||||
}
|
||||
// 情况 3:如果是修改密码场景,需要查询手机号,无需前端传递
|
||||
if (Objects.equals(reqVO.getScene(), SmsSceneEnum.MEMBER_UPDATE_PASSWORD.getScene())) {
|
||||
MemberUserDO user = userService.getUser(userId);
|
||||
// TODO 芋艿:后续 member user 手机非强绑定,这块需要做下调整;
|
||||
reqVO.setMobile(user.getMobile());
|
||||
}
|
||||
|
||||
// 执行发送
|
||||
smsCodeApi.sendSmsCode(AuthConvert.INSTANCE.convert(reqVO).setCreateIp(getClientIP())).checkError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateSmsCode(Long userId, AppAuthSmsValidateReqVO reqVO) {
|
||||
smsCodeApi.validateSmsCode(AuthConvert.INSTANCE.convert(reqVO));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppAuthLoginRespVO refreshToken(String refreshToken) {
|
||||
OAuth2AccessTokenRespDTO accessTokenDO = oauth2TokenApi.refreshAccessToken(refreshToken,
|
||||
OAuth2ClientConstants.CLIENT_ID_DEFAULT).getCheckedData();
|
||||
return AuthConvert.INSTANCE.convert(accessTokenDO, null);
|
||||
}
|
||||
|
||||
private void createLogoutLog(Long userId) {
|
||||
LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO();
|
||||
reqDTO.setLogType(LoginLogTypeEnum.LOGOUT_SELF.getType());
|
||||
reqDTO.setTraceId(TracerUtils.getTraceId());
|
||||
reqDTO.setUserId(userId);
|
||||
reqDTO.setUserType(getUserType().getValue());
|
||||
reqDTO.setUsername(getMobile(userId));
|
||||
reqDTO.setUserAgent(ServletUtils.getUserAgent());
|
||||
reqDTO.setUserIp(getClientIP());
|
||||
reqDTO.setResult(LoginResultEnum.SUCCESS.getResult());
|
||||
loginLogApi.createLoginLog(reqDTO).checkError();
|
||||
}
|
||||
|
||||
private String getMobile(Long userId) {
|
||||
if (userId == null) {
|
||||
return null;
|
||||
}
|
||||
MemberUserDO user = userService.getUser(userId);
|
||||
return user != null ? user.getMobile() : null;
|
||||
}
|
||||
|
||||
private UserTypeEnum getUserType() {
|
||||
return UserTypeEnum.MEMBER;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
package com.tashow.cloud.member.service.user;
|
||||
|
||||
import com.tashow.cloud.common.enums.TerminalEnum;
|
||||
import com.tashow.cloud.common.pojo.PageResult;
|
||||
import com.tashow.cloud.common.validation.Mobile;
|
||||
import com.tashow.cloud.member.controller.admin.user.vo.MemberUserPageReqVO;
|
||||
import com.tashow.cloud.member.controller.admin.user.vo.MemberUserUpdateReqVO;
|
||||
import com.tashow.cloud.member.controller.app.user.vo.*;
|
||||
import com.tashow.cloud.member.dal.dataobject.user.MemberUserDO;
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 会员用户 Service 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface MemberUserService {
|
||||
|
||||
/**
|
||||
* 通过手机查询用户
|
||||
*
|
||||
* @param mobile 手机
|
||||
* @return 用户对象
|
||||
*/
|
||||
MemberUserDO getUserByMobile(String mobile);
|
||||
|
||||
/**
|
||||
* 基于用户昵称,模糊匹配用户列表
|
||||
*
|
||||
* @param nickname 用户昵称,模糊匹配
|
||||
* @return 用户信息的列表
|
||||
*/
|
||||
List<MemberUserDO> getUserListByNickname(String nickname);
|
||||
|
||||
/**
|
||||
* 基于手机号创建用户。
|
||||
* 如果用户已经存在,则直接进行返回
|
||||
*
|
||||
* @param mobile 手机号
|
||||
* @param registerIp 注册 IP
|
||||
* @param terminal 终端 {@link TerminalEnum}
|
||||
* @return 用户对象
|
||||
*/
|
||||
MemberUserDO createUserIfAbsent(@Mobile String mobile, String registerIp, Integer terminal);
|
||||
|
||||
/**
|
||||
* 创建用户
|
||||
* 目的:三方登录时,如果未绑定用户时,自动创建对应用户
|
||||
*
|
||||
* @param nickname 昵称
|
||||
* @param avtar 头像
|
||||
* @param registerIp 注册 IP
|
||||
* @param terminal 终端 {@link TerminalEnum}
|
||||
* @return 用户对象
|
||||
*/
|
||||
MemberUserDO createUser(String nickname, String avtar, String registerIp, Integer terminal);
|
||||
|
||||
/**
|
||||
* 更新用户的最后登陆信息
|
||||
*
|
||||
* @param id 用户编号
|
||||
* @param loginIp 登陆 IP
|
||||
*/
|
||||
void updateUserLogin(Long id, String loginIp);
|
||||
|
||||
/**
|
||||
* 通过用户 ID 查询用户
|
||||
*
|
||||
* @param id 用户ID
|
||||
* @return 用户对象信息
|
||||
*/
|
||||
MemberUserDO getUser(Long id);
|
||||
|
||||
/**
|
||||
* 通过用户 ID 查询用户们
|
||||
*
|
||||
* @param ids 用户 ID
|
||||
* @return 用户对象信息数组
|
||||
*/
|
||||
List<MemberUserDO> getUserList(Collection<Long> ids);
|
||||
|
||||
/**
|
||||
* 【会员】修改基本信息
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param reqVO 基本信息
|
||||
*/
|
||||
void updateUser(Long userId, AppMemberUserUpdateReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 【会员】修改手机,基于手机验证码
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param reqVO 请求信息
|
||||
*/
|
||||
void updateUserMobile(Long userId, AppMemberUserUpdateMobileReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 【会员】修改手机,基于微信小程序的授权码
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param reqVO 请求信息
|
||||
*/
|
||||
void updateUserMobileByWeixin(Long userId, AppMemberUserUpdateMobileByWeixinReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 【会员】修改密码
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param reqVO 请求信息
|
||||
*/
|
||||
void updateUserPassword(Long userId, AppMemberUserUpdatePasswordReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 【会员】忘记密码
|
||||
*
|
||||
* @param reqVO 请求信息
|
||||
*/
|
||||
void resetUserPassword(AppMemberUserResetPasswordReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 判断密码是否匹配
|
||||
*
|
||||
* @param rawPassword 未加密的密码
|
||||
* @param encodedPassword 加密后的密码
|
||||
* @return 是否匹配
|
||||
*/
|
||||
boolean isPasswordMatch(String rawPassword, String encodedPassword);
|
||||
|
||||
/**
|
||||
* 【管理员】更新会员用户
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
*/
|
||||
void updateUser(@Valid MemberUserUpdateReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 【管理员】获得会员用户分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 会员用户分页
|
||||
*/
|
||||
PageResult<MemberUserDO> getUserPage(MemberUserPageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 更新用户的等级和经验
|
||||
*
|
||||
* @param id 用户编号
|
||||
* @param levelId 用户等级
|
||||
* @param experience 用户经验
|
||||
*/
|
||||
void updateUserLevel(Long id, Long levelId, Integer experience);
|
||||
|
||||
/**
|
||||
* 获得指定用户分组下的用户数量
|
||||
*
|
||||
* @param groupId 用户分组编号
|
||||
* @return 用户数量
|
||||
*/
|
||||
Long getUserCountByGroupId(Long groupId);
|
||||
|
||||
/**
|
||||
* 获得指定用户等级下的用户数量
|
||||
*
|
||||
* @param levelId 用户等级编号
|
||||
* @return 用户数量
|
||||
*/
|
||||
Long getUserCountByLevelId(Long levelId);
|
||||
|
||||
/**
|
||||
* 获得指定会员标签下的用户数量
|
||||
*
|
||||
* @param tagId 用户标签编号
|
||||
* @return 用户数量
|
||||
*/
|
||||
Long getUserCountByTagId(Long tagId);
|
||||
|
||||
/**
|
||||
* 更新用户的积分
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param point 积分数量
|
||||
* @return 更新结果
|
||||
*/
|
||||
boolean updateUserPoint(Long userId, Integer point);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
package com.tashow.cloud.member.service.user;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.tashow.cloud.common.enums.CommonStatusEnum;
|
||||
import com.tashow.cloud.common.enums.UserTypeEnum;
|
||||
import com.tashow.cloud.common.pojo.PageResult;
|
||||
import com.tashow.cloud.common.util.object.BeanUtils;
|
||||
import com.tashow.cloud.member.controller.admin.user.vo.MemberUserPageReqVO;
|
||||
import com.tashow.cloud.member.controller.admin.user.vo.MemberUserUpdateReqVO;
|
||||
import com.tashow.cloud.member.controller.app.user.vo.*;
|
||||
import com.tashow.cloud.member.convert.auth.AuthConvert;
|
||||
import com.tashow.cloud.member.convert.user.MemberUserConvert;
|
||||
import com.tashow.cloud.member.dal.dataobject.user.MemberUserDO;
|
||||
import com.tashow.cloud.member.dal.mysql.user.MemberUserMapper;
|
||||
import com.tashow.cloud.member.mq.producer.user.MemberUserProducer;
|
||||
import com.tashow.cloud.systemapi.api.sms.SmsCodeApi;
|
||||
import com.tashow.cloud.systemapi.api.sms.dto.code.SmsCodeUseReqDTO;
|
||||
import com.tashow.cloud.systemapi.api.social.SocialClientApi;
|
||||
import com.tashow.cloud.systemapi.api.social.dto.SocialWxPhoneNumberInfoRespDTO;
|
||||
import com.tashow.cloud.systemapi.enums.sms.SmsSceneEnum;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.TransactionSynchronization;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import static com.tashow.cloud.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static com.tashow.cloud.common.util.servlet.ServletUtils.getClientIP;
|
||||
import static com.tashow.cloud.memberapi.enums.ErrorCodeConstants.USER_MOBILE_USED;
|
||||
import static com.tashow.cloud.systemapi.enums.ErrorCodeConstants.USER_MOBILE_NOT_EXISTS;
|
||||
import static com.tashow.cloud.systemapi.enums.ErrorCodeConstants.USER_NOT_EXISTS;
|
||||
|
||||
|
||||
/**
|
||||
* 会员 User Service 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Valid
|
||||
@Slf4j
|
||||
public class MemberUserServiceImpl implements MemberUserService {
|
||||
|
||||
@Resource
|
||||
private MemberUserMapper memberUserMapper;
|
||||
|
||||
@Resource
|
||||
private SmsCodeApi smsCodeApi;
|
||||
|
||||
@Resource
|
||||
private SocialClientApi socialClientApi;
|
||||
|
||||
@Resource
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
@Resource
|
||||
private MemberUserProducer memberUserProducer;
|
||||
|
||||
@Override
|
||||
public MemberUserDO getUserByMobile(String mobile) {
|
||||
return memberUserMapper.selectByMobile(mobile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MemberUserDO> getUserListByNickname(String nickname) {
|
||||
return memberUserMapper.selectListByNicknameLike(nickname);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public MemberUserDO createUserIfAbsent(String mobile, String registerIp, Integer terminal) {
|
||||
// 用户已经存在
|
||||
MemberUserDO user = memberUserMapper.selectByMobile(mobile);
|
||||
if (user != null) {
|
||||
return user;
|
||||
}
|
||||
// 用户不存在,则进行创建
|
||||
return createUser(mobile, null, null, registerIp, terminal);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public MemberUserDO createUser(String nickname, String avtar, String registerIp, Integer terminal) {
|
||||
return createUser(null, nickname, avtar, registerIp, terminal);
|
||||
}
|
||||
|
||||
private MemberUserDO createUser(String mobile, String nickname, String avtar,
|
||||
String registerIp, Integer terminal) {
|
||||
// 生成密码
|
||||
String password = IdUtil.fastSimpleUUID();
|
||||
// 插入用户
|
||||
MemberUserDO user = new MemberUserDO();
|
||||
user.setMobile(mobile);
|
||||
user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启
|
||||
user.setPassword(encodePassword(password)); // 加密密码
|
||||
user.setRegisterIp(registerIp).setRegisterTerminal(terminal);
|
||||
user.setNickname(nickname).setAvatar(avtar); // 基础信息
|
||||
if (StrUtil.isEmpty(nickname)) {
|
||||
// 昵称为空时,随机一个名字,避免一些依赖 nickname 的逻辑报错,或者有点丑。例如说,短信发送有昵称时~
|
||||
user.setNickname("用户" + RandomUtil.randomNumbers(6));
|
||||
}
|
||||
memberUserMapper.insert(user);
|
||||
|
||||
// 发送 MQ 消息:用户创建
|
||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
||||
|
||||
@Override
|
||||
public void afterCommit() {
|
||||
memberUserProducer.sendUserCreateMessage(user.getId());
|
||||
}
|
||||
|
||||
});
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUserLogin(Long id, String loginIp) {
|
||||
memberUserMapper.updateById(new MemberUserDO().setId(id)
|
||||
.setLoginIp(loginIp).setLoginDate(LocalDateTime.now()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemberUserDO getUser(Long id) {
|
||||
return memberUserMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MemberUserDO> getUserList(Collection<Long> ids) {
|
||||
if (CollUtil.isEmpty(ids)) {
|
||||
return ListUtil.empty();
|
||||
}
|
||||
return memberUserMapper.selectBatchIds(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUser(Long userId, AppMemberUserUpdateReqVO reqVO) {
|
||||
MemberUserDO updateObj = BeanUtils.toBean(reqVO, MemberUserDO.class).setId(userId);
|
||||
memberUserMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateUserMobile(Long userId, AppMemberUserUpdateMobileReqVO reqVO) {
|
||||
// 1.1 检测用户是否存在
|
||||
MemberUserDO user = validateUserExists(userId);
|
||||
// 1.2 校验新手机是否已经被绑定
|
||||
validateMobileUnique(null, reqVO.getMobile());
|
||||
|
||||
// 2.1 校验旧手机和旧验证码
|
||||
// 补充说明:从安全性来说,老手机也校验 oldCode 验证码会更安全。但是由于 uni-app 商城界面暂时没做,所以这里不强制校验
|
||||
if (StrUtil.isNotEmpty(reqVO.getOldCode())) {
|
||||
smsCodeApi.useSmsCode(new SmsCodeUseReqDTO().setMobile(user.getMobile()).setCode(reqVO.getOldCode())
|
||||
.setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene()).setUsedIp(getClientIP())).checkError();
|
||||
}
|
||||
// 2.2 使用新验证码
|
||||
smsCodeApi.useSmsCode(new SmsCodeUseReqDTO().setMobile(reqVO.getMobile()).setCode(reqVO.getCode())
|
||||
.setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene()).setUsedIp(getClientIP())).checkError();
|
||||
|
||||
// 3. 更新用户手机
|
||||
memberUserMapper.updateById(MemberUserDO.builder().id(userId).mobile(reqVO.getMobile()).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUserMobileByWeixin(Long userId, AppMemberUserUpdateMobileByWeixinReqVO reqVO) {
|
||||
// 1.1 获得对应的手机号信息
|
||||
SocialWxPhoneNumberInfoRespDTO phoneNumberInfo = socialClientApi.getWxMaPhoneNumberInfo(
|
||||
UserTypeEnum.MEMBER.getValue(), reqVO.getCode()).getCheckedData();
|
||||
Assert.notNull(phoneNumberInfo, "获得手机信息失败,结果为空");
|
||||
// 1.2 校验新手机是否已经被绑定
|
||||
validateMobileUnique(userId, phoneNumberInfo.getPhoneNumber());
|
||||
|
||||
// 2. 更新用户手机
|
||||
memberUserMapper.updateById(MemberUserDO.builder().id(userId).mobile(phoneNumberInfo.getPhoneNumber()).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUserPassword(Long userId, AppMemberUserUpdatePasswordReqVO reqVO) {
|
||||
// 检测用户是否存在
|
||||
MemberUserDO user = validateUserExists(userId);
|
||||
// 校验验证码
|
||||
smsCodeApi.useSmsCode(new SmsCodeUseReqDTO().setMobile(user.getMobile()).setCode(reqVO.getCode())
|
||||
.setScene(SmsSceneEnum.MEMBER_UPDATE_PASSWORD.getScene()).setUsedIp(getClientIP())).checkError();
|
||||
|
||||
// 更新用户密码
|
||||
memberUserMapper.updateById(MemberUserDO.builder().id(userId)
|
||||
.password(passwordEncoder.encode(reqVO.getPassword())).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetUserPassword(AppMemberUserResetPasswordReqVO reqVO) {
|
||||
// 检验用户是否存在
|
||||
MemberUserDO user = validateUserExists(reqVO.getMobile());
|
||||
|
||||
// 使用验证码
|
||||
smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.MEMBER_RESET_PASSWORD,
|
||||
getClientIP())).checkError();
|
||||
|
||||
// 更新密码
|
||||
memberUserMapper.updateById(MemberUserDO.builder().id(user.getId())
|
||||
.password(passwordEncoder.encode(reqVO.getPassword())).build());
|
||||
}
|
||||
|
||||
private MemberUserDO validateUserExists(String mobile) {
|
||||
MemberUserDO user = memberUserMapper.selectByMobile(mobile);
|
||||
if (user == null) {
|
||||
throw exception(USER_MOBILE_NOT_EXISTS);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPasswordMatch(String rawPassword, String encodedPassword) {
|
||||
return passwordEncoder.matches(rawPassword, encodedPassword);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对密码进行加密
|
||||
*
|
||||
* @param password 密码
|
||||
* @return 加密后的密码
|
||||
*/
|
||||
private String encodePassword(String password) {
|
||||
return passwordEncoder.encode(password);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateUser(MemberUserUpdateReqVO updateReqVO) {
|
||||
// 校验存在
|
||||
validateUserExists(updateReqVO.getId());
|
||||
// 校验手机唯一
|
||||
validateMobileUnique(updateReqVO.getId(), updateReqVO.getMobile());
|
||||
|
||||
// 更新
|
||||
MemberUserDO updateObj = MemberUserConvert.INSTANCE.convert(updateReqVO);
|
||||
memberUserMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
MemberUserDO validateUserExists(Long id) {
|
||||
if (id == null) {
|
||||
return null;
|
||||
}
|
||||
MemberUserDO user = memberUserMapper.selectById(id);
|
||||
if (user == null) {
|
||||
throw exception(USER_NOT_EXISTS);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void validateMobileUnique(Long id, String mobile) {
|
||||
if (StrUtil.isBlank(mobile)) {
|
||||
return;
|
||||
}
|
||||
MemberUserDO user = memberUserMapper.selectByMobile(mobile);
|
||||
if (user == null) {
|
||||
return;
|
||||
}
|
||||
// 如果 id 为空,说明不用比较是否为相同 id 的用户
|
||||
if (id == null) {
|
||||
throw exception(USER_MOBILE_USED, mobile);
|
||||
}
|
||||
if (!user.getId().equals(id)) {
|
||||
throw exception(USER_MOBILE_USED, mobile);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<MemberUserDO> getUserPage(MemberUserPageReqVO pageReqVO) {
|
||||
return memberUserMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUserLevel(Long id, Long levelId, Integer experience) {
|
||||
// 0 代表无等级:防止UpdateById时,会被过滤掉的问题
|
||||
levelId = ObjectUtil.defaultIfNull(levelId, 0L);
|
||||
memberUserMapper.updateById(new MemberUserDO()
|
||||
.setId(id)
|
||||
.setLevelId(levelId).setExperience(experience)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getUserCountByGroupId(Long groupId) {
|
||||
return memberUserMapper.selectCountByGroupId(groupId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getUserCountByLevelId(Long levelId) {
|
||||
return memberUserMapper.selectCountByLevelId(levelId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getUserCountByTagId(Long tagId) {
|
||||
return memberUserMapper.selectCountByTagId(tagId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateUserPoint(Long id, Integer point) {
|
||||
if (point > 0) {
|
||||
memberUserMapper.updatePointIncr(id, point);
|
||||
} else if (point < 0) {
|
||||
return memberUserMapper.updatePointDecr(id, point) > 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.tashow.cloud.member.user;
|
||||
|
||||
import com.tashow.cloud.common.pojo.CommonResult;
|
||||
import com.tashow.cloud.memberapi.api.user.MemberUserApi;
|
||||
import com.tashow.cloud.memberapi.api.user.dto.MemberUserRespDTO;
|
||||
import com.tashow.cloud.member.convert.user.MemberUserConvert;
|
||||
import com.tashow.cloud.member.dal.dataobject.user.MemberUserDO;
|
||||
import com.tashow.cloud.member.service.user.MemberUserService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import static com.tashow.cloud.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static com.tashow.cloud.common.pojo.CommonResult.success;
|
||||
import static com.tashow.cloud.memberapi.enums.ErrorCodeConstants.USER_MOBILE_NOT_EXISTS;
|
||||
|
||||
/**
|
||||
* 会员用户的 API 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@RestController // 提供 RESTful API 接口,给 Feign 调用
|
||||
@Validated
|
||||
public class MemberUserApiImpl implements MemberUserApi {
|
||||
|
||||
@Resource
|
||||
private MemberUserService userService;
|
||||
|
||||
@Override
|
||||
public CommonResult<MemberUserRespDTO> getUser(Long id) {
|
||||
MemberUserDO user = userService.getUser(id);
|
||||
return success(MemberUserConvert.INSTANCE.convert2(user));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<List<MemberUserRespDTO>> getUserList(Collection<Long> ids) {
|
||||
return success(MemberUserConvert.INSTANCE.convertList2(userService.getUserList(ids)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<List<MemberUserRespDTO>> getUserListByNickname(String nickname) {
|
||||
return success(MemberUserConvert.INSTANCE.convertList2(userService.getUserListByNickname(nickname)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<MemberUserRespDTO> getUserByMobile(String mobile) {
|
||||
return success(MemberUserConvert.INSTANCE.convert2(userService.getUserByMobile(mobile)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> validateUser(Long id) {
|
||||
MemberUserDO user = userService.getUser(id);
|
||||
if (user == null) {
|
||||
throw exception(USER_MOBILE_NOT_EXISTS);
|
||||
}
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
--- #################### 注册中心 + 配置中心相关配置 ####################
|
||||
|
||||
spring:
|
||||
cloud:
|
||||
nacos:
|
||||
server-addr: 127.0.0.1:8848 # Nacos 服务器地址
|
||||
username: # Nacos 账号
|
||||
password: # Nacos 密码
|
||||
discovery: # 【配置中心】配置项
|
||||
namespace: dev # 命名空间。这里使用 dev 开发环境
|
||||
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
|
||||
metadata:
|
||||
version: 1.0.0 # 服务实例的版本号,可用于灰度发布
|
||||
config: # 【注册中心】配置项
|
||||
namespace: dev # 命名空间。这里使用 dev 开发环境
|
||||
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
spring:
|
||||
application:
|
||||
name: member-server
|
||||
|
||||
profiles:
|
||||
active: local
|
||||
|
||||
main:
|
||||
allow-circular-references: true # 允许循环依赖,因为项目是三层架构,无法避免这个情况。
|
||||
allow-bean-definition-overriding: true # 允许 Bean 覆盖,例如说 Feign 等会存在重复定义的服务
|
||||
|
||||
config:
|
||||
import:
|
||||
- optional:classpath:application-${spring.profiles.active}.yaml # 加载【本地】配置
|
||||
- optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml # 加载【Nacos】的配置
|
||||
- optional:nacos:application.yaml # 加载【Nacos】的配置
|
||||
@@ -0,0 +1,76 @@
|
||||
<configuration>
|
||||
<!-- 引用 Spring Boot 的 logback 基础配置 -->
|
||||
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
|
||||
<!-- 变量 tashow.info.base-package,基础业务包 -->
|
||||
<springProperty scope="context" name="tashow.info.base-package" source="tashow.info.base-package"/>
|
||||
<!-- 格式化输出:%d 表示日期,%X{tid} SkWalking 链路追踪编号,%thread 表示线程名,%-5level:级别从左显示 5 个字符宽度,%msg:日志消息,%n是换行符 -->
|
||||
<property name="PATTERN_DEFAULT" value="%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} | %highlight(${LOG_LEVEL_PATTERN:-%5p} ${PID:- }) | %boldYellow(%thread [%tid]) %boldGreen(%-40.40logger{39}) | %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
|
||||
|
||||
<!-- 控制台 Appender -->
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
|
||||
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
|
||||
<pattern>${PATTERN_DEFAULT}</pattern>
|
||||
</layout>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- 文件 Appender -->
|
||||
<!-- 参考 Spring Boot 的 file-appender.xml 编写 -->
|
||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
|
||||
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
|
||||
<pattern>${PATTERN_DEFAULT}</pattern>
|
||||
</layout>
|
||||
</encoder>
|
||||
<!-- 日志文件名 -->
|
||||
<file>${LOG_FILE}</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<!-- 滚动后的日志文件名 -->
|
||||
<fileNamePattern>${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz}</fileNamePattern>
|
||||
<!-- 启动服务时,是否清理历史日志,一般不建议清理 -->
|
||||
<cleanHistoryOnStart>${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false}</cleanHistoryOnStart>
|
||||
<!-- 日志文件,到达多少容量,进行滚动 -->
|
||||
<maxFileSize>${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB}</maxFileSize>
|
||||
<!-- 日志文件的总大小,0 表示不限制 -->
|
||||
<totalSizeCap>${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0}</totalSizeCap>
|
||||
<!-- 日志文件的保留天数 -->
|
||||
<maxHistory>${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-30}</maxHistory>
|
||||
</rollingPolicy>
|
||||
</appender>
|
||||
<!-- 异步写入日志,提升性能 -->
|
||||
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
|
||||
<!-- 不丢失日志。默认的,如果队列的 80% 已满,则会丢弃 TRACT、DEBUG、INFO 级别的日志 -->
|
||||
<discardingThreshold>0</discardingThreshold>
|
||||
<!-- 更改默认的队列的深度,该值会影响性能。默认值为 256 -->
|
||||
<queueSize>256</queueSize>
|
||||
<appender-ref ref="FILE"/>
|
||||
</appender>
|
||||
|
||||
<!-- SkyWalking GRPC 日志收集,实现日志中心。注意:SkyWalking 8.4.0 版本开始支持 -->
|
||||
<appender name="GRPC" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
|
||||
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
|
||||
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
|
||||
<pattern>${PATTERN_DEFAULT}</pattern>
|
||||
</layout>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- 本地环境 -->
|
||||
<springProfile name="local">
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
<appender-ref ref="GRPC"/> <!-- 本地环境下,如果不想接入 SkyWalking 日志服务,可以注释掉本行 -->
|
||||
<appender-ref ref="ASYNC"/> <!-- 本地环境下,如果不想打印日志,可以注释掉本行 -->
|
||||
</root>
|
||||
</springProfile>
|
||||
<!-- 其它环境 -->
|
||||
<springProfile name="dev,test,stage,prod,default">
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
<appender-ref ref="ASYNC"/>
|
||||
<appender-ref ref="GRPC"/>
|
||||
</root>
|
||||
</springProfile>
|
||||
|
||||
</configuration>
|
||||
@@ -0,0 +1,48 @@
|
||||
spring:
|
||||
main:
|
||||
lazy-initialization: true # 开启懒加载,加快速度
|
||||
banner-mode: off # 单元测试,禁用 Banner
|
||||
|
||||
--- #################### 数据库相关配置 ####################
|
||||
|
||||
spring:
|
||||
# 数据源配置项
|
||||
datasource:
|
||||
name: ruoyi-vue-pro
|
||||
url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模式;DATABASE_TO_UPPER 配置表和字段使用小写
|
||||
driver-class-name: org.h2.Driver
|
||||
username: sa
|
||||
password:
|
||||
druid:
|
||||
async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度
|
||||
initial-size: 1 # 单元测试,配置为 1,提升启动速度
|
||||
sql:
|
||||
init:
|
||||
schema-locations: classpath:/sql/create_tables.sql
|
||||
|
||||
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
|
||||
data:
|
||||
redis:
|
||||
host: 127.0.0.1 # 地址
|
||||
port: 16379 # 端口(单元测试,使用 16379 端口)
|
||||
database: 0 # 数据库索引
|
||||
|
||||
mybatis:
|
||||
lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试
|
||||
|
||||
--- #################### 定时任务相关配置 ####################
|
||||
|
||||
--- #################### 配置中心相关配置 ####################
|
||||
|
||||
--- #################### 服务保障相关配置 ####################
|
||||
|
||||
# Lock4j 配置项(单元测试,禁用 Lock4j)
|
||||
|
||||
--- #################### 监控相关配置 ####################
|
||||
|
||||
--- #################### 芋道相关配置 ####################
|
||||
|
||||
# 芋道配置项,设置当前项目所有自定义的配置
|
||||
yudao:
|
||||
info:
|
||||
base-package: cn.iocoder.yudao.module
|
||||
@@ -0,0 +1,4 @@
|
||||
<configuration>
|
||||
<!-- 引用 Spring Boot 的 logback 基础配置 -->
|
||||
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
|
||||
</configuration>
|
||||
@@ -0,0 +1,5 @@
|
||||
DELETE FROM "member_user";
|
||||
DELETE FROM "member_address";
|
||||
DELETE FROM "member_tag";
|
||||
DELETE FROM "member_level";
|
||||
DELETE FROM "member_group";
|
||||
@@ -0,0 +1,113 @@
|
||||
CREATE TABLE IF NOT EXISTS "member_user"
|
||||
(
|
||||
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '编号',
|
||||
"nickname" varchar(30) NOT NULL DEFAULT '' COMMENT '用户昵称',
|
||||
"name" varchar(30) NULL COMMENT '真实名字',
|
||||
sex tinyint null comment '性别',
|
||||
birthday datetime null comment '出生日期',
|
||||
area_id int null comment '所在地',
|
||||
mark varchar(255) null comment '用户备注',
|
||||
point int default 0 null comment '积分',
|
||||
"avatar" varchar(255) NOT NULL DEFAULT '' COMMENT '头像',
|
||||
"status" tinyint NOT NULL COMMENT '状态',
|
||||
"mobile" varchar(11) NOT NULL COMMENT '手机号',
|
||||
"password" varchar(100) NOT NULL DEFAULT '' COMMENT '密码',
|
||||
"register_ip" varchar(32) NOT NULL COMMENT '注册 IP',
|
||||
"login_ip" varchar(50) NULL DEFAULT '' COMMENT '最后登录IP',
|
||||
"login_date" datetime NULL DEFAULT NULL COMMENT '最后登录时间',
|
||||
"tag_ids" varchar(255) NULL DEFAULT NULL COMMENT '用户标签编号列表,以逗号分隔',
|
||||
"level_id" bigint NULL DEFAULT NULL COMMENT '等级编号',
|
||||
"experience" bigint NULL DEFAULT NULL COMMENT '经验',
|
||||
"group_id" bigint NULL DEFAULT NULL COMMENT '用户分组编号',
|
||||
"creator" varchar(64) NULL DEFAULT '' COMMENT '创建者',
|
||||
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
"updater" varchar(64) NULL DEFAULT '' COMMENT '更新者',
|
||||
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
"deleted" bit(1) NOT NULL DEFAULT '0' COMMENT '是否删除',
|
||||
"tenant_id" bigint not null default '0',
|
||||
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_id" bigint(20) NOT NULL,
|
||||
"detail_address" varchar(250) NOT NULL,
|
||||
"default_status" bit 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 '',
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT '用户收件地址';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "member_tag"
|
||||
(
|
||||
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
"name" varchar NOT NULL,
|
||||
"creator" varchar DEFAULT '',
|
||||
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updater" varchar DEFAULT '',
|
||||
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
"deleted" bit NOT NULL DEFAULT FALSE,
|
||||
"tenant_id" bigint NOT NULL default '0',
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT '会员标签';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "member_level"
|
||||
(
|
||||
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
"name" varchar NOT NULL,
|
||||
"experience" int NOT NULL,
|
||||
"level" int NOT NULL,
|
||||
"discount_percent" int NOT NULL,
|
||||
"icon" varchar NOT NULL,
|
||||
"background_url" varchar NOT NULL,
|
||||
"creator" varchar DEFAULT '',
|
||||
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updater" varchar DEFAULT '',
|
||||
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
"deleted" bit NOT NULL DEFAULT FALSE,
|
||||
"tenant_id" bigint not null default '0',
|
||||
"status" tinyint NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT '会员等级';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "member_group"
|
||||
(
|
||||
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
"name" varchar NOT NULL,
|
||||
"remark" varchar NOT NULL,
|
||||
"status" tinyint NOT NULL DEFAULT '0',
|
||||
"creator" varchar DEFAULT '',
|
||||
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updater" varchar DEFAULT '',
|
||||
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
"deleted" bit NOT NULL DEFAULT FALSE,
|
||||
"tenant_id" bigint not null default '0',
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT '用户分组';
|
||||
CREATE TABLE IF NOT EXISTS "member_brokerage_record"
|
||||
(
|
||||
"id" int NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
"user_id" bigint NOT NULL,
|
||||
"biz_id" varchar NOT NULL,
|
||||
"biz_type" varchar NOT NULL,
|
||||
"title" varchar NOT NULL,
|
||||
"price" int NOT NULL,
|
||||
"total_price" int NOT NULL,
|
||||
"description" varchar NOT NULL,
|
||||
"status" varchar NOT NULL,
|
||||
"frozen_days" int NOT NULL,
|
||||
"unfreeze_time" varchar,
|
||||
"creator" varchar DEFAULT '',
|
||||
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updater" varchar DEFAULT '',
|
||||
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
"deleted" bit NOT NULL DEFAULT FALSE,
|
||||
"tenant_id" bigint not null default '0',
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT '佣金记录';
|
||||
Reference in New Issue
Block a user