【个人笔记】微服务项目结构

微服务/src/main/java/com.xxx.xxx下面,项目结构往往大同小异,下面逐个进行说明

1.基础层

controller、service、mapper、domain.po,这四个在设计好数据库表结构后,可以由MP直接生成。

生成步骤:

  • 顶部菜单栏点Other→Config databas,连接数据库(先安装好MP插件)

  • 顶部菜单栏点Other→Code Geterator,设置配置项,然后生成

注意事项:

  • 根据需要,修改Controller层的路径为restful风格

  • 根据需要,修改Pojo类主键属性的自增策略

2.Constant (常量)

还有其他别名如:enums等

常量类定义:(如何根据sql表定义,以后再研究)

  • LessonStatus

  • PlanStatus

注意事项:

  • 注意修改pojo类中对应的字段

 private Integer status; //课程状态,0-未学习,1-学习中,2-已学完,3-已失效
 private Integer planStatus; //学习计划状态,0-没有计划,1-计划进行中

 private LessonStatus lessonStatus;
 private PlanStatus planStatus;

3.mq(监听器)

关于mq的使用,一般分为三个步骤:发送、接收、处理,案例如下:

1. 发送

 private final RabbitMqHelper rabbitMqHelper; //自定义的工具类,封装的rabbitTemplate
 ​
 public PlaceOrderResultVO enrolledFreeCourse(Long courseId) {
     //订单逻辑代码
     ........
     //发送MQ消息,添加到我的课表
     rabbitMqHelper.send(
         MqConstants.Exchange.ORDER_EXCHANGE, //交换机:order.topic
         MqConstants.Key.ORDER_PAY_KEY,       //RoutingKey:order.pay  
         OrderBasicDTO.builder()
         .orderId(orderId)
         .userId(userId)
         .courseIds(cIds)
         .finishTime(order.getFinishTime())
         .build()
 );
 

2. 接收:定义监听器

 package com.tianji.learning.mq;
 ​
 import com.tianji.api.dto.trade.OrderBasicDTO;
 import com.tianji.common.constants.MqConstants;
 import com.tianji.common.utils.CollUtils;
 import com.tianji.learning.service.ILearningLessonService;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.amqp.core.ExchangeTypes;
 import org.springframework.amqp.rabbit.annotation.Exchange;
 import org.springframework.amqp.rabbit.annotation.Queue;
 import org.springframework.amqp.rabbit.annotation.QueueBinding;
 import org.springframework.amqp.rabbit.annotation.RabbitListener;
 import org.springframework.stereotype.Component;
 ​
 @Slf4j
 @Component
 @RequiredArgsConstructor
 public class LessonChangeListener {
 ​
     private final ILearningLessonService lessonService;
 ​
     /**
      * 监听订单支付或课程报名的消息
      * @param order 订单信息
      */
     @RabbitListener(bindings = @QueueBinding(
             value = @Queue(value = "learning.lesson.pay.queue", durable = "true"),
             exchange = @Exchange(name = MqConstants.Exchange.ORDER_EXCHANGE, type = ExchangeTypes.TOPIC),
             key = MqConstants.Key.ORDER_PAY_KEY
     ))
     public void listenLessonPay(OrderBasicDTO order){
         // 1.健壮性处理
         if(order == null || order.getUserId() == null || CollUtils.isEmpty(order.getCourseIds())){
             // 数据有误,无需处理
             log.error("接收到MQ消息有误,订单数据为空");
             return;
         }
         // 2.添加课程
         log.debug("监听到用户{}的订单{},需要添加课程{}到课表中", order.getUserId(), order.getOrderId(), order.getCourseIds());
         lessonService.addUserLessons(order.getUserId(), order.getCourseIds());
     }
 }

3. 处理:多涉及调用接口

 package com.tianji.learning.service.impl;
 ​
 // 略
 ​
 @SuppressWarnings("ALL")
 @Service
 @RequiredArgsConstructor
 @Slf4j
 public class LearningLessonServiceImpl extends ServiceImpl<LearningLessonMapper, LearningLesson> implements ILearningLessonService {
 ​
     private final CourseClient courseClient;
 ​
     @Override
     @Transactional
     public void addUserLessons(Long userId, List<Long> courseIds) {
         // 1.查询课程有效期
         List<CourseSimpleInfoDTO> cInfoList = courseClient.getSimpleInfoList(courseIds);
         if (CollUtils.isEmpty(cInfoList)) {
             // 课程不存在,无法添加
             log.error("课程信息不存在,无法添加到课表");
             return;
         }
         // 2.循环遍历,处理LearningLesson数据
         List<LearningLesson> list = new ArrayList<>(cInfoList.size());
         for (CourseSimpleInfoDTO cInfo : cInfoList) {
             LearningLesson lesson = new LearningLesson();
             // 2.1.获取过期时间
             Integer validDuration = cInfo.getValidDuration();
             if (validDuration != null && validDuration > 0) {
                 LocalDateTime now = LocalDateTime.now();
                 lesson.setCreateTime(now);
                 lesson.setExpireTime(now.plusMonths(validDuration));
             }
             // 2.2.填充userId和courseId
             lesson.setUserId(userId);
             lesson.setCourseId(cInfo.getId());
             list.add(lesson);
         }
         // 3.批量新增
         saveBatch(list);
     }
 }
 

4.Domain

下面包含:po、vo、dto、query等,又因为设计不同,有些通用类会封装在通用模块common中。下面以分页查询为例,介绍位置

  • po:MP自动根据sql表生成

  • vo:数据展示,根据前端需要的数据进行设计,包含一些原型图需要,但没体现的数据,如各种id

  • dto:PageDTO,由于分页结果常用,所以定义在common中,定义了3个字段:1总条数、2总页数、3当前页数据list

  • query:PageQuery,通用,同样定义在common中,定义了:

    • 4个字段,pageNo、pageSize、isAsc、sortBy(排序字段)

    • 便捷方法toMpPage,可以把PageQuery对象转换为MybatisPlus中的Page对象

 
@RestController
 @RequestMapping("/lessons")
 @RequiredArgsConstructor
 public class LearningLessonController {
     
     private final ILearningLessonService lessonService;
 ​
     @GetMapping("/page")
     public PageDTO<LearningLessonVO> queryMyLessons(PageQuery query) {
         return lessonService.queryMyLessons(query);
     }
 }
 @Override
 public PageDTO<LearningLessonVO> queryMyLessons(PageQuery query) {
     // 1.获取当前登录用户
     Long userId = UserContext.getUser();
     // 2.分页查询
     // select * from learning_lesson where user_id = #{userId} order by latest_learn_time limit 0, 5
     Page<LearningLesson> page = lambdaQuery()
             .eq(LearningLesson::getUserId, userId) // where user_id = #{userId}
             .page(query.toMpPage("latest_learn_time", false));
     List<LearningLesson> records = page.getRecords();
     if (CollUtils.isEmpty(records)) {
         return PageDTO.empty(page);
     }
     // 3.查询课程信息
     Map<Long, CourseSimpleInfoDTO> cMap = queryCourseSimpleInfoList(records);
 ​
     // 4.封装VO返回
     List<LearningLessonVO> list = new ArrayList<>(records.size());
     // 4.1.循环遍历,把LearningLesson转为VO
     for (LearningLesson r : records) {
         // 4.2.拷贝基础属性到vo
         LearningLessonVO vo = BeanUtils.copyBean(r, LearningLessonVO.class);
         // 4.3.获取课程信息,填充到vo
         CourseSimpleInfoDTO cInfo = cMap.get(r.getCourseId());
         vo.setCourseName(cInfo.getName());
         vo.setCourseCoverUrl(cInfo.getCoverUrl());
         vo.setSections(cInfo.getSectionNum());
         list.add(vo);
     }
     return PageDTO.of(page, list);
 }
 ​
 private Map<Long, CourseSimpleInfoDTO> queryCourseSimpleInfoList(List<LearningLesson> records) {
     // 3.1.获取课程id
     Set<Long> cIds = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toSet());
     // 3.2.查询课程信息
     List<CourseSimpleInfoDTO> cInfoList = courseClient.getSimpleInfoList(cIds);
     if (CollUtils.isEmpty(cInfoList)) {
         // 课程不存在,无法添加
         throw new BadRequestException("课程信息不存在!");
     }
     // 3.3.把课程集合处理成Map,key是courseId,值是course本身
     Map<Long, CourseSimpleInfoDTO> cMap = cInfoList.stream()
             .collect(Collectors.toMap(CourseSimpleInfoDTO::getId, c -> c));
     return cMap;
 }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值