Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

luolin2611/record-account

Repository files navigation

记账助手
创建时间:2022年09月30日 07:41:39
描 述:本次的Api是对之前的研发进行重构
技 术 栈:SpringBoot、SpringCloudAlibaba(Nacos)、Mybatis Plus(自动生成代码且预置了一些增删改查)

项目架构

微服务模块介绍

common 模块

common作为模块,不是微服务,其作用大致如下。

  • 作为一般微服务的父pom,因此该pom依赖了一些常用的依赖库,例如springcloud、springboot、lombok等。
  • 作为多个微服务共同使用的工具类、bean集合,例如 Response、Response.buildXXXX、ResStatusEnum等。
  • 一些框架工具也放在此模块中,比如鉴权的拦截工具类、自定义异常拦截等。

gateway-service

本服务作为所有微服务的网关服务。

user-service

用户微服务

record-account-service

记账微服务

微服务之间关系

系统设计

销户系统设计

销户一般请求的是用户微服务,进行删除账户,由于其它的微服务会涉及到存储用户的信息,因此也需要同步删除其他微服务的用户信息;

实现的方式有两种:

  • RPC
  • 消息队列 MQ

由于销户涉及的微服务比较多,使用RPC的方式明显薄弱,因此选择MQ是最佳的选择。

系统设计流程图

知识整理

mybatis-plus 使用技巧

官网首页

https://www.baomidou.com/

条件构造器

https://www.baomidou.com/pages/10c804/

数据库分表查询

题目

根据用户id查询当月下,收入和支出的账单金额。

题目解析

1.使用group by 对收入和支出进行分组。

2.使用date_format方法指定请求年月。

3.使用聚合函数sum求值。

java代码

DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMM", Locale.CHINA);
String format = dtf.format(LocalDate.now());
QueryWrapper<RecordAccountDO> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper.select("classify_type classifyType, SUM(bill_money) as money")
 .apply("date_format(record_time, '%Y%m') = {0}", format)
 .groupBy("classify_type");
List<Map<String, Object>> maps = recordAccountMapper.selectMaps(objectQueryWrapper);

等价于sql代码

SELECT classify_type classifyType, SUM(bill_money) as money FROM t_record_account WHERE (date_format(record_time, '%Y%m') = ?) GROUP BY classify_type

响应结果

image-20221006200328756

联表查询的另一种体现

记账(t_record_account)表中有一个字段分类ID(classifyId),分类表中(t_classify)表中有一个字段图标ID(icon_id)。

实现目的

通过t_record_account查询出记账列表 --> 通过 classifyId 查询对应分类 --> 通过 icon_id 查询对应的图标对象 --> 将icon对象放入,t_record_account。

java代码

private void handlerIcon(List<DayRecordAccountObject> dayRecordAccountObjects) {
 List<Long> classifyIds = dayRecordAccountObjects.stream()
 .map(DayRecordAccountObject::getClassifyId).collect(Collectors.toList());
 // 从分类列表中剥离iconId,然后通过iconId 查训icon对象
 List<ClassifyDO> classifyDOS = classifyMapper
 .selectList(new QueryWrapper<ClassifyDO>().in("classify_id", classifyIds));
 List<IconDO> iconDOS = iconMapper.selectList(new QueryWrapper<IconDO>()
 .in("icon_id", classifyDOS.stream().map(ClassifyDO::getIconId).collect(Collectors.toList())));
 // 剥离出 (classifyId, icon对象)
 List<Map<Long, Icon>> iconMaps = classifyDOS.stream().flatMap(classifyDO -> iconDOS.stream()
 .filter(iconDO -> !ObjectUtils.notEqual(iconDO.getIconId(), classifyDO.getIconId()))
 .map(iconDO -> {
 // map存放 (classifyId, Icon对象)
 Map<Long, Icon> map = new HashMap<>();
 map.put(classifyDO.getClassifyId(), CommonUtil.copyProperties(iconDO, new Icon()));
 return map;
 }))
 .collect(Collectors.toList());
 // 把icon对象塞入 DayRecordAccountObject 对象中
 dayRecordAccountObjects.forEach(dayRecordAccountObject -> {
 Optional<Icon> iconOptional = iconMaps.stream()
 .filter(iconMap -> ObjectUtils.isNotEmpty(iconMap.get(dayRecordAccountObject.getClassifyId())))
 .map(iconMap -> iconMap.get(dayRecordAccountObject.getClassifyId())).findAny();
 dayRecordAccountObject.setIcon(iconOptional.orElse(null));
 });
}

核心点

1.通过classifyId查询出来的icon对象,使用map对象将 key=classifyId,value=iconObject 进行存储。

2.通过dayRecordAccountObjects遍历出来的对象进行添加icon图标对象。

JWT 相关

jwt(Json Web Token):jwt用于web与后台交互进行校验的token。

jwt 构成

jwt由三部分构成,分别为头部、载荷、签名构成。三部分中间使用 "." 进行分隔。

JWTString=Base64(Header).Base64(Payload).HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)

代码示例

Java 代码

String compact = Jwts.builder().setSubject("User Login Jwt Token")
 .claim("userId", 45)
 .claim("userName", "rollin")
 .setIssuedAt(new Date())
 .setExpiration(new Date(System.currentTimeMillis() + 2000))
 .signWith(SignatureAlgorithm.HS256, "recordaccount.jwt.key")
 .compact();

运行结果

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJVc2VyIExvZ2luIEp3dCBUb2tlbiIsInVzZXJJZCI6NDUsInVzZXJOYW1lIjoicm9sbGluIiwiaWF0IjoxNjY1MTE4OTE5LCJleHAiOjE2NjUxMTg5MjF9.p8XdYXk3-tIudJP8kQTnoCuF90iLIshb-gpisKmlY5o

示例剖析

构成示例

Header 部分

源串
eyJhbGciOiJIUzI1NiJ9
decode 串
{"alg":"HS256"}

载荷部分

源串
eyJzdWIiOiJVc2VyIExvZ2luIEp3dCBUb2tlbiIsInVzZXJJZCI6NDUsInVzZXJOYW1lIjoicm9sbGluIiwiaWF0IjoxNjY1MTE4OTE5LCJleHAiOjE2NjUxMTg5MjF9
decode 串
{"sub":"User Login Jwt Token","userId":45,"userName":"rollin","iat":1665118919,"exp":1665118921}

Signature 签名部分

p8XdYXk3-tIudJP8kQTnoCuF90iLIshb-gpisKmlY5o

签名算法

HMACSHA256(
 base64UrlEncode(header) + "." + 
 base64UrlEncode(payload),
 secret
)

Java 基础知识使用技巧

在Java Bean中在Getter方法中使用技巧

代码

public class CartVO {
 /**
 * 购物项
 */
 private List<CartItemVO> cartItems;
 /**
 * 购买总件数
 */
 private Integer totalNum;
 /**
 * 购物车总价格
 */
 private BigDecimal totalPrice;
 /**
 * 购物车实际支付价格
 */
 private BigDecimal realPayPrice;
 /**
 * 总件数
 * @return
 */
 public Integer getTotalNum() {
 if(this.cartItems!=null){
 int total = cartItems.stream().mapToInt(CartItemVO::getBuyNum).sum();
 return total;
 }
 return 0;
 }
 /**
 * 总价格
 * @return
 */
 public BigDecimal getTotalPrice() {
 BigDecimal amount = new BigDecimal("0");
 if(this.cartItems!=null){
 for(CartItemVO cartItemVO : cartItems){
 BigDecimal itemTotalAmount = cartItemVO.getTotalAmount();
 amount = amount.add(itemTotalAmount);
 }
 }
 return amount;
 }
 /**
 * 购物车里面实际支付的价格
 * @return
 */
 public BigDecimal getRealPayPrice() {
 BigDecimal amount = new BigDecimal("0");
 if(this.cartItems!=null){
 for(CartItemVO cartItemVO : cartItems){
 BigDecimal itemTotalAmount = cartItemVO.getTotalAmount();
 amount = amount.add(itemTotalAmount);
 }
 }
 return amount;
 }
 public List<CartItemVO> getCartItems() {
 return cartItems;
 }
 public void setCartItems(List<CartItemVO> cartItems) {
 this.cartItems = cartItems;
 }
}

解析

通过自定义Getter方法,将List中的内容进行归并计算;例如通过cartItems 计算出 totalNum、 totalPrice、 realPayPrice。

Swagger集成

原生集成

添加依赖

<dependency>
 <groupId>io.springfox</groupId>
 <artifactId>springfox-boot-starter</artifactId>
 <version>3.0.0</version>
</dependency>

访问地址

http://localhost:10012/swagger-ui/index.html

集成knife4j

添加依赖

<dependency>
 <groupId>com.github.xiaoymin</groupId>
 <artifactId>knife4j-spring-boot-starter</artifactId>
 <version>3.0.3</version>
</dependency>

访问地址

http://localhost:10012/doc.html

错误整理

idea 注入mapper报错报红

报错示例

image-20221003103412593

解决方案

方案一 (推荐)

将 @Autowird 改为 @Resource 即可。

方案二

为 @Autowired 注解设置required = false;他表达的意思是:使用 @Autowired 注解不再去校验userMapper是否存在了,也就不会有警告了

方案三

把IDEA的警告关闭掉;不建议使用,因为Idea的灵魂就是提示,去掉了就没多大灵魂所在。

image-20221003104216034

参考地址

https://blog.csdn.net/JFENG14/article/details/123281224

About

记账项目Api接口管理

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

AltStyle によって変換されたページ (->オリジナル) /