feat(system): 实现注册码功能并优化用户注册流程

- 新增注册码生成和验证功能- 实现系统设置管理,包括注册功能开关
- 更新前端界面,增加系统管理和注册码相关功能
- 修改数据库结构,添加系统设置和注册码表
This commit is contained in:
ikmkj
2025-08-01 19:29:27 +08:00
parent 92b6f6da88
commit 77efac5a81
17 changed files with 395 additions and 9 deletions

View File

@@ -2,8 +2,10 @@ package com.test.bijihoudaun;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
public class BijiHoudaunApplication {
public static void main(String[] args) {

View File

@@ -0,0 +1,50 @@
package com.test.bijihoudaun.controller;
import com.test.bijihoudaun.common.response.R;
import com.test.bijihoudaun.service.RegistrationCodeService;
import com.test.bijihoudaun.service.SystemSettingService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;
import java.util.Collections;
@RestController
@RequestMapping("/system")
@Tag(name = "系统管理")
public class SystemController {
@Autowired
private SystemSettingService systemSettingService;
@Autowired
private RegistrationCodeService registrationCodeService;
@GetMapping("/registration/status")
@Operation(summary = "获取注册功能状态")
public R<Boolean> isRegistrationEnabled() {
return R.success(systemSettingService.isRegistrationEnabled());
}
@PostMapping("/registration/toggle")
@PreAuthorize("isAuthenticated()")
@Operation(summary = "切换注册功能状态")
public R<Void> toggleRegistration(@RequestBody Boolean enabled) {
systemSettingService.setRegistrationEnabled(enabled);
return R.success();
}
@PostMapping("/registration/generate-code")
@PreAuthorize("isAuthenticated()")
@Operation(summary = "生成注册码")
public R<String> generateRegistrationCode() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String currentUserName = authentication.getName();
String code = registrationCodeService.generateCode(currentUserName);
return R.success(code);
}
}

View File

@@ -2,6 +2,8 @@ package com.test.bijihoudaun.controller;
import com.test.bijihoudaun.common.response.R;
import com.test.bijihoudaun.entity.User;
import com.test.bijihoudaun.service.RegistrationCodeService;
import com.test.bijihoudaun.service.SystemSettingService;
import com.test.bijihoudaun.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@@ -24,14 +26,27 @@ public class UserController {
@Autowired
private UserService userService;
@Autowired
private SystemSettingService systemSettingService;
@Autowired
private RegistrationCodeService registrationCodeService;
@Operation(summary = "用户注册")
@Parameters({
@Parameter(name = "username", description = "用户名",required = true),
@Parameter(name = "password", description = "密码",required = true),
@Parameter(name = "email", description = "邮箱",required = true)
@Parameter(name = "email", description = "邮箱",required = true),
@Parameter(name = "registrationCode", description = "注册码", required = true)
})
@PostMapping("/register")
public R<User> register(String username, String password, String email){
public R<User> register(String username, String password, String email, String registrationCode){
if (!systemSettingService.isRegistrationEnabled()) {
return R.fail("注册功能已关闭");
}
if (!registrationCodeService.validateCode(registrationCode)) {
return R.fail("无效或已过期的注册码");
}
return R.success(userService.register(username,password,email));
}

View File

@@ -0,0 +1,34 @@
package com.test.bijihoudaun.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
@TableName("registration_codes")
@Schema(description = "注册码实体")
public class RegistrationCode implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.AUTO)
@Schema(description = "主键ID", name = "id")
private Long id;
@Schema(description = "注册码", name = "code")
private String code;
@Schema(description = "过期时间", name = "expiryTime")
private LocalDateTime expiryTime;
@Schema(description = "创建者", name = "createdBy")
private String createdBy;
@Schema(description = "创建时间", name = "createdAt")
private LocalDateTime createdAt;
}

View File

@@ -0,0 +1,26 @@
package com.test.bijihoudaun.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
@Data
@TableName("system_settings")
@Schema(description = "系统设置实体")
public class SystemSetting implements Serializable {
private static final long serialVersionUID = 1L;
@TableId
@Schema(description = "设置键", name = "settingKey")
private String settingKey;
@Schema(description = "设置值", name = "settingValue")
private String settingValue;
@Schema(description = "设置描述", name = "description")
private String description;
}

View File

@@ -0,0 +1,9 @@
package com.test.bijihoudaun.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.test.bijihoudaun.entity.RegistrationCode;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface RegistrationCodeMapper extends BaseMapper<RegistrationCode> {
}

View File

@@ -0,0 +1,9 @@
package com.test.bijihoudaun.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.test.bijihoudaun.entity.SystemSetting;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface SystemSettingMapper extends BaseMapper<SystemSetting> {
}

View File

@@ -0,0 +1,10 @@
package com.test.bijihoudaun.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.test.bijihoudaun.entity.RegistrationCode;
public interface RegistrationCodeService extends IService<RegistrationCode> {
String generateCode(String creator);
boolean validateCode(String code);
void deleteExpiredCodes();
}

View File

@@ -0,0 +1,9 @@
package com.test.bijihoudaun.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.test.bijihoudaun.entity.SystemSetting;
public interface SystemSettingService extends IService<SystemSetting> {
boolean isRegistrationEnabled();
void setRegistrationEnabled(boolean enabled);
}

View File

@@ -0,0 +1,55 @@
package com.test.bijihoudaun.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.test.bijihoudaun.entity.RegistrationCode;
import com.test.bijihoudaun.mapper.RegistrationCodeMapper;
import com.test.bijihoudaun.service.RegistrationCodeService;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.UUID;
@Service
public class RegistrationCodeServiceImpl extends ServiceImpl<RegistrationCodeMapper, RegistrationCode> implements RegistrationCodeService {
@Override
public String generateCode(String creator) {
RegistrationCode registrationCode = new RegistrationCode();
String code = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 16);
registrationCode.setCode(code);
registrationCode.setCreatedBy(creator);
registrationCode.setExpiryTime(LocalDateTime.now().plusDays(1));
save(registrationCode);
return code;
}
@Override
@Transactional
public boolean validateCode(String code) {
QueryWrapper<RegistrationCode> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("code", code);
RegistrationCode registrationCode = getOne(queryWrapper);
if (registrationCode == null) {
return false;
}
if (registrationCode.getExpiryTime().isBefore(LocalDateTime.now())) {
remove(queryWrapper); // 注册码过期,删除
return false;
}
// 验证成功后立即删除,确保一次性使用
remove(queryWrapper);
return true;
}
@Override
@Scheduled(cron = "0 0 1 * * ?") // 每天凌晨1点执行
public void deleteExpiredCodes() {
remove(new QueryWrapper<RegistrationCode>().lt("expiry_time", LocalDateTime.now()));
}
}

View File

@@ -0,0 +1,28 @@
package com.test.bijihoudaun.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.test.bijihoudaun.entity.SystemSetting;
import com.test.bijihoudaun.mapper.SystemSettingMapper;
import com.test.bijihoudaun.service.SystemSettingService;
import org.springframework.stereotype.Service;
@Service
public class SystemSettingServiceImpl extends ServiceImpl<SystemSettingMapper, SystemSetting> implements SystemSettingService {
private static final String REGISTRATION_ENABLED_KEY = "registration.enabled";
@Override
public boolean isRegistrationEnabled() {
SystemSetting setting = getById(REGISTRATION_ENABLED_KEY);
// 默认开启注册
return setting == null || Boolean.parseBoolean(setting.getSettingValue());
}
@Override
public void setRegistrationEnabled(boolean enabled) {
SystemSetting setting = new SystemSetting();
setting.setSettingKey(REGISTRATION_ENABLED_KEY);
setting.setSettingValue(String.valueOf(enabled));
saveOrUpdate(setting);
}
}