微服务实战项目 —— 知识分享应用(二)

微服务实战项目 —— 知识分享应用(一)

使用 IDEA 自带的 HttpClient 记住登录信息

修改 http 登录接口测试代码,在 /login 登录接口响应成功后,将 token 存入 client.global 全局,然后就可以在 /client 接口请求中使用 {{ token }} 的形式使用它。
在这里插入图片描述

添加日志拦截器,统一打印日志流水号

share-common 模块新建 interceptor 子包,新建 Loginterceptor 日志拦截器。aspect 中的 LogAspect 日志切面类,注释掉 MDC 记录日志流水号的代码

在这里插入图片描述

share-user 模块的 config 包,新建 SpringMvcConfig.java 类,配置拦截器

在这里插入图片描述

重启,http 测试,发现日志流水号通过拦截器已经生效

在这里插入图片描述

如果我们注释掉拦截器配置类头部的 Configuration 注解,再次测试,会发现日志流水号就没有了,从而验证了拦截器的作用

在这里插入图片描述

提交:增加日志拦截器,统一打印日志流水号

增加 share-content 模块

新建 share-content 子模块,添加 share-common 依赖,数据库驱动依赖以及mybatis plus 依赖。仿照 user 模块实现启动类。

复制 user 模块 resource 目录下的 yml 和 xml 文件,修改部分内容如下:

在这里插入图片描述

新建 content_center 数据库,建表脚本如下:

/*
 Navicat Premium Data Transfer

 Source Server         : localhost
 Source Server Type    : MySQL
 Source Server Version : 80033 (8.0.33)
 Source Host           : localhost:3306
 Source Schema         : content_center

 Target Server Type    : MySQL
 Target Server Version : 80033 (8.0.33)
 File Encoding         : 65001

 Date: 08/10/2023 14:00:33
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for notice
-- ----------------------------
DROP TABLE IF EXISTS `notice`;
CREATE TABLE `notice` (
                          `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',
                          `content` varchar(255) NOT NULL DEFAULT '' COMMENT '内容',
                          `show_flag` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否显示 0:否 1:是',
                          `create_time` datetime NOT NULL COMMENT '创建时间',
                          PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

-- ----------------------------
-- Records of notice
-- ----------------------------
BEGIN;
INSERT INTO `notice` (`id`, `content`, `show_flag`, `create_time`) VALUES (1, '程序员的启蒙课', 1, '2023-09-29 12:36:49');
INSERT INTO `notice` (`id`, `content`, `show_flag`, `create_time`) VALUES (2, '终身学习者', 0, '2023-10-04 11:16:18');
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

新建 domain/entity 包,新建 Notice.java 实体类

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Notice {
    private Long id;
    private String content;
    private Boolean showFlag;
    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;
}

新建 mapper 包,新建 NoticeMapper.java 并实现。

新建 service 包,新建 NoticeService.java 并实现获取最新通知方法

@Service
public class NoticeService {
    @Resource
    private NoticeMapper noticeMapper;

    public Notice getLatestNotice() {
        List<Notice> noticeList = noticeMapper.selectList(new LambdaQueryWrapper<Notice>()
                                                          .eq(Notice::getShowFlag, 1)
                                                          .orderByDesc(Notice::getId));
        return noticeList.get(0);
    }
}

新建 controller 包,新建 ShareController.java 并完成获取最新通知的接口

@Slf4j
@RestController
@RequestMapping("share")
public class ShareController {
    @Resource
    private NoticeService noticeService;

    @GetMapping("notice")
    public CommonResp<Notice> getLatestNotice() {
        return CommonResp.success(noticeService.getLatestNotice());
    }
}

在 网关 模块的配置中,新增content模块的路由配置,仿照user完成。

在 网关 模块的路由白名单配置中,放行 /share/notice 接口

在根目录 http 目录下,新建 content-test.http 文件,测试接口

在这里插入图片描述

提交:新增 share-content 模块,编写获取最新公告接口

首页列表接口

content_center 新建 share 表

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for share
-- ----------------------------
DROP TABLE IF EXISTS `share`;
CREATE TABLE `share` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',
  `user_id` bigint NOT NULL DEFAULT '0' COMMENT '发布人id',
  `title` varchar(80) NOT NULL DEFAULT '' COMMENT '标题',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` datetime NOT NULL COMMENT '修改时间',
  `is_original` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否原创 0:否 1:是',
  `author` varchar(45) NOT NULL DEFAULT '' COMMENT '作者',
  `cover` varchar(256) NOT NULL DEFAULT '' COMMENT '封面',
  `summary` varchar(256) NOT NULL DEFAULT '' COMMENT '概要信息',
  `price` int NOT NULL DEFAULT '0' COMMENT '价格(需要的积分)',
  `download_url` varchar(256) NOT NULL DEFAULT '' COMMENT '下载地址',
  `buy_count` int NOT NULL DEFAULT '0' COMMENT '下载数 ',
  `show_flag` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否显示 0:否 1:是',
  `audit_status` varchar(10) NOT NULL DEFAULT '0' COMMENT '审核状态 NOT_YET: 待审核 PASSED:审核通过 REJECTED:审核不通过',
  `reason` varchar(200) DEFAULT '' COMMENT '审核不通过原因',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=135 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='分享表';

-- ----------------------------
-- Records of share
-- ----------------------------
BEGIN;
INSERT INTO `share` (`id`, `user_id`, `title`, `create_time`, `update_time`, `is_original`, `author`, `cover`, `summary`, `price`, `download_url`, `buy_count`, `show_flag`, `audit_status`, `reason`) VALUES (1, 2, 'Vue.js 核心技术', '2023-10-18 11:41:05', '2023-10-18 11:41:05', 0, 'Brian', 'https://img3.sycdn.imooc.com/5c21e60d0822d58e06000338-360-202.jpg', '了解vue 的核心技术,建立前端组件化的思想', 10, '链接: https://pan.baidu.com/s/1Hk9i9VOTz2eSuy8p-kFGrQ  密码: 5njn', 0, 1, 'PASS', '通过审核');
INSERT INTO `share` (`id`, `user_id`, `title`, `create_time`, `update_time`, `is_original`, `author`, `cover`, `summary`, `price`, `download_url`, `buy_count`, `show_flag`, `audit_status`, `reason`) VALUES (2, 1, 'Go语言从入门到达人之路', '2023-10-15 17:02:06', '2023-10-15 17:02:08', 0, 'Google工程师', 'https://img1.sycdn.imooc.com/szimg/5cf47ccf0834940306000338-240-180.jpg', '从0开始搭建分布式爬虫,理解分布式系统设计思想\n从0开始搭建分布式爬虫,理解分布式系统设计思想\n从0开始搭建分布式爬虫,理解分布式系统设计思想\n从0开始搭建分布式爬虫,理解分布式系统设计思想\n', 10, '链接: https://pan.baidu.com/s/1Hk9i9VOTz2eSuy8p-kFGrQ  密码: 5njn', 0, 1, 'PASS', '通过审核');
INSERT INTO `share` (`id`, `user_id`, `title`, `create_time`, `update_time`, `is_original`, `author`, `cover`, `summary`, `price`, `download_url`, `buy_count`, `show_flag`, `audit_status`, `reason`) VALUES (3, 1, 'Spring Cloud分布式微服务实战', '2023-10-15 16:00:23', '2023-10-15 16:00:26', 0, '风间影月', 'https://img3.mukewang.com/szimg/5f583a2609dc33b412000676-360-202.png', 'Spring Cloud分布式微服务实战\n养成应对复杂业务的综合技术能力\n分布式/前后端分离/项目分层聚合 一体化开发门户平台+媒体中心+运营中心3大业务平台', 10, '链接: https://pan.baidu.com/s/1Hk9i9VOTz2eSuy8p-kFGrQ  密码: 5njn', 0, 1, 'PASS', '通过审核');
INSERT INTO `share` (`id`, `user_id`, `title`, `create_time`, `update_time`, `is_original`, `author`, `cover`, `summary`, `price`, `download_url`, `buy_count`, `show_flag`, `audit_status`, `reason`) VALUES (4, 1, '算法与数据结构', '2023-10-01 17:19:56', '2023-10-01 17:19:58', 0, '算法爱好者', 'https://niit-soft.oss-cn-hangzhou.aliyuncs.com/share-app/算法与数据结构.jpg', '数据结构是一种具有一定逻辑关系,在计算机中应用某种存储结构,并且封装了相应操作的数据元素集合。它包含三方面的内容,逻辑关系、存储关系及操作。', 5, '链接: https://pan.baidu.com/s/1Hk9i9VOTz2eSuy8p-kFGrQ  密码: 5njn', 20, 1, 'PASS', '通过审核');
INSERT INTO `share` (`id`, `user_id`, `title`, `create_time`, `update_time`, `is_original`, `author`, `cover`, `summary`, `price`, `download_url`, `buy_count`, `show_flag`, `audit_status`, `reason`) VALUES (5, 1, '微信小程序实战', '2023-10-01 17:19:56', '2023-10-01 17:19:58', 0, 'Tencent', 'https://niit-soft.oss-cn-hangzhou.aliyuncs.com/share-app/微信小程序.jpg', '小程序是一种新的开放能力,开发者可以快速地开发一个小程序。小程序可以在微信内被便捷地获取和传播,同时具有出色的使用体验。', 10, '链接: https://pan.baidu.com/s/1Hk9i9VOTz2eSuy8p-kFGrQ  密码: 5njn', 20, 1, 'PASS', '通过审核');
INSERT INTO `share` (`id`, `user_id`, `title`, `create_time`, `update_time`, `is_original`, `author`, `cover`, `summary`, `price`, `download_url`, `buy_count`, `show_flag`, `audit_status`, `reason`) VALUES (6, 1, '消息队列rabbitMQ', '2023-10-01 17:19:56', '2023-10-01 17:19:58', 0, 'MQ社区', 'https://niit-soft.oss-cn-hangzhou.aliyuncs.com/share-app/rabbitMQ.jpg', '消息(Message)是指在应用间传送的数据。消息可以非常简单,比如只包含文本字符串,也可以更复杂,可能包含嵌入对象。', 15, '链接: https://pan.baidu.com/s/1Hk9i9VOTz2eSuy8p-kFGrQ  密码: 5njn', 20, 1, 'PASS', '通过审核');
INSERT INTO `share` (`id`, `user_id`, `title`, `create_time`, `update_time`, `is_original`, `author`, `cover`, `summary`, `price`, `download_url`, `buy_count`, `show_flag`, `audit_status`, `reason`) VALUES (7, 1, 'Vue.js中文文档', '2023-10-01 17:19:56', '2023-10-01 17:19:58', 0, '尤雨溪', 'https://niit-soft.oss-cn-hangzhou.aliyuncs.com/share-app/Vue.jpg', 'Vue是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。', 10, '链接: https://pan.baidu.com/s/1Hk9i9VOTz2eSuy8p-kFGrQ  密码: 5njn', 20, 1, 'PASS', '通过审核');
INSERT INTO `share` (`id`, `user_id`, `title`, `create_time`, `update_time`, `is_original`, `author`, `cover`, `summary`, `price`, `download_url`, `buy_count`, `show_flag`, `audit_status`, `reason`) VALUES (8, 1, 'Spring Cloud Alibaba实战', '2023-10-01 17:19:56', '2023-10-01 17:19:58', 0, '阿里巴巴', 'https://niit-soft.oss-cn-hangzhou.aliyuncs.com/share-app/Spring Cloud Alibaba.jpg', 'Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。', 10, '链接: https://pan.baidu.com/s/1Hk9i9VOTz2eSuy8p-kFGrQ  密码: 5njn', 20, 1, 'PASS', '通过审核');
INSERT INTO `share` (`id`, `user_id`, `title`, `create_time`, `update_time`, `is_original`, `author`, `cover`, `summary`, `price`, `download_url`, `buy_count`, `show_flag`, `audit_status`, `reason`) VALUES (9, 1, 'Python学习笔记', '2023-10-01 17:19:56', '2023-10-01 17:19:58', 1, 'Python之父', 'https://niit-soft.oss-cn-hangzhou.aliyuncs.com/share-app/Python.jpg', 'Python 是一种解释型、面向对象、动态数据类型的高级程序设计语言。Python 由 Guido van Rossum 于 1989 年底发明,第一个公开发行版发行于 1991 年。', 5, '链接: https://pan.baidu.com/s/1Hk9i9VOTz2eSuy8p-kFGrQ  密码: 5njn', 20, 1, 'PASS', '通过审核');
INSERT INTO `share` (`id`, `user_id`, `title`, `create_time`, `update_time`, `is_original`, `author`, `cover`, `summary`, `price`, `download_url`, `buy_count`, `show_flag`, `audit_status`, `reason`) VALUES (10, 2, 'Linux学习笔记', '2023-10-01 17:19:56', '2023-10-01 17:19:58', 1, 'Linux之父', 'https://niit-soft.oss-cn-hangzhou.aliyuncs.com/share-app/Linux.jpg', '一提到 Linux,许多人都会说到“自由”,但我不认为他们都知道“自由”的真正涵义。“自由”是一种权力, 它决定你的计算机能做什么,同时能够拥有这种“自由”的唯一方式就是知道计算机正在做什么。 “自由”是指一台没有任何秘密的计算机,你可以从它那里了解一切,只要你用心的去寻找。', 10, '链接: https://pan.baidu.com/s/1Hk9i9VOTz2eSuy8p-kFGrQ  密码: 5njn', 20, 1, 'PASS', '通过审核');
INSERT INTO `share` (`id`, `user_id`, `title`, `create_time`, `update_time`, `is_original`, `author`, `cover`, `summary`, `price`, `download_url`, `buy_count`, `show_flag`, `audit_status`, `reason`) VALUES (11, 2, 'JavaScript学习笔记', '2023-10-01 17:19:56', '2023-10-01 17:19:58', 1, 'JS大神', 'https://niit-soft.oss-cn-hangzhou.aliyuncs.com/share-app/JavaScript.jpg', 'JavaScript ( JS ) 是一种具有函数优先的轻量级,解释型或即时编译型的编程语言。', 10, '链接: https://pan.baidu.com/s/1Hk9i9VOTz2eSuy8p-kFGrQ  密码: 5njn', 20, 1, 'PASS', '通过审核');
INSERT INTO `share` (`id`, `user_id`, `title`, `create_time`, `update_time`, `is_original`, `author`, `cover`, `summary`, `price`, `download_url`, `buy_count`, `show_flag`, `audit_status`, `reason`) VALUES (12, 2, 'Java并发编程实战', '2023-09-29 16:14:21', '2023-09-29 16:14:24', 0, 'Java大神', 'https://coding.imooc.com/static/module/class/content/img/132/section2-1.png', '对于一个Java 程序员而言,能否熟练掌握并发编程是判断他优秀与否的重要标准之一。因为并发编程是Java 语言中最为晦涩的知识点,它涉及操作系统、内存、CPU、编程语言等多方面的基础能力,更为考验一个程序员的内功。', 10, '链接: https://pan.baidu.com/s/1Hk9i9VOTz2eSuy8p-kFGrQ  密码: 5njn', 20, 1, 'PASS', '通过审核');
INSERT INTO `share` (`id`, `user_id`, `title`, `create_time`, `update_time`, `is_original`, `author`, `cover`, `summary`, `price`, `download_url`, `buy_count`, `show_flag`, `audit_status`, `reason`) VALUES (13, 1, '微服务技术', '2023-09-29 12:35:16', '2023-09-29 12:35:20', 1, '佚名', 'https://niit-soft.oss-cn-hangzhou.aliyuncs.com/share-app/Spring%20Cloud%20Alibaba.jpg', 'Spring 要理解微服务,首先要先理解不是微服务的那些。通常跟微服务相对的是单体应用,即将所有功能都打包成在一个独立单元的应用程序。从单体应用到微服务并不是一蹴而就的,这是一个逐渐演变的过程。', 20, '链接: https://pan.baidu.com/s/1Hk9i9VOTz2eSuy8p-kFGrQ  密码: 5njn', 30, 0, 'PASS', '通过审核');
INSERT INTO `share` (`id`, `user_id`, `title`, `create_time`, `update_time`, `is_original`, `author`, `cover`, `summary`, `price`, `download_url`, `buy_count`, `show_flag`, `audit_status`, `reason`) VALUES (14, 2, '计算机基础', '2023-10-17 20:02:37', '2023-10-17 20:02:37', 1, 'mqxu', 'https://img2.sycdn.imooc.com/szimg/5b3082da0001d7e905400300-360-202.jpg', '计算机基础知识大全', 5, '链接: https://pan.baidu.com/s/1Hk9i9VOTz2eSuy8p-kFGrQ  密码: 5njn', 5, 0, 'NOT_YET', '');
INSERT INTO `share` (`id`, `user_id`, `title`, `create_time`, `update_time`, `is_original`, `author`, `cover`, `summary`, `price`, `download_url`, `buy_count`, `show_flag`, `audit_status`, `reason`) VALUES (15, 2, '测试资源', '2023-10-17 20:42:13', '2023-10-17 20:42:13', 1, '陶然然', 'https://img3.sycdn.imooc.com/szimg/5b3082da0001d7e905400300-360-202.jpg', '测试资源', 10, '链接: https://pan.baidu.com/s/1Hk9i9VOTz2eSuy8p-kFGrQ  密码: 5njn', 0, 0, 'NOT_YET', '');
INSERT INTO `share` (`id`, `user_id`, `title`, `create_time`, `update_time`, `is_original`, `author`, `cover`, `summary`, `price`, `download_url`, `buy_count`, `show_flag`, `audit_status`, `reason`) VALUES (16, 2, 'HTTP协议', '2023-10-18 00:07:16', '2023-10-18 00:07:16', 0, 'mqxu', 'https://img2.sycdn.imooc.com/szimg/5d1032ab08719e0906000338-360-202.jpg', 'HTTP协议相关知识', 5, '链接: https://pan.baidu.com/s/1Hk9i9VOTz2eSuy8p-kFGrQ  密码: 5njn', 0, 0, 'PASS', '通过审核');
INSERT INTO `share` (`id`, `user_id`, `title`, `create_time`, `update_time`, `is_original`, `author`, `cover`, `summary`, `price`, `download_url`, `buy_count`, `show_flag`, `audit_status`, `reason`) VALUES (17, 2, 'JavaScript中的算法', '2023-10-18 10:59:09', '2023-10-18 10:59:09', 0, 'Lewis', 'https://img4.sycdn.imooc.com/szimg/5edf24fd081fde7906000338-360-202.jpg', '用JS语言解决LeetCode上的经典算法题,对每一道题都进行线上测试,每题都有时间/空间复杂度分析。结合前端实际开发情景,带你掌握数据结构与算法。', 15, '链接: https://pan.baidu.com/s/1Hk9i9VOTz2eSuy8p-kFGrQ  密码: 5njn', 0, 0, 'NOT_YET', '');
INSERT INTO `share` (`id`, `user_id`, `title`, `create_time`, `update_time`, `is_original`, `author`, `cover`, `summary`, `price`, `download_url`, `buy_count`, `show_flag`, `audit_status`, `reason`) VALUES (18, 2, 'Node.js调试入门', '2023-10-18 11:38:10', '2023-10-18 11:38:10', 1, 'Lewis', 'https://img3.sycdn.imooc.com/5c3eaa0a08d7052706000338-360-202.jpg', '学会高效调试 Node.js 会让日常开发更高效', 10, '链接: https://pan.baidu.com/s/1Hk9i9VOTz2eSuy8p-kFGrQ  密码: 5njn', 0, 0, 'PASS', '通过审核');
INSERT INTO `share` (`id`, `user_id`, `title`, `create_time`, `update_time`, `is_original`, `author`, `cover`, `summary`, `price`, `download_url`, `buy_count`, `show_flag`, `audit_status`, `reason`) VALUES (19, 2, '软件测试进阶之路', '2023-10-18 11:39:58', '2023-10-18 11:39:58', 1, '风落几番', 'https://img3.sycdn.imooc.com/5c60f2e80984689c05400300-360-202.png', '新时代软件测试人员的职责、功能测试、性能测试等知识,让测试人员能更好的掌握学习路线。', 10, '链接: https://pan.baidu.com/s/1Hk9i9VOTz2eSuy8p-kFGrQ  密码: 5njn', 0, 0, 'NOT_YET', '');
INSERT INTO `share` (`id`, `user_id`, `title`, `create_time`, `update_time`, `is_original`, `author`, `cover`, `summary`, `price`, `download_url`, `buy_count`, `show_flag`, `audit_status`, `reason`) VALUES (20, 2, 'Spring Cloud Alibaba', '2023-10-18 16:20:34', '2023-10-18 16:20:34', 0, '阿里巴巴', 'https://img2.sycdn.imooc.com/szimg/5b3082da0001d7e905400300-360-202.jpg', '微服务全家桶系列组件', 20, '链接: https://pan.baidu.com/s/1Hk9i9VOTz2eSuy8p-kFGrQ  密码: 5njn', 0, 0, 'PASS', '通过审核');
INSERT INTO `share` (`id`, `user_id`, `title`, `create_time`, `update_time`, `is_original`, `author`, `cover`, `summary`, `price`, `download_url`, `buy_count`, `show_flag`, `audit_status`, `reason`) VALUES (21, 2, 'Docker 系统性入门+进阶实践', '2023-09-26 14:39:14', '2023-09-26 14:39:14', 1, 'ssy', 'https://img3.sycdn.imooc.com/szimg/60cc058408c34ece12000676-360-202.jpg', '近年来,容器技术在互联网行业大火,特别是在开发和运维方向,极大地解决了规模化和灵活化部署的问题。', 10, '链接: https://pan.baidu.com/s/1Hk9i9VOTz2eSuy8p-kFGrQ  密码: 5njn', 0, 0, 'PASS', '通过审核');
INSERT INTO `share` (`id`, `user_id`, `title`, `create_time`, `update_time`, `is_original`, `author`, `cover`, `summary`, `price`, `download_url`, `buy_count`, `show_flag`, `audit_status`, `reason`) VALUES (22, 2, 'ChatGPT 辅助开发 Vue3 项目', '2023-09-26 14:48:04', '2023-09-26 14:48:04', 1, '双越', 'https://img3.sycdn.imooc.com/szimg/64b0cc4f09a30aa812000676-360-202.png', '带你使用 ChatGPT + Copilot 等 AI 工具,从 0 到 1 开发一个 Vue3 仿简书项目,体验以 3 倍速度完成项目的需求、设计、开发、优化和测试,全面碾压“人肉”开发。让你轻松掌握开发工作提效方法,快速提升职场竞争力。', 10, '链接: https://pan.baidu.com/s/1Hk9i9VOTz2eSuy8p-kFGrQ  密码: 5njn', 0, 0, 'PASS', '通过审核');
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

mid_user_share 表

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for mid_user_share
-- ----------------------------
DROP TABLE IF EXISTS `mid_user_share`;
CREATE TABLE `mid_user_share` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `share_id` bigint NOT NULL COMMENT 'share.id',
  `user_id` bigint NOT NULL COMMENT 'user.id',
  PRIMARY KEY (`id`),
  KEY `fk_mid_user_share_share1_idx` (`share_id`),
  KEY `fk_mid_user_share_user1_idx` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户-分享中间表【描述用户购买的分享】';

-- ----------------------------
-- Records of mid_user_share
-- ----------------------------
BEGIN;
INSERT INTO `mid_user_share` (`id`, `share_id`, `user_id`) VALUES (1, 1, 1);
INSERT INTO `mid_user_share` (`id`, `share_id`, `user_id`) VALUES (2, 12, 1);
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

entity 新建两个表对应的实体类以及mapper

service 包新建 ShareService ,编写查询列表数据方法

@Slf4j
@Service
public class ShareService {

    @Resource
    private ShareMapper shareMapper;
    @Resource
    private MidUserShareMapper midUserShareMapper;

    /**
     * 查询用户可查看的资源列表
     *
     * @param title title
     * @param userId userId
     * @return {@link List}<{@link Share}>
     */
    public List<Share> getList(String title, Integer pageNo, Integer pageSize, Long userId) {
        // 构造查询条件
        LambdaQueryWrapper<Share> wrapper = new LambdaQueryWrapper<>();
        // 按照 id 降序查询所有数据
        wrapper.orderByDesc(Share::getId);
        // 如果标题关键词不空,则加上模糊查询条件,否则结果即所有数据
        if (CharSequenceUtil.isNotEmpty(title)) {
            wrapper.like(Share::getTitle, title);
        }
        // 过滤出所有已经通过审核的数据并需要显示的数据
        wrapper.eq(Share::getAuditStatus, AuditStatusEnum.PASSED.getDesc()).eq(Share::getShowFlag, true);

        // 分页器
        Page<Share> page = Page.of(pageNo, pageSize);

        // 执行按条件查询
        List<Share> shares = shareMapper.selectList(page, wrapper);
        log.info(shares + "");
        // 处理后的 Share 数据列表
        List<Share> sharesDeal;
        // 1. 如果用户未登录,name downloadUrl 全部设为 null
        if (userId == null) {
            sharesDeal = shares.stream().peek(share -> share.setDownloadUrl(null)).toList();
        }
        // 2. 如果用户登录了,那么查询 mid_user_share,如果没有数据,那么这条数据,那么这条 share 的 downloadUrl 也设为 null
        // 只有自己分享的资源才能直接看到下载链接,否则显示“兑换”
        else {
            sharesDeal = shares.stream().peek(share -> {
                MidUserShare midUserShare = midUserShareMapper.selectOne(new QueryWrapper<MidUserShare>().lambda()
                        .eq(MidUserShare::getUserId, userId)
                        .eq(MidUserShare::getShareId, share.getId())
                );
                if (midUserShare == null) {
                    share.setDownloadUrl(null);
                }
            }).toList();
        }
        return sharesDeal;
    }
}

ShareController 新增接口,查询数据, 封装一个私有方法,从 token 中提取 userId

PS:网关放行(不登录也能看到首页数据)

    private final int MAX = 100;

	@GetMapping("list")
    public CommonResp<List<Share>> getShareList(
            @RequestParam(required = false) String title,
            @RequestParam(required = false, defaultValue = "1") Integer pageNo,
            @RequestParam(required = false, defaultValue = "3") Integer pageSize,
            @RequestHeader(required = false, value = "token") String token
    ) {
        if (pageSize > MAX) {
            pageSize = MAX;
        }
        return CommonResp.success(shareService.getList(title, pageNo, pageSize, getUserIdFromToken(token)));
    }

    private long getUserIdFromToken(String token) {
        long userId = 0;
        String noToken = "no-token";
        if (!noToken.equals(token)) {
            JSONObject tokenObject = JwtUtil.getJSONObject(token);
            log.info("解析到的 token 数据为:{}", tokenObject);
            userId = tokenObject.getLong("id");
        } else {
            log.info("没有 token");
        }
        return userId;
    }

全局日期时间格式化

两种处理方式,自行选择

yml 方式实现

在每个需要的模块配置中添加如下配置,

spring:
  jackson:
  	# 时间格式
    date-format: yyyy-MM-dd HH:mm:ss
    # 时区
    time-zone: Asia/Shanghai

JavaBean 方式实现

JavaBean 方式比 yml 配置方式在多模块的微服务架构下实现更加便捷,只需要在公共模块写一个配置类就可以在全部模块(引入了公共模块的全部模块)实现时间的格式化

@Configuration
public class DateTimeFormatConfig {

    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
        return jacksonObjectMapperBuilder -> {
            jacksonObjectMapperBuilder.simpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 设置全局日期格式
            jacksonObjectMapperBuilder.timeZone("Asia/Shanghai"); // 设置时区
        };
    }
}

服务接入 Nacos 服务注册发现中心

全部模块加入 Nacos Discovery 依赖,网关额外加入 loadbalancer 依赖

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>

修改模块的配置:

  1. 业务模块,注释上下文,添加nacos配置,服务名称自行修改成和原上下文配置一致,减少网关层的配置改动
server:
  port: 8002
#  servlet:
#    context-path: /content-service

spring:
  application:
    name: content-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        namespace: dev
  1. 网关模块,添加配置,并且修改 routes 的 uri
server:
  port: 8000

spring:
  application:
    name: gateway-service
  cloud:
    gateway:
      discovery:
        locator:
          # 开启服务名称访问
          enabled: true
      routes:
        - id: route-user
          uri: lb://user-service
          predicates:
            - Path=/user-service/**
        - id: route-content
          uri: lb://content-service
          predicates:
            - Path=/content-service/**
	………………
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        namespace: dev

资源详情功能实现

用户中心准备工作

先在 UserService 中新增根据 id 找用户方法:

    /**
     * 根据id获取user
     *
     * @param id id
     * @return {@link User}
     */
    public User getUserById(Long id) {
        return userMapper.selectById(id);
    }

UserController 中新增接口

    @GetMapping("{id}")
    public CommonResp<User> getUserById(@PathVariable Long id) {
        return CommonResp.success(userService.getUserById(id));
    }

内容中心准备工作

添加 OpenFeign 依赖和 Loadbalancer 依赖

启动主类添加 Feign 的扫包注解

<!-- 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>

share-content 模块新建 feign 子包,新建 UserService 接口 (此处需要用到用户中心的实体类 User,就先复制一份过来,结合前面的学习,思考最终的解决方案)

@FeignClient(value = "user-service", path = "user")
public interface UserService {

    @GetMapping("{id}")
    CommonResp<User> getUserById(@PathVariable Long id);

}

实现

编写 ShareService 的 getShareById 方法,调用刚才编写的 OpenFeign 接口,实现服务之间的调用:

    /**
     * 获取咨询内容信息
     *
     * @param id id
     * @return {@link ShareVO}
     */
    public ShareVO getShareById(Long id) {
        Share share = shareMapper.selectById(id);
        User publisher = userService.getUserById(share.getUserId()).getData();
        return ShareVO.builder()
                .share(share)
                .nickname(publisher.getNickname())
                .avatarUrl(publisher.getAvatarUrl())
                .build();
    }


/// ShareVO
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ShareVO {
    private Share share;
    private String nickname;
    private String avatarUrl;
}

ShareController 新增根据 id 查询分享接口:

    @GetMapping("{id}")
    public CommonResp<ShareVO> getShareById(@PathVariable Long id) {
        return CommonResp.success(shareService.getShareById(id));
    }

提交:编写获取资源详情接口

资源兑换功能实现

用户中心相关

user_center 数据库,新建 bonus_event_log 数据表,建表脚本如下:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for bonus_event_log
-- ----------------------------
DROP TABLE IF EXISTS `bonus_event_log`;
CREATE TABLE `bonus_event_log` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'Id',
  `user_id` bigint DEFAULT NULL COMMENT '用户id',
  `value` int DEFAULT NULL COMMENT '积分操作值',
  `event` varchar(20) DEFAULT NULL COMMENT '积分事件(签到、投稿、兑换等)',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `description` varchar(100) DEFAULT NULL COMMENT '描述',
  PRIMARY KEY (`id`),
  KEY `fk_bonus_event_log_user1_idx` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=149 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='积分变更记录表';

-- ----------------------------
-- Records of bonus_event_log
-- ----------------------------
BEGIN;
INSERT INTO `bonus_event_log` (`id`, `user_id`, `value`, `event`, `create_time`, `description`) VALUES (1, 1, -20, 'BUY', '2023-09-26 14:38:33', '兑换分享');
INSERT INTO `bonus_event_log` (`id`, `user_id`, `value`, `event`, `create_time`, `description`) VALUES (2, 2, -5, 'BUY', '2023-09-26 14:44:04', '兑换分享');
INSERT INTO `bonus_event_log` (`id`, `user_id`, `value`, `event`, `create_time`, `description`) VALUES (3, 1, -10, 'BUY', '2023-09-26 14:55:09', '兑换分享');
INSERT INTO `bonus_event_log` (`id`, `user_id`, `value`, `event`, `create_time`, `description`) VALUES (4, 1, -10, 'BUY', '2023-09-26 15:00:49', '兑换分享');
INSERT INTO `bonus_event_log` (`id`, `user_id`, `value`, `event`, `create_time`, `description`) VALUES (5, 2, 50, 'CONTRIBUTE', '2023-09-26 15:02:17', '投稿加积分');
INSERT INTO `bonus_event_log` (`id`, `user_id`, `value`, `event`, `create_time`, `description`) VALUES (6, 2, 50, 'CONTRIBUTE', '2023-09-26 15:04:18', '投稿加积分');
INSERT INTO `bonus_event_log` (`id`, `user_id`, `value`, `event`, `create_time`, `description`) VALUES (7, 2, 50, 'CONTRIBUTE', '2023-09-26 15:04:39', '投稿加积分');
INSERT INTO `bonus_event_log` (`id`, `user_id`, `value`, `event`, `create_time`, `description`) VALUES (8, 1, -10, 'BUY', '2023-09-26 15:04:58', '兑换分享');
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

用户中心,domain/entity 包,新建 BonusEventLog 实体类;mapper 包,新建 BonusEventLogMapper 接口

domain/dto 子包,新建 UpdateBonusDTO(用户添加积分 DTO 类)

@Data
public class UpdateBonusDTO {
    private Integer bonus;
    private String event;
    private String desc;
    private Long userId;
}

UserService ,注入 BonusEventLogMapper,增加修改用户积分方法

    /**
     * 更新积分
     *
     * @param bonusDTO dto
     * @return {@link User}
     */
    @Transactional(rollbackFor = Exception.class)
    public User updateUserBonus(UpdateBonusDTO bonusDTO) {
        // 获取数据
        User user = userMapper.selectById(bonusDTO.getUserId());
        Integer bonus = bonusDTO.getBonus();
        // 更新数据
        user.setBonus(user.getBonus() + bonus);
        userMapper.updateById(user);
        // 积分日志
        bonusEventLogMapper.insert(BonusEventLog.builder()
                .id(SnowUtil.getSnowflakeNextId())
                .userId(bonusDTO.getUserId())
                .value(bonus)
                .event(bonusDTO.getEvent())
                .description(bonusDTO.getDesc())
                .createTime(new Date())
                .build());
        return user;
    }

UserController 新增更新用户积分接口

    @PutMapping("bonus")
    public CommonResp<User> updateBonus(@RequestBody UpdateBonusDTO updateBonusDTO) {
        return CommonResp.success(userService.updateUserBonus(updateBonusDTO));
    }

内容中心相关

domain/dto 子包,新建 ExchangeDTO 类

@Data
public class ExchangeDTO {
    private Long userId;
    private Long shareId;
}

内容中心,feign 子包中 UserService 接口增加方法,调用用户中心的修改用户积分接口。需要复制用户中心的 UserAddBonusMsgDTO 类过来。

    @PutMapping("bonus")
    CommonResp<User> updateBonus(@RequestBody UpdateBonusDTO updateBonusDTO);

实现

ShareService 增加兑换方法

    @Transactional(rollbackFor = Exception.class)
    public Share exchangeShare(ExchangeDTO exchangeDTO) {
        // 查询信息
        Share share = shareMapper.selectById(exchangeDTO.getShareId());
        if (share == null) {
            throw new IllegalArgumentException("资源不存在!");
        }
        User user = userService.getUserById(exchangeDTO.getUserId()).getData();
        // 用户是否兑换过
        MidUserShare midUserShare = midUserShareMapper.selectOne(new QueryWrapper<MidUserShare>().lambda()
                        .eq(MidUserShare::getUserId, exchangeDTO.getUserId())
                        .eq(MidUserShare::getShareId, exchangeDTO.getShareId()));
        if (midUserShare != null) {
            return share;
        }
        // 判断用户积分是否足够兑换
        if (user.getBonus() < share.getPrice()) {
            throw new IllegalArgumentException("用户积分不足!");
        }
        // 更新用户积分
        userService.updateBonus(UpdateBonusDTO.builder()
                        .bonus(share.getPrice() * -1)
                        .desc("兑换分享")
                        .event("BUY")
                        .userId(exchangeDTO.getUserId())
                .build());
        // 插入关联表数据
        midUserShareMapper.insert(MidUserShare.builder()
                        .id(SnowUtil.getSnowflakeNextId())
                        .shareId(exchangeDTO.getShareId())
                        .userId(exchangeDTO.getUserId())
                .build());
        return share;
    }

ShareController 新增兑换接口

    @PostMapping("exchange")
    public CommonResp<Share> exchangeContent(@RequestBody ExchangeDTO exchangeDTO) {
        return CommonResp.success(shareService.exchangeShare(exchangeDTO));
    }

提交:编写资源兑换接口
.event(“BUY”)
.userId(exchangeDTO.getUserId())
.build());
// 插入关联表数据
midUserShareMapper.insert(MidUserShare.builder()
.id(SnowUtil.getSnowflakeNextId())
.shareId(exchangeDTO.getShareId())
.userId(exchangeDTO.getUserId())
.build());
return share;
}


ShareController 新增兑换接口

```java
    @PostMapping("exchange")
    public CommonResp<Share> exchangeContent(@RequestBody ExchangeDTO exchangeDTO) {
        return CommonResp.success(shareService.exchangeShare(exchangeDTO));
    }

提交:编写资源兑换接口

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值