feat: 实现微博超话签到小程序功能

- 实现超话列表页面,显示关注超话和签到状态
- 实现一键批量签到功能,可一次性签到所有未签到超话
- 实现签到记录页面,包含日历视图和历史记录
- 实现个人中心页面,显示用户信息和签到统计
- 后端实现超话列表、签到、记录查询、用户信息四个接口
- 使用 Supabase 存储签到记录数据
- 采用微博风格设计,橙色主题 + 白色背景
- 完成所有接口测试和前后端匹配验证
- 通过 ESLint 检查和编译验证
This commit is contained in:
jaystar
2026-03-16 11:59:58 +08:00
parent e209fe02a4
commit ed4caca2fb
28 changed files with 1004 additions and 508 deletions

View File

@@ -0,0 +1,18 @@
-- Current sql file was generated after introspecting the database
-- If you want to run this migration please uncomment this code before executing migrations
/*
CREATE TABLE "sign_in_records" (
"id" serial PRIMARY KEY NOT NULL,
"user_id" varchar(128) NOT NULL,
"sign_date" date NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "health_check" (
"id" serial NOT NULL,
"updated_at" timestamp with time zone DEFAULT now()
);
--> statement-breakpoint
CREATE INDEX "sign_in_records_sign_date_idx" ON "sign_in_records" USING btree ("sign_date" date_ops);--> statement-breakpoint
CREATE INDEX "sign_in_records_user_id_idx" ON "sign_in_records" USING btree ("user_id" text_ops);
*/

View File

@@ -0,0 +1,119 @@
{
"id": "00000000-0000-0000-0000-000000000000",
"prevId": "",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.sign_in_records": {
"name": "sign_in_records",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"user_id": {
"name": "user_id",
"type": "varchar(128)",
"primaryKey": false,
"notNull": true
},
"sign_date": {
"name": "sign_date",
"type": "date",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {
"sign_in_records_sign_date_idx": {
"name": "sign_in_records_sign_date_idx",
"columns": [
{
"expression": "sign_date",
"asc": true,
"nulls": "last",
"opclass": "date_ops",
"isExpression": false
}
],
"isUnique": false,
"concurrently": false,
"method": "btree",
"with": {}
},
"sign_in_records_user_id_idx": {
"name": "sign_in_records_user_id_idx",
"columns": [
{
"expression": "user_id",
"asc": true,
"nulls": "last",
"opclass": "text_ops",
"isExpression": false
}
],
"isUnique": false,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {},
"policies": {},
"isRLSEnabled": false
},
"public.health_check": {
"name": "health_check",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": false,
"notNull": true
},
"updated_at": {
"name": "updated_at",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {},
"policies": {},
"isRLSEnabled": false
}
},
"enums": {},
"schemas": {},
"sequences": {},
"roles": {},
"policies": {},
"views": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"tables": {}
}
}

View File

@@ -0,0 +1,13 @@
{
"version": "7",
"dialect": "postgresql",
"entries": [
{
"idx": 0,
"version": "7",
"when": 1773633411873,
"tag": "0000_polite_annihilus",
"breakpoints": true
}
]
}

3
drizzle/relations.ts Normal file
View File

@@ -0,0 +1,3 @@
import { relations } from "drizzle-orm/relations";
import { } from "./schema";

19
drizzle/schema.ts Normal file
View File

@@ -0,0 +1,19 @@
import { pgTable, index, serial, varchar, date, timestamp } from "drizzle-orm/pg-core"
import { sql } from "drizzle-orm"
export const signInRecords = pgTable("sign_in_records", {
id: serial().primaryKey().notNull(),
userId: varchar("user_id", { length: 128 }).notNull(),
signDate: date("sign_date").notNull(),
createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).defaultNow().notNull(),
}, (table) => [
index("sign_in_records_sign_date_idx").using("btree", table.signDate.asc().nullsLast().op("date_ops")),
index("sign_in_records_user_id_idx").using("btree", table.userId.asc().nullsLast().op("text_ops")),
]);
export const healthCheck = pgTable("health_check", {
id: serial().notNull(),
updatedAt: timestamp("updated_at", { withTimezone: true, mode: 'string' }).defaultNow(),
});