目录
功能描述
实现微信步数的同步(也就是获取当前的微信步数更新至数据库)
实现获取我当日的微信步数的查询
实现按照日排行,周排行,月排行榜的功能
主要思路:通过前端传递的encryptedData加密数据包,解密后得到微信步数,此文中的方式,为前端获取微信步数并将加密数据包发送后端解析。
注意的点:数据库表设计为,一个用户,一天一条步数信息,在当天,第一次获取微信步数后为新增,之后同步均为更新,在根据不同条件获取排名时,将同一个用户,处于这个时间段内的步数相加后排序得出排名。
微信步数同步
请求参数实体
@Data
public class StepDTO implements Serializable {
@ApiModelProperty(value = "小程序用户id",required=true)
@NotBlank(message = "小程序用户id不能为空")
private String appUserId;
@ApiModelProperty(value = "微信昵称",required=true)
@NotBlank(message = "微信昵称不能为空")
private String nickName;
//前端传递的步数数据包 用于解密获取步数
@ApiModelProperty(value = "数据包",required=true)
@NotBlank(message = "前端数据包不能为空")
private String encryptedData;
@ApiModelProperty(value = "iv" ,required=true)
@NotBlank(message = "iv不能为空")
private String iv;
@ApiModelProperty(value = "session_key" ,required=false)
private String sessionKey;
@ApiModelProperty(value = "步数")
private String step;
}
返回结果实体
@Data
public class BizStep extends BaseEntity{
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "小程序用户id")
private String appUserId;
@ApiModelProperty(value = "微信昵称")
private String wechatName;
@ApiModelProperty(value = "姓名")
private String name;
@ApiModelProperty(value = "步数")
private Integer step;
@ApiModelProperty(value = "头像")
private String avatar ;
@ApiModelProperty(value = "session_key")
private String sessionKey;
@ApiModelProperty(value = "手机号码")
private String mobile;
}
微信步数VO
@Data
public class WxRunDataBO {
@ApiModelProperty("stepInfoList")
private List<StepInfoListBO> stepInfoList;
@ApiModelProperty("stepInfoList")
private WatermarkBO watermark;
}
@Data
public class StepInfoListBO {
@ApiModelProperty("timestamp")
private Long timestamp;
@ApiModelProperty("step")
private Integer step;
}
控制层
@Resource
private BizStepService bizStepService;
@PostMapping("/synchronize")
public ResponseEntity<BizStep> uploadStep(@RequestBody @Valid StepDTO stepDTO) {
return ResultUtil.success("同步步数功",this.bizStepService.uploadStep(stepDTO));
}
service
业务层
@Resource
@Lazy
private AccountClient accountClient;
@Resource
private BizStepMapper bizStepMapper;
@Transactional(rollbackFor = Exception.class)
public BizStep uploadStep(StepDTO stepDTO) {
Assert.isTrue(LocalTime.now().isBefore(LocalTime.parse("22:00:00")), "每日同步步数截止时间为22:00");
//获取微信用户数据 此处的目的为获取用户的sessionKey
WxUser wxUser = accountClient.getById(stepDTO.getAppUserId());
stepDTO.setSessionKey(wxUser.getSessionKey());
//获取用户步数
String step = this.decryptWeChatStep(stepDTO);
BizStep bizStep = new BizStep();
//step入库
if (StrUtil.isNotBlank(step)) {
LocalDateTime localDateTime = LocalDateTime.now();
String today = DateTimeFormatter.ofPattern("yyyy-MM-dd").format(localDateTime);
bizStep =
this.getOne(Wrappers.lambdaQuery(BizStep.class).eq(BizStep::getAppUserId,
stepDTO.getAppUserId())
.apply("to_char(created_date,'yyyy-MM-dd') = {0}", today));
if (null != bizStep) {
//如果今天舒徐不为空则更新
bizStep.setStep(Integer.valueOf(step));
this.updateById(bizStep);
//入果用户头像更改,需要更新历史数据的头像 保持一致性
if (!wxUser.getAvatar().equals(bizStep.getAvatar())) {
this.update(Wrappers.lambdaUpdate(BizStep.class).eq(BizStep::getAppUserId, stepDTO.getAppUserId())
.set(BizStep::getAvatar, wxUser.getAvatar()));
}
//保持昵称的一致性 如果昵称改变保持历史数据的一致
if (!wxUser.getNickName().equals(bizStep.getName())) {
this.update(Wrappers.lambdaUpdate(BizStep.class).eq(BizStep::getAppUserId, stepDTO.getAppUserId())
.set(BizStep::getName, wxUser.getNickName()));
}
} else {
BizStep newBizStep = new BizStep();
//如果今天数据为空则新增
newBizStep.setAppUserId(stepDTO.getAppUserId());
newBizStep.setWechatName(wxUser.getNickName());
newBizStep.setName(wxUser.getNickName());
newBizStep.setAvatar(wxUser.getAvatar());
newBizStep.setSessionKey(wxUser.getSessionKey());
newBizStep.setStep(Integer.valueOf(step));
newBizStep.setMobile(wxUser.getMobile());
this.save(newBizStep);
return newBizStep;
}
}
return bizStep;
}
/**
* 微信步数同步
*
* @param stepDTO
* @return
*/
public String decryptWeChatStep(StepDTO stepDTO) {
String step = "0";
byte[] encrypData = Base64.decodeBase64(stepDTO.getEncryptedData());
byte[] ivData = Base64.decodeBase64(stepDTO.getIv());
byte[] sessionKeyB = Base64.decodeBase64(stepDTO.getSessionKey());
try {
AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivData);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
SecretKeySpec keySpec = new SecretKeySpec(sessionKeyB, AES);
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
byte[] doFinal = cipher.doFinal(encrypData);
String result = new String(doFinal);
WxRunDataBO wxRunDataBO = JSON.parseObject(result, WxRunDataBO.class);
List<StepInfoListBO> stepInfoList = wxRunDataBO.getStepInfoList();
Assert.isTrue(stepInfoList.size() > 0, "微信没有您今日的步数");
StepInfoListBO stepInfoListBO = stepInfoList.get(stepInfoList.size() - 1);
log.info("stepInfoListBO={}", stepInfoListBO);
Long timestamp = stepInfoListBO.getTimestamp();
step = String.valueOf(stepInfoListBO.getStep());
log.info("step={}", step);
} catch (Exception e) {
log.error("解析步数报错", e);
throw new IllegalArgumentException("微信步数解析异常");
}
return step;
}
获取我当日的步数
入参实体(结果实体同上BizStep)
@Data
public class MineStepDTO {
@ApiModelProperty(value = "小程序用户id",required=true)
@NotBlank(message = "小程序用户id不能为空")
private String appUserId;
}
controller层
@PostMapping("/getStep")
@ApiOperation(value = "获取我当天的步数" , notes = "")
@PreAuthorize("hasRole('MINI_APP')")
public ResponseEntity<BizStep> getMineStep(@RequestBody @Valid MineStepDTO mineStepDTO) {
return ResultUtil.success(this.bizStepService.getMineStep(mineStepDTO));
}
service
对我方步数表的查询
public BizStep getMineStep(MineStepDTO mineStepDTO) {
LocalDateTime localDateTime = LocalDateTime.now();
String today = DateTimeFormatter.ofPattern("yyyy-MM-dd").format(localDateTime);
BizStep bizStep = this.getOne(Wrappers.lambdaQuery(BizStep.class).eq(BizStep::getAppUserId, mineStepDTO.getAppUserId())
.apply("to_char(created_date,'yyyy-MM-dd') = {0}", today));
if (null == bizStep) {
//如果今日无数据 返回0步
BizStep newBizStep = new BizStep();
//如果今天数据为空则新增
newBizStep.setAppUserId(mineStepDTO.getAppUserId());
newBizStep.setStep(0);
return newBizStep;
}
return bizStep;
}
日排行,周排行,月排行榜的实现
请求实体
@Data
public class RankDTO implements Serializable {
@ApiModelProperty(value = "排序方式 月month 周week 日day", required = true)
@NotBlank(message = "请选择排序方式")
private String type;
@ApiModelProperty(value = "小程序用户id", required = true)
@NotBlank(message = "小程序用户id不能为空")
private String appUserId;
//当前页,默认第一页
@ApiModelProperty(value = "当前页")
private Integer currPage = 1;
//每页记录数,默认为10
@ApiModelProperty(value = "显示条数")
private Integer size = 100000;
}
controller
@PostMapping("/rank")
public ResponseEntity<List<StepRankVO>> getRank(@RequestBody @Valid RankDTO rankDTO) {
return ResultUtil.success(this.bizStepService.getRank(rankDTO));
}
service
public StepRankVO getRank(RankDTO rankDTO) {
StepRankVO stepRankVO = new StepRankVO();
//时间条件
String startDate = "";
String endDate = "";
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
if (MONTH.equals(rankDTO.getType())) {
calendar.add(Calendar.MONTH, 0);
calendar.set(Calendar.DAY_OF_MONTH, 1);
startDate = dateFormat.format(calendar.getTime()) + " 00:00:00";
calendar.add(Calendar.MONTH, 1);
calendar.set(Calendar.DAY_OF_MONTH, 0);
endDate = dateFormat.format(calendar.getTime()) + " 23:59:59";
}
if (WEEK.equals(rankDTO.getType())) {
calendar.setFirstDayOfWeek(Calendar.MONDAY);
calendar.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
startDate = dateFormat.format(calendar.getTime()) + " 00:00:00";
calendar.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
endDate = dateFormat.format(calendar.getTime()) + " 23:59:59";
}
if (DAY.equals(rankDTO.getType())) {
calendar.add(Calendar.DATE, 0);
String today = dateFormat.format(calendar.getTime());
startDate = today + " 00:00:00";
endDate = today + " 23:59:59";
}
Page<RankResultVO> ranks = this.bizStepMapper.getRank(new Page<>(rankDTO.getCurrPage(), rankDTO.getSize()), startDate, endDate);
if (CollectionUtil.isNotEmpty(ranks.getRecords())) {
stepRankVO.setRankList(ranks.getRecords());
RankResultVO myRankVO = new RankResultVO();
List<RankResultVO> myRank = ranks.getRecords().stream().filter(e -> rankDTO.getAppUserId().equals(e.getAppUserId())).collect(Collectors.toList());
if (CollectionUtil.isNotEmpty(myRank)) {
BeanUtils.copyProperties(myRank.get(0), myRankVO);
}
stepRankVO.setMine(myRankVO);
}
return stepRankVO;
}
mapper
@Mapper
public interface BizStepMapper extends BaseMapper<BizStep> {
Page<RankResultVO> getRank(Page<RankResultVO> page, @Param("startDate") String startDate, @Param("endDate") String endDate);
}
xml
<select id="getRank" resultType="com.vo.mp.RankResultVO">
SELECT
rank ( ) over ( ORDER BY t.step DESC ) AS rank,
t.step,
t.app_user_id,
t.NAME,
t.avatar
FROM
(
SELECT
sum( a.step ) AS step,
a.app_user_id,
a.NAME,
a.avatar
FROM
ssyy_biz_step a
where
to_char(created_date,'yyyy-MM-dd HH24:MI:ss') <![CDATA[>=]]> #{startDate}
and
to_char(created_date,'yyyy-MM-dd HH24:MI:ss')<![CDATA[<=]]> #{endDate}
GROUP BY
a.app_user_id,
a.NAME,
a.avatar
) t
</select>