Files
gjm 3790821ce0 feat: 阶段 1 最小闭环 - 建表 SQL、/wechat 事件、/auth 接口、首次关注免费授权
- sql/schema.sql: users/authorizations/auth_scenes/usage_logs + sessions 建表
- db.py: aiomysql 连接池,lifespan 内初始化与释放
- wechat_api.py: access_token 缓存 + 临时二维码创建
- auth.py: /auth/create_scene、/auth/status,handle_scan 事务内幂等处理扫码
- wechat.py: 接入 DB 生命周期,处理 subscribe/SCAN 事件;移除多余的 openid query 参数
- 首次关注赠送 7 天免费授权,has_claimed_free 条件更新保证幂等
- config.py/.env.example: 新增 FREE_AUTH_DAYS/SCENE_TTL_SECONDS/SESSION_TTL_HOURS
2026-09-26 22:25:02 +08:00

100 lines
6.2 KiB
SQL
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- 微信扫码授权服务 — 数据库结构(阶段 1)
-- 目标环境:MySQL 8.0
-- 执行方式:mysql -u root -p < sql/schema.sql
CREATE DATABASE IF NOT EXISTS `wechat_api`
DEFAULT CHARACTER SET utf8mb4
DEFAULT COLLATE utf8mb4_unicode_ci;
USE `wechat_api`;
-- ---------------------------------------------------------------------------
-- 5.1 users — 用户表
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `users` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`openid` VARCHAR(64) NOT NULL COMMENT '微信 OpenID',
`nickname` VARCHAR(100) DEFAULT NULL COMMENT '昵称(可选)',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '首次关注时间',
`last_seen_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最近活跃时间',
`has_claimed_free` TINYINT NOT NULL DEFAULT 0 COMMENT '是否已领过免费授权',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_openid` (`openid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表';
-- ---------------------------------------------------------------------------
-- 5.2 authorizations — 授权表
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `authorizations` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` BIGINT UNSIGNED NOT NULL COMMENT '外键 users.id',
`type` ENUM('time','points') NOT NULL COMMENT '授权类型',
`start_at` DATETIME DEFAULT NULL COMMENT '时间授权开始',
`end_at` DATETIME DEFAULT NULL COMMENT '时间授权结束',
`remaining_points` INT NOT NULL DEFAULT 0 COMMENT '积分余额',
`total_points` INT NOT NULL DEFAULT 0 COMMENT '积分总量',
`source` ENUM('free','purchase','admin') NOT NULL COMMENT '来源',
`status` ENUM('pending','active','expired','exhausted','cancelled')
NOT NULL DEFAULT 'pending' COMMENT '状态',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_user_status` (`user_id`, `status`),
CONSTRAINT `fk_auth_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='授权表';
-- ---------------------------------------------------------------------------
-- 5.3 auth_scenes — 扫码场景表
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `auth_scenes` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`scene_str` VARCHAR(128) NOT NULL COMMENT '二维码场景值',
`device_id` VARCHAR(128) DEFAULT NULL COMMENT 'MFC 设备标识',
`status` ENUM('pending','scanned','authorized','expired')
NOT NULL DEFAULT 'pending' COMMENT '场景状态',
`user_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '扫码用户',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`expires_at` DATETIME NOT NULL COMMENT '过期时间,默认 300 秒',
`authorized_at` DATETIME DEFAULT NULL COMMENT '授权完成时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_scene_str` (`scene_str`),
KEY `idx_scene_status` (`scene_str`, `status`),
CONSTRAINT `fk_scene_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='扫码场景表';
-- ---------------------------------------------------------------------------
-- 5.4 usage_logs — 使用日志
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `usage_logs` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` BIGINT UNSIGNED NOT NULL,
`device_id` VARCHAR(128) DEFAULT NULL,
`authorization_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '本次扣减的授权',
`cost_type` ENUM('time','points') NOT NULL,
`cost_points` INT NOT NULL DEFAULT 0 COMMENT '时间授权为 0',
`used_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_user_time` (`user_id`, `used_at`),
CONSTRAINT `fk_log_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_log_auth` FOREIGN KEY (`authorization_id`) REFERENCES `authorizations` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='使用日志';
-- ---------------------------------------------------------------------------
-- sessions — 会话令牌表(需求 9:session_token 服务端存储,有效期 24 小时)
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `sessions` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`token` VARCHAR(64) NOT NULL COMMENT '随机生成的会话令牌',
`user_id` BIGINT UNSIGNED NOT NULL,
`device_id` VARCHAR(128) DEFAULT NULL COMMENT '签发时绑定的设备',
`scene_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '签发来源场景',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`expires_at` DATETIME NOT NULL COMMENT '过期时间,默认 24 小时',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_token` (`token`),
KEY `idx_session_user` (`user_id`),
KEY `idx_session_scene` (`scene_id`),
CONSTRAINT `fk_session_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_session_scene` FOREIGN KEY (`scene_id`) REFERENCES `auth_scenes` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='会话令牌表';