repo init

This commit is contained in:
ivmiku
2024-08-03 20:30:51 +08:00
commit dc534d7ee6
32 changed files with 1321 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
package com.ivmiku.tutorial;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.ivmiku.tutorial.mapper")
public class Main8072 {
public static void main(String[] args) {
SpringApplication.run(Main8072.class, args);
}
}

View File

@@ -0,0 +1,30 @@
package com.ivmiku.tutorial.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(lettuceConnectionFactory);
// 设置key序列化方式stringRedisSerializer.string() 等价于 new StringRedisSerializer()
redisTemplate.setKeySerializer(RedisSerializer.string());
// 设置value的序列化方式json使用GenericJackson2JsonRedisSerializer替换默认序列化RedisSerializer.json() 等价于 new GenericJackson2JsonRedisSerializer()
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
// 设置hash的key的序列化方式
redisTemplate.setHashKeySerializer(RedisSerializer.string());
// 设置hash的value的序列化方式
redisTemplate.setHashValueSerializer(RedisSerializer.json());
// 使配置生效
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}

View File

@@ -0,0 +1,128 @@
package com.ivmiku.tutorial.config;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.binarywang.wx.miniapp.bean.WxMaKefuMessage;
import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage;
import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
import cn.binarywang.wx.miniapp.message.WxMaMessageHandler;
import cn.binarywang.wx.miniapp.message.WxMaMessageRouter;
import com.alibaba.nacos.shaded.com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.error.WxRuntimeException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.File;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@Configuration
@EnableConfigurationProperties(WxMaProperties.class)
public class WxMaConfiguration {
private final WxMaProperties properties;
@Autowired
public WxMaConfiguration(WxMaProperties properties) {
this.properties = properties;
}
@Bean
public WxMaService wxMaService() {
List<WxMaProperties.Config> configs = this.properties.getConfigs();
if (configs == null) {
throw new WxRuntimeException("大哥拜托先看下项目首页的说明readme文件添加下相关配置注意别配错了");
}
WxMaService maService = new WxMaServiceImpl();
maService.setMultiConfigs(
configs.stream()
.map(a -> {
WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
// WxMaDefaultConfigImpl config = new WxMaRedisConfigImpl(new JedisPool());
// 使用上面的配置时需要同时引入jedis-lock的依赖否则会报类无法找到的异常
config.setAppid(a.getAppid());
config.setSecret(a.getSecret());
config.setToken(a.getToken());
config.setAesKey(a.getAesKey());
config.setMsgDataFormat(a.getMsgDataFormat());
return config;
}).collect(Collectors.toMap(WxMaDefaultConfigImpl::getAppid, a -> a, (o, n) -> o)));
return maService;
}
@Bean
public WxMaMessageRouter wxMaMessageRouter(WxMaService wxMaService) {
final WxMaMessageRouter router = new WxMaMessageRouter(wxMaService);
router
.rule().handler(logHandler).next()
.rule().async(false).content("订阅消息").handler(subscribeMsgHandler).end()
.rule().async(false).content("文本").handler(textHandler).end()
.rule().async(false).content("图片").handler(picHandler).end()
.rule().async(false).content("二维码").handler(qrcodeHandler).end();
return router;
}
private final WxMaMessageHandler subscribeMsgHandler = (wxMessage, context, service, sessionManager) -> {
service.getMsgService().sendSubscribeMsg(WxMaSubscribeMessage.builder()
.templateId("此处更换为自己的模板id")
.data(Lists.newArrayList(
new WxMaSubscribeMessage.MsgData("keyword1", "339208499")))
.toUser(wxMessage.getFromUser())
.build());
return null;
};
private final WxMaMessageHandler logHandler = (wxMessage, context, service, sessionManager) -> {
log.info("收到消息:" + wxMessage.toString());
service.getMsgService().sendKefuMsg(WxMaKefuMessage.newTextBuilder().content("收到信息为:" + wxMessage.toJson())
.toUser(wxMessage.getFromUser()).build());
return null;
};
private final WxMaMessageHandler textHandler = (wxMessage, context, service, sessionManager) -> {
service.getMsgService().sendKefuMsg(WxMaKefuMessage.newTextBuilder().content("回复文本消息")
.toUser(wxMessage.getFromUser()).build());
return null;
};
private final WxMaMessageHandler picHandler = (wxMessage, context, service, sessionManager) -> {
try {
WxMediaUploadResult uploadResult = service.getMediaService()
.uploadMedia("image", "png",
ClassLoader.getSystemResourceAsStream("tmp.png"));
service.getMsgService().sendKefuMsg(
WxMaKefuMessage
.newImageBuilder()
.mediaId(uploadResult.getMediaId())
.toUser(wxMessage.getFromUser())
.build());
} catch (WxErrorException e) {
e.printStackTrace();
}
return null;
};
private final WxMaMessageHandler qrcodeHandler = (wxMessage, context, service, sessionManager) -> {
try {
final File file = service.getQrcodeService().createQrcode("123", 430);
WxMediaUploadResult uploadResult = service.getMediaService().uploadMedia("image", file);
service.getMsgService().sendKefuMsg(
WxMaKefuMessage
.newImageBuilder()
.mediaId(uploadResult.getMediaId())
.toUser(wxMessage.getFromUser())
.build());
} catch (WxErrorException e) {
e.printStackTrace();
}
return null;
};
}

View File

@@ -0,0 +1,42 @@
package com.ivmiku.tutorial.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.List;
@Data
@ConfigurationProperties(prefix = "wx.miniapp")
public class WxMaProperties {
private List<Config> configs;
@Data
public static class Config {
/**
* 设置微信小程序的appid
*/
private String appid;
/**
* 设置微信小程序的Secret
*/
private String secret;
/**
* 设置微信小程序消息服务器配置的token
*/
private String token;
/**
* 设置微信小程序消息服务器配置的EncodingAESKey
*/
private String aesKey;
/**
* 消息格式XML或者JSON
*/
private String msgDataFormat;
}
}

View File

@@ -0,0 +1,59 @@
package com.ivmiku.tutorial.controller;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.util.WxMaConfigHolder;
import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson2.JSON;
import com.ivmiku.tutorial.entity.User;
import com.ivmiku.tutorial.mapper.UserMapper;
import com.ivmiku.tutorial.response.Result;
import com.ivmiku.tutorial.utils.RedisUtil;
import jakarta.annotation.Resource;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private WxMaService wxMaService;
@Resource
private UserMapper userMapper;
@Resource
private RedisUtil redisUtil;
@GetMapping("/login")
public Object login(@RequestParam(name = "code") String code, @RequestParam(name = "rawdata") String rawData, @RequestParam(name = "signature") String signature
) {
WxMaJscode2SessionResult session;
try {
session = wxMaService.getUserService().getSessionInfo(code);
} catch (WxErrorException e) {
return JSON.toJSON(Result.error(e.getMessage()));
}
StpUtil.login(session.getOpenid());
HashMap<String, Object> map = new HashMap<>();
if (userMapper.selectById(session.getOpenid()) == null) {
if (!wxMaService.getUserService().checkUserInfo(session.getSessionKey(), rawData, signature)) {
WxMaConfigHolder.remove();//清理ThreadLocal
return JSON.toJSON(Result.error("用户数据验证错误!"));
}
JSONObject raw = JSONObject.parseObject(rawData);
User user = new User();
user.setOpenid(session.getOpenid());
user.setNickname(raw.getString("nickname"));
user.setAvatarUrl(raw.getString("avatarUrl"));
userMapper.insert(user);
}
map.put("token", StpUtil.getTokenValue());
redisUtil.insertKey(session.getOpenid(), session.getSessionKey());
WxMaConfigHolder.remove();
return JSON.toJSON(Result.ok(map));
}
}

View File

@@ -0,0 +1,14 @@
package com.ivmiku.tutorial.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@TableName("user")
@Data
public class User {
@TableId
private String openid;
private String nickname;
private String avatarUrl;
}

View File

@@ -0,0 +1,7 @@
package com.ivmiku.tutorial.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ivmiku.tutorial.entity.User;
public interface UserMapper extends BaseMapper<User> {
}

View File

@@ -0,0 +1,61 @@
package com.ivmiku.tutorial.utils;
import cn.dev33.satoken.exception.SaTokenException;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.fastjson2.JSON;
import com.ivmiku.tutorial.response.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import java.sql.SQLException;
import java.text.ParseException;
/**
* @author Aurora
*/
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(value = SaTokenException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public Object SaTokenExceptionHandler(SaTokenException e) {
log.error("SaTokenException:" + e.getLocalizedMessage());
return JSON.toJSON(Result.error("身份认证错误:" + e.getMessage()));
}
@ExceptionHandler(value = ParseException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public Object parseExceptionHandler(ParseException e) {
log.error("ParseException:" + e.getLocalizedMessage());
return JSON.toJSON(Result.error("请检查日期是否合法"));
}
@ExceptionHandler(value = SQLException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public Object SQLExceptionHandler(SQLException e) {
log.error("SQLException:" + e.getLocalizedMessage());
return JSON.toJSON(Result.error("数据库出错!(内部错误)\n" + e.getMessage()));
}
@ExceptionHandler(value = BlockException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public Object sentinelHandler(BlockException e) {
return JSON.toJSON(Result.error("系统繁忙,请稍后再试"));
}
@ExceptionHandler(value = Exception.class)
@ResponseBody
public Object exceptionHandler(Exception e) {
log.error("Exception:" + e.getLocalizedMessage());
return JSON.toJSON(Result.error("服务器内部错误:" + e.getClass() + ":" + e.getMessage()));
}
}

View File

@@ -0,0 +1,22 @@
package com.ivmiku.tutorial.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
/**
* @author Aurora
*/
@Component
public class RedisUtil {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public void insertKey(String userId, String sessionKey) {
redisTemplate.opsForValue().set("sessionkey:" + userId, sessionKey);
}
public String getKey(String userId) {
return (String) redisTemplate.opsForValue().get("sessionkey:" + userId);
}
}

View File

@@ -0,0 +1,21 @@
wx.miniapp.configs[0].appid=wx0d4fdb5c7bf3b12b
wx.miniapp.configs[0].secret=989f155fcc3aee616568473faf1b1d3b
spring.data.redis.host=redis
spring.data.redis.port=6379
spring.data.redis.database=0
spring.application.name=user
server.port=8072
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=Shuodedaoli114514
spring.datasource.url=jdbc:mysql://mysql:4514/tutorial?useUnicode=true&characterEncoding=utf8&useSSL=false&ServerTimezone=Asia/Shanghai
spring.cloud.nacos.discovery.server-addr=nacos:8848
spring.cloud.nacos.discovery.enabled=true
management.zipkin.tracing.endpoint=http://zipkin:9411/api/v2/spans
management.tracing.sampling.probability=1.0

View File

@@ -0,0 +1,15 @@
wx.miniapp.configs[0].appid=wx0d4fdb5c7bf3b12b
wx.miniapp.configs[0].secret=989f155fcc3aee616568473faf1b1d3b
spring.data.redis.host=localhost
spring.data.redis.port=6379
spring.data.redis.database=0
spring.application.name=user
server.port=8072
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=12345abcde
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/tutorial?useUnicode=true&characterEncoding=utf8&useSSL=false&ServerTimezone=Asia/Shanghai

View File

@@ -0,0 +1 @@
spring.profiles.active=dep