diff --git a/flossom-common/flossom-common-core/src/main/java/com/flossom/common/core/domain/entity/WxClockStatistics.java b/flossom-common/flossom-common-core/src/main/java/com/flossom/common/core/domain/entity/WxClockStatistics.java new file mode 100644 index 0000000..9183888 --- /dev/null +++ b/flossom-common/flossom-common-core/src/main/java/com/flossom/common/core/domain/entity/WxClockStatistics.java @@ -0,0 +1,116 @@ +package com.flossom.common.core.domain.entity; + +import java.math.BigDecimal; + +import com.flossom.common.core.annotation.Excel; +import com.flossom.common.core.web.domain.BaseEntity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 用户打卡统计信息对象 wx_clock_statistics + * + * @author flossom + * @date 2024-01-30 + */ +public class WxClockStatistics extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** + * $column.columnComment + */ + private Long id; + + /** + * 年份 + */ + @Excel(name = "年") + private Integer year; + + /** + * 月份 + */ + @Excel(name = "月份") + private Integer month; + + /** + * 微信用户 + */ + @Excel(name = "微信用户") + private Long userId; + + /** + * 打卡天数 + */ + @Excel(name = "打卡天数") + private Integer clockNum; + + /** + * 超越多少比例用户 + */ + @Excel(name = "超越多少比例用户") + private BigDecimal percentage; + + /** + * 状态(0正常 1停用) + */ + @Excel(name = "状态", readConverterExp = "0=正常,1=停用") + private Long status; + + public void setId(Long id) { + this.id = id; + } + + public Long getId() { + return id; + } + + public void setMonth(Integer month) { + this.month = month; + } + + public Integer getMonth() { + return month; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public Long getUserId() { + return userId; + } + + public void setClockNum(Integer clockNum) { + this.clockNum = clockNum; + } + + public Integer getClockNum() { + return clockNum; + } + + public void setPercentage(BigDecimal percentage) { + this.percentage = percentage; + } + + public BigDecimal getPercentage() { + return percentage; + } + + public void setStatus(Long status) { + this.status = status; + } + + public Long getStatus() { + return status; + } + + public Integer getYear() { + return year; + } + + public void setYear(Integer year) { + this.year = year; + } +} diff --git a/flossom-common/flossom-common-core/src/main/java/com/flossom/common/core/mapper/WxClockStatisticsMapper.java b/flossom-common/flossom-common-core/src/main/java/com/flossom/common/core/mapper/WxClockStatisticsMapper.java new file mode 100644 index 0000000..0d0fd80 --- /dev/null +++ b/flossom-common/flossom-common-core/src/main/java/com/flossom/common/core/mapper/WxClockStatisticsMapper.java @@ -0,0 +1,65 @@ +package com.flossom.common.core.mapper; + +import com.flossom.common.core.domain.entity.WxClockStatistics; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + + +/** + * 用户打卡统计信息Mapper接口 + * + * @author flossom + * @date 2024-01-30 + */ +public interface WxClockStatisticsMapper { + /** + * 查询用户打卡统计信息 + * + * @param id 用户打卡统计信息主键 + * @return 用户打卡统计信息 + */ + public WxClockStatistics selectWxClockStatisticsById(Long id); + + /** + * 查询用户打卡统计信息列表 + * + * @param wxClockStatistics 用户打卡统计信息 + * @return 用户打卡统计信息集合 + */ + public List selectWxClockStatisticsList(WxClockStatistics wxClockStatistics); + + /** + * 新增用户打卡统计信息 + * + * @param wxClockStatistics 用户打卡统计信息 + * @return 结果 + */ + public int insertWxClockStatistics(WxClockStatistics wxClockStatistics); + + /** + * 修改用户打卡统计信息 + * + * @param wxClockStatistics 用户打卡统计信息 + * @return 结果 + */ + public int updateWxClockStatistics(WxClockStatistics wxClockStatistics); + + /** + * 删除用户打卡统计信息 + * + * @param id 用户打卡统计信息主键 + * @return 结果 + */ + public int deleteWxClockStatisticsById(Long id); + + /** + * 批量删除用户打卡统计信息 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteWxClockStatisticsByIds(Long[] ids); + + List selectByUserIdAndYearMonth(@Param("userIdList") List userIdList, @Param("year") int year, @Param("month") int month); +} diff --git a/flossom-common/flossom-common-core/src/main/java/com/flossom/common/core/mapper/WxUserMemberMapper.java b/flossom-common/flossom-common-core/src/main/java/com/flossom/common/core/mapper/WxUserMemberMapper.java index 88f2a38..e3b4c3e 100644 --- a/flossom-common/flossom-common-core/src/main/java/com/flossom/common/core/mapper/WxUserMemberMapper.java +++ b/flossom-common/flossom-common-core/src/main/java/com/flossom/common/core/mapper/WxUserMemberMapper.java @@ -89,4 +89,7 @@ public interface WxUserMemberMapper { List selectWxUserMemberRetByIdList(@Param("userIdList") List userIdList); List selectWxUserMemberRetListByVm(WxUserMemberVm wxUserMemberVm); + + Integer selectWxUserTotal(); + } diff --git a/flossom-common/flossom-common-core/src/main/java/com/flossom/common/core/utils/PageUtils.java b/flossom-common/flossom-common-core/src/main/java/com/flossom/common/core/utils/PageUtils.java index da43468..b841efd 100644 --- a/flossom-common/flossom-common-core/src/main/java/com/flossom/common/core/utils/PageUtils.java +++ b/flossom-common/flossom-common-core/src/main/java/com/flossom/common/core/utils/PageUtils.java @@ -15,7 +15,7 @@ public class PageUtils extends PageHelper /** * 设置请求分页数据 */ - public static void startPage() + public static Integer startPage() { PageDomain pageDomain = TableSupport.buildPageRequest(); Integer pageNum = pageDomain.getPageNum(); @@ -23,6 +23,7 @@ public class PageUtils extends PageHelper String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy()); Boolean reasonable = pageDomain.getReasonable(); PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable); + return pageNum; } /** diff --git a/flossom-common/flossom-common-core/src/main/java/com/flossom/common/core/web/controller/BaseController.java b/flossom-common/flossom-common-core/src/main/java/com/flossom/common/core/web/controller/BaseController.java index 479ac4f..6878a5c 100644 --- a/flossom-common/flossom-common-core/src/main/java/com/flossom/common/core/web/controller/BaseController.java +++ b/flossom-common/flossom-common-core/src/main/java/com/flossom/common/core/web/controller/BaseController.java @@ -44,9 +44,9 @@ public class BaseController /** * 设置请求分页数据 */ - protected void startPage() + protected Integer startPage() { - PageUtils.startPage(); + return PageUtils.startPage(); } /** diff --git a/flossom-common/flossom-common-core/src/main/resources/mapper/WxClockLogMapper.xml b/flossom-common/flossom-common-core/src/main/resources/mapper/WxClockLogMapper.xml index 9bcb998..4598318 100644 --- a/flossom-common/flossom-common-core/src/main/resources/mapper/WxClockLogMapper.xml +++ b/flossom-common/flossom-common-core/src/main/resources/mapper/WxClockLogMapper.xml @@ -13,6 +13,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + + @@ -23,10 +25,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + + - select id, user_id, instrument_id, instrument_name, clock_content, status, create_by, create_time from wx_clock_log + select id, user_id, instrument_id, instrument_name, clock_content, status, create_by, create_time, update_by, update_time from wx_clock_log + + + and `year` = #{year} + and `month` = #{month} + and user_id = #{userId} + and clock_num = #{clockNum} + and percentage = #{percentage} + and status = #{status} + + + + + + + + + insert into wx_clock_statistics + + `year`, + `month`, + user_id, + clock_num, + percentage, + status, + create_by, + create_time, + update_by, + update_time, + + + #{year}, + #{month}, + #{userId}, + #{clockNum}, + #{percentage}, + #{status}, + #{createBy}, + #{createTime}, + #{updateBy}, + #{updateTime}, + + + + + update wx_clock_statistics + + `year` = #{year}, + `month` = #{month}, + user_id = #{userId}, + clock_num = #{clockNum}, + percentage = #{percentage}, + status = #{status}, + create_by = #{createBy}, + create_time = #{createTime}, + update_by = #{updateBy}, + update_time = #{updateTime}, + + where id = #{id} + + + + delete from wx_clock_statistics where id = #{id} + + + + delete from wx_clock_statistics where id in + + #{id} + + + \ No newline at end of file diff --git a/flossom-common/flossom-common-core/src/main/resources/mapper/WxUserMemberMapper.xml b/flossom-common/flossom-common-core/src/main/resources/mapper/WxUserMemberMapper.xml index 9374677..8aff65f 100644 --- a/flossom-common/flossom-common-core/src/main/resources/mapper/WxUserMemberMapper.xml +++ b/flossom-common/flossom-common-core/src/main/resources/mapper/WxUserMemberMapper.xml @@ -341,6 +341,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" SELECT count(1) as count FROM `wx_user_member` WHERE devices_num > 0 + + diff --git a/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/controller/WxClockLogController.java b/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/controller/WxClockLogController.java index 4f3ff30..d0132e3 100644 --- a/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/controller/WxClockLogController.java +++ b/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/controller/WxClockLogController.java @@ -3,8 +3,7 @@ package com.flossom.miniProgram.controller; import com.flossom.common.core.constant.Constants; import com.flossom.common.core.domain.R; import com.flossom.common.core.domain.SysFile; -import com.flossom.common.core.domain.entity.WxClockLog; -import com.flossom.common.core.domain.entity.WxNursingLog; +import com.flossom.common.core.domain.entity.WxClockStatistics; import com.flossom.common.core.domain.req.WxClockLogReq; import com.flossom.common.core.domain.ret.WxClockLogRet; import com.flossom.common.core.exception.ServiceException; @@ -126,4 +125,27 @@ public class WxClockLogController extends BaseController { } + /** + * 获取打卡统计 + */ + @GetMapping("/clockStatistics") + public TableDataInfo clockStatistics(WxClockStatistics wxClockStatistics) { + Integer pageNum = startPage(); + List list = wxClockLogService.clockStatistics(wxClockStatistics, pageNum); + return getDataTable(list); + } + + + /** + * TODO: 后期迁移到 system 项目中 + * 定时任务接口 + * 每月第一天将上个月在redis中的数据同步到数据库中,计算打卡比率 + */ + @GetMapping("/clockStatisticsTimedTask") + public void clockStatisticsTimedTask(@RequestParam(value = "userIdList", required = false) List userIdList, + @RequestParam(value = "year", required = false) Integer year, + @RequestParam(value = "month", required = false) Integer month) { + wxClockLogService.clockStatisticsTimedTask(userIdList, year, month); + } + } diff --git a/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/service/IWxClockLogService.java b/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/service/IWxClockLogService.java index b006e01..22830d1 100644 --- a/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/service/IWxClockLogService.java +++ b/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/service/IWxClockLogService.java @@ -1,6 +1,7 @@ package com.flossom.miniProgram.service; import com.flossom.common.core.domain.entity.WxClockLog; +import com.flossom.common.core.domain.entity.WxClockStatistics; import com.flossom.common.core.domain.req.WxClockLogReq; import com.flossom.common.core.domain.ret.WxClockLogRet; @@ -16,4 +17,7 @@ public interface IWxClockLogService { List selectWxClockLogList(); + List clockStatistics(WxClockStatistics wxClockStatistics, Integer pageNum); + + void clockStatisticsTimedTask(List userIdList, Integer year, Integer month); } diff --git a/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/service/impl/WxClockLogServiceImpl.java b/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/service/impl/WxClockLogServiceImpl.java index d3e99f0..0f47c58 100644 --- a/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/service/impl/WxClockLogServiceImpl.java +++ b/flossom-modules/flossom-mini-program/src/main/java/com/flossom/miniProgram/service/impl/WxClockLogServiceImpl.java @@ -4,27 +4,31 @@ import com.flossom.common.core.domain.entity.*; import com.flossom.common.core.domain.req.WxClockLogReq; import com.flossom.common.core.domain.ret.WxClockLogRet; import com.flossom.common.core.enums.Status; -import com.flossom.common.core.mapper.WxClockImgMapper; -import com.flossom.common.core.mapper.WxClockInstrumentLogMapper; -import com.flossom.common.core.mapper.WxClockLogMapper; -import com.flossom.common.core.mapper.WxInstrumentMapper; +import com.flossom.common.core.mapper.*; import com.flossom.common.core.utils.DateUtils; import com.flossom.common.security.utils.SecurityUtils; import com.flossom.miniProgram.service.IWxClockLogService; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.util.Arrays; import java.util.List; import java.util.Objects; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import java.util.stream.Stream; @Service +@RefreshScope public class WxClockLogServiceImpl implements IWxClockLogService { @Autowired @@ -39,6 +43,18 @@ public class WxClockLogServiceImpl implements IWxClockLogService { @Autowired private WxInstrumentMapper wxInstrumentMapper; + @Autowired + private WxClockStatisticsMapper wxClockStatisticsMapper; + + @Autowired + private RedisTemplate redisTemplate; + + @Autowired + private WxUserMemberMapper wxUserMemberMapper; + + @Value("${rank.clock.redisKey}") + private String CLOCK_RANK_REDIS_KEY; + @Override @Transactional @@ -86,10 +102,45 @@ public class WxClockLogServiceImpl implements IWxClockLogService { .collect(Collectors.joining(",")); wxClockLog.setInstrumentName(instrumentNameList); } - if (wxClockLog.getId() == null) { - wxClockLogMapper.insertWxClockLog(wxClockLog); - } else { + + if (wxClockLog.getId() != null) { + wxClockLog.setUpdateBy(SecurityUtils.getLoginUser().getWxUserMember().getNickname()); + wxClockLog.setUpdateTime(DateUtils.getNowDate()); wxClockLogMapper.updateWxClockLog(wxClockLog); + } else { + // 当天第一次打卡 + wxClockLogMapper.insertWxClockLog(wxClockLog); + + /* 保存redis构建排名 */ + String redisKey = CLOCK_RANK_REDIS_KEY + LocalDate.now().getYear() + LocalDate.now().getMonthValue(); + redisTemplate.opsForZSet().incrementScore(redisKey, wxUserMember.getId(), 1); + // 保存打卡记录,不计算比例 + List userIdList = Arrays.asList(wxUserMember.getId()); + List wxClockStatisticsList = wxClockStatisticsMapper.selectByUserIdAndYearMonth(userIdList, LocalDate.now().getYear(), LocalDate.now().getMonthValue()); + if (wxClockStatisticsList != null && wxClockStatisticsList.size() == 0) { + // 数据库不存在则新增,也就是当月第一次打卡 + WxClockStatistics save = new WxClockStatistics(); + save.setYear(LocalDate.now().getYear()); + save.setMonth(LocalDate.now().getMonthValue()); + save.setUserId(wxUserMember.getId()); + save.setClockNum(1); + save.setPercentage(new BigDecimal(0)); + save.setStatus(Status.OK.getCode().longValue()); + save.setCreateBy(wxUserMember.getNickname()); + save.setCreateTime(DateUtils.getNowDate()); + save.setUpdateBy(wxUserMember.getNickname()); + save.setUpdateTime(DateUtils.getNowDate()); + wxClockStatisticsMapper.insertWxClockStatistics(save); + } else { + // 数据库存在则更新打卡次数,也就是当月第二次以上打卡 + WxClockStatistics wxClockStatistics = wxClockStatisticsList.get(0); + WxClockStatistics update = new WxClockStatistics(); + update.setId(wxClockStatistics.getId()); + update.setClockNum(wxClockStatistics.getClockNum() + 1); + update.setUpdateBy(wxUserMember.getNickname()); + update.setUpdateTime(DateUtils.getNowDate()); + wxClockStatisticsMapper.updateWxClockStatistics(update); + } } /* 先删除当天的打卡图片,保存打卡图片 */ @@ -142,6 +193,8 @@ public class WxClockLogServiceImpl implements IWxClockLogService { updateClockLog.setInstrumentName(newWxClockLog.getInstrumentName() + "," + instrument.getName()); } updateClockLog.setId(newWxClockLog.getId()); + updateClockLog.setUpdateBy(SecurityUtils.getLoginUser().getWxUserMember().getNickname()); + updateClockLog.setUpdateTime(DateUtils.getNowDate()); wxClockLogMapper.updateWxClockLog(updateClockLog); } } @@ -182,7 +235,7 @@ public class WxClockLogServiceImpl implements IWxClockLogService { WxClockImg wxClockImg = new WxClockImg(); wxClockImg.setUserClockId(wxClockLogRet.getId()); List wxClockImgs = wxClockImgMapper.selectWxClockImgList(wxClockImg); - if (wxClockImgs!=null && wxClockImgs.size()>0) { + if (wxClockImgs != null && wxClockImgs.size() > 0) { List collect = wxClockImgs.stream().map(WxClockImg::getClockImg).collect(Collectors.toList()); wxClockLogRet.setClockImg(collect); } @@ -190,4 +243,62 @@ public class WxClockLogServiceImpl implements IWxClockLogService { } return list; } + + @Override + public List clockStatistics(WxClockStatistics wxClockStatistics, Integer pageNum) { + // 获取历史统计信息,数据在数据库 + List list = wxClockStatisticsMapper.selectWxClockStatisticsList(wxClockStatistics); + if (list != null && list.size() > 0 && pageNum == 1) { + /* 当前月,则需要从redis中获取排名,计算比例 */ + WxClockStatistics isCurrent = list.get(0); + // 获取排名 + String redisKey = CLOCK_RANK_REDIS_KEY + LocalDate.now().getYear() + LocalDate.now().getMonthValue(); + Long rank = redisTemplate.opsForZSet().reverseRank(redisKey, SecurityUtils.getLoginUser().getWxUserMember().getId()); + if (rank != null) { + // 计算超越百分比 + Integer wxUserTotal = wxUserMemberMapper.selectWxUserTotal(); + BigDecimal percentage = new BigDecimal(wxUserTotal - (rank + 1)) + .divide(new BigDecimal(wxUserTotal), 3, RoundingMode.HALF_UP); + isCurrent.setPercentage(percentage); + } + } + return list; + } + + @Override + public void clockStatisticsTimedTask(List userIdList, Integer year, Integer month) { + if (year == null || month == null) { + LocalDate localDate = LocalDate.now().minusMonths(1); + year = localDate.getYear(); + month = localDate.getMonthValue(); + } + List wxClockStatisticsList = wxClockStatisticsMapper.selectByUserIdAndYearMonth(userIdList, year, month); + if (wxClockStatisticsList != null && wxClockStatisticsList.size() > 0) { + String redisKey = CLOCK_RANK_REDIS_KEY + year + month; + for (WxClockStatistics wxClockStatistics : wxClockStatisticsList) { + redisTemplate.opsForZSet().add(redisKey, wxClockStatistics.getUserId(), wxClockStatistics.getClockNum()); + } + } + if (wxClockStatisticsList != null && wxClockStatisticsList.size() > 0) { + String redisKey = CLOCK_RANK_REDIS_KEY + year + month; + WxClockStatistics update; + for (WxClockStatistics wxClockStatistics : wxClockStatisticsList) { + // 获取排名 + Long rank = redisTemplate.opsForZSet().reverseRank(redisKey, wxClockStatistics.getUserId()); + // 计算超越百分比 + Integer wxUserTotal = wxUserMemberMapper.selectWxUserTotal(); + BigDecimal percentage = new BigDecimal(wxUserTotal - (rank + 1)) + .divide(new BigDecimal(wxUserTotal), 3, RoundingMode.HALF_UP); + update = new WxClockStatistics(); + update.setPercentage(percentage); + update.setId(wxClockStatistics.getId()); + // update.setUpdateBy(SecurityUtils.getUsername()); + update.setUpdateTime(DateUtils.getNowDate()); + wxClockStatisticsMapper.updateWxClockStatistics(update); + } + } + redisTemplate.delete(CLOCK_RANK_REDIS_KEY + year + month); + } + + }