goframe框架签到系统项目(安装 redis )

文章目录

需求说明


一、功能定位

签到功能作为用户激励体系的核心组件,已成为互联网产品的标准配置。该功能巧妙连接了用户行为激励与运营活动,通过轻量级的交互方式实现多重价值:用户获得即时满足感与成就感,产品方则有效提升活跃度与用户粘性。

二、功能定义与表现形式

签到功能引导用户定期访问产品并完成特定操作(如点击按钮、访问页面)的机制。为增强趣味性,不同产品会进行场景化包装:

  • 基础型:"签到","打卡"
  • 场景型:"喂养宠物","收取能量"
  • 游戏化:"抽奖转盘","集卡玩法"

三、设计目标体系

  1. 用户维度

    追求"实际收益=奖励价值-操作成本"的最优解。奖励形式包括:

    物质奖励:积分、优惠券、实物

    精神奖励:徽章、等级、排行榜

  2. 产品维度

  3. 商业维度

    通过提升 DAU/MAU 等核心指标,为广告变现、会员服务等商业模式奠定用户基础。

四、体验设计原则

核心是构建"高价值感知 - 低成本操作"的平衡:

价值可视化:即时反馈奖励内容

操作简洁:1-2步完成流程

成长可视:进度条、连续天数提示

五、实现方式

一、互动签到

  1. 手动签到

核心特点:由用户主动触发操作(如点击按钮、滑动屏幕幕等),需用用户明确感知并参与,互动存在感强,可强化用户对签到行为的认知。

典型场景:

内容社区(如小红书、知乎):通过手动签到激励用户每日活跃,积累签到积分可换取社区权益(如勋章、流量奖励等)。

电商平台(如淘宝、京东):结合"每日签到领红包/优惠券"活动,吸引用户进入APP完成签到,提升商品浏览转率。

教育类产品:用于课后打卡,记录学习进度,培养用户持续学习习惯。

  1. 自动签到

核心特点:用户无感知完成签到,无需额外操作,仅需保持登录状态或启动应用即可触发,交互流程轻量化。

典型场景:

工具类产品(如办公软件WPS、笔记应用印象笔记):用户以效率为导向,自动签到到激励用户活跃数据。

高频使用的社交APP(如微信、QQ):默认自动签到统计登录天数,用于隐性激励(如等级体系成长),不干扰核心社交功能。

系统级应用(如手机系统自带的健康管理APP):自动同步设备使用数据,实现"无感打卡"健康记录。

  1. 互动签到

核心特点:通过多步骤任务或趣味玩法引导用户参与,融合"签到 + 互动"双重属性,提升用户参与和动机和娱乐性。

典型场景:

游戏化产品(如蚂蚁庄园、合成类小游戏):设置"签到 + 收集道具 + 解锁剧情"链条,例:每天签到领取种子,种植后换取奖励。

社交电商/裂变活动(如拼多多"签到领现金"):结合分享、浏览商品等任务,用户完成"签到→做任务→提现"流程,驱动拉新和转化。

儿童教育APP:采用动画、答题等互动形式,将签到转化为趣味关卡(如"每日解锁一个汉字拼图"),吸引低年龄用户持续使用。

  1. 周期设计

每日签到:基础奖励,培养习惯。

连续签到:周期内不断签到,奖励递增。

累计奖励:弹性机制,降低断签惩罚。

混合模式:结合每日+周期奖励。

六、适用性分析

适用场景

内容驱动型产品(社交、电商、资讯),需培养用户使用习惯,提升打开频次。

不适用场景

功能驱动型产品(工具、效率类),用户需求具有即时性,签到行为与核心价值关联弱。

Prompt 相关(AI 辅助开发)

生成UI设计图

你是一位资深全栈软件开发工程师,同时精通产品规划和UI设计。现在我要开发一个每天签到领取积分的APP,主要页面和功能如下:

项目有两个页面,首页和积分详情页。

  1. 首页页面最左展示总积分和查看详情链接,点击积分详情跳转到积分详情页查看积分明细记录。
  2. 首页中间展示当前月积分,展示当月连续签到天数和剩余可补签次数,以及当前月份每日签到详情。
  3. 积分详情页以列表形式展示积分明细记录。

参考下面的内容,结合用户需求,以产品经理的视角去规划APP的功能、页面和交互:

  1. 作为设计师思考这些原型界面的设计,并以设计师的视角去输出完整的UI/UX;
  2. 直接使用交互的HTML模板,确保所有元素都符合移动端的交互习惯,文件命名为:prototype.html;
  3. 使用 FontAwesome 等开源图标库,让原型更精美和接近真实;
  4. 引入 tailwindcss 来完成页面样式,而不使用 style 样式;
  5. 图片使用 unsplash。

输出的 prototype.html 需要可以直接进行前端开发。

生成前端代码

你是一个资深全栈开发工程师,使用 Vue3 + typescript + vite 设计开发一个每天签到领积分的 H5 应用。

  1. 以 prototype.html 文件为原型进行开发,去掉项目中无用的初始 vue 组件和样式。
  2. 尽量使用社区成熟的 UI 框架或插件来实现,使用 tailwindcss 和 FontAwesome。
  3. 使用 pinia 管理状态。
  4. 使用 axios 进行网络请求。

后端提供以下几个接口。

  1. 查询积分接口,返回总积分。
  2. 签到记录接口,返回当月签到记录、当月连续签到天数、当月可补签次数。
  3. 当天签到接口。
  4. 补签接口,参数带日期。
  5. 积分记录列表接口。

功能点:

  1. 签到领积分,每天签到领 1 积分。
  2. 登录后展示总积分,点击积分详情展示积分明细。
  3. 每月连续签到到 3 天、7 天、15 天有额外积分奖励,分别奖励 5 积分、10 积分、20 积分,每个月满签奖励 100 积分。
  4. 当月支持补签,每次补签消耗 100 积分,且每月最多补签三次。
  5. 展示用户的本月连续签到次数和当月剩余补签次数。

生成数据表设计和API设计

你是一个资深软件架构师,熟悉各种互联网应用程序的架构设计。现在需要你设计一个每日签到领取积分的应用程序。

主要功能点:

  1. 用户每天签到可得 1 积分。
  2. 每个月连续签到 3、7、15 天和当月满签还会额外获得 5 积分、10 积分、20 积分和 100 积分。
  3. 需要统计用户的累计签到次数和当月连续签到次数。
  4. 支持用户补签当月的记录,补签 1 次需要消耗 100 积分,每个月最多补签 3 次。
  5. 首页展示用户总积分和签到日历(标识出补签记录),支持查看之前的签到日历。
  6. 支持用户查看积分详情。

参考互联网大厂的设计规范和经验,请输出 MySQL 数据库表结构设计和遵循 RESTful 风格的 API 设计。

生成了 prototype.html 和 API 文档之后,你就找一个 code agent(Cursor/trae)让他写前端代码了。

后端技术方案设计

用户模块

主要实现 用户注册、用户登录和用户鉴权三部分。

鉴权使用 JWT,同时提供 刷新 Token 接口,支持使用 refresh token 获取新 token。

签到模块

签到功能分为「每日签到」和「补签」两部分,同时需要返回用户签到月历和当月连续签到天数。

每日签到只允许签到当天的日期。

补签只允许补签当月的日期,并且每个月最多补签 3 次。

签到还需要额外考虑,连续签到奖励。

  • 每日签到触发连续签到奖励。
  • 补签也可以触发连续签到奖励。

积分模块

积分模块主要为用户当前积分和积分明细两个功能,同时还要支持用户签到功能的联动。

  • 每日签到 +1 积分
  • 每月连续签到额外奖励 +x 积分
  • 用户补签消耗 -100 积分

RESTful API

用户模块相关API

签到模块相关API

积分模块相关API

数据表设计

sql 复制代码
-- 用户基本信息表
DROP TABLE IF EXISTS `userinfo`;

CREATE TABLE `userinfo` (
  `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `user_id` BIGINT UNSIGNED NOT NULL COMMENT '用户ID',
  `username` VARCHAR(50) NOT NULL COMMENT '用户名',
  `password` VARCHAR(60) NOT NULL COMMENT '用户密码(bcrypt 加密)',
  `email` VARCHAR(100) COMMENT '用户邮箱',
  `avatar` VARCHAR(512) COMMENT '用户头像',
  `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `deleted_at` TIMESTAMP NULL COMMENT '删除时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_user_id` (`user_id`),
  UNIQUE KEY `uk_username` (`username`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户基本信息表';


-- 用户积分汇总表
DROP TABLE IF EXISTS `user_points`;

CREATE TABLE `user_points` (
  `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `user_id` BIGINT UNSIGNED NOT NULL COMMENT '用户ID',
  `points` BIGINT DEFAULT 0 COMMENT '当前可用积分',
  `points_total` BIGINT DEFAULT 0 COMMENT '累计获得积分',
  `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `deleted_at` TIMESTAMP NULL COMMENT '删除时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_user_id` (`user_id`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户积分汇总表';


-- 用户积分交易明细表
DROP TABLE IF EXISTS `user_points_transactions`;

CREATE TABLE `user_points_transactions` (
  `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `user_id` BIGINT UNSIGNED NOT NULL COMMENT '用户ID',
  `points_change` BIGINT NOT NULL COMMENT '积分变动值(正数为增加,负数为扣除)',
  `current_balance` BIGINT NOT NULL COMMENT '当前余额',
  `transaction_type` TINYINT NOT NULL COMMENT '交易类型(1:签到 2:连续签到 3:补签 4:每日任务 5:福利任务)',
  `description` VARCHAR(100) COMMENT '积分变动说明',
  `ext_json` VARCHAR(1024) NOT NULL DEFAULT '' COMMENT '扩展字段',
  `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `deleted_at` TIMESTAMP NULL COMMENT '删除时间',
  PRIMARY KEY (`id`),
  KEY `idx_user_id` (`user_id`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户积分明细表';


-- 月度奖励记录表
DROP TABLE IF EXISTS `user_monthly_bonus_log`;

CREATE TABLE `user_monthly_bonus_log` (
  `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `user_id` BIGINT UNSIGNED NOT NULL COMMENT '用户ID',
  `year_month` CHAR(6) NOT NULL COMMENT '年月 (YYYYMM)',
  `bonus_type` TINYINT NOT NULL COMMENT '奖励类型 (1:连续签到3天 2:连续签到7天 3:连续签到15天 4:月满签)',
  `description` VARCHAR(100) COMMENT '积分变动说明',
  `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `deleted_at` TIMESTAMP NULL COMMENT '删除时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_user_yearmonth_type` (`user_id`, `year_month`, `bonus_type`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户月度连续签到奖励记录表';


-- 签到记录表
DROP TABLE IF EXISTS `user_checkin_records`;

CREATE TABLE `user_checkin_records` (
  `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '记录ID',
  `user_id` BIGINT UNSIGNED NOT NULL COMMENT '用户ID',
  `checkin_date` DATE NOT NULL COMMENT '签到日期',
  `checkin_type` TINYINT NOT NULL DEFAULT 1 COMMENT '签到类型: 1=正常签到, 2=补签',
  `points_awarded_base` INT NOT NULL DEFAULT 1 COMMENT '获得积分',
  `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `deleted_at` TIMESTAMP NULL COMMENT '删除时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_user_date` (`user_id`, `checkin_date`),
  KEY `idx_user_id` (`user_id`),
  KEY `idx_checkin_date` (`checkin_date`),
  KEY `idx_checkin_type` (`checkin_type`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '签到记录表';

采用 Redis Bitmap 存储用户签到数据,采用 MySQL 数据库存储用户数据和积分数据

安装 Redis

官方文档

bash 复制代码
sudo apt-get install lsb-release curl gpg
curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
sudo chmod 644 /usr/share/keyrings/redis-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.list
sudo apt-get update
sudo apt-get install redis
bash 复制代码
sudo systemctl enable redis-server
sudo systemctl start redis-server
bash 复制代码
root@GoLang:~/proj/proj2/goframeProj# ps -ef | grep redis
redis     136758       1  0 21:54 ?        00:00:00 /usr/bin/redis-server 127.0.0.1:6379
root      137070  134397  0 21:55 pts/0    00:00:00 grep --color=auto redis

输入exit可以退出该命令行

Redis Bitmap 介绍

由于签到数据只有签到和没签到两种状态,可以用0和1来表示签到状态。

  • 0:表示没签到

  • 1:表示已签到

而签到日期又是一些数据量固定的连续数据,所以非常适合使用 Redis 的 Bitmap 来存储。

在介绍项目的 Redis Key 设置之前,先来了解下 Redis 中的 Bitmap。

一、核心概念

Redis Bitmap 是 Redis 中一种基于二进制位(bit)的存储结构,本质上是一个字节数组(byte array),每个元素(bit)只能是 0 或 1。它并非独立的数据类型,而是基于字符串(String)类型实现的位操作抽象,可以看作是一个可变长度的二进制向量,适用于大规模数据的状态统计、快速查询和内存优化场景。

二、技术特性

1. 存储原理

每个位(bit)仅占 1 位空间,1MB 内存可存储 8388608 个状态位(约 800 万),相比传统的布尔值数组(每个元素占 1 字节)节省 8 倍内存,最大支持 2^32-1 位(约42.9亿)。

按需分配:Bitmap 的内存分配并非预先确定,而是根据用户设置的最大偏移量(offset)动态调整。比如,当你设置了 SETBIT key 1000 1,Redis 会确保该 key 至少能容纳 1001 位(偏移量从 0 开始)。

字节对齐:内存是以字节(8 位)为单位进行分配的。若你设置的最大偏移量是 1000,Redis 会分配 ceil(1001/8) = 126 字节(也就是 1008 位)。

自动扩展机制:设置超出当前范围的 offset时会自动扩容

2. 性能优势

时间复杂度:基本操作O(1), 位运算O(N)

空间效率:1亿用户每日签到仅需约12MB存储

原子性操作:适合高并发场景

3. 常用命令

go 复制代码
package main

import (
	"context"
	"fmt"

	"github.com/redis/go-redis/v9"
)

// 拉取 go-redis v9 依赖
// go get github.com/redis/go-redis/v9
// 整理依赖(推荐)
// go mod tidy

// bitmap 示例代码
func main() {
	// 初始化一个 Redis 客户端,并指定 Redis 服务所在的地址和端口。在后续的操作中,可以通过 rdb 对象与 Redis 服务器进行通信。
	rdb := redis.NewClient(&redis.Options{
		Addr: "localhost:6379",
	})
	defer rdb.Close()

	ctx := context.Background()

	bitmapDemo(ctx, rdb)
}

// bitmap 示例
func bitmapDemo(ctx context.Context, rdb *redis.Client) {
	key := "test:bitmap"

	// setbit
	// 把 test:bitmap 这张表里的 第 0 个位置 设成 1
	// SETBIT 返回的是"设置之前这个位置的值"
	// 第一次设置返回值一定是0(之前是没开)
	val1 := rdb.SetBit(ctx, key, 0, 1).Val()
	fmt.Printf("setbit ret: %v\n", val1)

	// getbit
	val2 := rdb.GetBit(ctx, key, 0).Val()
	fmt.Printf("getbit ret: %v\n", val2)

	// bitcount:统计有多少个 bit 是 1
	// Start:0, End:-1:表示从第 0 个字节开始到最后一个字节(-1 是末尾)
	val3 := rdb.BitCount(ctx, key, &redis.BitCount{Start: 0, End: -1}).Val()
	fmt.Printf("bitcount ret: %v\n", val3)

	// bitop
	key2 := "test:bitmap:2"
	rdb.SetBit(ctx, key2, 2, 1)
	// offset: 0 1 2
	// 		   1 0 0
	// 		   0 0 1
	key3 := "test:bitmap:3"

	// BITOP AND:对两个 Bitmap 做按位与,结果写入 key3
	val4 := rdb.BitOpAnd(ctx, key3, key, key2).Val()
	fmt.Printf("bitop ret: %v\n", val4)

	val5 := rdb.BitCount(ctx, key3, &redis.BitCount{Start: 0, End: -1}).Val()
	fmt.Printf("bitcount ret: %v\n", val5)

	// bitpos: 找"第一个等于 1 的 bit 在哪里"
	val7 := rdb.BitPos(ctx, key2, 1).Val()
	fmt.Printf("bitpos ret: %v\n", val7)

	rdb.SetBit(ctx, key2, 0, 1)
	rdb.SetBit(ctx, key2, 1, 1)
	val6 := rdb.BitCount(ctx, key2, &redis.BitCount{Start: 0, End: -1}).Val()
	fmt.Printf("get ret: %v\n", val6)

	// Bitmap 在 Redis 里,本质还是 String,但它不是"给人看的字符串"
	// val8 := rdb.Get(ctx, key2).Val()
	// fmt.Printf("val8: %v\n", val8)

	// 只有在你存的是 普通字符串 / 数字字符串 时:GET 才"合理"
	rdb.Set(ctx, "score", "100", 0)
	val9 := rdb.Get(ctx, "score").Val() // "100"
	fmt.Printf("val9: %v\n", val9)
}
bash 复制代码
root@GoLang:~/proj/proj2/goframeProj# go run ./main.go
setbit ret: 1
getbit ret: 1
bitcount ret: 1
bitop ret: 1
bitcount ret: 1
bitpos ret: 0
get ret: 3
val9: 100
root@GoLang:~/proj/proj2/goframeProj# 

4. 典型应用场景

1. 用户在线状态统计

场景:记录用户是否在线(1 表示在线,0 表示离线)

实现:用 user:online 作为键,用户 ID 作为 offset,通过 SETBIT 更新状态,GETBIT 查询状态。

优势:百万用户仅需约 125KB 内存(100 万 ÷ 8 ÷ 1024 ≈ 122KB)。

2. 签到打卡系统

场景:记录用户一个月内的签到情况(1 表示签到,0 表示未签到)。

实现:用 user::uid:month 作为键,天数(1-31)作为 offset,每日签到时调用 SETBIT。

统计:通过 BITCOUNT 计算当月签到天数,或用 BITOP AND 统计多个用户的共同签到天数。

3. 布隆过滤器(Bloom Filter)

场景:快速判断元素是否存在于集合中(存在误判可能,适合允许容错的场景)。

实现:利用多个哈希函数将元素映射到 Bitmap 的不同位,设置为 1;查询时检查所有映射位是否为 1。

优势:相比传统哈希表,内存占用极低(例如存储 100 万元素仅需约 1MB 内存)。

4. 活跃用户统计(UV)

场景:统计一段时间内的活跃用户数(如日活、周活)。

实现:每天用一个独立的 Bitmap(如 day:20231001::uv),用户 ID 作为 offset,登录时置为 1;通过 BITOP OR 合并多天的 Bitmap 后,用 BITCOUNT 统计总活跃用户数。

之后我会持续更新,如果喜欢我的文章,请记得一键三连哦,点赞关注收藏,你的每一个赞每一份关注每一次收藏都将是我前进路上的无限动力 !!!↖(▔▽▔)↗感谢支持!

相关推荐
5 小时前
TIDB——TIKV——分布式事务与MVCC
数据库·分布式·tidb·分布式数据库·tikv·
5 小时前
TIDB——TIKV——RocksDB
数据库·分布式·tidb·分布式数据库·
Gauss松鼠会5 小时前
【openGauss】如何在openGauss中实现类似Oracle中constructor function、member function的功能
数据库·oracle·database·gaussdb
顾安r5 小时前
12.16 脚本网页 Golang标准库
golang·html
roman_日积跬步-终至千里5 小时前
【后端】预生产环境与生产环境数据库表隔离方案
数据库
漂亮的小碎步丶5 小时前
【4】Spring Boot项目中Spring核心容器原理详解
java·后端·spring
过期动态5 小时前
JDBC进阶篇:拓展功能与连接池运用详解
java·开发语言·数据库·mysql·oracle·intellij-idea·mybatis
yuezhilangniao5 小时前
PostgreSQL vs MySQL:从零开始基础命令对比指南
数据库·mysql·postgresql
枫叶丹45 小时前
【Qt开发】Qt窗口(十) -> QInputDialog 输入对话框
c语言·开发语言·数据库·c++·qt