diff --git a/dev-protocol-springcloud/SpringCloud项目介绍.md b/dev-protocol-springcloud/SpringCloud项目介绍.md index 486c1de..a944c0c 100644 --- a/dev-protocol-springcloud/SpringCloud项目介绍.md +++ b/dev-protocol-springcloud/SpringCloud项目介绍.md @@ -22,9 +22,10 @@ ## 通用工程模块 +- 通用服务 - [身份拦截] + - - 授权、鉴权中心微服务 - [dev-protocol-springcloud-project-authority-center](dev-protocol-springcloud-project-authority-center) - - 用户账户微服务 - 商品微服务 diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-gateway/README-SpringCloud-Gateway.md b/dev-protocol-springcloud/dev-protocol-springcloud-gateway/README-SpringCloud-Gateway.md index ab4a8fb..7cf529e 100644 --- a/dev-protocol-springcloud/dev-protocol-springcloud-gateway/README-SpringCloud-Gateway.md +++ b/dev-protocol-springcloud/dev-protocol-springcloud-gateway/README-SpringCloud-Gateway.md @@ -78,7 +78,7 @@ "name": "Path" } ], - "uri": "lb://dev-protocol-spring-cloud-nacos" + "uri": "lb://dev-protocol-spring-cloud-nacos", "filters": [ { "name": "HeaderToken" diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/pic/用户账户微服务总设计.png b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/pic/用户账户微服务总设计.png new file mode 100644 index 0000000..649ba51 Binary files /dev/null and b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/pic/用户账户微服务总设计.png differ diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/pic/账户微服务在业务中的位置.png b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/pic/账户微服务在业务中的位置.png new file mode 100644 index 0000000..560bc34 Binary files /dev/null and b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/pic/账户微服务在业务中的位置.png differ diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/pom.xml b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/pom.xml new file mode 100644 index 0000000..1052b55 --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/pom.xml @@ -0,0 +1,98 @@ + + + 4.0.0 + + org.example + dev-protocol + 1.0-SNAPSHOT + ../../pom.xml + + + dev-protocol-springcloud-project-account-service + + dev-protocol-springcloud-project-account-service + 账户服务 + jar + + + 8 + 8 + UTF-8 + + + + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + org.springframework.cloud + spring-cloud-starter-zipkin + + + org.springframework.kafka + spring-kafka + 2.5.0.RELEASE + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + mysql + mysql-connector-java + 8.0.12 + runtime + + + org.example + dev-protocol-springcloud-project-service-config + 1.0-SNAPSHOT + + + org.example + dev-protocol-springcloud-project-service-sdk + 1.0-SNAPSHOT + + + org.apache.commons + commons-lang3 + 3.12.0 + + + org.apache.commons + commons-collections4 + 4.4 + + + + + + ${artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + + \ No newline at end of file diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/java/org/example/AccountApplication.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/java/org/example/AccountApplication.java new file mode 100644 index 0000000..d3a87fe --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/java/org/example/AccountApplication.java @@ -0,0 +1,20 @@ +package org.example; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; + +/** + *

用户账户微服务启动入口

+ * 127.0.0.1:8003/dev-protocol-springcloud-project-account-service/swagger-ui.html 原生的地址信息 + * 127.0.0.1:8003/dev-protocol-springcloud-project-account-service/doc.html 美化之后的地址信息 + * */ +@EnableJpaAuditing +@SpringBootApplication +@EnableDiscoveryClient +public class AccountApplication { + public static void main(String[] args) { + SpringApplication.run(AccountApplication.class, args); + } +} \ No newline at end of file diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/java/org/example/controller/AddressController.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/java/org/example/controller/AddressController.java new file mode 100644 index 0000000..6e45322 --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/java/org/example/controller/AddressController.java @@ -0,0 +1,58 @@ +package org.example.controller; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.example.account.AddressInfo; +import org.example.common.TableId; +import org.example.service.IAddressService; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +/** + *

用户地址服务 Controller

+ * */ +@Api(tags = "用户地址服务") +@Slf4j +@RestController +@RequestMapping("/address") +public class AddressController { + + private final IAddressService addressService; + + public AddressController(IAddressService addressService) { + this.addressService = addressService; + } + + // value 是简述, notes 是详细的描述信息 + @ApiOperation(value = "创建", notes = "创建用户地址信息", httpMethod = "POST") + @PostMapping("/create-address") + public TableId createAddressInfo(@RequestBody AddressInfo addressInfo) { + return addressService.createAddressInfo(addressInfo); + } + + @ApiOperation(value = "当前用户", notes = "获取当前登录用户地址信息", httpMethod = "GET") + @GetMapping("/current-address") + public AddressInfo getCurrentAddressInfo() { + return addressService.getCurrentAddressInfo(); + } + + @ApiOperation(value = "获取用户地址信息", + notes = "通过 id 获取用户地址信息, id 是 Address 表的主键", + httpMethod = "GET") + @GetMapping("/address-info") + public AddressInfo getAddressInfoById(@RequestParam Long id) { + return addressService.getAddressInfoById(id); + } + + @ApiOperation(value = "获取用户地址信息", + notes = "通过 TableId 获取用户地址信息", httpMethod = "POST") + @PostMapping("/address-info-by-table-id") + public AddressInfo getAddressInfoByTablesId(@RequestBody TableId tableId) { + return addressService.getAddressInfoByTableId(tableId); + } +} diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/java/org/example/controller/BalanceController.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/java/org/example/controller/BalanceController.java new file mode 100644 index 0000000..a344f56 --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/java/org/example/controller/BalanceController.java @@ -0,0 +1,40 @@ +package org.example.controller; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.example.account.BalanceInfo; +import org.example.service.IBalanceService; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + *

用户余额服务 Controller

+ * */ +@Api(tags = "用户余额服务") +@Slf4j +@RestController +@RequestMapping("/balance") +public class BalanceController { + + private final IBalanceService balanceService; + + public BalanceController(IBalanceService balanceService) { + this.balanceService = balanceService; + } + + @ApiOperation(value = "当前用户", notes = "获取当前用户余额信息", httpMethod = "GET") + @GetMapping("/current-balance") + public BalanceInfo getCurrentUserBalanceInfo() { + return balanceService.getCurrentUserBalanceInfo(); + } + + @ApiOperation(value = "扣减", notes = "扣减用于余额", httpMethod = "PUT") + @PutMapping("/deduct-balance") + public BalanceInfo deductBalance(@RequestBody BalanceInfo balanceInfo) { + return balanceService.deductBalance(balanceInfo); + } +} diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/java/org/example/dao/EcommerceAddressDao.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/java/org/example/dao/EcommerceAddressDao.java new file mode 100644 index 0000000..acb1baa --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/java/org/example/dao/EcommerceAddressDao.java @@ -0,0 +1,17 @@ +package org.example.dao; + +import org.example.entity.Address; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +/** + *

EcommerceAddress Dao 接口定义

+ * */ +public interface EcommerceAddressDao extends JpaRepository { + + /** + *

根据 用户 id 查询地址信息

+ * */ + List
findAllByUserId(Long userId); +} diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/java/org/example/dao/EcommerceBalanceDao.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/java/org/example/dao/EcommerceBalanceDao.java new file mode 100644 index 0000000..d69944e --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/java/org/example/dao/EcommerceBalanceDao.java @@ -0,0 +1,13 @@ +package org.example.dao; + +import org.example.entity.Balance; +import org.springframework.data.jpa.repository.JpaRepository; + +/** + *

Balance Dao 接口定义

+ * */ +public interface EcommerceBalanceDao extends JpaRepository { + + /** 根据 userId 查询 EcommerceBalance 对象 */ + Balance findByUserId(Long userId); +} diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/java/org/example/entity/Address.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/java/org/example/entity/Address.java new file mode 100644 index 0000000..4f097d3 --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/java/org/example/entity/Address.java @@ -0,0 +1,106 @@ +package org.example.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.example.account.AddressInfo; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EntityListeners; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + +/** + *

用户地址表实体类定义

+ * */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@EntityListeners(AuditingEntityListener.class) +@Table(name = "t_dev_protocol_cloud_address") +public class Address { + + /** 自增主键 */ + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + /** 用户 id */ + @Column(name = "user_id", nullable = false) + private Long userId; + + /** 用户名 */ + @Column(name = "username", nullable = false) + private String username; + + /** 电话 */ + @Column(name = "phone", nullable = false) + private String phone; + + /** 省 */ + @Column(name = "province", nullable = false) + private String province; + + /** 市 */ + @Column(name = "city", nullable = false) + private String city; + + /** 详细地址 */ + @Column(name = "address_detail", nullable = false) + private String addressDetail; + + /** 创建时间 */ + @CreatedDate + @Column(name = "create_time", nullable = false) + private Date createTime; + + /** 更新时间 */ + @LastModifiedDate + @Column(name = "update_time", nullable = false) + private Date updateTime; + + /** + *

根据 userId + AddressItem 得到 Address

+ * */ + public static Address to(Long userId, AddressInfo.AddressItem addressItem) { + + Address ecommerceAddress = new Address(); + + ecommerceAddress.setUserId(userId); + ecommerceAddress.setUsername(addressItem.getUsername()); + ecommerceAddress.setPhone(addressItem.getPhone()); + ecommerceAddress.setProvince(addressItem.getProvince()); + ecommerceAddress.setCity(addressItem.getCity()); + ecommerceAddress.setAddressDetail(addressItem.getAddressDetail()); + + return ecommerceAddress; + } + + /** + *

将 Address 对象转成 AddressInfo

+ * */ + public AddressInfo.AddressItem toAddressItem() { + + AddressInfo.AddressItem addressItem = new AddressInfo.AddressItem(); + + addressItem.setId(this.id); + addressItem.setUsername(this.username); + addressItem.setPhone(this.phone); + addressItem.setProvince(this.province); + addressItem.setCity(this.city); + addressItem.setAddressDetail(this.addressDetail); + addressItem.setCreateTime(this.createTime); + addressItem.setUpdateTime(this.updateTime); + + return addressItem; + } +} diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/java/org/example/entity/Balance.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/java/org/example/entity/Balance.java new file mode 100644 index 0000000..7225a32 --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/java/org/example/entity/Balance.java @@ -0,0 +1,53 @@ +package org.example.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EntityListeners; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + +/** + *

用户账户余额表实体类定义

+ * */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Entity +@EntityListeners(AuditingEntityListener.class) +@Table(name = "t_dev_protocol_cloud_balance") +public class Balance { + + /** 自增主键 */ + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + /** 用户 id */ + @Column(name = "user_id", nullable = false) + private Long userId; + + /** 账户余额 */ + @Column(name = "balance", nullable = false) + private Long balance; + + /** 创建时间 */ + @CreatedDate + @Column(name = "create_time", nullable = false) + private Date createTime; + + /** 更新时间 */ + @LastModifiedDate + @Column(name = "update_time", nullable = false) + private Date updateTime; +} diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/java/org/example/service/IAddressService.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/java/org/example/service/IAddressService.java new file mode 100644 index 0000000..01894cc --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/java/org/example/service/IAddressService.java @@ -0,0 +1,31 @@ +package org.example.service; + + +import org.example.account.AddressInfo; +import org.example.common.TableId; + +/** + *

用户地址相关服务接口定义

+ * */ +public interface IAddressService { + + /** + *

创建用户地址信息

+ * */ + TableId createAddressInfo(AddressInfo addressInfo); + + /** + *

获取当前登录的用户地址信息

+ * */ + AddressInfo getCurrentAddressInfo(); + + /** + *

通过 id 获取用户地址信息, id 是 Address 表的主键

+ * */ + AddressInfo getAddressInfoById(Long id); + + /** + *

通过 TableId 获取用户地址信息

+ * */ + AddressInfo getAddressInfoByTableId(TableId tableId); +} diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/java/org/example/service/IBalanceService.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/java/org/example/service/IBalanceService.java new file mode 100644 index 0000000..300ebab --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/java/org/example/service/IBalanceService.java @@ -0,0 +1,21 @@ +package org.example.service; + + +import org.example.account.BalanceInfo; + +/** + *

用于余额相关的服务接口定义

+ * */ +public interface IBalanceService { + + /** + *

获取当前用户余额信息

+ * */ + BalanceInfo getCurrentUserBalanceInfo(); + + /** + *

扣减用户余额

+ * @param balanceInfo 代表想要扣减的余额 + * */ + BalanceInfo deductBalance(BalanceInfo balanceInfo); +} diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/java/org/example/service/impl/AddressServiceImpl.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/java/org/example/service/impl/AddressServiceImpl.java new file mode 100644 index 0000000..1fc0dd6 --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/java/org/example/service/impl/AddressServiceImpl.java @@ -0,0 +1,110 @@ +package org.example.service.impl; + +import com.alibaba.fastjson2.JSON; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.example.account.AddressInfo; +import org.example.common.TableId; +import org.example.dao.EcommerceAddressDao; +import org.example.entity.Address; +import org.example.filter.AccessContext; +import org.example.service.IAddressService; +import org.example.vo.LoginUserInfo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +/** + *

用户地址相关服务接口实现

+ * */ +@Slf4j +@Service +@Transactional(rollbackFor = Exception.class) +public class AddressServiceImpl implements IAddressService { + + private final EcommerceAddressDao addressDao; + + public AddressServiceImpl(EcommerceAddressDao addressDao) { + this.addressDao = addressDao; + } + + /** + *

存储多个地址信息

+ * */ + @Override + public TableId createAddressInfo(AddressInfo addressInfo) { + + // 不能直接从参数中获取用户的 id 信息 + LoginUserInfo loginUserInfo = AccessContext.getLoginUserInfo(); + + // 将传递的参数转换成实体对象 + List
ecommerceAddresses = addressInfo.getAddressItems().stream() + .map(a -> Address.to(loginUserInfo.getId(), a)) + .collect(Collectors.toList()); + + // 保存到数据表并把返回记录的 id 给调用方 + List
savedRecords = addressDao.saveAll(ecommerceAddresses); + List ids = savedRecords.stream() + .map(Address::getId).collect(Collectors.toList()); + log.info("create address info: [{}], [{}]", loginUserInfo.getId(), + JSON.toJSONString(ids)); + + return new TableId( + ids.stream().map(TableId.Id::new).collect(Collectors.toList()) + ); + } + + @Override + public AddressInfo getCurrentAddressInfo() { + + LoginUserInfo loginUserInfo = AccessContext.getLoginUserInfo(); + + // 根据 userId 查询到用户的地址信息, 再实现转换 + List
ecommerceAddresses = addressDao.findAllByUserId( + loginUserInfo.getId() + ); + List addressItems = ecommerceAddresses.stream() + .map(Address::toAddressItem) + .collect(Collectors.toList()); + + return new AddressInfo(loginUserInfo.getId(), addressItems); + } + + @Override + public AddressInfo getAddressInfoById(Long id) { + + Address ecommerceAddress = addressDao.findById(id).orElse(null); + if (null == ecommerceAddress) { + throw new RuntimeException("address is not exist"); + } + + return new AddressInfo( + ecommerceAddress.getUserId(), + Collections.singletonList(ecommerceAddress.toAddressItem()) + ); + } + + @Override + public AddressInfo getAddressInfoByTableId(TableId tableId) { + + List ids = tableId.getIds().stream() + .map(TableId.Id::getId).collect(Collectors.toList()); + log.info("get address info by table id: [{}]", JSON.toJSONString(ids)); + + List
ecommerceAddresses = addressDao.findAllById(ids); + if (CollectionUtils.isEmpty(ecommerceAddresses)) { + return new AddressInfo(-1L, Collections.emptyList()); + } + + List addressItems = ecommerceAddresses.stream() + .map(Address::toAddressItem) + .collect(Collectors.toList()); + + return new AddressInfo( + ecommerceAddresses.get(0).getUserId(), addressItems + ); + } +} diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/java/org/example/service/impl/BalanceServiceImpl.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/java/org/example/service/impl/BalanceServiceImpl.java new file mode 100644 index 0000000..c8bcd23 --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/java/org/example/service/impl/BalanceServiceImpl.java @@ -0,0 +1,76 @@ +package org.example.service.impl; + +import lombok.extern.slf4j.Slf4j; +import org.example.account.BalanceInfo; +import org.example.dao.EcommerceBalanceDao; +import org.example.entity.Balance; +import org.example.filter.AccessContext; +import org.example.service.IBalanceService; +import org.example.vo.LoginUserInfo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + *

用于余额相关服务接口实现

+ * */ +@Slf4j +@Service +@Transactional(rollbackFor = Exception.class) +public class BalanceServiceImpl implements IBalanceService { + + private final EcommerceBalanceDao balanceDao; + + public BalanceServiceImpl(EcommerceBalanceDao balanceDao) { + this.balanceDao = balanceDao; + } + + @Override + public BalanceInfo getCurrentUserBalanceInfo() { + + LoginUserInfo loginUserInfo = AccessContext.getLoginUserInfo(); + BalanceInfo balanceInfo = new BalanceInfo( + loginUserInfo.getId(), 0L + ); + + Balance ecommerceBalance = + balanceDao.findByUserId(loginUserInfo.getId()); + if (null != ecommerceBalance) { + balanceInfo.setBalance(ecommerceBalance.getBalance()); + } else { + // 如果还没有用户余额记录, 这里创建出来,余额设定为0即可 + Balance newBalance = new Balance(); + newBalance.setUserId(loginUserInfo.getId()); + newBalance.setBalance(0L); + log.info("init user balance record: [{}]", + balanceDao.save(newBalance).getId()); + } + + return balanceInfo; + } + + @Override + public BalanceInfo deductBalance(BalanceInfo balanceInfo) { + + LoginUserInfo loginUserInfo = AccessContext.getLoginUserInfo(); + + // 扣减用户余额的一个基本原则: 扣减额 <= 当前用户余额 + Balance ecommerceBalance = + balanceDao.findByUserId(loginUserInfo.getId()); + if (null == ecommerceBalance + || ecommerceBalance.getBalance() - balanceInfo.getBalance() < 0 + ) { + throw new RuntimeException("user balance is not enough!"); + } + + Long sourceBalance = ecommerceBalance.getBalance(); + ecommerceBalance.setBalance(ecommerceBalance.getBalance() - balanceInfo.getBalance()); + log.info("deduct balance: [{}], [{}], [{}]", + balanceDao.save(ecommerceBalance).getId(), sourceBalance, + balanceInfo.getBalance()); + + return new BalanceInfo( + ecommerceBalance.getUserId(), + ecommerceBalance.getBalance() + ); + } +} diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/resources/bootstrap.yml b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..1a3b3da --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/resources/bootstrap.yml @@ -0,0 +1,67 @@ +server: + port: 8003 + servlet: + context-path: /dev-protocol-springcloud-project-account-service + +spring: + application: + name: dev-protocol-springcloud-project-account-service + cloud: + nacos: + discovery: + enabled: true # 如果不想使用 Nacos 进行服务注册和发现, 设置为 false 即可 + server-addr: 127.0.0.1:8848 + # server-addr: 127.0.0.1:8848,127.0.0.1:8849,127.0.0.1:8850 # Nacos 服务器地址 + namespace: 1ccc74ae-9398-4dbe-b9d7-4f9addf9f40c + metadata: + management: + context-path: ${server.servlet.context-path}/actuator + kafka: + bootstrap-servers: 127.0.0.1:9092 + producer: + retries: 3 + consumer: + auto-offset-reset: latest + sleuth: + sampler: + probability: 1.0 # 采样比例, 1.0 表示 100%, 默认是 0.1 + zipkin: + sender: + type: kafka # 默认是 web + base-url: http://localhost:9411/ + jpa: + show-sql: true + hibernate: + ddl-auto: none + properties: + hibernate.show_sql: true + hibernate.format_sql: true + hibernate: + dialect: org.hibernate.dialect.MySQLDialect + open-in-view: false + datasource: + # 数据源 + url: jdbc:mysql://127.0.0.1:3306/dev_protocol_springcloud_project?autoReconnect=true&useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8 + username: root + password: root + type: com.zaxxer.hikari.HikariDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + # 连接池 + hikari: + maximum-pool-size: 8 + minimum-idle: 4 + idle-timeout: 30000 + connection-timeout: 30000 + max-lifetime: 45000 + auto-commit: true + pool-name: devProtocolSpringcloudHikariCP + +# 暴露端点 +management: + endpoints: + web: + exposure: + include: '*' + endpoint: + health: + show-details: always diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/resources/http/account-address.http b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/resources/http/account-address.http new file mode 100644 index 0000000..acd87d4 --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/resources/http/account-address.http @@ -0,0 +1,43 @@ +### 创建用户地址信息 +POST http://127.0.0.1:9001/dev-protocol-springcloud-gateway/dev-protocol-springcloud-project-account-service/address/create-address +Content-Type: application/json +e-commerce-user: eyJhbGciOiJSUzI1NiJ9.eyJlLWNvbW1lcmNlLXVzZXIiOiJ7XCJpZFwiOjEwLFwidXNlcm5hbWVcIjpcIlFpbnlpQGltb29jLmNvbVwifSIsImp0aSI6IjU2ZGViY2NiLTlkNTEtNDMzMC04NjFhLTY5MWI0YWE2NzY1MiIsImV4cCI6MTYyNDcyMzIwMH0.M9Xg__zvXvAeALMcAs1LIJN41_JGo7Od1bw0218AbmzrBMZ-9WT6plow62gSBfxa5Xbm79t6VeHq8iC7a0ZYBF2RemNaXmrgIkmwxjg12jO56hTCRv1jghAbiFZknTxXA4QQ6dB3MS4mE19MDt6P3f3ckLwyl-IjE9O5_c-wg47Sb8odfrm_K2RMx_2ZMQAfNjHzxCzrQyNRWhqSQTpSVAuUScaxlnf4EvEwav8FT0QICIlx8oZyU7fh_zBtfFL4EhQlHfcU-eo6Nw4YUVA0knIrkAXD8jqCowx7Mej8khQ6zXNUB2V_uTtc2P2VW1UHlWSAiFCR_qkvBGSWO0VH_w + +{ + "userId": 10, + "addressItems": [ + { + "username": "zxcx", + "phone": "16600000001", + "province": "上海市", + "city": "上海市", + "addressDetail": "闵行区" + } + ] +} + +### 当前登录用户地址信息 +GET http://127.0.0.1:9001/dev-protocol-springcloud-gateway/dev-protocol-springcloud-project-account-service/address/current-address +Accept: application/json +e-commerce-user: eyJhbGciOiJSUzI1NiJ9.eyJlLWNvbW1lcmNlLXVzZXIiOiJ7XCJpZFwiOjEwLFwidXNlcm5hbWVcIjpcIlFpbnlpQGltb29jLmNvbVwifSIsImp0aSI6IjU2ZGViY2NiLTlkNTEtNDMzMC04NjFhLTY5MWI0YWE2NzY1MiIsImV4cCI6MTYyNDcyMzIwMH0.M9Xg__zvXvAeALMcAs1LIJN41_JGo7Od1bw0218AbmzrBMZ-9WT6plow62gSBfxa5Xbm79t6VeHq8iC7a0ZYBF2RemNaXmrgIkmwxjg12jO56hTCRv1jghAbiFZknTxXA4QQ6dB3MS4mE19MDt6P3f3ckLwyl-IjE9O5_c-wg47Sb8odfrm_K2RMx_2ZMQAfNjHzxCzrQyNRWhqSQTpSVAuUScaxlnf4EvEwav8FT0QICIlx8oZyU7fh_zBtfFL4EhQlHfcU-eo6Nw4YUVA0knIrkAXD8jqCowx7Mej8khQ6zXNUB2V_uTtc2P2VW1UHlWSAiFCR_qkvBGSWO0VH_w + +### 通过 id 获取用户地址信息 +GET http://127.0.0.1:9001/dev-protocol-springcloud-gateway/dev-protocol-springcloud-project-account-service/address/address-info?id=2 +Accept: application/json +e-commerce-user: eyJhbGciOiJSUzI1NiJ9.eyJlLWNvbW1lcmNlLXVzZXIiOiJ7XCJpZFwiOjEwLFwidXNlcm5hbWVcIjpcIlFpbnlpQGltb29jLmNvbVwifSIsImp0aSI6IjU2ZGViY2NiLTlkNTEtNDMzMC04NjFhLTY5MWI0YWE2NzY1MiIsImV4cCI6MTYyNDcyMzIwMH0.M9Xg__zvXvAeALMcAs1LIJN41_JGo7Od1bw0218AbmzrBMZ-9WT6plow62gSBfxa5Xbm79t6VeHq8iC7a0ZYBF2RemNaXmrgIkmwxjg12jO56hTCRv1jghAbiFZknTxXA4QQ6dB3MS4mE19MDt6P3f3ckLwyl-IjE9O5_c-wg47Sb8odfrm_K2RMx_2ZMQAfNjHzxCzrQyNRWhqSQTpSVAuUScaxlnf4EvEwav8FT0QICIlx8oZyU7fh_zBtfFL4EhQlHfcU-eo6Nw4YUVA0knIrkAXD8jqCowx7Mej8khQ6zXNUB2V_uTtc2P2VW1UHlWSAiFCR_qkvBGSWO0VH_w + +### 获取用户地址信息 +POST http://127.0.0.1:9001/dev-protocol-springcloud-gateway/dev-protocol-springcloud-project-account-service/address/address-info-by-table-id +Content-Type: application/json +e-commerce-user: eyJhbGciOiJSUzI1NiJ9.eyJlLWNvbW1lcmNlLXVzZXIiOiJ7XCJpZFwiOjEwLFwidXNlcm5hbWVcIjpcIlFpbnlpQGltb29jLmNvbVwifSIsImp0aSI6IjU2ZGViY2NiLTlkNTEtNDMzMC04NjFhLTY5MWI0YWE2NzY1MiIsImV4cCI6MTYyNDcyMzIwMH0.M9Xg__zvXvAeALMcAs1LIJN41_JGo7Od1bw0218AbmzrBMZ-9WT6plow62gSBfxa5Xbm79t6VeHq8iC7a0ZYBF2RemNaXmrgIkmwxjg12jO56hTCRv1jghAbiFZknTxXA4QQ6dB3MS4mE19MDt6P3f3ckLwyl-IjE9O5_c-wg47Sb8odfrm_K2RMx_2ZMQAfNjHzxCzrQyNRWhqSQTpSVAuUScaxlnf4EvEwav8FT0QICIlx8oZyU7fh_zBtfFL4EhQlHfcU-eo6Nw4YUVA0knIrkAXD8jqCowx7Mej8khQ6zXNUB2V_uTtc2P2VW1UHlWSAiFCR_qkvBGSWO0VH_w + +{ + "ids": [ + { + "id": 1 + }, + { + "id": 2 + } + ] +} diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/resources/http/account-balance.http b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/resources/http/account-balance.http new file mode 100644 index 0000000..3dab2e4 --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/resources/http/account-balance.http @@ -0,0 +1,14 @@ +### 获取当前用户余额信息 +GET http://127.0.0.1:9001/dev-protocol-springcloud-gateway/dev-protocol-springcloud-project-account-service/balance/current-balance +Accept: application/json +e-commerce-user: eyJhbGciOiJSUzI1NiJ9.eyJlLWNvbW1lcmNlLXVzZXIiOiJ7XCJpZFwiOjEwLFwidXNlcm5hbWVcIjpcIlFpbnlpQGltb29jLmNvbVwifSIsImp0aSI6IjU2ZGViY2NiLTlkNTEtNDMzMC04NjFhLTY5MWI0YWE2NzY1MiIsImV4cCI6MTYyNDcyMzIwMH0.M9Xg__zvXvAeALMcAs1LIJN41_JGo7Od1bw0218AbmzrBMZ-9WT6plow62gSBfxa5Xbm79t6VeHq8iC7a0ZYBF2RemNaXmrgIkmwxjg12jO56hTCRv1jghAbiFZknTxXA4QQ6dB3MS4mE19MDt6P3f3ckLwyl-IjE9O5_c-wg47Sb8odfrm_K2RMx_2ZMQAfNjHzxCzrQyNRWhqSQTpSVAuUScaxlnf4EvEwav8FT0QICIlx8oZyU7fh_zBtfFL4EhQlHfcU-eo6Nw4YUVA0knIrkAXD8jqCowx7Mej8khQ6zXNUB2V_uTtc2P2VW1UHlWSAiFCR_qkvBGSWO0VH_w + +### 扣减用户余额 +PUT http://127.0.0.1:9001/dev-protocol-springcloud-gateway/dev-protocol-springcloud-project-account-service/balance/deduct-balance +Content-Type: application/json +e-commerce-user: eyJhbGciOiJSUzI1NiJ9.eyJlLWNvbW1lcmNlLXVzZXIiOiJ7XCJpZFwiOjEwLFwidXNlcm5hbWVcIjpcIlFpbnlpQGltb29jLmNvbVwifSIsImp0aSI6IjU2ZGViY2NiLTlkNTEtNDMzMC04NjFhLTY5MWI0YWE2NzY1MiIsImV4cCI6MTYyNDcyMzIwMH0.M9Xg__zvXvAeALMcAs1LIJN41_JGo7Od1bw0218AbmzrBMZ-9WT6plow62gSBfxa5Xbm79t6VeHq8iC7a0ZYBF2RemNaXmrgIkmwxjg12jO56hTCRv1jghAbiFZknTxXA4QQ6dB3MS4mE19MDt6P3f3ckLwyl-IjE9O5_c-wg47Sb8odfrm_K2RMx_2ZMQAfNjHzxCzrQyNRWhqSQTpSVAuUScaxlnf4EvEwav8FT0QICIlx8oZyU7fh_zBtfFL4EhQlHfcU-eo6Nw4YUVA0knIrkAXD8jqCowx7Mej8khQ6zXNUB2V_uTtc2P2VW1UHlWSAiFCR_qkvBGSWO0VH_w + +{ + "userId": 10, + "balance": 2000 +} diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/resources/sql/t_ecommerce_address.sql b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/resources/sql/t_ecommerce_address.sql new file mode 100644 index 0000000..c8b4e13 --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/resources/sql/t_ecommerce_address.sql @@ -0,0 +1,13 @@ +-- 创建 t_ecommerce_address 数据表 +CREATE TABLE IF NOT EXISTS `dev_protocol_springcloud_project`.`t_dev_protocol_cloud_address` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增主键', + `user_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '用户 id', + `username` varchar(64) NOT NULL DEFAULT '' COMMENT '用户名', + `phone` varchar(64) NOT NULL DEFAULT '' COMMENT '电话号码', + `province` varchar(64) NOT NULL DEFAULT '' COMMENT '省', + `city` varchar(64) NOT NULL DEFAULT '' COMMENT '市', + `address_detail` varchar(256) NOT NULL DEFAULT '' COMMENT '详细地址', + `create_time` datetime NOT NULL DEFAULT '0000-01-01 00:00:00' COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT '0000-01-01 00:00:00' COMMENT '更新时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='用户地址表'; diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/resources/sql/t_ecommerce_balance.sql b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/resources/sql/t_ecommerce_balance.sql new file mode 100644 index 0000000..2767a8b --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/main/resources/sql/t_ecommerce_balance.sql @@ -0,0 +1,10 @@ +-- 创建 t_ecommerce_balance 数据表 +CREATE TABLE IF NOT EXISTS `dev_protocol_springcloud_project`.`t_dev_protocol_cloud_balance` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增主键', + `user_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '用户 id', + `balance` bigint(20) NOT NULL DEFAULT 0 COMMENT '账户余额', + `create_time` datetime NOT NULL DEFAULT '0000-01-01 00:00:00' COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT '0000-01-01 00:00:00' COMMENT '更新时间', + PRIMARY KEY (`id`), + UNIQUE KEY `user_id_key` (`user_id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='用户账户余额表'; diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/test/java/org/example/AccountApplicationTest.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/test/java/org/example/AccountApplicationTest.java new file mode 100644 index 0000000..cedd07c --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/test/java/org/example/AccountApplicationTest.java @@ -0,0 +1,19 @@ +package org.example; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +/** + *

用户账户微服务环境可用性校验

+ * */ +@SpringBootTest +@RunWith(SpringRunner.class) +public class AccountApplicationTest { + + @Test + public void contextLoad() { + + } +} diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/test/java/org/example/service/AddressServiceTest.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/test/java/org/example/service/AddressServiceTest.java new file mode 100644 index 0000000..8cc7303 --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/test/java/org/example/service/AddressServiceTest.java @@ -0,0 +1,76 @@ +package org.example.service; + +import com.alibaba.fastjson2.JSON; +import lombok.extern.slf4j.Slf4j; +import org.example.account.AddressInfo; +import org.example.common.TableId; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.Collections; + +/** + *

用户地址相关服务功能测试

+ * */ +@Slf4j +public class AddressServiceTest extends BaseTest { + + @Autowired + private IAddressService addressService; + + /** + *

测试创建用户地址信息

+ * */ + @Test + public void testCreateAddressInfo() { + + AddressInfo.AddressItem addressItem = new AddressInfo.AddressItem(); + addressItem.setUsername("qqq"); + addressItem.setPhone("18800000001"); + addressItem.setProvince("上海市"); + addressItem.setCity("上海市"); + addressItem.setAddressDetail("陆家嘴"); + + log.info("test create address info: [{}]", JSON.toJSONString( + addressService.createAddressInfo( + new AddressInfo(loginUserInfo.getId(), + Collections.singletonList(addressItem)) + ) + )); + } + + /** + *

测试获取当前登录用户地址信息

+ * */ + @Test + public void testGetCurrentAddressInfo() { + + log.info("test get current user info: [{}]", JSON.toJSONString( + addressService.getCurrentAddressInfo() + )); + } + + /** + *

测试通过 id 获取用户地址信息

+ * */ + @Test + public void testGetAddressInfoById() { + + log.info("test get address info by id: [{}]", JSON.toJSONString( + addressService.getAddressInfoById(1L) + )); + } + + /** + *

测试通过 TableId 获取用户地址信息

+ * */ + @Test + public void testGetAddressInfoByTableId() { + + log.info("test get address info by table id: [{}]", JSON.toJSONString( + addressService.getAddressInfoByTableId( + new TableId(Collections.singletonList(new TableId.Id(1L))) + ) + )); + } +} diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/test/java/org/example/service/BalanceServiceTest.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/test/java/org/example/service/BalanceServiceTest.java new file mode 100644 index 0000000..9b68802 --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/test/java/org/example/service/BalanceServiceTest.java @@ -0,0 +1,43 @@ +package org.example.service; + +import com.alibaba.fastjson2.JSON; +import lombok.extern.slf4j.Slf4j; +import org.example.account.BalanceInfo; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +/** + *

用于余额相关服务测试

+ * */ +@Slf4j +public class BalanceServiceTest extends BaseTest { + + @Autowired + private IBalanceService balanceService; + + /** + *

测试获取当前用户的余额信息

+ * */ + @Test + public void testGetCurrentUserBalanceInfo() { + + log.info("test get current user balance info: [{}]", JSON.toJSONString( + balanceService.getCurrentUserBalanceInfo() + )); + } + + /** + *

测试扣减用于余额

+ * */ + @Test + public void testDeductBalance() { + + BalanceInfo balanceInfo = new BalanceInfo(); + balanceInfo.setUserId(loginUserInfo.getId()); + balanceInfo.setBalance(1000L); + + log.info("test deduct balance: [{}]", JSON.toJSONString( + balanceService.deductBalance(balanceInfo) + )); + } +} diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/test/java/org/example/service/BaseTest.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/test/java/org/example/service/BaseTest.java new file mode 100644 index 0000000..bce73dc --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/src/test/java/org/example/service/BaseTest.java @@ -0,0 +1,31 @@ +package org.example.service; + +import org.example.filter.AccessContext; +import org.example.vo.LoginUserInfo; +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +/** + *

测试用例基类, 填充登录用户信息

+ * */ +@SpringBootTest +@RunWith(SpringRunner.class) +public abstract class BaseTest { + + protected final LoginUserInfo loginUserInfo = new LoginUserInfo( + 10L, "q@bbbbbbyyyyyy.com" + ); + + @Before + public void init() { + AccessContext.setLoginUserInfo(loginUserInfo); + } + + @After + public void destroy() { + AccessContext.clearLoginUserInfo(); + } +} diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/用户账户微服务.md b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/用户账户微服务.md new file mode 100644 index 0000000..82f8ae5 --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-account-service/用户账户微服务.md @@ -0,0 +1,80 @@ +## 用户账户微服务 + +### 用户身份登录统一拦截 +- 所有需要开发的微服务通用的配置 + +### 集成 Swagger2 实现代码即文档 +- + +### 用户账户微服务功能设计 +- + +### 数据表及 ORM 过程 +- + +### 用户地址与余额服务接口定义 +- Service 返回包装成类进行返回, 尽量不要仅仅只返回一个ID, 防止后续的扩展需求 +- 不要设计通过请求参数来获取当前用户的余额信息, 因为可以通过请求劫持来拿到别的请求信息, 防止越权 +### 用户地址相关服务接口实现 + + +### 用户地址服务接口可用性测试(测试用例) + + +### 用户余额相关服务接口实现 + + +### 用户余额服务接口可用性测试(测试用例) + + +### 用户账户微服务对外 HTTP 接口 + + +### 验证用户账户微服务功能可用性 +```json +[ + { + "id": "dev-protocol-springcloud-project-account-service", + "order": 0, // order 越小优先级越高0 + "predicates": [ + { + "args": { + "pattern": "/dev-protocol-springcloud-gateway/dev-protocol-springcloud-project-account-service/**" + }, + "name": "Path" + } + ], + "uri": "lb://dev-protocol-springcloud-project-account-service", + "filters": [ + { + "name": "HeaderToken" + }, + { + "name": "StripPrefix", + "args": { + "parts": "1" // 用来跳过上面的 pattern 前面的配置, 因为 [bootstrap.yml] 中的配置 context-path + } + } + ] + } +] +``` +- 补充放在nacos 的 gateway 的配置文件中 +### 用户账户微服务总结 + +- 微服务开始之前的准备工作 + - 用户身份登录拦截 + - 在请求进入 service 之前解析 header 中的 token 信息, 并填充用户信息到上下文中 + - 在请求结束之后,清理掉上下文中的用户信息 + - 对于一些特定的 HTTP 请求不要拦截(即白名单 ) + - 代码即文档:引入 Swagger + - pom 中添加依赖配置 + - 自定义配置 Swagger +--- +- 微服务模块的设计思想 + - 微服务模块应该是低耦合、尽可能多的重用代码 + - Tips:设计并不唯-! + - ![用户账户微服务总设计.png](pic/用户账户微服务总设计.png) +--- +- 用户账户微服务的功能及在业务中的位置 + - ![账户微服务在业务中的位置.png](pic/账户微服务在业务中的位置.png) \ No newline at end of file diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/电商-授权鉴权服务.md b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/电商-授权鉴权服务.md index 46e57b2..61acd2e 100644 --- a/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/电商-授权鉴权服务.md +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/电商-授权鉴权服务.md @@ -33,5 +33,26 @@ - http 脚本验证 对外接口是否可用 ## 授权、鉴权中心微服务总结 +- 对比基于 Token 与基于服务器的身份认证 + - 最为传统的做法,客户端存储 Cookie(一般是 Sessionid),服务器存储 Session + - Session 是每次用户认证通过以后,服务器需要创建一条记录保存用户信息,通常是在内存中,随着认证通过的用户越来越多,服务器的在这里的开销就会越来越大 + - 在不同域名之前切换时,请求可能会被禁止;即跨域问题 +--- +- 基于 Token(JWT)的身份认证 + - JWT 与 Session的差异相同点是,它们都是存储用户信息;然而,Session是在服务器端的,而JWT是在客户端的 + - JWT方式将用户状态分散到了客户端中,可以明显减轻服务端的内存压力 +--- +- 对比基于 Token 与基于服务器的身份认证 + - 两者优缺点的对比 + - 解析方法: JWT 使用算法直接解析得到用户信息; Session 需要额外的数据映射实现匹配 + - 管理方法: JWT只有过期时间的限制:Session 数据保存在服务器,可控性更强 + - 跨平台: JWT就是一段字符串,可以任意传播;Session 跨平台需要有统一的解析平台,较为繁琐 + - 时效性: JWT 一旦生成,独立存在,很难做特殊控制:Session 时效性完全由服务端的逻辑说了算 + - Tips:各自都有优缺点, 都是登录, 授权的解决方案 +--- - 生成数据库 文档 - - [DBDocTest.java] \ No newline at end of file + - [DBDocTest.java] + + + + diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-mvc-config/pom.xml b/dev-protocol-springcloud/dev-protocol-springcloud-project-mvc-config/pom.xml index 225a19c..f726566 100644 --- a/dev-protocol-springcloud/dev-protocol-springcloud-project-mvc-config/pom.xml +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-mvc-config/pom.xml @@ -28,6 +28,10 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.boot + spring-boot-starter-test + diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-service-config/README.md b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-config/README.md new file mode 100644 index 0000000..1ad77da --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-config/README.md @@ -0,0 +1,6 @@ +# 介绍 +- 正常服务的通用配置 +- 开发别的模块也要直接进行导入 + +# 已实现功能 +- 统一身份拦截器 \ No newline at end of file diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-service-config/pom.xml b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-config/pom.xml new file mode 100644 index 0000000..0f30289 --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-config/pom.xml @@ -0,0 +1,51 @@ + + + 4.0.0 + + org.example + dev-protocol + 1.0-SNAPSHOT + ../../pom.xml + + + dev-protocol-springcloud-project-service-config + + + dev-protocol-springcloud-project-service-config + 服务通用配置 + + + 8 + 8 + UTF-8 + + + + + org.example + dev-protocol-springcloud-project-mvc-config + 1.0-SNAPSHOT + + + + + io.springfox + springfox-swagger2 + 2.9.2 + + + io.springfox + springfox-swagger-ui + 2.9.2 + + + + com.github.xiaoymin + swagger-bootstrap-ui + 1.9.3 + + + + \ No newline at end of file diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-service-config/src/main/java/org/example/conf/DevProtocolWebMvcConfig.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-config/src/main/java/org/example/conf/DevProtocolWebMvcConfig.java new file mode 100644 index 0000000..b78b70d --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-config/src/main/java/org/example/conf/DevProtocolWebMvcConfig.java @@ -0,0 +1,44 @@ +package org.example.conf; + +import org.example.filter.LoginUserInfoInterceptor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; + +/** + *

Web Mvc 配置

+ * */ +@Configuration +@SuppressWarnings("all") +public class DevProtocolWebMvcConfig extends WebMvcConfigurationSupport { + + /** + *

添加拦截器配置

+ * */ + @Override + protected void addInterceptors(InterceptorRegistry registry) { + + // 添加用户身份统一登录拦截的拦截器 + registry.addInterceptor(new LoginUserInfoInterceptor()) + .addPathPatterns("/**").order(0); + } + + /** + *

让 MVC 加载 Swagger 的静态资源

+ * */ + @Override + protected void addResourceHandlers(ResourceHandlerRegistry registry) { + + registry.addResourceHandler("/**"). + addResourceLocations("classpath:/static/"); + registry.addResourceHandler("swagger-ui.html") + .addResourceLocations("classpath:/META-INF/resources/"); + registry.addResourceHandler("doc.html") + .addResourceLocations("classpath:/META-INF/resources/"); + registry.addResourceHandler("/webjars/**") + .addResourceLocations("classpath:/META-INF/resources/webjars/"); + + super.addResourceHandlers(registry); + } +} diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-service-config/src/main/java/org/example/conf/SwaggerConfig.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-config/src/main/java/org/example/conf/SwaggerConfig.java new file mode 100644 index 0000000..6062f12 --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-config/src/main/java/org/example/conf/SwaggerConfig.java @@ -0,0 +1,56 @@ +package org.example.conf; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.Contact; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +/** + *

Swagger 配置类

+ * 原生: /swagger-ui.html + * 美化: /doc.html + * */ +@Configuration +@EnableSwagger2 +@SuppressWarnings("all") +public class SwaggerConfig { + + /** + *

Swagger 实例 Bean 是 Docket, 所以通过配置 Docket 实例来配置 Swagger

+ * */ + @Bean + public Docket docket() { + + return new Docket(DocumentationType.SWAGGER_2) + // 展示在 Swagger 页面上的自定义工程描述信息 + .apiInfo(apiInfo()) + // 选择展示哪些接口 + .select() + // 只有 org.example 包内的才去展示 + // fixme 这个可以后续进行把一些不展示的接口进行规避 + .apis(RequestHandlerSelectors.basePackage("org.example")) + .paths(PathSelectors.any()) + .build(); + } + + /** + *

Swagger 的描述信息

+ * */ + public ApiInfo apiInfo() { + + return new ApiInfoBuilder() + .title("dev-protocol-micro-service") + .description("dev-protocol-springcloud-project") + .contact(new Contact( + "q", "www.q.com", "q@by.com" + )) + .version("1.0") + .build(); + } +} diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-service-config/src/main/java/org/example/filter/AccessContext.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-config/src/main/java/org/example/filter/AccessContext.java new file mode 100644 index 0000000..9595eec --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-config/src/main/java/org/example/filter/AccessContext.java @@ -0,0 +1,25 @@ +package org.example.filter; + +import org.example.vo.LoginUserInfo; + +/** + *

使用 ThreadLocal 去单独存储每一个线程携带的 LoginUserInfo 信息

+ * 要及时的清理我们保存到 ThreadLocal 中的用户信息: + * 1. 保证没有资源泄露 + * 2. 保证线程在重用时, 不会出现数据混乱 + * */ +public class AccessContext { + private static final ThreadLocal loginUserInfo = new ThreadLocal<>(); + + public static LoginUserInfo getLoginUserInfo() { + return loginUserInfo.get(); + } + + public static void setLoginUserInfo(LoginUserInfo loginUserInfo_) { + loginUserInfo.set(loginUserInfo_); + } + + public static void clearLoginUserInfo() { + loginUserInfo.remove(); + } +} diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-service-config/src/main/java/org/example/filter/LoginUserInfoInterceptor.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-config/src/main/java/org/example/filter/LoginUserInfoInterceptor.java new file mode 100644 index 0000000..071b216 --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-config/src/main/java/org/example/filter/LoginUserInfoInterceptor.java @@ -0,0 +1,84 @@ +package org.example.filter; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.example.constant.CommonConstant; +import org.example.utils.TokenParseUtils; +import org.example.vo.LoginUserInfo; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + *

用户身份统一登录拦截

+ * */ +@SuppressWarnings("all") +@Slf4j +@Component +public class LoginUserInfoInterceptor implements HandlerInterceptor { + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, + Object handler) throws Exception { + + // 部分请求不需要带有身份信息, 即白名单 + if (checkWhiteListUrl(request.getRequestURI())) { + return true; + } + + // 先尝试从 http header 里面拿到 token + String token = request.getHeader(CommonConstant.JWT_USER_INFO_KEY); + + LoginUserInfo loginUserInfo = null; + try { + loginUserInfo = TokenParseUtils.parseUserInfoFromToken(token); + } catch (Exception ex) { + log.error("parse login user info error: [{}]", ex.getMessage(), ex); + } + + // 如果程序走到这里, 说明 header 中没有 token 信息 + if (null == loginUserInfo) { + throw new RuntimeException("can not parse current login user"); + } + + log.info("set login user info: [{}]", request.getRequestURI()); + // 设置当前请求上下文, 把用户信息填充进去 + AccessContext.setLoginUserInfo(loginUserInfo); + + return true; + } + + @Override + public void postHandle(HttpServletRequest request, HttpServletResponse response, + Object handler, ModelAndView modelAndView) throws Exception { + + } + + /** + *

在请求完全结束后调用, 常用于清理资源等工作

+ * */ + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, + Object handler, Exception ex) throws Exception { + + if (null != AccessContext.getLoginUserInfo()) { + AccessContext.clearLoginUserInfo(); + } + } + + /** + *

校验是否是白名单接口

+ * swagger2 接口 + * */ + private boolean checkWhiteListUrl(String url) { + + return StringUtils.containsAny( + url, + "springfox", "swagger", "v2", + "webjars", "doc.html" + ); + } +} diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-service-config/src/main/java/org/example/package-info.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-config/src/main/java/org/example/package-info.java new file mode 100644 index 0000000..3035e19 --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-config/src/main/java/org/example/package-info.java @@ -0,0 +1 @@ +package org.example; \ No newline at end of file diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-service-sdk/README.md b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-sdk/README.md new file mode 100644 index 0000000..a6bf9dd --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-sdk/README.md @@ -0,0 +1,6 @@ +# 介绍 +- 供数据传输的数据结构, 以及转换方式 +- 开发别的模块也要直接进行导入 + +# 已加入实体 +- 账号微服务 sdk \ No newline at end of file diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-service-sdk/pom.xml b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-sdk/pom.xml new file mode 100644 index 0000000..d4535ff --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-sdk/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + org.example + dev-protocol + 1.0-SNAPSHOT + ../../pom.xml + + + dev-protocol-springcloud-project-service-sdk + jar + + + dev-protocol-springcloud-project-service-sdk + 服务模块 SDK + + + 8 + 8 + UTF-8 + + + + + + io.springfox + springfox-swagger2 + 2.9.2 + + + io.springfox + springfox-swagger-ui + 2.9.2 + + + + com.github.xiaoymin + swagger-bootstrap-ui + 1.9.3 + + + + org.projectlombok + lombok + + + + \ No newline at end of file diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-service-sdk/src/main/java/org/example/account/AddressInfo.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-sdk/src/main/java/org/example/account/AddressInfo.java new file mode 100644 index 0000000..5a63aa7 --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-sdk/src/main/java/org/example/account/AddressInfo.java @@ -0,0 +1,80 @@ +package org.example.account; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; +import java.util.List; + +/** + *

用户地址信息

+ * */ +@ApiModel(description = "用户地址信息") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class AddressInfo { + + @ApiModelProperty(value = "地址所属用户 id") + private Long userId; + + @ApiModelProperty(value = "地址详细信息") + private List addressItems; + + /** + *

单个的地址信息

+ * */ + @ApiModel(description = "用户的单个地址信息") + @Data + @NoArgsConstructor + @AllArgsConstructor + public static class AddressItem { + + @ApiModelProperty(value = "地址表主键 id") + private Long id; + + @ApiModelProperty(value = "用户姓名") + private String username; + + @ApiModelProperty(value = "电话") + private String phone; + + @ApiModelProperty(value = "省") + private String province; + + @ApiModelProperty(value = "市") + private String city; + + @ApiModelProperty(value = "详细的地址") + private String addressDetail; + + @ApiModelProperty(value = "创建时间") + private Date createTime; + + @ApiModelProperty(value = "更新时间") + private Date updateTime; + + public AddressItem(Long id) { + this.id = id; + } + + /** + *

将 AddressItem 转换成 UserAddress

+ * */ + public UserAddress toUserAddress() { + + UserAddress userAddress = new UserAddress(); + + userAddress.setUsername(this.username); + userAddress.setPhone(this.phone); + userAddress.setProvince(this.province); + userAddress.setCity(this.city); + userAddress.setAddressDetail(this.addressDetail); + + return userAddress; + } + } +} diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-service-sdk/src/main/java/org/example/account/BalanceInfo.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-sdk/src/main/java/org/example/account/BalanceInfo.java new file mode 100644 index 0000000..f13cca1 --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-sdk/src/main/java/org/example/account/BalanceInfo.java @@ -0,0 +1,23 @@ +package org.example.account; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + *

用户账户余额信息

+ * */ +@ApiModel(description = "用户账户余额信息") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class BalanceInfo { + + @ApiModelProperty(value = "用户主键 id") + private Long userId; + + @ApiModelProperty(value = "用户账户余额") + private Long balance; +} diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-service-sdk/src/main/java/org/example/account/UserAddress.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-sdk/src/main/java/org/example/account/UserAddress.java new file mode 100644 index 0000000..38ec11e --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-sdk/src/main/java/org/example/account/UserAddress.java @@ -0,0 +1,32 @@ +package org.example.account; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + *

用户地址信息

+ * */ +@ApiModel(description = "用户地址信息") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class UserAddress { + + @ApiModelProperty(value = "用户姓名") + private String username; + + @ApiModelProperty(value = "电话") + private String phone; + + @ApiModelProperty(value = "省") + private String province; + + @ApiModelProperty(value = "市") + private String city; + + @ApiModelProperty(value = "详细的地址") + private String addressDetail; +} diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-service-sdk/src/main/java/org/example/common/TableId.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-sdk/src/main/java/org/example/common/TableId.java new file mode 100644 index 0000000..44ddf80 --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-sdk/src/main/java/org/example/common/TableId.java @@ -0,0 +1,32 @@ +package org.example.common; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + *

主键 ids

+ * */ +@ApiModel(description = "通用 id 对象") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class TableId { + + @ApiModelProperty(value = "数据表记录主键") + private List ids; + + @ApiModel(description = "数据表记录主键对象") + @Data + @NoArgsConstructor + @AllArgsConstructor + public static class Id { + + @ApiModelProperty(value = "数据表记录主键") + private Long id; + } +} diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-service-sdk/src/main/java/org/example/goods/DeductGoodsInventory.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-sdk/src/main/java/org/example/goods/DeductGoodsInventory.java new file mode 100644 index 0000000..8591790 --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-sdk/src/main/java/org/example/goods/DeductGoodsInventory.java @@ -0,0 +1,23 @@ +package org.example.goods; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + *

扣减商品库存

+ * */ +@ApiModel(description = "扣减商品库存对象") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class DeductGoodsInventory { + + @ApiModelProperty(value = "商品主键 id") + private Long goodsId; + + @ApiModelProperty(value = "扣减个数") + private Integer count; +} diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-service-sdk/src/main/java/org/example/goods/GoodsInfo.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-sdk/src/main/java/org/example/goods/GoodsInfo.java new file mode 100644 index 0000000..ae22694 --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-sdk/src/main/java/org/example/goods/GoodsInfo.java @@ -0,0 +1,80 @@ +package org.example.goods; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; + +/** + *

商品信息

+ * */ +@ApiModel(description = "详细的商品信息") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class GoodsInfo { + + @ApiModelProperty(value = "商品表主键 id") + private Long id; + + @ApiModelProperty(value = "商品类别编码") + private String goodsCategory; + + @ApiModelProperty(value = "品牌分类编码") + private String brandCategory; + + @ApiModelProperty(value = "商品名称") + private String goodsName; + + @ApiModelProperty(value = "商品图片") + private String goodsPic; + + @ApiModelProperty(value = "商品描述信息") + private String goodsDescription; + + @ApiModelProperty(value = "商品状态") + private Integer goodsStatus; + + @ApiModelProperty(value = "商品价格, 单位: 分") + private Integer price; + + @ApiModelProperty(value = "商品属性") + private GoodsProperty goodsProperty; + + @ApiModelProperty(value = "供应量") + private Long supply; + + @ApiModelProperty(value = "库存") + private Long inventory; + + @ApiModelProperty(value = "创建时间") + private Date createTime; + + @ApiModelProperty(value = "更新时间") + private Date updateTime; + + /** + *

商品属性

+ * */ + @ApiModel(description = "商品属性对象") + @Data + @NoArgsConstructor + @AllArgsConstructor + public static class GoodsProperty { + + @ApiModelProperty(value = "尺寸") + private String size; + + @ApiModelProperty(value = "颜色") + private String color; + + @ApiModelProperty(value = "材质") + private String material; + + @ApiModelProperty(value = "图案") + private String pattern; + } +} diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-service-sdk/src/main/java/org/example/goods/SimpleGoodsInfo.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-sdk/src/main/java/org/example/goods/SimpleGoodsInfo.java new file mode 100644 index 0000000..4a84b84 --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-sdk/src/main/java/org/example/goods/SimpleGoodsInfo.java @@ -0,0 +1,33 @@ +package org.example.goods; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + *

简单的商品信息: 封面

+ * */ +@ApiModel(description = "简单的商品信息") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class SimpleGoodsInfo { + + @ApiModelProperty(value = "商品表主键 id") + private Long id; + + @ApiModelProperty(value = "商品名称") + private String goodsName; + + @ApiModelProperty(value = "商品图片") + private String goodsPic; + + @ApiModelProperty(value = "商品价格, 单位: 分") + private Integer price; + + public SimpleGoodsInfo(Long id) { + this.id = id; + } +} diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-service-sdk/src/main/java/org/example/package-info.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-sdk/src/main/java/org/example/package-info.java new file mode 100644 index 0000000..3035e19 --- /dev/null +++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-service-sdk/src/main/java/org/example/package-info.java @@ -0,0 +1 @@ +package org.example; \ No newline at end of file diff --git a/pom.xml b/pom.xml index 076a458..da3e3d8 100644 --- a/pom.xml +++ b/pom.xml @@ -57,6 +57,9 @@ dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center dev-protocol-springcloud/dev-protocol-springcloud-project-mvc-config dev-protocol-springcloud/dev-protocol-springcloud-project-common + dev-protocol-springcloud/dev-protocol-springcloud-project-account-service + dev-protocol-springcloud/dev-protocol-springcloud-project-service-config + dev-protocol-springcloud/dev-protocol-springcloud-project-service-sdk