5.22 拆分用户微服务
This commit is contained in:
parent
a3e7faed94
commit
9bd4c9beaf
@ -10,6 +10,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>cart-service</artifactId>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
@ -44,7 +45,11 @@
|
||||
<version>1.0.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.heima</groupId>
|
||||
<artifactId>hm-api</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
|
@ -3,8 +3,10 @@ package com.hmall.cart;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
|
||||
@MapperScan("com.hmall.cart.mapper")
|
||||
@EnableFeignClients(basePackages= "com.hmall.api.client")
|
||||
@SpringBootApplication
|
||||
public class CartApplication {
|
||||
public static void main(String[] args) {
|
||||
|
@ -3,6 +3,9 @@ package com.hmall.cart.service.impl;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
|
||||
import com.hmall.api.client.ItemClient;
|
||||
import com.hmall.api.dto.ItemDTO;
|
||||
import com.hmall.cart.domain.dto.CartFormDTO;
|
||||
import com.hmall.cart.domain.po.Cart;
|
||||
import com.hmall.cart.domain.vo.CartVO;
|
||||
@ -12,9 +15,6 @@ import com.hmall.common.exception.BizIllegalException;
|
||||
import com.hmall.common.utils.BeanUtils;
|
||||
import com.hmall.common.utils.CollUtils;
|
||||
import com.hmall.common.utils.UserContext;
|
||||
|
||||
import com.hmall.domain.dto.ItemDTO;
|
||||
import com.hmall.service.IItemService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@ -37,7 +37,8 @@ import java.util.stream.Collectors;
|
||||
@RequiredArgsConstructor
|
||||
public class CartServiceImpl extends ServiceImpl<CartMapper, Cart> implements ICartService {
|
||||
|
||||
// private final IItemService itemService;
|
||||
private final ItemClient itemClient; //@RequiredArgsConstructor会为final字段生成构造器
|
||||
|
||||
|
||||
@Override
|
||||
public void addItem2Cart(CartFormDTO cartFormDTO) {
|
||||
@ -81,25 +82,25 @@ public class CartServiceImpl extends ServiceImpl<CartMapper, Cart> implements IC
|
||||
}
|
||||
|
||||
private void handleCartItems(List<CartVO> vos) {
|
||||
// // 1.获取商品id
|
||||
// Set<Long> itemIds = vos.stream().map(CartVO::getItemId).collect(Collectors.toSet());
|
||||
// // 2.查询商品
|
||||
// List<ItemDTO> items = itemService.queryItemByIds(itemIds);
|
||||
// if (CollUtils.isEmpty(items)) {
|
||||
// return;
|
||||
// }
|
||||
// // 3.转为 id 到 item的map
|
||||
// Map<Long, ItemDTO> itemMap = items.stream().collect(Collectors.toMap(ItemDTO::getId, Function.identity()));
|
||||
// // 4.写入vo
|
||||
// for (CartVO v : vos) {
|
||||
// ItemDTO item = itemMap.get(v.getItemId());
|
||||
// if (item == null) {
|
||||
// continue;
|
||||
// }
|
||||
// v.setNewPrice(item.getPrice());
|
||||
// v.setStatus(item.getStatus());
|
||||
// v.setStock(item.getStock());
|
||||
// }
|
||||
// 1.获取商品id
|
||||
Set<Long> itemIds = vos.stream().map(CartVO::getItemId).collect(Collectors.toSet());
|
||||
// 2.查询商品
|
||||
List<ItemDTO> items = itemClient.queryItemByIds(itemIds);
|
||||
if (CollUtils.isEmpty(items)) {
|
||||
return;
|
||||
}
|
||||
// 3.转为 id 到 item的map
|
||||
Map<Long, ItemDTO> itemMap = items.stream().collect(Collectors.toMap(ItemDTO::getId, Function.identity()));
|
||||
// 4.写入vo
|
||||
for (CartVO v : vos) {
|
||||
ItemDTO item = itemMap.get(v.getItemId());
|
||||
if (item == null) {
|
||||
continue;
|
||||
}
|
||||
v.setNewPrice(item.getPrice());
|
||||
v.setStatus(item.getStatus());
|
||||
v.setStock(item.getStock());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,4 +1,4 @@
|
||||
hm:
|
||||
db:
|
||||
host: localhost # 修改为你自己的虚拟机IP地址
|
||||
host: 124.71.159.195 # 修改为你自己的虚拟机IP地址
|
||||
pw: 123456 # 修改为docker中的MySQL密码
|
@ -40,4 +40,8 @@ knife4j:
|
||||
api-rule-resources:
|
||||
- com.hmall.cart.controller
|
||||
|
||||
feign:
|
||||
httpclient:
|
||||
enabled: true # 使用 Apache HttpClient(默认关闭)
|
||||
|
||||
# keytool -genkeypair -alias hmall -keyalg RSA -keypass hmall123 -keystore hmall.jks -storepass hmall123
|
41
hm-api/pom.xml
Normal file
41
hm-api/pom.xml
Normal file
@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>hmall</artifactId>
|
||||
<groupId>com.heima</groupId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>hm-api</artifactId>
|
||||
<dependencies>
|
||||
|
||||
<!--openFeign-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
<!--负载均衡器-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.openfeign</groupId>
|
||||
<artifactId>feign-httpclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
<version>1.6.6</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
</project>
|
16
hm-api/src/main/java/com/hmall/api/client/ItemClient.java
Normal file
16
hm-api/src/main/java/com/hmall/api/client/ItemClient.java
Normal file
@ -0,0 +1,16 @@
|
||||
package com.hmall.api.client;
|
||||
|
||||
import com.hmall.api.dto.ItemDTO;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@FeignClient("item-service")
|
||||
public interface ItemClient {
|
||||
|
||||
@GetMapping("/items")
|
||||
List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);
|
||||
}
|
34
hm-api/src/main/java/com/hmall/api/dto/ItemDTO.java
Normal file
34
hm-api/src/main/java/com/hmall/api/dto/ItemDTO.java
Normal file
@ -0,0 +1,34 @@
|
||||
package com.hmall.api.dto;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@ApiModel(description = "商品实体")
|
||||
public class ItemDTO {
|
||||
@ApiModelProperty("商品id")
|
||||
private Long id;
|
||||
@ApiModelProperty("SKU名称")
|
||||
private String name;
|
||||
@ApiModelProperty("价格(分)")
|
||||
private Integer price;
|
||||
@ApiModelProperty("库存数量")
|
||||
private Integer stock;
|
||||
@ApiModelProperty("商品图片")
|
||||
private String image;
|
||||
@ApiModelProperty("类目名称")
|
||||
private String category;
|
||||
@ApiModelProperty("品牌名称")
|
||||
private String brand;
|
||||
@ApiModelProperty("规格")
|
||||
private String spec;
|
||||
@ApiModelProperty("销量")
|
||||
private Integer sold;
|
||||
@ApiModelProperty("评论数")
|
||||
private Integer commentCount;
|
||||
@ApiModelProperty("是否是推广广告,true/false")
|
||||
private Boolean isAD;
|
||||
@ApiModelProperty("商品状态 1-正常,2-下架,3-删除")
|
||||
private Integer status;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
hm:
|
||||
db:
|
||||
host: localhost # 修改为你自己的虚拟机IP地址
|
||||
host: 124.71.159.195 # 修改为你自己的虚拟机IP地址
|
||||
pw: 123456 # 修改为docker中的MySQL密码
|
@ -44,7 +44,11 @@
|
||||
<version>1.0.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!--nacos 服务注册发现-->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
|
@ -1,4 +1,4 @@
|
||||
hm:
|
||||
db:
|
||||
host: localhost # 修改为你自己的虚拟机IP地址
|
||||
host: 124.71.159.195 # 修改为你自己的虚拟机IP地址
|
||||
pw: 123456 # 修改为docker中的MySQL密码
|
@ -3,6 +3,10 @@ server:
|
||||
spring:
|
||||
application:
|
||||
name: item-service
|
||||
cloud:
|
||||
nacos:
|
||||
server-addr: 124.71.159.195:8848 # nacos地址
|
||||
|
||||
profiles:
|
||||
active: local
|
||||
datasource:
|
||||
|
1
pom.xml
1
pom.xml
@ -14,6 +14,7 @@
|
||||
<module>item-service</module>
|
||||
<module>cart-service</module>
|
||||
<module>user-service</module>
|
||||
<module>hm-api</module>
|
||||
</modules>
|
||||
|
||||
<parent>
|
||||
|
@ -15,5 +15,53 @@
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.heima</groupId>
|
||||
<artifactId>hm-api</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<!--common-->
|
||||
<dependency>
|
||||
<groupId>com.heima</groupId>
|
||||
<artifactId>hm-common</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<!--web-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--数据库-->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
</dependency>
|
||||
<!--mybatis-->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.heima</groupId>
|
||||
<artifactId>hm-service</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<!--nacos 服务注册发现-->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
@ -0,0 +1,16 @@
|
||||
package com.hmall.user;
|
||||
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
|
||||
@MapperScan("com.hmall.user.mapper")
|
||||
@EnableFeignClients(basePackages= "com.hmall.api.client")
|
||||
@SpringBootApplication
|
||||
public class UserApplication {
|
||||
public static void main(String[] args) {
|
||||
|
||||
SpringApplication.run(UserApplication.class, args);
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.hmall.user.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "hm.jwt")
|
||||
public class JwtProperties {
|
||||
private Resource location;
|
||||
private String password;
|
||||
private String alias;
|
||||
private Duration tokenTTL = Duration.ofMinutes(10);
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package com.hmall.user.config;
|
||||
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.rsa.crypto.KeyStoreKeyFactory;
|
||||
|
||||
import java.security.KeyPair;
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(JwtProperties.class)
|
||||
public class SecurityConfig {
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder(){
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public KeyPair keyPair(JwtProperties properties){
|
||||
// 获取秘钥工厂
|
||||
KeyStoreKeyFactory keyStoreKeyFactory =
|
||||
new KeyStoreKeyFactory(
|
||||
properties.getLocation(),
|
||||
properties.getPassword().toCharArray());
|
||||
//读取钥匙对
|
||||
return keyStoreKeyFactory.getKeyPair(
|
||||
properties.getAlias(),
|
||||
properties.getPassword().toCharArray());
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package com.hmall.user.controller;
|
||||
import com.hmall.common.exception.BadRequestException;
|
||||
import com.hmall.common.utils.BeanUtils;
|
||||
import com.hmall.common.utils.CollUtils;
|
||||
import com.hmall.common.utils.UserContext;
|
||||
|
||||
import com.hmall.user.domain.po.Address;
|
||||
|
||||
import com.hmall.user.domain.dto.AddressDTO;
|
||||
import com.hmall.user.service.IAddressService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 前端控制器
|
||||
* </p>
|
||||
*
|
||||
* @author 虎哥
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/addresses")
|
||||
@RequiredArgsConstructor
|
||||
@Api(tags = "收货地址管理接口")
|
||||
public class AddressController {
|
||||
|
||||
private final IAddressService addressService;
|
||||
|
||||
@ApiOperation("根据id查询地址")
|
||||
@GetMapping("{addressId}")
|
||||
public AddressDTO findAddressById(@ApiParam("地址id") @PathVariable("addressId") Long id) {
|
||||
// 1.根据id查询
|
||||
Address address = addressService.getById(id);
|
||||
// 2.判断当前用户
|
||||
Long userId = UserContext.getUser();
|
||||
if(!address.getUserId().equals(userId)){
|
||||
throw new BadRequestException("地址不属于当前登录用户");
|
||||
}
|
||||
return BeanUtils.copyBean(address, AddressDTO.class);
|
||||
}
|
||||
@ApiOperation("查询当前用户地址列表")
|
||||
@GetMapping
|
||||
public List<AddressDTO> findMyAddresses() {
|
||||
// 1.查询列表
|
||||
List<Address> list = addressService.query().eq("user_id", UserContext.getUser()).list();
|
||||
// 2.判空
|
||||
if (CollUtils.isEmpty(list)) {
|
||||
return CollUtils.emptyList();
|
||||
}
|
||||
// 3.转vo
|
||||
return BeanUtils.copyList(list, AddressDTO.class);
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package com.hmall.user.controller;
|
||||
|
||||
|
||||
import com.hmall.user.domain.dto.LoginFormDTO;
|
||||
import com.hmall.user.domain.vo.UserLoginVO;
|
||||
import com.hmall.user.service.IUserService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiImplicitParams;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@Api(tags = "用户相关接口")
|
||||
@RestController
|
||||
@RequestMapping("/users")
|
||||
@RequiredArgsConstructor
|
||||
public class UserController {
|
||||
|
||||
private final IUserService userService;
|
||||
|
||||
@ApiOperation("用户登录接口")
|
||||
@PostMapping("login")
|
||||
public UserLoginVO login(@RequestBody @Validated LoginFormDTO loginFormDTO){
|
||||
return userService.login(loginFormDTO);
|
||||
}
|
||||
|
||||
@ApiOperation("扣减余额")
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "pw", value = "支付密码"),
|
||||
@ApiImplicitParam(name = "amount", value = "支付金额")
|
||||
})
|
||||
@PutMapping("/money/deduct")
|
||||
public void deductMoney(@RequestParam("pw") String pw,@RequestParam("amount") Integer amount){
|
||||
userService.deductMoney(pw, amount);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,28 @@
|
||||
package com.hmall.user.domain.dto;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@ApiModel(description = "收货地址实体")
|
||||
public class AddressDTO {
|
||||
@ApiModelProperty("id")
|
||||
private Long id;
|
||||
@ApiModelProperty("省")
|
||||
private String province;
|
||||
@ApiModelProperty("市")
|
||||
private String city;
|
||||
@ApiModelProperty("县/区")
|
||||
private String town;
|
||||
@ApiModelProperty("手机")
|
||||
private String mobile;
|
||||
@ApiModelProperty("详细地址")
|
||||
private String street;
|
||||
@ApiModelProperty("联系人")
|
||||
private String contact;
|
||||
@ApiModelProperty("是否是默认 1默认 0否")
|
||||
private Integer isDefault;
|
||||
@ApiModelProperty("备注")
|
||||
private String notes;
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package com.hmall.user.domain.dto;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Data
|
||||
@ApiModel(description = "登录表单实体")
|
||||
public class LoginFormDTO {
|
||||
@ApiModelProperty(value = "用户名", required = true)
|
||||
@NotNull(message = "用户名不能为空")
|
||||
private String username;
|
||||
@NotNull(message = "密码不能为空")
|
||||
@ApiModelProperty(value = "用户名", required = true)
|
||||
private String password;
|
||||
@ApiModelProperty(value = "是否记住我", required = false)
|
||||
private Boolean rememberMe = false;
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
package com.hmall.user.domain.po;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
*
|
||||
* </p>
|
||||
*
|
||||
* @author 虎哥
|
||||
* @since 2023-05-05
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@Accessors(chain = true)
|
||||
@TableName("address")
|
||||
public class Address implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 省
|
||||
*/
|
||||
private String province;
|
||||
|
||||
/**
|
||||
* 市
|
||||
*/
|
||||
private String city;
|
||||
|
||||
/**
|
||||
* 县/区
|
||||
*/
|
||||
private String town;
|
||||
|
||||
/**
|
||||
* 手机
|
||||
*/
|
||||
private String mobile;
|
||||
|
||||
/**
|
||||
* 详细地址
|
||||
*/
|
||||
private String street;
|
||||
|
||||
/**
|
||||
* 联系人
|
||||
*/
|
||||
private String contact;
|
||||
|
||||
/**
|
||||
* 是否是默认 1默认 0否
|
||||
*/
|
||||
private Integer isDefault;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String notes;
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
package com.hmall.user.domain.po;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import com.hmall.user.enums.UserStatus;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 用户表
|
||||
* </p>
|
||||
*
|
||||
* @author 虎哥
|
||||
* @since 2023-05-05
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@Accessors(chain = true)
|
||||
@TableName("user")
|
||||
public class User implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 密码,加密存储
|
||||
*/
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 注册手机号
|
||||
*/
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 使用状态(1正常 2冻结)
|
||||
*/
|
||||
private UserStatus status;
|
||||
|
||||
/**
|
||||
* 账户余额
|
||||
*/
|
||||
private Integer balance;
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package com.hmall.user.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class UserLoginVO {
|
||||
private String token;
|
||||
private Long userId;
|
||||
private String username;
|
||||
private Integer balance;
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package com.hmall.user.enums;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.EnumValue;
|
||||
import com.hmall.common.exception.BadRequestException;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public enum UserStatus {
|
||||
FROZEN(0, "禁止使用"),
|
||||
NORMAL(1, "已激活"),
|
||||
;
|
||||
@EnumValue
|
||||
int value;
|
||||
String desc;
|
||||
|
||||
UserStatus(Integer value, String desc) {
|
||||
this.value = value;
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
public static UserStatus of(int value) {
|
||||
if (value == 0) {
|
||||
return FROZEN;
|
||||
}
|
||||
if (value == 1) {
|
||||
return NORMAL;
|
||||
}
|
||||
throw new BadRequestException("账户状态错误");
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package com.hmall.user.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.hmall.user.domain.po.Address;
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author 虎哥
|
||||
* @since 2023-05-05
|
||||
*/
|
||||
public interface AddressMapper extends BaseMapper<Address> {
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package com.hmall.user.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import com.hmall.user.domain.po.User;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 用户表 Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author 虎哥
|
||||
* @since 2023-05-05
|
||||
*/
|
||||
public interface UserMapper extends BaseMapper<User> {
|
||||
@Update("update user set balance = balance - ${totalFee} where id = #{userId}")
|
||||
void updateMoney(@Param("userId") Long userId, @Param("totalFee") Integer totalFee);
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package com.hmall.user.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.hmall.user.domain.po.Address;
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 服务类
|
||||
* </p>
|
||||
*
|
||||
* @author 虎哥
|
||||
* @since 2023-05-05
|
||||
*/
|
||||
public interface IAddressService extends IService<Address> {
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package com.hmall.user.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.hmall.user.domain.dto.LoginFormDTO;
|
||||
import com.hmall.user.domain.po.User;
|
||||
import com.hmall.user.domain.vo.UserLoginVO;
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 用户表 服务类
|
||||
* </p>
|
||||
*
|
||||
* @author 虎哥
|
||||
* @since 2023-05-05
|
||||
*/
|
||||
public interface IUserService extends IService<User> {
|
||||
|
||||
UserLoginVO login(LoginFormDTO loginFormDTO);
|
||||
|
||||
void deductMoney(String pw, Integer totalFee);
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package com.hmall.user.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
|
||||
import com.hmall.user.domain.po.Address;
|
||||
import com.hmall.user.mapper.AddressMapper;
|
||||
import com.hmall.user.service.IAddressService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 服务实现类
|
||||
* </p>
|
||||
*
|
||||
* @author 虎哥
|
||||
* @since 2023-05-05
|
||||
*/
|
||||
@Service
|
||||
public class AddressServiceImpl extends ServiceImpl<AddressMapper, Address> implements IAddressService {
|
||||
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
package com.hmall.user.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.hmall.common.exception.BadRequestException;
|
||||
import com.hmall.common.exception.BizIllegalException;
|
||||
import com.hmall.common.exception.ForbiddenException;
|
||||
import com.hmall.common.utils.UserContext;
|
||||
|
||||
import com.hmall.user.config.JwtProperties;
|
||||
import com.hmall.user.domain.dto.LoginFormDTO;
|
||||
import com.hmall.user.domain.po.User;
|
||||
import com.hmall.user.domain.vo.UserLoginVO;
|
||||
import com.hmall.user.enums.UserStatus;
|
||||
import com.hmall.user.mapper.UserMapper;
|
||||
import com.hmall.user.service.IUserService;
|
||||
import com.hmall.user.utils.JwtTool;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 用户表 服务实现类
|
||||
* </p>
|
||||
*
|
||||
* @author 虎哥
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
|
||||
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
|
||||
private final JwtTool jwtTool;
|
||||
|
||||
private final JwtProperties jwtProperties;
|
||||
|
||||
@Override
|
||||
public UserLoginVO login(LoginFormDTO loginDTO) {
|
||||
// 1.数据校验
|
||||
String username = loginDTO.getUsername();
|
||||
String password = loginDTO.getPassword();
|
||||
// 2.根据用户名或手机号查询
|
||||
User user = lambdaQuery().eq(User::getUsername, username).one();
|
||||
Assert.notNull(user, "用户名错误");
|
||||
// 3.校验是否禁用
|
||||
if (user.getStatus() == UserStatus.FROZEN) {
|
||||
throw new ForbiddenException("用户被冻结");
|
||||
}
|
||||
// 4.校验密码
|
||||
if (!passwordEncoder.matches(password, user.getPassword())) {
|
||||
throw new BadRequestException("用户名或密码错误");
|
||||
}
|
||||
// 5.生成TOKEN
|
||||
String token = jwtTool.createToken(user.getId(), jwtProperties.getTokenTTL());
|
||||
// 6.封装VO返回
|
||||
UserLoginVO vo = new UserLoginVO();
|
||||
vo.setUserId(user.getId());
|
||||
vo.setUsername(user.getUsername());
|
||||
vo.setBalance(user.getBalance());
|
||||
vo.setToken(token);
|
||||
return vo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deductMoney(String pw, Integer totalFee) {
|
||||
log.info("开始扣款");
|
||||
// 1.校验密码
|
||||
User user = getById(UserContext.getUser());
|
||||
if(user == null || !passwordEncoder.matches(pw, user.getPassword())){
|
||||
// 密码错误
|
||||
throw new BizIllegalException("用户密码错误");
|
||||
}
|
||||
|
||||
// 2.尝试扣款
|
||||
try {
|
||||
baseMapper.updateMoney(UserContext.getUser(), totalFee);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("扣款失败,可能是余额不足!", e);
|
||||
}
|
||||
log.info("扣款成功");
|
||||
}
|
||||
}
|
81
user-service/src/main/java/com/hmall/user/utils/JwtTool.java
Normal file
81
user-service/src/main/java/com/hmall/user/utils/JwtTool.java
Normal file
@ -0,0 +1,81 @@
|
||||
package com.hmall.user.utils;
|
||||
|
||||
import cn.hutool.core.exceptions.ValidateException;
|
||||
import cn.hutool.jwt.JWT;
|
||||
import cn.hutool.jwt.JWTValidator;
|
||||
import cn.hutool.jwt.signers.JWTSigner;
|
||||
import cn.hutool.jwt.signers.JWTSignerUtil;
|
||||
import com.hmall.common.exception.UnauthorizedException;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.time.Duration;
|
||||
import java.util.Date;
|
||||
|
||||
@Component
|
||||
public class JwtTool {
|
||||
private final JWTSigner jwtSigner;
|
||||
|
||||
public JwtTool(KeyPair keyPair) {
|
||||
this.jwtSigner = JWTSignerUtil.createSigner("rs256", keyPair);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 access-token
|
||||
*
|
||||
* @return access-token
|
||||
*/
|
||||
public String createToken(Long userId, Duration ttl) {
|
||||
// 1.生成jws
|
||||
return JWT.create()
|
||||
.setPayload("user", userId)
|
||||
.setExpiresAt(new Date(System.currentTimeMillis() + ttl.toMillis()))
|
||||
.setSigner(jwtSigner)
|
||||
.sign();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析token
|
||||
*
|
||||
* @param token token
|
||||
* @return 解析刷新token得到的用户信息
|
||||
*/
|
||||
public Long parseToken(String token) {
|
||||
// 1.校验token是否为空
|
||||
if (token == null) {
|
||||
throw new UnauthorizedException("未登录");
|
||||
}
|
||||
// 2.校验并解析jwt
|
||||
JWT jwt;
|
||||
try {
|
||||
jwt = JWT.of(token).setSigner(jwtSigner);
|
||||
} catch (Exception e) {
|
||||
throw new UnauthorizedException("无效的token", e);
|
||||
}
|
||||
// 2.校验jwt是否有效
|
||||
if (!jwt.verify()) {
|
||||
// 验证失败
|
||||
throw new UnauthorizedException("无效的token");
|
||||
}
|
||||
// 3.校验是否过期
|
||||
try {
|
||||
JWTValidator.of(jwt).validateDate();
|
||||
} catch (ValidateException e) {
|
||||
throw new UnauthorizedException("token已经过期");
|
||||
}
|
||||
// 4.数据格式校验
|
||||
Object userPayload = jwt.getPayload("user");
|
||||
if (userPayload == null) {
|
||||
// 数据为空
|
||||
throw new UnauthorizedException("无效的token");
|
||||
}
|
||||
|
||||
// 5.数据解析
|
||||
try {
|
||||
return Long.valueOf(userPayload.toString());
|
||||
} catch (RuntimeException e) {
|
||||
// 数据格式有误
|
||||
throw new UnauthorizedException("无效的token");
|
||||
}
|
||||
}
|
||||
}
|
4
user-service/src/main/resources/application-dev.yaml
Normal file
4
user-service/src/main/resources/application-dev.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
hm:
|
||||
db:
|
||||
host: mysql
|
||||
pw: 123
|
4
user-service/src/main/resources/application-local.yaml
Normal file
4
user-service/src/main/resources/application-local.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
hm:
|
||||
db:
|
||||
host: 124.71.159.195 # 修改为你自己的虚拟机IP地址
|
||||
pw: 123456 # 修改为docker中的MySQL密码
|
57
user-service/src/main/resources/application.yaml
Normal file
57
user-service/src/main/resources/application.yaml
Normal file
@ -0,0 +1,57 @@
|
||||
server:
|
||||
port: 8084
|
||||
spring:
|
||||
application:
|
||||
name: user-service
|
||||
cloud:
|
||||
nacos:
|
||||
server-addr: 124.71.159.195:8848 # nacos地址
|
||||
profiles:
|
||||
active: local
|
||||
datasource:
|
||||
url: jdbc:mysql://${hm.db.host}:3306/hm-user?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
username: root
|
||||
password: ${hm.db.pw}
|
||||
mybatis-plus:
|
||||
configuration:
|
||||
default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
|
||||
global-config:
|
||||
db-config:
|
||||
update-strategy: not_null
|
||||
id-type: auto
|
||||
logging:
|
||||
level:
|
||||
com.hmall: debug
|
||||
pattern:
|
||||
dateformat: HH:mm:ss:SSS
|
||||
file:
|
||||
path: "logs/${spring.application.name}"
|
||||
knife4j:
|
||||
enable: true
|
||||
openapi:
|
||||
title: 黑马商城用户服务接口文档
|
||||
description: "黑马商城用户服务接口文档"
|
||||
email: zhangsan@itcast.cn
|
||||
concat: 宇哥
|
||||
url: https://www.itcast.cn
|
||||
version: v1.0.0
|
||||
group:
|
||||
default:
|
||||
group-name: default
|
||||
api-rule: package
|
||||
api-rule-resources:
|
||||
- com.hmall.user.controller
|
||||
|
||||
feign:
|
||||
httpclient:
|
||||
enabled: true # 使用 Apache HttpClient(默认关闭)
|
||||
|
||||
hm:
|
||||
jwt:
|
||||
location: classpath:hmall.jks
|
||||
alias: hmall
|
||||
password: hmall123
|
||||
tokenTTL: 30m
|
||||
|
||||
# keytool -genkeypair -alias hmall -keyalg RSA -keypass hmall123 -keystore hmall.jks -storepass hmall123
|
BIN
user-service/src/main/resources/hmall.jks
Normal file
BIN
user-service/src/main/resources/hmall.jks
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user