使用 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>
修改模块的配置:
- 业务模块,注释上下文,添加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
- 网关模块,添加配置,并且修改 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));
}
提交:编写资源兑换接口