https://blog.csdn.net/weixin_45581692/article/details/127264865
1. 在service模块中创建子模块service_cms

2. 创建配置文件
# 服务端口号
server.port=8004
# 服务名
spring.application.name=service-cms
# 返回json的全局时间格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
# mysql数据库连接
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/guliedu?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=qwer`123
# mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
# 配置mapper.xml文件的路径
mybatis-plus.mapper-locations=classpath:com/hxp/educms/mapper/xml/*.xml
3. 创建数据库表
CREATE TABLE `crm_banner` (
`id` char(19) NOT NULL DEFAULT '' COMMENT 'ID',
`title` varchar(20) DEFAULT '' COMMENT '标题',
`image_url` varchar(500) NOT NULL DEFAULT '' COMMENT '图片地址',
`link_url` varchar(500) DEFAULT '' COMMENT '链接地址',
`sort` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '排序',
`is_deleted` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '逻辑删除 1(true)已删除, 0(false)未删除',
`gmt_create` datetime NOT NULL COMMENT '创建时间',
`gmt_modified` datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_name` (`title`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='首页banner表';
4. 根据表用代码生成器生成代码
5. 主启动类
@SpringBootApplication
@ComponentScan({"com.hxp"})
@MapperScan("com.hxp.educms.mapper")
public class CmsApplication {
public static void main(String[] args) {
SpringApplication.run(CmsApplication.class, args);
}
}

@RestController
@RequestMapping("/educms/bannerfront")
public class BannerFrontController {
@Autowired
private CrmBannerService bannerService;
//查询所有banner
@GetMapping("getAllBanner")
public R getAllBanner() {
List<CrmBanner> list = bannerService.selectAllBanner();
return R.ok().data("list", list);
}
}
@Service
public class CrmBannerServiceImpl extends ServiceImpl<CrmBannerMapper, CrmBanner> implements CrmBannerService {
//查询banner
@Override
public List<CrmBanner> selectAllBanner() {
//根据id进行降序排列,显示排列之后前两条记录
QueryWrapper<CrmBanner> wrapper = new QueryWrapper<>();
wrapper.orderByDesc("id");
wrapper.last("limit 2"); //last方法,拼接sql
List<CrmBanner> list = baseMapper.selectList(wrapper);
return list;
}
}


在课程模块中,写前台显示热门课程和热门讲师的接口,为了区分创建一个front包,表示是前台系统相关的接口。

@RestController
@RequestMapping("/eduservice/indexfront")
public class IndexFrontController {
@Autowired
private EduCourseService courseService;
@Autowired
private EduTeacherService teacherService;
//查询前8条热门课程,查询前4条名师
@GetMapping("index")
public R index() {
//查询前8条热门课程
QueryWrapper<EduCourse> wrapper = new QueryWrapper<>();
wrapper.orderByDesc("id");
wrapper.last("limit 8");
List<EduCourse> eduList = courseService.list(wrapper);
//查询前4条名师
QueryWrapper<EduTeacher> teacherWrapper = new QueryWrapper<>();
teacherWrapper.orderByDesc("id");
teacherWrapper.last("limit 4");
List<EduTeacher> teacherList = teacherService.list(teacherWrapper);
return R.ok().data("eduList",eduList).data("teacherList",teacherList);
}
}
1. 在公共模块的pom.xml中引入redis依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-pool2artifactId>
<version>2.6.0version>
dependency>
2. 在service-base模块添加redis配置类
@EnableCaching //开启缓存
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
template.setConnectionFactory(factory);
//key序列化方式
template.setKeySerializer(redisSerializer);
//value序列化
template.setValueSerializer(jackson2JsonRedisSerializer);
//value hashmap序列化
template.setHashValueSerializer(jackson2JsonRedisSerializer);
return template;
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置序列化(解决乱码的问题),过期时间600秒
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(600))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
return cacheManager;
}
}
3. 在方法上加缓存注解
(1)@Cacheable:根据方法对其返回结果进行缓存,下次请求时,如果缓存存在,则直接读取缓存数据返回;如果缓存不存在,则执行方法,并把返回的结果存入缓存中。
属性:value,缓存名,必填,指定缓存存放在哪块命名空间;key,可选,可以使用SpEL标签自定义缓存的key。
(2)@CachePut:使用该注解标志的方法,每次都会执行,并将结果存入指定的缓存中。其他方法可以直接从响应的缓存中读取缓存数据,而不需要再去查询数据库。一般用在新增方法上。
(3)@CacheEvict:使用该注解标志的方法,会清空指定的缓存,一般用在更新或者删除方法上。

4. 在service-cms模块中,添加配置
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.database=0
spring.redis.timeout=1800000
spring.redis.lettuce.pool.max-active=20
spring.redis.lettuce.pool.max-wait=-1
#最大阻塞等待时间(负数表示没限制)
spring.redis.lettuce.pool.max-idle=5
spring.redis.lettuce.pool.min-idle=0
1. 在service下创建子模块,用于阿里云短信服务

2. 配置文件
# 服务端口
server.port=8005
# 服务名
spring.application.name=service-msm
# mysql数据库连接
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/guliedu?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=qwer`123
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.database= 0
spring.redis.timeout=1800000
spring.redis.lettuce.pool.max-active=20
spring.redis.lettuce.pool.max-wait=-1
#最大阻塞等待时间(负数表示没限制)
spring.redis.lettuce.pool.max-idle=5
spring.redis.lettuce.pool.min-idle=0
#最小空闲
#返回json的全局时间格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
3. 引入依赖
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
dependency>
<dependency>
<groupId>com.aliyungroupId>
<artifactId>aliyun-java-sdk-coreartifactId>
dependency>
4. 编写随机数工具类,为了生成短信验证码的数字
public class RandomUtil {
private static final Random random = new Random();
private static final DecimalFormat fourdf = new DecimalFormat("0000");
private static final DecimalFormat sixdf = new DecimalFormat("000000");
public static String getFourBitRandom() {
return fourdf.format(random.nextInt(10000));
}
public static String getSixBitRandom() {
return sixdf.format(random.nextInt(1000000));
}
/**
* 给定数组,抽取n个数据
* @param list
* @param n
* @return
*/
public static ArrayList getRandom(List list, int n) {
Random random = new Random();
HashMap<Object, Object> hashMap = new HashMap<Object, Object>();
// 生成随机数字并存入HashMap
for (int i = 0; i < list.size(); i++) {
int number = random.nextInt(100) + 1;
hashMap.put(number, i);
}
// 从HashMap导入数组
Object[] robjs = hashMap.values().toArray();
ArrayList r = new ArrayList();
// 遍历数组并打印数据
for (int i = 0; i < n; i++) {
r.add(list.get((int) robjs[i]));
System.out.print(list.get((int) robjs[i]) + "\t");
}
System.out.print("\n");
return r;
}
}
5. 编写接口
@RestController
@RequestMapping("/edumsm/msm")
//@CrossOrigin
public class MsmController {
@Autowired
private MsmService msmService;
@Autowired
private RedisTemplate<String,String> redisTemplate;
//发送短信
@GetMapping("send/{phone}")
public R sendMsm(@PathVariable String phone) {
//1 从redis获取验证码,如果获取到直接返回
String code = redisTemplate.opsForValue().get(phone);
if (!StringUtils.isEmpty(code)) {
return R.ok();
}
//2 如果redis获取不到,进行阿里云发送
//生成随机值,传递阿里云进行发送
code = RandomUtil.getFourBitRandom();
Map<String, Object> param = new HashMap<>();
param.put("code",code);
//调用service发送短信的方法
boolean isSend = msmService.send(param,phone);
if (isSend) {
//发送成功,把发送成功验证码放到redis里面
//设置有效时间
redisTemplate.opsForValue().set(phone,code,5, TimeUnit.MINUTES); // 5分钟
return R.ok();
} else {
return R.error().message("短信发送失败");
}
}
}
@Service
public class MsmServiceImpl implements MsmService {
//发送短信
@Override
public boolean send(Map<String, Object> param, String phone) {
if(StringUtils.isEmpty(phone)) return false;
DefaultProfile profile =
DefaultProfile.getProfile("default", "LTAI5tEz9ABsH5qFZsxXg5K2", "MHJngccYekVjxzp0YtKQU4HTJsr5Ty");
IAcsClient client = new DefaultAcsClient(profile);
//设置相关固定的参数
CommonRequest request = new CommonRequest();
//request.setProtocol(ProtocolType.HTTPS);
request.setMethod(MethodType.POST);
request.setDomain("dysmsapi.aliyuncs.com");
request.setVersion("2017-05-25");
request.setAction("SendSms");
//设置发送相关的参数
request.putQueryParameter("PhoneNumbers",phone); //手机号
request.putQueryParameter("SignName","阿里云短信测试"); //申请阿里云 签名名称
request.putQueryParameter("TemplateCode","SMS_154950909"); //申请阿里云 模板code
request.putQueryParameter("TemplateParam", JSONObject.toJSONString(param)); //验证码数据,转换json数据传递
try {
//最终发送
CommonResponse response = client.getCommonResponse(request);
boolean success = response.getHttpResponse().isSuccess();
return success;
}catch(Exception e) {
e.printStackTrace();
return false;
}
}
}
注意:这里要填自己阿里云模板的信息。

1. 引入依赖
<dependency>
<groupId>io.jsonwebtokengroupId>
<artifactId>jjwtartifactId>
dependency>
2. 在common_utils下编写JWT工具类

public class JwtUtils {
//常量
public static final long EXPIRE = 1000 * 60 * 60 * 24; //token过期时间
public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO"; //秘钥
//生成token字符串的方法
public static String getJwtToken(String id, String nickname){
String JwtToken = Jwts.builder()
.setHeaderParam("typ", "JWT")
.setHeaderParam("alg", "HS256")
.setSubject("guli-user")
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
.claim("id", id) //设置token主体部分 ,存储用户信息
.claim("nickname", nickname)
.signWith(SignatureAlgorithm.HS256, APP_SECRET)
.compact();
return JwtToken;
}
/**
* 判断token是否存在与有效
* @param jwtToken
* @return
*/
public static boolean checkToken(String jwtToken) {
if(StringUtils.isEmpty(jwtToken)) return false;
try {
Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 判断token是否存在与有效
* @param request
* @return
*/
public static boolean checkToken(HttpServletRequest request) {
try {
String jwtToken = request.getHeader("token");
if(StringUtils.isEmpty(jwtToken)) return false;
Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 根据token字符串获取会员id
* @param request
* @return
*/
public static String getMemberIdByJwtToken(HttpServletRequest request) {
String jwtToken = request.getHeader("token");
if(StringUtils.isEmpty(jwtToken)) return "";
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
Claims claims = claimsJws.getBody();
return (String)claims.get("id");
}
}
1. 在service模块下创建子模块

2. 创建ucenter用户表
CREATE TABLE `ucenter_member` (
`id` char(19) NOT NULL COMMENT '会员id',
`openid` varchar(128) DEFAULT NULL COMMENT '微信openid',
`mobile` varchar(11) DEFAULT '' COMMENT '手机号',
`password` varchar(255) DEFAULT NULL COMMENT '密码',
`nickname` varchar(50) DEFAULT NULL COMMENT '昵称',
`sex` tinyint(2) unsigned DEFAULT NULL COMMENT '性别 1 女,2 男',
`age` tinyint(3) unsigned DEFAULT NULL COMMENT '年龄',
`avatar` varchar(255) DEFAULT NULL COMMENT '用户头像',
`sign` varchar(100) DEFAULT NULL COMMENT '用户签名',
`is_disabled` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否禁用 1(true)已禁用, 0(false)未禁用',
`is_deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '逻辑删除 1(true)已删除, 0(false)未删除',
`gmt_create` datetime NOT NULL COMMENT '创建时间',
`gmt_modified` datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='会员表';
3. 根据数据库表,用代码生成器生成代码
4. properties配置和主启动类
# 服务端口
server.port=8160
# 服务名
spring.application.name=service-ucenter
# mysql数据库连接
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/guliedu?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=qwer`123
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.database= 0
spring.redis.timeout=1800000
spring.redis.lettuce.pool.max-active=20
spring.redis.lettuce.pool.max-wait=-1
#最大阻塞等待时间(负数表示没限制)
spring.redis.lettuce.pool.max-idle=5
spring.redis.lettuce.pool.min-idle=0
#最小空闲
#请求处理的超时时间
ribbon.ReadTimeout=120000
#请求连接的超时时间
ribbon.ConnectTimeout=30000
#返回json的全局时间格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
# 配置mapper.xml文件的路径
mybatis-plus.mapper-locations=classpath:com/hxp/educenter/mapper/xml/*.xml
# nacos服务地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
@ComponentScan({"com.hxp"})
@SpringBootApplication
@MapperScan("com.hxp.educenter.mapper")
public class UcenterApplication {
public static void main(String[] args) {
SpringApplication.run(UcenterApplication.class, args);
}
}
5. 编写接口
@RestController
@RequestMapping("/educenter/member")
public class UcenterMemberController {
@Autowired
private UcenterMemberService memberService;
//登录
@PostMapping ("login")
public R loginUser(@RequestBody UcenterMember ucenterMember) {
//调用service方法实现登录
//返回token值,使用jwt生成
String token = memberService.login(ucenterMember);
return R.ok().data("token", token);
}
}
@Service
public class UcenterMemberServiceImpl extends ServiceImpl<UcenterMemberMapper, UcenterMember> implements UcenterMemberService {
//登录方法
@Override
public String login(UcenterMember ucenterMember) {
//获取登录手机号和密码
String mobile = ucenterMember.getMobile();
String password = ucenterMember.getPassword();
//手机号和密码非空判断
if (StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password)) {
throw new GuliException(20001, "登录失败");
}
//判断手机号是否正确
QueryWrapper<UcenterMember> wrapper = new QueryWrapper<>();
wrapper.eq("mobile", mobile);
UcenterMember mobileMember = baseMapper.selectOne(wrapper);
//判断对象是否为空
if (mobileMember == null) {
throw new GuliException(20001, "登录失败");
}
//判断密码
// 对输入的密码进行加密,再和数据库中的密码进行比较。加密方式MD5
if (!MD5.encrypt(password).equals(mobileMember.getPassword())) {
throw new GuliException(20001, "登录失败");
}
//判断用户是否禁用
if (mobileMember.getIsDisabled()) {
throw new GuliException(20001, "登录失败");
}
//登录成功,生成token字符串
String jwtToken = JwtUtils.getJwtToken(mobileMember.getId(), mobileMember.getNickname());
return jwtToken;
}
}
注意:上面在和数据库表中密码做判断的时候,进行了MD5加密,因为数据库中的密码都要进行加密再存储。
public class MD5 {
public static String encrypt(String strSrc) {
try {
char hexChars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', 'a', 'b', 'c', 'd', 'e', 'f' };
byte[] bytes = strSrc.getBytes();
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(bytes);
bytes = md.digest();
int j = bytes.length;
char[] chars = new char[j * 2];
int k = 0;
for (int i = 0; i < bytes.length; i++) {
byte b = bytes[i];
chars[k++] = hexChars[b >>> 4 & 0xf];
chars[k++] = hexChars[b & 0xf];
}
return new String(chars);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
throw new RuntimeException("MD5加密出错!!+" + e);
}
}
}
1. 创建vo实体类,封装注册数据,包含验证码属性
@Data
public class RegisterVo {
@ApiModelProperty(value = "昵称")
private String nickname;
@ApiModelProperty(value = "手机号")
private String mobile;
@ApiModelProperty(value = "密码")
private String password;
@ApiModelProperty(value = "验证码")
private String code;
}
2. 编写接口
//注册
@PostMapping("register")
public R registerUser(@RequestBody RegisterVo registerVo) {
memberService.register(registerVo);
return R.ok();
}
@Autowired
private RedisTemplate<String, String> redisTemplate;
//注册
@Override
public void register(RegisterVo registerVo) {
//获取注册的数据
String code = registerVo.getCode(); //验证码
String mobile = registerVo.getMobile(); //手机号
String nickname = registerVo.getNickname(); //昵称
String password = registerVo.getPassword(); //密码
//非空判断
if (StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password)
|| StringUtils.isEmpty(code) || StringUtils.isEmpty(nickname)) {
throw new GuliException(20001, "注册失败");
}
//判断验证码,获取redis验证码
String redisCode = redisTemplate.opsForValue().get(mobile);
if (!code.equals(redisCode)) {
throw new GuliException(20001, "注册失败");
}
//判断手机号是否重复,表里存在相同手机号不进行添加
QueryWrapper<UcenterMember> wrapper = new QueryWrapper<>();
wrapper.eq("mobile", mobile);
Integer count = baseMapper.selectCount(wrapper);
if (count > 0) {
throw new GuliException(20001, "用户已存在");
}
//数据添加进数据库
UcenterMember ucenterMember = new UcenterMember();
ucenterMember.setMobile(mobile);
ucenterMember.setNickname(nickname);
ucenterMember.setPassword(MD5.encrypt(password)); //密码需要加密
ucenterMember.setIsDisabled(false); //用户不禁用
ucenterMember.setAvatar("http://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTKDRfib8wy7A2ltERKh4VygxdjVC1x5OaOb1t9hot4JNt5agwaVLdJLcD9vJCNcxkvQnlvLYIPfrZw/132");
baseMapper.insert(ucenterMember);
}

//根据token获取用户信息
@GetMapping("getMemberInfo")
public R getMemberInfo(HttpServletRequest request) {
//调用jwt工具类的方法,根据request对象获取头信息,返回用户id
String memberId = JwtUtils.getMemberIdByJwtToken(request);
//查询数据库根据用户id获取用户信息
UcenterMember member = memberService.getById(memberId);
return R.ok().data("userInfo", member);
}

OAuth2:一种解决方案,仅是授权框架,仅用于授权代理。
1. 准备工作
(1)注册开发者资质:在open.weixin.qq.com进行注册。
(2)注册支持企业类型,注册之后会提供微信id和微信秘钥。
(3)申请网站应用名称
(4)需要域名地址
2. 生成二维码
(1)在service-ucenter模块配置文件,配置id、秘钥和域名地址
# 微信开放平台 appid
wx.open.app_id=wxed9954c01bb89b47
# 微信开放平台 appsecret
wx.open.app_secret=a7482517235173ddb4083788de60b90e
# 微信开放平台 重定向url
wx.open.redirect_url=http://localhost:8160/api/ucenter/wx/callback
(2)创建类读取配置文件内容
@Component
public class ConstantWxUtils implements InitializingBean {
@Value("${wx.open.app_id}")
private String appId;
@Value("${wx.open.app_secret}")
private String appSecret;
@Value("${wx.open.redirect_url}")
private String redirectUrl;
public static String WX_OPEN_APP_ID;
public static String WX_OPEN_APP_SECRET;
public static String WX_OPEN_REDIRECT_URL;
@Override
public void afterPropertiesSet() throws Exception {
WX_OPEN_APP_ID = appId;
WX_OPEN_APP_SECRET = appSecret;
WX_OPEN_REDIRECT_URL = redirectUrl;
}
}
(3)生成二维码
直接请求微信提供固定的地址,向地址后面拼接参数。
@Controller //只是请求地址,不需要返回数据
@RequestMapping("/api/ucenter/wx")
public class WxApiController {
//1 生成微信扫描二维码
@GetMapping("login")
public String getWxCode() {
//固定地址,后面拼接参数
// String url = "https://open.weixin.qq.com/" +
// "connect/qrconnect?appid="+ ConstantWxUtils.WX_OPEN_APP_ID+"&response_type=code";
// 微信开放平台授权baseUrl %s相当于?代表占位符
String baseUrl = "https://open.weixin.qq.com/connect/qrconnect" +
"?appid=%s" +
"&redirect_uri=%s" +
"&response_type=code" +
"&scope=snsapi_login" +
"&state=%s" +
"#wechat_redirect";
//对redirect_url进行URLEncoder编码
String redirectUrl = ConstantWxUtils.WX_OPEN_REDIRECT_URL;
try {
redirectUrl = URLEncoder.encode(redirectUrl, "utf-8");
}catch(Exception e) {
}
//设置%s里面值
String url = String.format(
baseUrl,
ConstantWxUtils.WX_OPEN_APP_ID,
redirectUrl,
"atguigu"
);
//重定向到请求微信地址里面
return "redirect:"+url;
}
}
注意两点:1. 拼接参数的方式;2. 对地址做的URLEncoder编码

3. 获取扫码人信息
分析:扫描二维码,去调用本地接口,然后跳转页面。

在接口中获取扫码人的信息

过程:

用到的技术点:httpclient(发出请求得到结果)、json转换工具(gson)
引入依赖:
<dependency>
<groupId>org.apache.httpcomponentsgroupId>
<artifactId>httpclientartifactId>
dependency>
<dependency>
<groupId>commons-iogroupId>
<artifactId>commons-ioartifactId>
dependency>
<dependency>
<groupId>com.google.code.gsongroupId>
<artifactId>gsonartifactId>
dependency>
httpclient工具类:
略
获取扫码人信息的接口:
@Autowired
private UcenterMemberService memberService;
//2 获取扫描人信息,添加数据
@GetMapping("callback")
public String callback(String code, String state) {
try {
//1 获取code值,临时票据,类似于验证码
//2 拿着code请求 微信固定的地址,得到两个值 accsess_token 和 openid
String baseAccessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token" +
"?appid=%s" +
"&secret=%s" +
"&code=%s" +
"&grant_type=authorization_code";
//拼接三个参数 :id 秘钥 和 code值
String accessTokenUrl = String.format(
baseAccessTokenUrl,
ConstantWxUtils.WX_OPEN_APP_ID,
ConstantWxUtils.WX_OPEN_APP_SECRET,
code
);
//请求这个拼接好的地址,得到返回两个值 accsess_token 和 openid
//使用httpclient发送请求,得到返回结果
String accessTokenInfo = HttpClientUtils.get(accessTokenUrl);
//从accessTokenInfo字符串获取出来两个值 accsess_token 和 openid
//把accessTokenInfo字符串转换map集合,根据map里面key获取对应值
//使用json转换工具 Gson
Gson gson = new Gson();
HashMap mapAccessToken = gson.fromJson(accessTokenInfo, HashMap.class);
String access_token = (String)mapAccessToken.get("access_token");
String openid = (String)mapAccessToken.get("openid");
//把扫描人信息添加数据库里面
//判断数据表里面是否存在相同微信信息,根据openid判断
UcenterMember member = memberService.getOpenIdMember(openid);
if(member == null) {//memeber是空,表没有相同微信数据,进行添加
//3 拿着得到accsess_token 和 openid,再去请求微信提供固定的地址,获取到扫描人信息
//访问微信的资源服务器,获取用户信息
String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +
"?access_token=%s" +
"&openid=%s";
//拼接两个参数
String userInfoUrl = String.format(
baseUserInfoUrl,
access_token,
openid
);
//发送请求
String userInfo = HttpClientUtils.get(userInfoUrl);
//获取返回userinfo字符串扫描人信息
HashMap userInfoMap = gson.fromJson(userInfo, HashMap.class);
String nickname = (String)userInfoMap.get("nickname");//昵称
String headimgurl = (String)userInfoMap.get("headimgurl");//头像
member = new UcenterMember();
member.setOpenid(openid);
member.setNickname(nickname);
member.setAvatar(headimgurl);
memberService.save(member);
}
//使用jwt根据member对象生成token字符串
String jwtToken = JwtUtils.getJwtToken(member.getId(), member.getNickname());
//最后:返回首页面,通过路径传递token字符串
return "redirect:http://localhost:3000?token="+jwtToken;
}catch(Exception e) {
throw new GuliException(20001,"登录失败");
}
}
跟之前写的后台讲师分页查询不同,之前使用了Element-ui的组件,这里需要用原始的分页查询。
后台管理系统的分页查询:

前台的讲师分页:

在front包下,创建TeacherFrontController类
@RestController
@RequestMapping("/eduservice/teacherfront")
public class TeacherFrontController {
@Autowired
private EduTeacherService teacherService;
//分页查询讲师的方法
@PostMapping("getTeacherFrontList/{page}/{limit}")
public R getTeacherFrontList(@PathVariable long page, @PathVariable long limit) {
Page<EduTeacher> pageTeacher = new Page<>(page, limit);
Map<String, Object> map = teacherService.getTeacherFrontList(pageTeacher);
return R.ok().data(map);
}
}
//分页查询讲师
@Override
public Map<String, Object> getTeacherFrontList(Page<EduTeacher> pageTeacher) {
QueryWrapper<EduTeacher> wrapper = new QueryWrapper<>();
wrapper.orderByDesc("id");
baseMapper.selectPage(pageTeacher, wrapper);
List<EduTeacher> records = pageTeacher.getRecords();
long current = pageTeacher.getCurrent();
long pages = pageTeacher.getPages();
long size = pageTeacher.getSize();
long total = pageTeacher.getTotal();
boolean hasNext = pageTeacher.hasNext();
boolean hasPrevious = pageTeacher.hasPrevious();
//把分页数据获取出来,放到map集合
Map<String, Object> map = new HashMap<>();
map.put("items", records);
map.put("current", current);
map.put("pages", pages);
map.put("size", size);
map.put("total", total);
map.put("hasNext", hasNext);
map.put("hasPrevious", hasPrevious);
return map;
}
根据前端传递过来的讲师id,查询讲师基本信息和讲师所讲课程。
@Autowired
private EduTeacherService teacherService;
@Autowired
private EduCourseService courseService;
//讲师详情
@GetMapping("getTeacherFrontInfo/{teacherId}")
public R getTeacherFrontInfo(@PathVariable String teacherId) {
//1 根据讲师id查询讲师基本信息
EduTeacher eduTeacher = teacherService.getById(teacherId);
//2 根据讲师id查询所讲课程
QueryWrapper<EduCourse> wrapper = new QueryWrapper<>();
wrapper.eq("teacher_id", teacherId);
List<EduCourse> courseList = courseService.list(wrapper);
return R.ok().data("teacher",eduTeacher).data("courseList",courseList);
}


1. 课程列表vo类
@Data
public class CourseFrontVo {
@ApiModelProperty(value = "课程名称")
private String title;
@ApiModelProperty(value = "讲师id")
private String teacherId;
@ApiModelProperty(value = "一级类别id")
private String subjectParentId;
@ApiModelProperty(value = "二级类别id")
private String subjectId;
@ApiModelProperty(value = "销量排序")
private String buyCountSort;
@ApiModelProperty(value = "最新时间排序")
private String gmtCreateSort;
@ApiModelProperty(value = "价格排序")
private String priceSort;
}
2. 编写接口
@RestController
@RequestMapping("/eduservice/coursefront")
@CrossOrigin
public class CourseFrontController {
@Autowired
private EduCourseService courseService;
// 条件查询课程 带分页
@PostMapping("getFrontCourseList/{page}/{limit}")
public R getFrontCourseList(@PathVariable long page, @PathVariable long limit,
@RequestBody(required = false)CourseFrontVo courseFrontVo) {
Page<EduCourse> pageCourse = new Page<>(page, limit);
Map<String ,Object> map = courseService.getCourseFrontList(pageCourse,courseFrontVo);
return R.ok().data(map);
}
}
//条件查询课程带分页
@Override
public Map<String, Object> getCourseFrontList(Page<EduCourse> pageCourse, CourseFrontVo courseFrontVo) {
QueryWrapper<EduCourse> wrapper = new QueryWrapper<>();
if (!StringUtils.isEmpty(courseFrontVo.getSubjectParentId())) {
wrapper.eq("subject_parent_id", courseFrontVo.getSubjectParentId());
}
if (!StringUtils.isEmpty(courseFrontVo.getSubjectId())) {
wrapper.eq("subject_id", courseFrontVo.getSubjectId());
}
if (!StringUtils.isEmpty(courseFrontVo.getBuyCountSort())) {
wrapper.orderByDesc("buy_count");
}
if (!StringUtils.isEmpty(courseFrontVo.getGmtCreateSort())) {
wrapper.orderByDesc("gmt_create");
}
if (!StringUtils.isEmpty(courseFrontVo.getPriceSort())) {
wrapper.orderByDesc("price");
}
baseMapper.selectPage(pageCourse, wrapper);
List<EduCourse> records = pageCourse.getRecords();
long current = pageCourse.getCurrent();
long pages = pageCourse.getPages();
long size = pageCourse.getSize();
long total = pageCourse.getTotal();
boolean hasNext = pageCourse.hasNext();
boolean hasPrevious = pageCourse.hasPrevious();
//把分页数据获取出来,放到map集合
Map<String, Object> map = new HashMap<>();
map.put("items", records);
map.put("current", current);
map.put("pages", pages);
map.put("size", size);
map.put("total", total);
map.put("hasNext", hasNext);
map.put("hasPrevious", hasPrevious);
return map;
}


1. 课程详情vo对象
@Data
public class CourseWebVo {
private String id;
private String title;
private BigDecimal price;
private Integer lessonNum;
private String cover;
private Long buyCount;
private Long viewCount;
private String description;
private String teacherId;
private String teacherName;
private String intro;
private String avatar;
@ApiModelProperty(value = "课程一级分类ID")
private String subjectLevelOneId;
@ApiModelProperty(value = "课程一级名称")
private String subjectLevelOne;
@ApiModelProperty(value = "课程二级分类ID")
private String subjectLevelTwoId;
@ApiModelProperty(value = "课程二级名称")
private String subjectLevelTwo;
}
2. 编写接口
@Autowired
private EduCourseService courseService;
@Autowired
private EduChapterService chapterService;
@GetMapping("getFrontCourseInfo/{courseId}")
public R getFrontCourseInfo(@PathVariable String courseId) {
//根据课程id,编写sql语句查询课程信息
CourseWebVo courseWebVo = courseService.getBaseCourseInfo(courseId);
//根据课程id查询章节和小节
List<ChapterVo> chapterVideoList = chapterService.getChapterVideoByCourseId(courseId);
return R.ok().data("courseWebVo",courseWebVo).data("chapterVideoList",chapterVideoList);
}
//根据课程id查询课程基本信息
@Override
public CourseWebVo getBaseCourseInfo(String courseId) {
return baseMapper.getBaseCourseInfo(courseId);
}
3. 编写sql语句,查询出显示在页面的数据:课程基本信息、课程分类、课程描述、所属讲师
<select id="getBaseCourseInfo" resultType="com.hxp.eduservice.entity.frontvo.CourseWebVo">
SELECT ec.id, ec.title, ec.price, ec.lesson_num AS lessonNum, ec.cover,
ec.buy_count AS buyCount, ec.view_count AS viewCount,
ecd.description,
et.id AS teacherId, et.name AS teacherName, et.intro, et.avatar,
es1.id AS subjectLevelOneId, es1.title AS subjectLevelOne,
es2.id AS subjectLevelTwoId, es2.title AS subjectLevelTwo
FROM edu_course ec LEFT JOIN edu_course_description ecd ON ec.id=ecd.id
LEFT JOIN edu_teacher et ON ec.teacher_id=et.id
LEFT JOIN edu_subject es1 ON ec.subject_parent_id=es1.id
LEFT JOIN edu_subject es2 ON ec.subject_id=es2.id
WHERE ec.id=#{courseId};
select>
1. 编写接口
用获取视频凭证的方式播放视频
//获取视频凭证
@GetMapping("getPlayAuth/{id}")
public R getPlayAuth(@PathVariable String id) {
try {
//创建初始化对象
DefaultAcsClient client =
InitVodClient.initVodClient(ConstantVodUtils.ACCESS_KEY_ID, ConstantVodUtils.ACCESS_KEY_SECRET);
//创建获取凭证request和response对象
GetVideoPlayAuthRequest request = new GetVideoPlayAuthRequest();
//向request设置视频id
request.setVideoId(id);
//调用方法得到凭证
GetVideoPlayAuthResponse response = client.getAcsResponse(request);
String playAuth = response.getPlayAuth();
return R.ok().data("playAuth", playAuth);
} catch (Exception e) {
throw new GuliException(20001, "获取凭证失败");
}
}
2. 点击某个小节,打开新的页面进行视频播放
在小节上加上超链接



点击“立即购买”:生成订单,跳转页面将订单数据显示在订单上。
点击“去支付”:生成微信支付二维码,每隔三秒查询支付状态是否支付成功。支付成功后,将支付状态改成已支付,并生成支付记录到数据库表中,然后跳转到课程详情页面。
1. 数据库表
CREATE TABLE `t_order` (
`id` char(19) NOT NULL DEFAULT '',
`order_no` varchar(20) NOT NULL DEFAULT '' COMMENT '订单号',
`course_id` varchar(19) NOT NULL DEFAULT '' COMMENT '课程id',
`course_title` varchar(100) DEFAULT NULL COMMENT '课程名称',
`course_cover` varchar(255) DEFAULT NULL COMMENT '课程封面',
`teacher_name` varchar(20) DEFAULT NULL COMMENT '讲师名称',
`member_id` varchar(19) NOT NULL DEFAULT '' COMMENT '会员id',
`nickname` varchar(50) DEFAULT NULL COMMENT '会员昵称',
`mobile` varchar(11) DEFAULT NULL COMMENT '会员手机',
`total_fee` decimal(10,2) DEFAULT '0.01' COMMENT '订单金额(分)',
`pay_type` tinyint(3) DEFAULT NULL COMMENT '支付类型(1:微信 2:支付宝)',
`status` tinyint(3) DEFAULT NULL COMMENT '订单状态(0:未支付 1:已支付)',
`is_deleted` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '逻辑删除 1(true)已删除, 0(false)未删除',
`gmt_create` datetime NOT NULL COMMENT '创建时间',
`gmt_modified` datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `ux_order_no` (`order_no`),
KEY `idx_course_id` (`course_id`),
KEY `idx_member_id` (`member_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单';
CREATE TABLE `t_pay_log` (
`id` char(19) NOT NULL DEFAULT '',
`order_no` varchar(20) NOT NULL DEFAULT '' COMMENT '订单号',
`pay_time` datetime DEFAULT NULL COMMENT '支付完成时间',
`total_fee` decimal(10,2) DEFAULT '0.01' COMMENT '支付金额(分)',
`transaction_id` varchar(30) DEFAULT NULL COMMENT '交易流水号',
`trade_state` char(20) DEFAULT NULL COMMENT '交易状态',
`pay_type` tinyint(3) NOT NULL DEFAULT '0' COMMENT '支付类型(1:微信 2:支付宝)',
`attr` text COMMENT '其他属性',
`is_deleted` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '逻辑删除 1(true)已删除, 0(false)未删除',
`gmt_create` datetime NOT NULL COMMENT '创建时间',
`gmt_modified` datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_order_no` (`order_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='支付日志表';
2. 用代码生成器生成代码
3. 生成订单接口

controller层:
@Autowired
private OrderService orderService;
// 生成订单
@PostMapping("createOrder/{courseId}")
public R saveOrder(@PathVariable String courseId, HttpServletRequest request) {
//在request请求中,根据token得到用户id,根据课程id和用户id创建订单
String orderNo = orderService.createOrders(courseId, JwtUtils.getMemberIdByJwtToken(request));
return R.ok().data("orderId", orderNo);
}
想通过远程调用的方式拿到两个对象,需要做如下操作:
(1)在公共模块创建两个vo对象

(2)在用户模块中,写查询用户信息的接口,用于订单模块做远程调用

(3)在edu模块中,写查询课程信息的接口,用于订单模块远程调用查询课程信息

(4)在order模块,加上nacos配置和主启动类上的注解


(5)在order模块,编写远程调用的接口
@Component
@FeignClient("service-edu")
public interface EduClient {
//根据课程id查询课程信息
@PostMapping("/eduservice/coursefront/getCourseInfoOrder/{id}")
public CourseWebVoOrder getCourseInfoOrder(@PathVariable("id") String id);
}
@Component
@FeignClient("service-ucenter")
public interface UcenterClient {
//根据用户id获取用户信息
@PostMapping("/educenter/member/getUserInfoOrder/{id}")
public UcenterMemberOrder getUserInfoOrder(@PathVariable String id);
}
service层,创建订单,返回订单号
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
@Autowired
private EduClient eduClient;
@Autowired
private UcenterClient ucenterClient;
//生成订单
@Override
public String createOrders(String courseId, String memberIdByJwtToken) {
//通过远程调用根据用户id获取用户信息
UcenterMemberOrder userInfoOrder = ucenterClient.getUserInfoOrder(memberIdByJwtToken);
//通过远程调用根据课程id获取课程信息
CourseWebVoOrder courseInfoOrder = eduClient.getCourseInfoOrder(courseId);
//创建Order对象,向order对象里面设置需要数据
Order order = new Order();
order.setOrderNo(OrderNoUtils.getOrderNo());
order.setCourseId(courseId);
order.setCourseTitle(courseInfoOrder.getCover());
order.setTeacherName(courseInfoOrder.getTeacherName());
order.setTotalFee(courseInfoOrder.getPrice());
order.setMemberId(memberIdByJwtToken);
order.setMobile(userInfoOrder.getMobile());
order.setNickname(userInfoOrder.getNickname());
order.setStatus(0); //订单状态(0:未支付 1:已支付)
order.setPayType(1); //支付类型,微信1
baseMapper.insert(order);
//返回订单号
return order.getOrderNo();
}
}
前面已经将订单创建出来了,再根据订单号查询出订单数据,显示在订单页面上。
// 根据订单号查询订单信息
@GetMapping("getOrderInfo/{orderId}")
public R getOrderInfo(@PathVariable String orderId) {
QueryWrapper<Order> wrapper = new QueryWrapper<>();
wrapper.eq("order_no", orderId);
Order order = orderService.getOne(wrapper);
return R.ok().data("item",order);
}
1. 尚硅谷提供的微信支付账号
weixin:
pay:
#关联的公众号appid
appid: wx74862e0dfcf69954
#商户号
partner: 1558950191
#商户key
partnerkey: T6m9iK73b0kn9g5v426MKfHQH7X8rKwb
#回调地址
notifyurl: http://guli.shop/api/order/weixinPay/weixinNotify
2. 引入依赖
<dependency>
<groupId>com.github.wxpaygroupId>
<artifactId>wxpay-sdkartifactId>
<version>0.0.3version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
dependency>
3. 编写接口
@RestController
@RequestMapping("/eduorder/paylog")
@CrossOrigin
public class PayLogController {
@Autowired
private PayLogService payLogService;
//生成微信支付二维码接口,参数是订单号
@GetMapping("createNative/{orderNo}")
public R createNative(@PathVariable String orderNo) {
//返回信息,包含二维码地址,还有其他需要的信息
Map map = payLogService.createNative(orderNo);
return R.ok().data(map);
}
}
@Service
public class PayLogServiceImpl extends ServiceImpl<PayLogMapper, PayLog> implements PayLogService {
@Autowired
private OrderService orderService;
//生成微信支付二维码接口
@Override
public Map createNative(String orderNo) {
try {
//1 根据订单号查询订单信息
QueryWrapper<Order> wrapper = new QueryWrapper<>();
wrapper.eq("order_no", orderNo);
Order order = orderService.getOne(wrapper);
//2 使用map设置生成二维码需要的参数
Map m = new HashMap();
m.put("appid","wx74862e0dfcf69954");
m.put("mch_id", "1558950191");
m.put("nonce_str", WXPayUtil.generateNonceStr());
m.put("body", order.getCourseTitle()); //课程标题
m.put("out_trade_no", orderNo); //订单号
m.put("total_fee", order.getTotalFee().multiply(new BigDecimal("100")).longValue()+"");
m.put("spbill_create_ip", "127.0.0.1");
m.put("notify_url", "http://guli.shop/api/order/weixinPay/weixinNotify\n");
m.put("trade_type", "NATIVE");
//3 发送httpclient请求,传递参数xml格式,微信支付提供的固定的地址
HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");
//设置xml格式的参数
client.setXmlParam(WXPayUtil.generateSignedXml(m,"T6m9iK73b0kn9g5v426MKfHQH7X8rKwb"));
client.setHttps(true);
//执行post请求发送
client.post();
//4 得到发送请求返回结果
//返回内容,是使用xml格式返回
String xml = client.getContent();
//把xml格式转换map集合,把map集合返回,其中包含二维码地址
Map<String,String> resultMap = WXPayUtil.xmlToMap(xml);
//最终返回数据 的封装
Map map = new HashMap();
map.put("out_trade_no", orderNo);
map.put("course_id", order.getCourseId());
map.put("total_fee", order.getTotalFee());
map.put("result_code", resultMap.get("result_code")); //返回二维码操作状态码
map.put("code_url", resultMap.get("code_url")); //二维码地址
return map;
}catch(Exception e) {
throw new GuliException(20001,"生成二维码失败");
}
}
}
查询订单状态,如果查询结果是“支付成功”,则改变支付状态并添加支付记录。
//查询订单支付状态
//参数:订单号,根据订单号查询支付状态
@GetMapping("queryPayStatus/{orderNo}")
public R queryPayStatus(@PathVariable String orderNo) {
Map<String,String> map = payLogService.queryPayStatus(orderNo);
if (map == null) {
return R.error().message("支付出错了");
}
//如果返回map里面不为空,通过map获取订单状态
if (map.get("trade_state").equals("SUCCESS")) { //支付成功
//添加记录到支付表,更新订单表订单状态
payLogService.updateOrdersStatus(map);
return R.ok().message("支付成功");
}
return R.ok().message("支付中");
}
//查询订单支付状态
@Override
public Map<String, String> queryPayStatus(String orderNo) {
try {
//1、封装参数
Map m = new HashMap<>();
m.put("appid", "wx74862e0dfcf69954");
m.put("mch_id", "1558950191");
m.put("out_trade_no", orderNo);
m.put("nonce_str", WXPayUtil.generateNonceStr());
//2 发送httpclient
HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery");
client.setXmlParam(WXPayUtil.generateSignedXml(m,"T6m9iK73b0kn9g5v426MKfHQH7X8rKwb"));
client.setHttps(true);
client.post();
//3 得到请求返回内容
String xml = client.getContent();
Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);
//6、转成Map再返回
return resultMap;
}catch(Exception e) {
return null;
}
}
//添加支付记录和更新订单状态
@Override
public void updateOrdersStatus(Map<String, String> map) {
//从map获取订单号
String orderNo = map.get("out_trade_no");
//根据订单号查询订单信息
QueryWrapper<Order> wrapper = new QueryWrapper<>();
wrapper.eq("order_no",orderNo);
Order order = orderService.getOne(wrapper);
//更新订单表订单状态
if(order.getStatus().intValue() == 1) { return; }
order.setStatus(1);//1代表已经支付
orderService.updateById(order);
//向支付表添加支付记录
PayLog payLog = new PayLog();
payLog.setOrderNo(orderNo); //订单号
payLog.setPayTime(new Date()); //订单完成时间
payLog.setPayType(1);//支付类型 1微信
payLog.setTotalFee(order.getTotalFee());//总金额(分)
payLog.setTradeState(map.get("trade_state"));//支付状态
payLog.setTransactionId(map.get("transaction_id")); //流水号
payLog.setAttr(JSONObject.toJSONString(map));
baseMapper.insert(payLog);
}
1、如果课程是免费的,按钮显示“立即观看”
2、如果课程已经支付,按钮显示“立即观看”
3、如果课程没有购买,或者不是免费课程,按钮显示“立即购买”
(1)根据课程id和用户id,查询订单表中的订单状态,如果状态为1表示已经支付,否则没有支付。
//根据课程id和用户id查询订单表中的订单状态
@GetMapping("isBuyCourse/{courseId}/{memberId}")
public boolean isBuyCourse(@PathVariable String courseId, @PathVariable String memberId) {
QueryWrapper<Order> wrapper = new QueryWrapper<>();
wrapper.eq("course_id", courseId);
wrapper.eq("member_id", memberId);
wrapper.eq("status", 1); //支付状态
int count = orderService.count(wrapper);
if (count > 0) { //表示已经支付
return true;
} else {
return false;
}
}
(2)修改原来写的课程详情查询的接口:远程调用刚才的写的接口,判断课程是否支付
@Component
@FeignClient("service-order")
public interface OrdersClient {
//根据课程id和用户id查询订单表中的订单状态
@GetMapping("/eduorder/order/isBuyCourse/{courseId}/{memberId}")
public boolean isBuyCourse(@PathVariable("courseId") String courseId, @PathVariable("memberId") String memberId);
}
