feat: 支持会员编号
This commit is contained in:
54
sql-scripts/add-member-number-migration.sql
Normal file
54
sql-scripts/add-member-number-migration.sql
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
-- 会员编号字段迁移脚本
|
||||||
|
-- 执行日期: 2025-09-26
|
||||||
|
-- 描述: 为用户表添加会员编号字段,按照创建时间从1开始递增编号
|
||||||
|
|
||||||
|
-- Step 1: 添加会员编号字段
|
||||||
|
ALTER TABLE `t_users`
|
||||||
|
ADD COLUMN `member_number` INT NULL COMMENT '会员编号(按注册时间递增)' AFTER `gender`;
|
||||||
|
|
||||||
|
-- Step 2: 创建索引以提高查询性能
|
||||||
|
CREATE INDEX `idx_member_number` ON `t_users` (`member_number`);
|
||||||
|
|
||||||
|
-- Step 3: 为现有用户按照创建时间分配会员编号(从1开始递增)
|
||||||
|
SET @row_number = 0;
|
||||||
|
UPDATE `t_users`
|
||||||
|
SET `member_number` = (@row_number := @row_number + 1)
|
||||||
|
WHERE `member_number` IS NULL
|
||||||
|
ORDER BY `created_at` ASC;
|
||||||
|
|
||||||
|
-- Step 4: 验证更新结果
|
||||||
|
SELECT
|
||||||
|
COUNT(*) as total_users,
|
||||||
|
MIN(member_number) as min_member_number,
|
||||||
|
MAX(member_number) as max_member_number,
|
||||||
|
COUNT(DISTINCT member_number) as unique_member_numbers
|
||||||
|
FROM `t_users`
|
||||||
|
WHERE `member_number` IS NOT NULL;
|
||||||
|
|
||||||
|
-- Step 5: 显示前10个用户的会员编号分配情况
|
||||||
|
SELECT
|
||||||
|
`id`,
|
||||||
|
`name`,
|
||||||
|
`member_number`,
|
||||||
|
`created_at`,
|
||||||
|
`is_guest`
|
||||||
|
FROM `t_users`
|
||||||
|
WHERE `member_number` IS NOT NULL
|
||||||
|
ORDER BY `member_number` ASC
|
||||||
|
LIMIT 10;
|
||||||
|
|
||||||
|
-- Step 6: 检查是否有重复的会员编号(应该为0)
|
||||||
|
SELECT
|
||||||
|
member_number,
|
||||||
|
COUNT(*) as count
|
||||||
|
FROM `t_users`
|
||||||
|
WHERE `member_number` IS NOT NULL
|
||||||
|
GROUP BY `member_number`
|
||||||
|
HAVING COUNT(*) > 1;
|
||||||
|
|
||||||
|
-- 注意事项:
|
||||||
|
-- 1. 此脚本会为所有现有用户分配会员编号,按照创建时间排序
|
||||||
|
-- 2. 会员编号从1开始递增
|
||||||
|
-- 3. 包含游客用户也会分配编号
|
||||||
|
-- 4. 如果只希望为正式用户(非游客)分配编号,请在更新语句中添加条件: WHERE `member_number` IS NULL AND `is_guest` = 0
|
||||||
|
-- 5. 执行前请备份数据库
|
||||||
@@ -44,6 +44,14 @@ export class User extends Model {
|
|||||||
})
|
})
|
||||||
declare gender: Gender;
|
declare gender: Gender;
|
||||||
|
|
||||||
|
// 会员编号
|
||||||
|
@Column({
|
||||||
|
type: DataType.INTEGER,
|
||||||
|
allowNull: true,
|
||||||
|
comment: '会员编号(按注册时间递增)',
|
||||||
|
})
|
||||||
|
declare memberNumber: number;
|
||||||
|
|
||||||
// 会员有效期
|
// 会员有效期
|
||||||
@Column({
|
@Column({
|
||||||
type: DataType.DATE,
|
type: DataType.DATE,
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export class UsersController {
|
|||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@Get('/info')
|
@Get('/info')
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
@ApiOperation({ summary: '创建用户' })
|
@ApiOperation({ summary: '获取用户信息' })
|
||||||
@ApiBody({ type: CreateUserDto })
|
@ApiBody({ type: CreateUserDto })
|
||||||
@ApiResponse({ type: UserResponseDto })
|
@ApiResponse({ type: UserResponseDto })
|
||||||
async getProfile(@CurrentUser() user: AccessTokenPayload): Promise<UserResponseDto> {
|
async getProfile(@CurrentUser() user: AccessTokenPayload): Promise<UserResponseDto> {
|
||||||
|
|||||||
@@ -527,11 +527,14 @@ export class UsersService {
|
|||||||
throw new BadRequestException('无法获取用户邮箱信息');
|
throw new BadRequestException('无法获取用户邮箱信息');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const memberNumber = await this.assignMemberNumber();
|
||||||
|
|
||||||
user = await this.userModel.create({
|
user = await this.userModel.create({
|
||||||
id: userId,
|
id: userId,
|
||||||
name: userName,
|
name: userName,
|
||||||
mail: userEmail,
|
mail: userEmail,
|
||||||
gender: Gender.MALE,
|
gender: Gender.MALE,
|
||||||
|
memberNumber,
|
||||||
freeUsageCount: DEFAULT_FREE_USAGE_COUNT,
|
freeUsageCount: DEFAULT_FREE_USAGE_COUNT,
|
||||||
lastLogin: new Date(),
|
lastLogin: new Date(),
|
||||||
avatar: 'https://plates-1251306435.cos.ap-guangzhou.myqcloud.com/images/seal-avatar/1.jpeg'
|
avatar: 'https://plates-1251306435.cos.ap-guangzhou.myqcloud.com/images/seal-avatar/1.jpeg'
|
||||||
@@ -735,10 +738,13 @@ export class UsersService {
|
|||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
// 创建新的游客用户
|
// 创建新的游客用户
|
||||||
|
const memberNumber = await this.assignMemberNumber();
|
||||||
|
|
||||||
user = await this.userModel.create({
|
user = await this.userModel.create({
|
||||||
id: guestUserId,
|
id: guestUserId,
|
||||||
name: `游客_${deviceName}`,
|
name: `游客_${deviceName}`,
|
||||||
gender: Gender.MALE, // 默认性别
|
gender: Gender.MALE, // 默认性别
|
||||||
|
memberNumber,
|
||||||
isGuest: true,
|
isGuest: true,
|
||||||
deviceId,
|
deviceId,
|
||||||
deviceName,
|
deviceName,
|
||||||
@@ -2529,4 +2535,30 @@ export class UsersService {
|
|||||||
return dayjsDate.format('YYYY-MM-DD');
|
return dayjsDate.format('YYYY-MM-DD');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动分配会员编号
|
||||||
|
* @returns Promise<number> 新的会员编号
|
||||||
|
*/
|
||||||
|
private async assignMemberNumber(): Promise<number> {
|
||||||
|
try {
|
||||||
|
// 查找当前最大的会员编号
|
||||||
|
const result = await this.userModel.findOne({
|
||||||
|
attributes: [[this.sequelize.fn('MAX', this.sequelize.col('member_number')), 'maxMemberNumber']],
|
||||||
|
raw: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const maxMember = result as unknown as { maxMemberNumber: number | null };
|
||||||
|
|
||||||
|
// 返回下一个编号,如果没有现有编号则从1开始
|
||||||
|
const nextMemberNumber = (maxMember?.maxMemberNumber || 0) + 1;
|
||||||
|
|
||||||
|
this.logger.log(`分配新会员编号: ${nextMemberNumber}`);
|
||||||
|
return nextMemberNumber;
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`assignMemberNumber error: ${error instanceof Error ? error.message : String(error)}`);
|
||||||
|
// 如果查询失败,返回基于时间戳的编号作为备选
|
||||||
|
return Math.floor(Date.now() / 1000) % 100000;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user