Compare commits

...

3 Commits

Author SHA1 Message Date
24cbd183de 修正连续天数的判断问题 2022-11-21 15:54:20 +08:00
28bb3fd5d5 完成签到的逻辑 2022-11-21 15:31:56 +08:00
7eba424661 查询用户信息 2022-11-21 14:13:45 +08:00
29 changed files with 609 additions and 44 deletions

View File

@ -82,6 +82,18 @@
<artifactId>bcprov-jdk15on</artifactId>
<version>1.57</version>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.33.0</version>
</dependency>
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId>
<version>1.33.0</version>
</dependency>
</dependencies>

View File

@ -0,0 +1,22 @@
package me.xiaoyan.point.api.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class ApplicationConfig implements WebMvcConfigurer {
// 跨域 目前小程序不需要暂时不用考虑
// @Override
// public void addCorsMappings(CorsRegistry registry) {
// registry.addMapping("/**").allowedOrigins("*");
// }
@Override
public void addInterceptors(InterceptorRegistry registry) {
}
}

View File

@ -10,6 +10,7 @@ public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor paginationInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}

View File

@ -0,0 +1,104 @@
package me.xiaoyan.point.api.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
//redis配置类
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 配置连接工厂
template.setConnectionFactory(factory);
//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值默认使用JDK的序列化方式
Jackson2JsonRedisSerializer jackson = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
// 指定要序列化的域field,get和set,以及修饰符范围ANY是都有包括private和public
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 指定序列化输入的类型类必须是非final修饰的final修饰的类比如String,Integer等会跑出异常
//om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
jackson.setObjectMapper(om);
// 值采用json序列化
template.setValueSerializer(jackson);
//使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
// 设置hash key 和value序列化模式
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(jackson);
template.afterPropertiesSet();
return template;
}
/**
* 对hash类型的数据操作
*
* @param redisTemplate
* @return
*/
@Bean
public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForHash();
}
/**
* 对redis字符串类型数据操作
*
* @param redisTemplate
* @return
*/
@Bean
public ValueOperations<String, Object> valueOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForValue();
}
/**
* 对链表类型的数据操作
*
* @param redisTemplate
* @return
*/
@Bean
public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForList();
}
/**
* 对无序集合类型的数据操作
*
* @param redisTemplate
* @return
*/
@Bean
public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForSet();
}
/**
* 对有序集合类型的数据操作
*
* @param redisTemplate
* @return
*/
@Bean
public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForZSet();
}
}

View File

@ -27,7 +27,7 @@ public class SwaggerConfiguration {
// 创建api文档信息
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
return new Docket(DocumentationType.OAS_30)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("me.xiaoyan.point.api.controller"))

View File

@ -0,0 +1,23 @@
package me.xiaoyan.point.api.controller;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import me.xiaoyan.point.api.pojo.PointRecord;
import me.xiaoyan.point.api.pojo.vo.PageParam;
import me.xiaoyan.point.api.service.PointRecordService;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
@RestController
@RequestMapping("point")
public class PointController {
@Resource
private PointRecordService pointRecordService;
public IPage<PointRecord> list(@RequestBody PageParam param){
return pointRecordService.histories(StpUtil.getLoginIdAsInt(),param);
}
}

View File

@ -0,0 +1,30 @@
package me.xiaoyan.point.api.controller;
import cn.dev33.satoken.stp.StpUtil;
import io.swagger.annotations.ApiOperation;
import me.xiaoyan.point.api.pojo.vo.SignResult;
import me.xiaoyan.point.api.service.SignRecordService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@RequestMapping("sign")
public class SignController {
@Resource
private SignRecordService signRecordService;
@ApiOperation("今日签到")
@RequestMapping("today")
public SignResult signToday() {
return signRecordService.signToday(StpUtil.getLoginIdAsInt());
}
@ApiOperation("签到信息")
@RequestMapping("info")
public SignResult signInfo() {
return signRecordService.info(StpUtil.getLoginIdAsInt());
}
}

View File

@ -0,0 +1,45 @@
package me.xiaoyan.point.api.controller;
import cn.dev33.satoken.stp.StpUtil;
import io.swagger.annotations.ApiOperation;
import lombok.SneakyThrows;
import me.xiaoyan.point.api.pojo.UserInfo;
import me.xiaoyan.point.api.pojo.vo.SignResult;
import me.xiaoyan.point.api.pojo.vo.UserLoginData;
import me.xiaoyan.point.api.service.UserInfoService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@RequestMapping("user")
public class UserinfoController {
@Resource
private UserInfoService userInfoService;
// code => openid等信息
// 使用wx.login获取到的code进行登录完成登录后下发用户登录凭证
@ApiOperation("登录")
@PostMapping("login")
@SneakyThrows
public String login(@Validated @RequestBody UserLoginData data) {
//@NotNull(message = "登录code不能为空")
UserInfo info = userInfoService.login(data);
// 完成登录
StpUtil.login(info.getId());
// 可以考虑将用户信息保存到sa-token
// StpUtil.getSession().set("userinfo",info);
return StpUtil.getTokenInfo().getTokenValue();
}
@ApiOperation("获取登录用户的基本信息")
@RequestMapping("info")
public UserInfo getInfo() {
int uid = StpUtil.getLoginIdAsInt();
return userInfoService.getInfoById(uid);
}
}

View File

@ -1,29 +0,0 @@
package me.xiaoyan.point.api.controller;
import lombok.SneakyThrows;
import me.xiaoyan.point.api.pojo.UserInfo;
import me.xiaoyan.point.api.pojo.vo.UserLoginData;
import me.xiaoyan.point.api.service.UserInfoService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
@RestController
public class WechatController {
@Resource
private UserInfoService userInfoService;
// code => openid等信息
// 使用wx.login获取到的code进行登录完成登录后下发用户登录凭证
@RequestMapping("/wechat/login")
@SneakyThrows
public UserInfo login(@Validated @RequestBody UserLoginData data) {
//@NotNull(message = "登录code不能为空")
return userInfoService.login(data);
}
}

View File

@ -1,10 +1,14 @@
package me.xiaoyan.point.api.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import me.xiaoyan.point.api.pojo.Point;
import me.xiaoyan.point.api.pojo.SignRecord;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface SignRecordMapper extends BaseMapper<SignRecord> {
public int signCountByDate(@Param("uid") int uid, @Param("date") String date);
public List<SignRecord> selectRecentDaysRecord(@Param("uid") int uid,@Param("limit")int limit);
}

View File

@ -2,21 +2,26 @@ package me.xiaoyan.point.api.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
@Data
@Accessors(chain = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Point implements Serializable {
@TableId(type = IdType.NONE)
private Integer uid;
private Integer totalPoint;
private Integer validPoint;
private Integer expirePoint;
private Integer expireTime;
private Integer updateTime;
private Date expireTime;
private Date updateTime;
}

View File

@ -3,8 +3,10 @@ package me.xiaoyan.point.api.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
@ -13,6 +15,8 @@ import java.util.Date;
@Data
@Accessors(chain = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TableName("point_record")
public class PointRecord implements Serializable {
@TableId(type = IdType.AUTO)

View File

@ -2,8 +2,10 @@ package me.xiaoyan.point.api.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
@ -11,7 +13,9 @@ import java.util.Date;
@Data
@Accessors(chain = true)
@Builder
@Builder // 如果有builder就需要设置 @NoArgsConstructor @AllArgsConstructor
@NoArgsConstructor
@AllArgsConstructor
public class SignRecord implements Serializable {
@TableId(type = IdType.AUTO)
private Integer id;

View File

@ -1,10 +1,13 @@
package me.xiaoyan.point.api.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
@ -13,6 +16,8 @@ import java.util.Date;
@Data
@Accessors(chain = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TableName("userinfo")
public class UserInfo implements Serializable {
/**
@ -57,4 +62,6 @@ public class UserInfo implements Serializable {
* 状态
*/
private Integer status;
@TableField(exist = false)
private Point pointInfo;
}

View File

@ -0,0 +1,22 @@
package me.xiaoyan.point.api.pojo.vo;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@Builder
public class PageParam {
private Integer page;
private Integer pageSize = 20;
public Page getPage(){
return new Page().setCurrent(page).setSize(pageSize);
}
}

View File

@ -0,0 +1,19 @@
package me.xiaoyan.point.api.pojo.vo;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Builder
@Accessors(chain = true)
public class SignResult {
/**
* 今日是否已经签到
*/
private boolean signToday;
/**
* 连续签到天数
*/
private int continuousDays;
}

View File

@ -3,6 +3,7 @@ package me.xiaoyan.point.api.pojo.vo;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
@Data
@ -13,4 +14,8 @@ public class UserLoginData implements Serializable {
private String iv;
@NotEmpty(message = "code参数不能为空")
private String code;
/**
* 推荐用户id
*/
private int recommend;
}

View File

@ -1,8 +1,12 @@
package me.xiaoyan.point.api.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import me.xiaoyan.point.api.pojo.PointRecord;
import me.xiaoyan.point.api.pojo.UserInfo;
import me.xiaoyan.point.api.pojo.vo.PageParam;
import java.util.List;
public interface PointRecordService extends IService<PointRecord> {
/**
@ -13,4 +17,8 @@ public interface PointRecordService extends IService<PointRecord> {
* @return
*/
public PointRecord record(Integer uid,Integer point,String reason);
IPage<PointRecord> histories(Integer uid, PageParam param);
IPage<PointRecord> exchangeHistories(Integer uid, PageParam param);
}

View File

@ -3,7 +3,20 @@ package me.xiaoyan.point.api.service;
import com.baomidou.mybatisplus.extension.service.IService;
import me.xiaoyan.point.api.pojo.SignRecord;
import me.xiaoyan.point.api.pojo.UserInfo;
import me.xiaoyan.point.api.pojo.vo.SignResult;
public interface SignRecordService extends IService<SignRecord> {
/**
* 今日是否签到
*
* @return
*/
public SignResult signToday(int uid);
/**
* 签到信息
*
* @return
*/
public SignResult info(int uid);
}

View File

@ -12,4 +12,6 @@ public interface UserInfoService extends IService<UserInfo> {
* @return
*/
public UserInfo login(UserLoginData data);
UserInfo getInfoById(Integer uid);
}

View File

@ -1,16 +1,20 @@
package me.xiaoyan.point.api.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import me.xiaoyan.point.api.error.BizException;
import me.xiaoyan.point.api.mapper.PointRecordMapper;
import me.xiaoyan.point.api.pojo.Point;
import me.xiaoyan.point.api.pojo.PointRecord;
import me.xiaoyan.point.api.pojo.vo.PageParam;
import me.xiaoyan.point.api.service.PointRecordService;
import me.xiaoyan.point.api.service.PointService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.List;
@Service
public class PointRecordServiceImpl extends ServiceImpl<PointRecordMapper, PointRecord>
@ -43,4 +47,20 @@ public class PointRecordServiceImpl extends ServiceImpl<PointRecordMapper, Point
this.save(record);
return record;
}
@Override
public IPage<PointRecord> histories(Integer uid, PageParam param) {
QueryWrapper<PointRecord> q = new QueryWrapper();
q.eq("uid",uid);
this.getBaseMapper().selectPage(param.getPage(), q);
return null;
}
public IPage<PointRecord> exchangeHistories(Integer uid, PageParam param) {
QueryWrapper<PointRecord> q = new QueryWrapper();
q.eq("uid",uid);
q.lt("point",0);
this.getBaseMapper().selectPage(param.getPage(), q);
return null;
}
}

View File

@ -1,12 +1,77 @@
package me.xiaoyan.point.api.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import me.xiaoyan.point.api.error.BizException;
import me.xiaoyan.point.api.mapper.SignRecordMapper;
import me.xiaoyan.point.api.pojo.SignRecord;
import me.xiaoyan.point.api.pojo.vo.SignResult;
import me.xiaoyan.point.api.service.PointRecordService;
import me.xiaoyan.point.api.service.SignRecordService;
import me.xiaoyan.point.api.util.DateUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.*;
@Service
public class SignRecordServiceImpl extends ServiceImpl<SignRecordMapper, SignRecord>
implements SignRecordService {
@Resource
private PointRecordService pointRecordService;
public boolean todayIsSign(int uid) {
return this.getBaseMapper().signCountByDate(uid, DateUtils.today()) > 0;
}
@Transactional
@Override
public SignResult signToday(int uid) {
if (todayIsSign(uid)) throw BizException.create("今日已经签过了");
// 计算连续的签到天数 连续签到积分的公式 = 天数 * 5
int days = continuousDays(uid, true);
// TODO 完成签到
SignRecord record = SignRecord.builder()
.uid(uid)
.point(5 * days)
.build();
pointRecordService.record(uid, 5 * days, "签到" + days + "天,获得积分");
save(record);
// 封装签到信息
return SignResult.builder()
.signToday(true)
.continuousDays(days)
.build();
}
public int continuousDays(int uid, boolean isSign) {
// 如果是签到数据则只查6天否则查询7天
final List<SignRecord> signRecords = this.getBaseMapper().selectRecentDaysRecord(uid, isSign ? 6 : 7);
List<Date> dates = new ArrayList<>();
if (isSign) {
dates.add(new Date());
}else{
// 没有记录直接返回0
if(signRecords.size() == 0) return 0;
Date firstDate = signRecords.get(0).getCreateTime();
// 第一个记录是否是今天
if(!DateUtils.isToday(firstDate) && !DateUtils.isYesterday(firstDate)){
return 0;
}
}
signRecords.forEach(it -> dates.add(it.getCreateTime()));
Date[] dateArr = new Date[dates.size()];
return DateUtils.continuousDays(dates.toArray(dateArr));
}
@Override
public SignResult info(int uid) {
return SignResult.builder()
.signToday(todayIsSign(uid)) // 今天是否签到
.continuousDays(continuousDays(uid, false))
.build();
}
}

View File

@ -27,6 +27,7 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.io.Serializable;
@Service
@Slf4j
@ -53,7 +54,20 @@ public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo>
@Resource
private PointRecordService pointRecordService;
public void firstLogin(UserInfo info) {
public void firstLogin(UserInfo info, int recommendUid) {
if (recommendUid > 0) {
// 判断推荐者是否存在
if (this.getById(recommendUid) != null) {
// 存在则设置后续操作
info.setParentId(recommendUid);
// 增加推荐积分
pointRecordService.record(
recommendUid,
recommendPoint,
"推荐新用户"
);
}
}
// 先新增用户基本信息
save(info);
// 新增积分信息
@ -90,7 +104,7 @@ public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo>
userInfo = wechatUserInfo.getUserinfo().setOpenId(sessionData.getOpenid());
//
// 不存在走 第一次登录流程
firstLogin(userInfo);
firstLogin(userInfo, data.getRecommend());
}
// 首先
return userInfo;
@ -126,4 +140,12 @@ public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo>
return data;
}
}
public UserInfo getInfoById(Integer id) {
final UserInfo userinfo = getById(id);
if (userinfo != null) {
userinfo.setPointInfo(pointService.getById(userinfo.getId()));
}
return userinfo;
}
}

View File

@ -0,0 +1,77 @@
package me.xiaoyan.point.api.util;
import lombok.SneakyThrows;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateUtils {
private final static SimpleDateFormat full = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private final static SimpleDateFormat date = new SimpleDateFormat("yyyy-MM-dd");
public static String today() {
return date.format(new Date());
}
public static String now() {
return full.format(new Date());
}
public static String formatDate(Date d) {
return date.format(d);
}
public static String formatDatetime(Date d) {
return full.format(d);
}
@SneakyThrows
public static Date parse(String str) {
return full.parse(str);
}
public static int continuousDays(Date[] dates) {
if(dates.length == 0){
return 0;
}
int totalDays = 1;
for (int i = 1;i < dates.length; i ++){
Date d1 = dates[i-1];
Date d2 = dates[i];
if(!isBetween(d1,d2)){
break;
}
totalDays ++;
}
return totalDays;
}
public static boolean isToday(String d){
try {
return isToday(full.parse(d));
} catch (ParseException e) {
return false;
}
}
public static boolean isToday(Date d){
if(d == null) return false;
return today().equalsIgnoreCase(formatDate(d));
}
public static boolean isYesterday(Date d){
if(d == null) return false;
Date today = new Date(); // 今天
if(today.getTime() < d.getTime()) return false;
return isBetween(today,d);
}
public static boolean isBetween(Date d1,Date d2){
// 直接获取两个日期之间的毫秒,计算
Date date1 = parse(formatDate(d1) + " 00:00:00");
Date date2 = parse(formatDate(d2) + " 00:00:00");
// 两个日期之间相差的毫秒数
long diffTimes = Math.abs(date1.getTime() - date2.getTime());
return diffTimes <= 24 * 3600 * 1000;
}
}

View File

@ -8,6 +8,8 @@ application:
first_login: 200
recommend: 100
spring:
profiles:
active: dev
application:
name: point_api
datasource:
@ -51,4 +53,21 @@ mybatis-plus:
configuration:
# 日志接口
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
cache-enabled: true
cache-enabled: true
############## Sa-Token 配置 (文档: https://sa-token.cc) ##############
sa-token:
# token名称 (同时也是cookie名称)
token-name: token
# token有效期单位s 默认30天, -1代表永不过期
timeout: -1
# token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
activity-timeout: -1
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
is-concurrent: true
# 在多人登录同一账号时是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
is-share: false
# token风格
token-style: uuid
# 是否输出操作日志
is-log: true

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 KiB

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="me.xiaoyan.point.api.mapper.SignRecordMapper">
<select id="signCountByDate" resultType="java.lang.Integer">
select count(*)
from points_sys.sign_record
where date(create_time) = #{date}
and uid = #{uid}
</select>
<select id="selectRecentDaysRecord" resultType="me.xiaoyan.point.api.pojo.SignRecord">
select *
from points_sys.sign_record
where date(create_time) >= date_sub(curdate(), interval 7 day)
and uid = #{uid}
order by create_time desc
limit #{limit};
</select>
</mapper>

View File

@ -0,0 +1,38 @@
package me.xiaoyan.point.api;
import me.xiaoyan.point.api.util.DateUtils;
import org.junit.jupiter.api.Test;
import org.springframework.util.Assert;
import java.util.Date;
public class DateTests {
@Test
void testBetween() {
Date d1 = DateUtils.parse("2022-11-21 12:12:12");
Date d2 = DateUtils.parse("2022-11-20 11:11:11");
Date d3 = DateUtils.parse("2022-11-22 11:11:11");
Assert.isTrue(
DateUtils.isBetween(d1, d2), "不是相邻的"
);
Assert.isTrue(
!DateUtils.isBetween(d3, d2), "是相邻的"
);
}
@Test
void testContinuousDays() {
Date d1 = DateUtils.parse("2022-11-22 12:12:12");
Date d2 = DateUtils.parse("2022-11-21 11:11:11");
Date d3 = DateUtils.parse("2022-11-20 11:11:11");
Date d4 = DateUtils.parse("2022-11-18 11:11:11");
Date d5 = DateUtils.parse("2022-11-17 11:11:11");
// 连续天数
int days = DateUtils.continuousDays(
new Date[]{d1, d2, d3, d4, d5}
);
System.out.println("连续天数" + days);
Assert.isTrue(days == 3, "连续天数不正确");
}
}

View File

@ -11,16 +11,21 @@ public class WechatTests {
@DisplayName("解密用户信息")
@Test
void testDecode(){
String encryptedData = "Ezl1S3BHjBeg/HP0Nim/c9eLLYP5L1kgvqSpR+RQYFG6c3Qx0K5U6btEY5IByCw1OsnZ0hKDccNJn3VA/ZgpkxjDODt+XLcph3KqEL6LDU9BLDRFxCI7u+eHBorz5HWYzGITXiuyPb9NWmGAPXwwp0abFqqaycb5u4oii7I/tnh7NIRIcMxAft1YfbVdzQDjraRHFH5hg6Eh4RGSjy6rg1bG/sMecw4+XWM1psTjKBYNwtsG5oxBWja6DniPhmWU6ZwjVMfgJX5Z7wcw2vtmuDPOMCEE1SEcwmTcQ5YKLlPsPBAJhrF3Lxg9oTD1/IlZ";
String iv = "ckSRPBLA61WONzUDINIWMg==";
// String encryptedData = "Ezl1S3BHjBeg/HP0Nim/c9eLLYP5L1kgvqSpR+RQYFG6c3Qx0K5U6btEY5IByCw1OsnZ0hKDccNJn3VA/ZgpkxjDODt+XLcph3KqEL6LDU9BLDRFxCI7u+eHBorz5HWYzGITXiuyPb9NWmGAPXwwp0abFqqaycb5u4oii7I/tnh7NIRIcMxAft1YfbVdzQDjraRHFH5hg6Eh4RGSjy6rg1bG/sMecw4+XWM1psTjKBYNwtsG5oxBWja6DniPhmWU6ZwjVMfgJX5Z7wcw2vtmuDPOMCEE1SEcwmTcQ5YKLlPsPBAJhrF3Lxg9oTD1/IlZ";
// String iv = "ckSRPBLA61WONzUDINIWMg==";
String sessionKey= "091WPNkl23zoha4xbpnl2cev9l4WPNk7";
String encryptedData = "3mmRZjsqy3Tpgw78jm7HNB3n6/pb8nX9jS1GgngV4tHGSe+yhKIXy7u7kxXz7nKS5lwNqt+UQCXF3EHHd1PQJowtpYWNhLBjNZcGjEdIi6a5pxntmwmFsjTX+SjPNmq5Th4iKSwSyMjLIByZqJt7N3hB5OyT5YAo54lsJrln8DJoPE1m6kddHcLqnJy+g12QQTP1u3tFvCEostrCpb109Bhe889wAxm55ekPHTQ+JWYCsPy0TsjmyvqZCQZyB+RNtlW/ohCpLe4oCOF0Nz9Id6N+Kj6bXvgDcEMBf3vwMXSnbWHuqJAuLUWwbsspB/2T";
String iv = "qfrpHwHjjqPQVlzzxE9D1A==";
// sessionKey= "PKX9EeH6y+pzz8qYrga2jQ==";
// encryptedData = "SCK0Ik7THl+USkwTRqTQ9BYGe6rWlXosQ8fWA3I3AsFHTCuPnjbsjFooIEZVcS6mq911XeP5BJJBpPU6A1O3aNuC9L7ebXqTMQX83bVBtaDQySvCIlyyq26xhm8AbWWexl5994NJDpKkNml9ilbYia99bF8bXzXvLCksQQkz82EpZTqztzmCrTdFBZOIrJ+lDnl7rSBWJvVVtoagzgSq2Ux59LcJtxCukIUoZ8fz54//Hm4GhrLucO4zPKTi087f77Pd9K9Rz3LLJ79NMQHHQLtZ38Ws79IKoHBZ7xHXbl3O8xPeTBrrWeHbNfNs1CbOyoe0RwXVjs/fMR9451PeLVM5jg4fj3IDyTFjpx5aUzQRaIrSY/BjVFJoxU/viwQC6LsCBOyXl5uV5h+qIPC5suFbmhl5Q56eU07wbOjNxktEIJsIgbrT+GWRZVPba3dUo+6RoZySxCMA16TRKfuTwjXceV3oNueFdTNyw05s9N43OkrLeKcz1dFeNnpA9DHEVngB1J7MY4RkazdKaWzrld1DjxW6+rk01GgtAr3+H88=";
// iv = "Ql/m+Ksll5ziCCZj+07J6g==";
WechatUserInfo wechatUserInfo = WechatDecryptDataUtil.decryptData(
"SCK0Ik7THl+USkwTRqTQ9BYGe6rWlXosQ8fWA3I3AsFHTCuPnjbsjFooIEZVcS6mq911XeP5BJJBpPU6A1O3aNuC9L7ebXqTMQX83bVBtaDQySvCIlyyq26xhm8AbWWexl5994NJDpKkNml9ilbYia99bF8bXzXvLCksQQkz82EpZTqztzmCrTdFBZOIrJ+lDnl7rSBWJvVVtoagzgSq2Ux59LcJtxCukIUoZ8fz54//Hm4GhrLucO4zPKTi087f77Pd9K9Rz3LLJ79NMQHHQLtZ38Ws79IKoHBZ7xHXbl3O8xPeTBrrWeHbNfNs1CbOyoe0RwXVjs/fMR9451PeLVM5jg4fj3IDyTFjpx5aUzQRaIrSY/BjVFJoxU/viwQC6LsCBOyXl5uV5h+qIPC5suFbmhl5Q56eU07wbOjNxktEIJsIgbrT+GWRZVPba3dUo+6RoZySxCMA16TRKfuTwjXceV3oNueFdTNyw05s9N43OkrLeKcz1dFeNnpA9DHEVngB1J7MY4RkazdKaWzrld1DjxW6+rk01GgtAr3+H88=",
"PKX9EeH6y+pzz8qYrga2jQ==",
"Ql/m+Ksll5ziCCZj+07J6g=="
encryptedData,
sessionKey,
iv
);
System.out.println(wechatUserInfo);
// System.out.println(wechatUserInfo);
}
}