1、我们的需求是需要做一个登录的统计:
全年登录人次(人次)
今日登录人次(人次)
今日登录人数(人)
2、我们在mysql中创建两张表:
t_login_statistic(登录统计表)、t_login_statistic_day(登录统计天表)
CREATE TABLE
`t_login_statistic` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`login_type` tinyint(2) NOT NULL COMMENT '登录方式:1:第一种登录,2:第二种登录,3:第三种登录',
`user_id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户id',
`login_time` timestamp NULL DEFAULT NULL COMMENT '登录时间',
PRIMARY KEY (`id`)
) ENGINE = InnoDB AUTO_INCREMENT = 1008 DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '登录统计表'
CREATE TABLE
`t_login_statistic_day` (
`day` varchar(20) NOT NULL COMMENT '日期',
`login_count` int(11) DEFAULT '0' COMMENT '登录人次',
`login_user_count` int(11) DEFAULT '0' COMMENT '登录人数',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`day`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '登录统计天表'
思路:登录事件-检查登录统计天表中是否存在记录(t_login_statistic_day)-如果存在记录-查询当天统计表是否存在数据 (t_login_statistic)-如果存在记录-当天的登录次数加1 (t_login_statistic_day)-如果不存在记录-当天的登录次数加1,当天的登录人次加1 (t_login_statistic_day)-如果统计天表不存在记录-直接当天的登录次数和登录人数插入一条数据
service:
/**
* @author haijin
* @description: 登录统计相关业务
* @date 2025/1/13 15:18
*/
@Slf4j
@Service
public class LoginStatisticService {
@Autowired
private LoginStatisticDao loginStatisticDao;
@Transactional( value = "msbTxManager",rollbackFor = Exception.class)
public ResponseModel addStatics(LoginSuccessEventPara loginSuccessEventPara) {
String userId = UserContextHolder.getContext().getUserId();
log.info("当前用户id【{}】",userId);
LoginStatisticVo loginStatisticVo = LoginStatisticVo.builder()
.loginTime(new Date())
.userId(userId)
.build();
// 事件id
Integer eventId = loginSuccessEventPara.getEventId();
LoginTypeEnum loginTypeEnum = LoginTypeEnum.getInstance(eventId);
if (LoginTypeEnum.LOGIN_4A_EVENT == loginTypeEnum) {
// 4a登录事件
loginStatisticVo.setLoginType(LoginTypeEnum.LOGIN_4A_EVENT.getCode());
} else if (LoginTypeEnum.LOGIN_LOGIN_EVENT == loginTypeEnum) {
// 本地登录
loginStatisticVo.setLoginType(LoginTypeEnum.LOGIN_LOGIN_EVENT.getCode());
} else if (LoginTypeEnum.LOGIN_SSO_EVENT == loginTypeEnum) {
// sso登录
loginStatisticVo.setLoginType(LoginTypeEnum.LOGIN_SSO_EVENT.getCode());
} else {
throw MsbException.logic("登录统计-登录方式异常【{}】",eventId);
}
// 查询当天统计表是否存在数据 (t_login_statistic)
boolean existsByUserIdAndLoginTimeOfDay = loginStatisticDao.existsByUserIdAndLoginTimeOfDay(userId);
// 记录登录统计表 (t_login_statistic)
loginStatisticDao.insertLoginStatistic(loginStatisticVo);
log.info("existsByUserIdAndLoginTimeOfDay【{}】",existsByUserIdAndLoginTimeOfDay);
// 检查登录统计天表中是否存在记录 (t_login_statistic_day)
String curDateStr = DateUtil.date2Str("yyyyMMdd", new Date());
boolean existsByDay = loginStatisticDao.existsByDay(curDateStr);
if (existsByDay) {
// 天表中已经存在了当天的记录了 (t_login_statistic_day)
if (existsByUserIdAndLoginTimeOfDay) {
// 统计表中已经当前userid的数据存在了
// 当天的登录次数加1 (t_login_statistic_day)
loginStatisticDao.incrementLoginCountByDay(curDateStr);
} else {
// 当天的登录次数加1,当天的登录人次加1 (t_login_statistic_day)
loginStatisticDao.incrementLoginUserCountByDay(curDateStr);
}
} else {
// 天表中不存在当天的记录,直接当天的登录次数和登录人数插入一条数据
loginStatisticDao.insertLoginStatisticDay(curDateStr,1,1);
}
return ResponseModel.success();
}
/**
* 登录统计天表查询
* @return igonre
*/
public LoginStatisticResponseVo query() {
// 当前日期
String curDate = DateUtil.date2Str("yyyyMMdd", new Date());
LoginStatisticDayVo loginStatisticDayVo = loginStatisticDao.findByDay(curDate);
Long countCurrentYear = loginStatisticDao.sumLoginCountCurrentYear();
if (loginStatisticDayVo == null) {
countCurrentYear = countCurrentYear == null ? 0L : countCurrentYear;
return LoginStatisticResponseVo.builder()
// 今日登录次数
.loginCount(0L)
// 年登录人次
.yearLoginCount(countCurrentYear)
// 今日登录人数
.loginUserCount(0L)
.build();
}
return LoginStatisticResponseVo.builder()
// 今日登录次数
.loginCount(loginStatisticDayVo.getLoginCount())
// 年登录人次
.yearLoginCount(countCurrentYear)
// 今日登录人数
.loginUserCount(loginStatisticDayVo.getLoginUserCount())
.build();
}
}
dao:
/**
* @author haijin
* @description: 登录统计
* @date 2025/1/13 15:38
*/
@Mapper
@Repository
public interface LoginStatisticDao {
/**
* 插入登录统计表数据
* @param loginStatisticVo 登录统计数据
*/
void insertLoginStatistic(@Param("loginStatisticVo") LoginStatisticVo loginStatisticVo);
/**
* 根据用户ID和当天的登录时间检查是否存在记录
* @param userId 用户ID
* @return ignore
*/
boolean existsByUserIdAndLoginTimeOfDay(@Param("userId") String userId);
/**
* 根据日期(格式:yyyyMMdd)检查登录统计天表中是否存在记录
* @param day 日期字符串,格式为 yyyyMMdd
* @return ignore
*/
boolean existsByDay(@Param("day") String day);
/**
* 向登录统计天表插入或更新数据
* @param day 日期字符串,格式为 yyyyMMdd
* @param loginCount 登录人次
* @param loginUserCount 登录人数
*/
void insertLoginStatisticDay(@Param("day")String day,@Param("loginCount") int loginCount,@Param("loginUserCount") int loginUserCount);
/**
* 根据日期对登录统计天表的 login_count 进行加1操作并更新 update_time
* @param day 日期字符串,格式为 yyyyMMdd
*/
void incrementLoginCountByDay(String day);
/**
* 根据日期对登录统计天表的 login_user_count 进行加1操作并更新 update_time
* @param day 日期字符串,格式为 yyyyMMdd
*/
void incrementLoginUserCountByDay(String day);
/**
* 根据日期查询登录统计天表中的记录
* @param day 日期字符串,格式为 yyyyMMdd
* @return ignore
*/
LoginStatisticDayVo findByDay(String day);
/**
* 查询当前年份的 login_count 总和
* @return ignore
*/
Long sumLoginCountCurrentYear();
}
dao-xml:
<?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="xyz.haijin.dao.login.LoginStatisticDao">
<insert id="insertLoginStatistic" parameterType="xyz.haijin.vo.statistic.login.LoginStatisticVo">
INSERT INTO t_login_statistic (
login_type,
user_id,
login_time
) VALUES (
#{loginStatisticVo.loginType},
#{loginStatisticVo.userId},
#{loginStatisticVo.loginTime}
)
</insert>
<select id="existsByUserIdAndLoginTimeOfDay" parameterType="map" resultType="boolean">
SELECT EXISTS (
SELECT 1
FROM t_login_statistic
WHERE user_id = #{userId}
// 日期在当天内
<![CDATA[ AND login_time BETWEEN CURDATE() AND CURDATE() + INTERVAL 1 DAY - INTERVAL 1 SECOND]]>
)
</select>
<select id="existsByDay" parameterType="string" resultType="boolean">
SELECT EXISTS (
SELECT 1
FROM t_login_statistic_day
WHERE `day` = #{day}
)
</select>
<insert id="insertLoginStatisticDay" parameterType="map">
INSERT INTO t_login_statistic_day (
`day`,
`login_count`,
`login_user_count`,
`update_time`
) VALUES (
#{day},
#{loginCount},
#{loginUserCount},
NOW()
)
</insert>
<update id="incrementLoginCountByDay" parameterType="string">
UPDATE t_login_statistic_day
SET login_count = login_count + 1,
update_time = NOW()
WHERE `day` = #{day}
</update>
<update id="incrementLoginUserCountByDay" parameterType="string">
UPDATE t_login_statistic_day
SET login_user_count = login_user_count + 1
,login_count = login_count + 1,
update_time = NOW()
WHERE `day` = #{day}
</update>
<select id="findByDay" parameterType="string" resultType="xyz.haijin.login.LoginStatisticDayVo">
SELECT
`day`,
`login_count` as loginCount,
`login_user_count` as loginUserCount,
`update_time` as updateTime
FROM t_login_statistic_day
WHERE `day` = #{day}
</select>
<select id="sumLoginCountCurrentYear" resultType="Long">
SELECT SUM(login_count) AS totalLoginCount
FROM t_login_statistic_day
WHERE YEAR(STR_TO_DATE(`day`, '%Y%m%d')) = YEAR(CURRENT_DATE)
</select>
</mapper>
feigin调用:
/**
* @author haijin
* @description: 登录统计微服务接口
* @date 2025/1/14 9:39
*/
@FeignClient(value ="loginStatisticClient", url = "localhost:8080" , path = "/loginStatistic")
public interface LoginStatisticClient {
/**
* 登录统计添加
* @param loginSuccessEventPara 登录统计参数
* @return ignore
*/
@PostMapping("/add")
ResponseModel addStatics(@RequestHeader("Authorization") String authorization, @RequestBody LoginSuccessEventPara loginSuccessEventPara);
/**
* 登录统计查询
* @return ignore
*/
@PostMapping("/query")
ItemResponseModel<LoginStatisticResponseVo> query();
}