微服务/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;
}