news 2026/10/10 13:50:07

签到只记个日期就亏了:连续天数 + 第七天翻倍,这套配置驱动的签到积分我一次讲透

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
签到只记个日期就亏了:连续天数 + 第七天翻倍,这套配置驱动的签到积分我一次讲透

签到这种功能,看着人畜无害,真做起来一堆细节。日结零工系统里我要的是"每天来打开 App"的习惯,光记个sign_date没意义,得跟积分挂钩,还得让用户有连续签到的动力。

落地版本是配置驱动的连续签到:基础积分 + 连续第 7 天翻倍奖励,积分规则全放配置表,运营改数字不用发版。今天把实现思路和几个坑一次讲透。

数据设计:一张表记住连续状态

签到表不存"今天第几天",而是存连续签到天数,查昨天的记录就能算出今天应该给多少:

publicclassUmsSign{privateStringid;privateStringuserId;privateStringsignDate;// yyyy-MM-ddprivateIntegerseriesDays;// 连续签到天数privateIntegerintegral;// 本次签到获得积分privateDatecreateTime;// getter/setter 略}

积分规则不写死在代码里,全部走配置表(BaseConfig),两个 key:JF_SIGN(每日基础积分)和JF_SIGN_SEVEN(连续七日奖励积分)。运营想调数值,后台改配置就行。

-- 签到记录表(精简)CREATETABLE`ums_sign`(`id`VARCHAR(32)NOTNULL,`user_id`VARCHAR(32)NOTNULLCOMMENT'用户ID',`sign_date`DATENOTNULLCOMMENT'签到日期',`series_days`INTDEFAULT1COMMENT'连续签到天数',`integral`INTDEFAULT0COMMENT'本次获得积分',`create_time`DATETIMEDEFAULTNULL,PRIMARYKEY(`id`),UNIQUEKEY`uk_user_date`(`user_id`,`sign_date`)COMMENT'防重复签到')ENGINE=InnoDBDEFAULTCHARSET=utf8mb4COMMENT='签到记录表';

那个唯一索引是兜底,代码里查了"今天是否已签到",但并发下两个请求同时过检查就是双签到,唯一键直接拦住。

核心逻辑:连续天数怎么算

签到的核心就三步:查今天是否已签 → 查昨天是否签过 → 按昨天的连续天数推今天的:

@OverridepublicbooleanaddSign(StringuserId){Stringday=DateUtils.getCurrentTime("yyyy-MM-dd");// 1. 今天是否已签到if(getSign(userId,day)!=null){thrownewRuntimeException("今日已签到,无需重复");}UmsSignsign=newUmsSign();sign.setSignDate(day);sign.setCreateTime(newDate());sign.setUserId(userId);// 2. 基础积分(配置)BaseConfigconfig=configService.getConfigByCode(BizConstants.JF_SIGN);intbaseIntegral=Integer.parseInt(config.getConfigValue());sign.setIntegral(baseIntegral);// 3. 查昨天是否签过StringlastDay=DateUtils.formatDate(DateUtil.offsetDay(newDate(),-1),"yyyy-MM-dd");UmsSignlastSign=getSign(userId,lastDay);if(lastSign!=null){if(lastSign.getSeriesDays()==7){// 昨天已是第7天,今天重新开始sign.setSeriesDays(1);}else{// 连续天数 +1sign.setSeriesDays(lastSign.getSeriesDays()+1);if(lastSign.getSeriesDays()==6){// 第7天,翻倍奖励BaseConfigmax=configService.getConfigByCode(BizConstants.JF_SIGN_SEVEN);sign.setIntegral(Integer.parseInt(max.getConfigValue()));}}}else{// 昨天没签,今天从第1天算起sign.setSeriesDays(1);}// 4. 记积分流水if(lastSign!=null&&lastSign.getSeriesDays()==6){integralLogService.addIntegralLog(userId,BizConstants.JF_SIGN_SEVEN,"","每日签到(连续七日)获得积分:");}else{integralLogService.addIntegralLog(userId,BizConstants.JF_SIGN,"","每日签到获得积分:");}returnthis.save(sign);}

注意几个边界:第 7 天翻倍奖励判断的是lastSign.getSeriesDays() == 6,也就是昨天连续到第 6 天,今天签到就是第 7 天;如果昨天已经是第 7 天(== 7),今天重新从第 1 天开始。这里最容易写错的是把"今天第几天"跟"昨天第几天"搞混。

前端要的"今天能拿多少分"

签到页顶部要显示"今天签到可得 X 积分",这个接口把三档数值一次性给前端,前端就不用写死规则了:

@OverridepublicMapgetLeftReSignCount(StringuserId){Mapmap=newHashMap<>();BaseConfigconfig=configService.getConfigByCode(BizConstants.JF_SIGN);intbaseIntegral=Integer.parseInt(config.getConfigValue());BaseConfigseven=configService.getConfigByCode(BizConstants.JF_SIGN_SEVEN);intsevenIntegral=Integer.parseInt(seven.getConfigValue());map.put("baseIntegral",baseIntegral);map.put("sevenIntegral",sevenIntegral);StringlastDay=DateUtils.formatDate(DateUtil.offsetDay(newDate(),-1),"yyyy-MM-dd");UmsSignlastSign=getSign(userId,lastDay);inttodaySign=baseIntegral;if(lastSign!=null&&lastSign.getSeriesDays()==6){todaySign=sevenIntegral;// 明天就是第7天}map.put("todaySign",todaySign);returnmap;}

这里有个和直觉相反的点:判断"明天签到能拿多少"看的是昨天的记录。昨天连续到第 6 天,今天签到拿翻倍;昨天连续到第 7 天,今天签了就是新一轮第 1 天,拿基础分。前端如果不理解这个逻辑,容易把"连续 7 天"显示成"连续 8 天"。

踩坑:并发双签 + 补签留的坑

现象:上线第一周,后台查到同一天同一个用户两条签到记录,积分还发了双份。排查过程:翻日志发现两条记录 create_time 只差 300ms,怀疑并发;定位思路:getSign检查"今天是否已签"是查完再插入,两个请求都查到"没签",双双落库;最终解决:给(user_id, sign_date)加唯一索引兜底,同时签到时包一层事务,先 insert 后检查唯一键冲突,冲突就回滚报"今日已签到"。唯一索引加完后,并发场景直接抛DuplicateKeyException,再也没出现过双签。

顺带说个真实的坑:代码里addReSign(补签)至今是空实现return false——需求砍了但接口留着。这种半成品接口如果前端调了会拿到"失败"但没有明确提示,宁可没有也别留假接口,要么实现要么直接删。

积分流水:签到是入口,账要记清楚

签到本身不是目的,积分后面要能换东西,所以每一笔积分都得有流水。签到积分走integral_log,加积分和写流水在同一个事务里:

CREATETABLE`integral_log`(`id`VARCHAR(32)NOTNULL,`user_id`VARCHAR(32)NOTNULLCOMMENT'用户ID',`change_type`VARCHAR(32)NOTNULLCOMMENT'JF_SIGN/JF_SIGN_SEVEN/...',`change_val`INTNOTNULLCOMMENT'变动值,正负',`balance`INTNOTNULLCOMMENT'变动后余额快照',`remark`VARCHAR(255)DEFAULTNULL,`create_time`DATETIMEDEFAULTNULL,PRIMARYKEY(`id`),KEY`idx_user_time`(`user_id`,`create_time`))ENGINE=InnoDBDEFAULTCHARSET=utf8mb4COMMENT='积分流水表';

balance那个字段是余额快照,对账神器。用户说"我积分少了",拉流水一算就知道差在哪一笔;没有快照就只能猜。签到列表接口顺带分页,后台看签到记录、前端看我的签到历史都用它:

@OverridepublicIPagegetSignList(Pagepage,UmsSignparams){returnbaseMapper.getSignList(page,params);}

这里也提醒一句:签到查询接口里orderByDesc("create_time")和orderByDesc("sign_date")是两个排序口径,列表页用创建时间、日历视图用签到日期,别混。

前端状态:今天签了没、连续几天、还剩几次

后端把数据给全了,前端才能少写死逻辑。签到页需要三个状态:今天是否已签、当前连续几天、明天签到能拿多少。后端getLastSign一次返回最近两天记录,前端自己推连续天数;getSignIntegral返回三档积分数值。前端拿到后渲染日历格子:已签的格子打勾、未签的置灰、连续第 7 天的格子加个星星角标。

有个交互细节值得说:连续天数断了要不要提示?我们的处理是今天没签、昨天签过,签到页顶部弹一条"昨天没签到,连续天数已中断,今天重新开始",比用户自己发现少了几十积分体验好得多。这逻辑放前端判断:昨天无记录 + 前天有记录,就提示中断。

签到还有个反作弊的点:同一账号多端签到。后端按user_id + sign_date唯一键兜底后,同一用户手机、平板各签一次,第二次直接抛"今日已签到",积分不会重复发。用户视角是"多端同步",体验没问题。

可以直接抄走的清单

  • 连续天数存字段:series_days随记录落库,查昨天一条记录就能推今天。
  • 规则进配置表:基础分、翻倍分走BaseConfig,运营改配置不发版。
  • 唯一索引兜底:(user_id, sign_date)唯一键防并发双签,比代码检查可靠。
  • 第 7 天判定用昨天:lastSign.seriesDays == 6是翻倍条件,别用今天的。
  • 积分必须留流水:加积分的同时写integral_log,对账、补发全靠它。

项目源码:https://gitee.com/gzqkl/xllg

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/10 13:50:01

Linux虚拟桌面显示协议落地:内核KMS、无头渲染与协议分层实战

简介&#xff1a;这是一份面向Linux系统开发者、云计算工程师及桌面云技术研究人员的专业文献&#xff0c;聚焦虚拟桌面显示协议在Linux平台下的实现路径。内容从Linux主流图形系统X Window System的X Server、X Protocol、X Client三层架构讲起&#xff0c;逐项解析直接X11协议…

作者头像 李华
网站建设 2026/10/10 13:45:20

UNet实现遥感图像语义分割:PyTorch毕业设计源码与踩坑实践

简介&#xff1a;一份基于UNet的遥感图像语义分割Python毕业设计项目&#xff0c;含可运行源码与配套论文&#xff0c;面向计算机、地理信息等专业学生&#xff0c;适用于毕业设计、课程设计及期末大作业。项目源码经本地编译运行&#xff0c;评审分达98分&#xff0c;难度适中…

作者头像 李华
网站建设 2026/10/10 13:44:55

功能测试从入门到进阶:流程、用例设计与避坑指南

刚带过一批转行的新人&#xff0c;发现很多人对“功能测试”这事儿&#xff0c;要么觉得太简单&#xff0c;要么觉得没技术含量。但实际面试和工作中&#xff0c;最容易被问住的恰恰是这些基础问题&#xff1a;功能测试到底测什么&#xff1f;怎么保证用例不遗漏&#xff1f;提…

作者头像 李华
网站建设 2026/10/10 13:43:06

maven常用仓库地址、阿里云中央仓库首页

常见仓库地址 <mirror><id>nexus-aliyun</id><mirrorOf>central</mirrorOf><name>Nexus aliyun</name><url>http://maven.aliyun.com/nexus/content/groups/public</url></mirror><repositories><repos…

作者头像 李华
网站建设 2026/10/10 13:42:51

用户愤怒模式:用极端操作锤炼软件稳定性

1. 先说透&#xff1a;为什么“用户愤怒模式”才是软件质量的试金石那天的上线前的平静&#xff0c;是被一条工单撕裂的。某云盘项目在凌晨接到了大批用户投诉&#xff0c;说上传图片一直转圈&#xff0c;转着转着直接闪退。团队所有人都在前排排队看监控&#xff0c;结果后台的…

作者头像 李华
网站建设 2026/10/10 13:42:19

C++ ONNX Runtime 部署 YOLOv11-CLS 图像分类模型实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华