feat:新增系统配置模块(Entity/Mapper/DTO/Service/Controller)+ 登录配置公开接口
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
-- 系统配置表
|
||||
CREATE TABLE t_system_config (
|
||||
id VARCHAR(64) PRIMARY KEY,
|
||||
config_key VARCHAR(100) NOT NULL UNIQUE COMMENT '配置唯一标识',
|
||||
config_value VARCHAR(500) NOT NULL DEFAULT '' COMMENT '配置值',
|
||||
value_type VARCHAR(20) NOT NULL DEFAULT 'string' COMMENT '值类型: boolean/string/number/json',
|
||||
config_group VARCHAR(50) NOT NULL DEFAULT 'system' COMMENT '配置分组: system/login/notification',
|
||||
config_name VARCHAR(100) NOT NULL DEFAULT '' COMMENT '显示名称',
|
||||
description VARCHAR(300) DEFAULT '' COMMENT '配置说明',
|
||||
sort_order INT NOT NULL DEFAULT 0 COMMENT '排序号',
|
||||
is_visible TINYINT NOT NULL DEFAULT 1 COMMENT '是否在管理后台可见: 0-隐藏, 1-显示',
|
||||
create_by VARCHAR(64) DEFAULT NULL,
|
||||
create_time DATETIME DEFAULT NULL,
|
||||
update_by VARCHAR(64) DEFAULT NULL,
|
||||
update_time DATETIME DEFAULT NULL,
|
||||
is_deleted TINYINT NOT NULL DEFAULT 0
|
||||
) COMMENT = '系统配置表';
|
||||
|
||||
-- 初始数据:短信登录开关(默认关闭)
|
||||
INSERT INTO t_system_config (id, config_key, config_value, value_type, config_group, config_name, description, sort_order, is_visible, create_time, update_time, is_deleted)
|
||||
VALUES (
|
||||
REPLACE(UUID(), '-', ''),
|
||||
'sms_login_enabled',
|
||||
'false',
|
||||
'boolean',
|
||||
'login',
|
||||
'短信登录',
|
||||
'是否启用手机号+短信验证码登录方式',
|
||||
1,
|
||||
1,
|
||||
NOW(),
|
||||
NOW(),
|
||||
0
|
||||
);
|
||||
@@ -60,6 +60,7 @@ public class WebMvcConfig implements WebMvcConfigurer {
|
||||
"/actuator/**", // Actuator endpoints
|
||||
"/admin/**", // 排除管理员路径,由管理员拦截器处理
|
||||
"/auth/wechat/login", // WeChat mini program login endpoint
|
||||
"/auth/loginConfig", // 登录方式配置接口(免登录)
|
||||
"/error" // Spring Boot error page
|
||||
)
|
||||
.order(2); // 优先级2
|
||||
|
||||
@@ -10,9 +10,11 @@ import com.emotion.dto.response.ResetPasswordResponse;
|
||||
|
||||
import com.emotion.dto.response.AuthResponse;
|
||||
import com.emotion.dto.response.CaptchaResponse;
|
||||
import com.emotion.dto.response.LoginConfigResponse;
|
||||
import com.emotion.dto.response.SmsCodeResponse;
|
||||
import com.emotion.dto.response.UserInfoResponse;
|
||||
import com.emotion.service.AuthService;
|
||||
import com.emotion.service.SystemConfigService;
|
||||
import com.emotion.service.TokenService;
|
||||
import com.emotion.util.UserContextUtils;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
@@ -41,6 +43,9 @@ public class AuthController {
|
||||
@Autowired
|
||||
private TokenService tokenService;
|
||||
|
||||
@Autowired
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
/**
|
||||
* 用户登录(简化版:手机号+验证码,不存在则自动注册)
|
||||
*/
|
||||
@@ -61,6 +66,16 @@ public class AuthController {
|
||||
return Result.success("登录成功", response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取登录方式配置
|
||||
*/
|
||||
@GetMapping("/loginConfig")
|
||||
@Operation(summary = "获取登录方式配置", description = "返回当前启用的登录方式,供小程序登录页动态渲染")
|
||||
public Result<LoginConfigResponse> getLoginConfig() {
|
||||
LoginConfigResponse response = systemConfigService.getLoginConfig();
|
||||
return Result.success(response);
|
||||
}
|
||||
|
||||
@PostMapping(value = "/register")
|
||||
@Operation(summary = "用户注册", description = "使用手机号、密码和短信验证码进行注册")
|
||||
public Result<AuthResponse> register(@Valid @RequestBody RegisterRequest request) {
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.SystemConfigUpdateRequest;
|
||||
import com.emotion.dto.response.SystemConfigResponse;
|
||||
import com.emotion.service.SystemConfigService;
|
||||
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.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 系统配置管理控制器(管理端)
|
||||
*
|
||||
* @author system
|
||||
* @date 2026-06-27
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/admin/systemConfig")
|
||||
@Tag(name = "系统配置管理", description = "系统配置的查询和更新功能")
|
||||
public class SystemConfigController {
|
||||
|
||||
@Autowired
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
/**
|
||||
* 获取所有可见配置列表
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获取配置列表", description = "获取所有可见的系统配置列表")
|
||||
public Result<List<SystemConfigResponse>> list() {
|
||||
List<SystemConfigResponse> configs = systemConfigService.getVisibleConfigList();
|
||||
return Result.success(configs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量更新配置
|
||||
*/
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "批量更新配置", description = "批量更新系统配置值")
|
||||
public Result<Void> update(@RequestBody @Validated List<SystemConfigUpdateRequest> requests) {
|
||||
systemConfigService.batchUpdateConfigs(requests);
|
||||
return Result.success("更新成功", null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* 系统配置更新请求
|
||||
*
|
||||
* @author system
|
||||
* @date 2026-06-27
|
||||
*/
|
||||
@Data
|
||||
public class SystemConfigUpdateRequest {
|
||||
|
||||
/**
|
||||
* 配置唯一标识
|
||||
*/
|
||||
@NotBlank(message = "配置标识不能为空")
|
||||
private String configKey;
|
||||
|
||||
/**
|
||||
* 配置值
|
||||
*/
|
||||
@NotBlank(message = "配置值不能为空")
|
||||
private String configValue;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.emotion.dto.response;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 登录方式配置响应
|
||||
* 返回给小程序,告知当前启用的登录方式
|
||||
*
|
||||
* @author system
|
||||
* @date 2026-06-27
|
||||
*/
|
||||
@Data
|
||||
public class LoginConfigResponse {
|
||||
|
||||
/**
|
||||
* 是否启用微信登录
|
||||
*/
|
||||
private Boolean wechatLoginEnabled;
|
||||
|
||||
/**
|
||||
* 是否启用短信登录
|
||||
*/
|
||||
private Boolean smsLoginEnabled;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.emotion.dto.response;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 系统配置响应
|
||||
*
|
||||
* @author system
|
||||
* @date 2026-06-27
|
||||
*/
|
||||
@Data
|
||||
public class SystemConfigResponse {
|
||||
|
||||
/**
|
||||
* 配置唯一标识
|
||||
*/
|
||||
private String configKey;
|
||||
|
||||
/**
|
||||
* 配置值
|
||||
*/
|
||||
private String configValue;
|
||||
|
||||
/**
|
||||
* 值类型: boolean/string/number/json
|
||||
*/
|
||||
private String valueType;
|
||||
|
||||
/**
|
||||
* 配置分组
|
||||
*/
|
||||
private String configGroup;
|
||||
|
||||
/**
|
||||
* 显示名称
|
||||
*/
|
||||
private String configName;
|
||||
|
||||
/**
|
||||
* 配置说明
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 排序号
|
||||
*/
|
||||
private Integer sortOrder;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.emotion.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.emotion.common.BaseEntity;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 系统配置实体
|
||||
*
|
||||
* @author system
|
||||
* @date 2026-06-27
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@TableName("t_system_config")
|
||||
public class SystemConfig extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 配置唯一标识
|
||||
*/
|
||||
@TableField("config_key")
|
||||
private String configKey;
|
||||
|
||||
/**
|
||||
* 配置值
|
||||
*/
|
||||
@TableField("config_value")
|
||||
private String configValue;
|
||||
|
||||
/**
|
||||
* 值类型: boolean/string/number/json
|
||||
*/
|
||||
@TableField("value_type")
|
||||
private String valueType;
|
||||
|
||||
/**
|
||||
* 配置分组: system/login/notification
|
||||
*/
|
||||
@TableField("config_group")
|
||||
private String configGroup;
|
||||
|
||||
/**
|
||||
* 显示名称
|
||||
*/
|
||||
@TableField("config_name")
|
||||
private String configName;
|
||||
|
||||
/**
|
||||
* 配置说明
|
||||
*/
|
||||
@TableField("description")
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 排序号
|
||||
*/
|
||||
@TableField("sort_order")
|
||||
private Integer sortOrder;
|
||||
|
||||
/**
|
||||
* 是否在管理后台可见: 0-隐藏, 1-显示
|
||||
*/
|
||||
@TableField("is_visible")
|
||||
private Integer isVisible;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.emotion.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.emotion.entity.SystemConfig;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 系统配置Mapper接口
|
||||
*
|
||||
* @author system
|
||||
* @date 2026-06-27
|
||||
*/
|
||||
@Mapper
|
||||
public interface SystemConfigMapper extends BaseMapper<SystemConfig> {
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.emotion.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.emotion.dto.request.SystemConfigUpdateRequest;
|
||||
import com.emotion.dto.response.LoginConfigResponse;
|
||||
import com.emotion.dto.response.SystemConfigResponse;
|
||||
import com.emotion.entity.SystemConfig;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 系统配置服务接口
|
||||
*
|
||||
* @author system
|
||||
* @date 2026-06-27
|
||||
*/
|
||||
public interface SystemConfigService extends IService<SystemConfig> {
|
||||
|
||||
/**
|
||||
* 获取所有可见配置列表
|
||||
*/
|
||||
List<SystemConfigResponse> getVisibleConfigList();
|
||||
|
||||
/**
|
||||
* 批量更新配置
|
||||
*/
|
||||
void batchUpdateConfigs(List<SystemConfigUpdateRequest> requests);
|
||||
|
||||
/**
|
||||
* 获取配置值(带 Redis 缓存)
|
||||
*
|
||||
* @param configKey 配置标识
|
||||
* @return 配置值字符串,不存在返回 null
|
||||
*/
|
||||
String getConfigValue(String configKey);
|
||||
|
||||
/**
|
||||
* 获取登录方式配置
|
||||
*/
|
||||
LoginConfigResponse getLoginConfig();
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package com.emotion.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.emotion.dto.request.SystemConfigUpdateRequest;
|
||||
import com.emotion.dto.response.LoginConfigResponse;
|
||||
import com.emotion.dto.response.SystemConfigResponse;
|
||||
import com.emotion.entity.SystemConfig;
|
||||
import com.emotion.mapper.SystemConfigMapper;
|
||||
import com.emotion.service.SystemConfigService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 系统配置服务实现类
|
||||
*
|
||||
* @author system
|
||||
* @date 2026-06-27
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class SystemConfigServiceImpl extends ServiceImpl<SystemConfigMapper, SystemConfig> implements SystemConfigService {
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
private static final String CACHE_PREFIX = "system_config:";
|
||||
private static final long CACHE_EXPIRE_HOURS = 24;
|
||||
private static final String SMS_LOGIN_ENABLED_KEY = "sms_login_enabled";
|
||||
|
||||
@Override
|
||||
public List<SystemConfigResponse> getVisibleConfigList() {
|
||||
LambdaQueryWrapper<SystemConfig> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(SystemConfig::getIsVisible, 1)
|
||||
.orderByAsc(SystemConfig::getSortOrder);
|
||||
List<SystemConfig> configs = list(wrapper);
|
||||
return configs.stream().map(this::convertToResponse).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void batchUpdateConfigs(List<SystemConfigUpdateRequest> requests) {
|
||||
for (SystemConfigUpdateRequest request : requests) {
|
||||
LambdaUpdateWrapper<SystemConfig> updateWrapper = new LambdaUpdateWrapper<>();
|
||||
updateWrapper.eq(SystemConfig::getConfigKey, request.getConfigKey())
|
||||
.set(SystemConfig::getConfigValue, request.getConfigValue());
|
||||
update(updateWrapper);
|
||||
|
||||
// 清除缓存
|
||||
redisTemplate.delete(CACHE_PREFIX + request.getConfigKey());
|
||||
log.info("更新系统配置: key={}, value={}", request.getConfigKey(), request.getConfigValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConfigValue(String configKey) {
|
||||
// 先查 Redis 缓存
|
||||
String cacheKey = CACHE_PREFIX + configKey;
|
||||
String cached = (String) redisTemplate.opsForValue().get(cacheKey);
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
// 查数据库
|
||||
LambdaQueryWrapper<SystemConfig> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(SystemConfig::getConfigKey, configKey);
|
||||
SystemConfig config = getOne(wrapper);
|
||||
if (config == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 写入缓存
|
||||
redisTemplate.opsForValue().set(cacheKey, config.getConfigValue(), CACHE_EXPIRE_HOURS, TimeUnit.HOURS);
|
||||
return config.getConfigValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginConfigResponse getLoginConfig() {
|
||||
LoginConfigResponse response = new LoginConfigResponse();
|
||||
response.setWechatLoginEnabled(true);
|
||||
|
||||
String smsEnabled = getConfigValue(SMS_LOGIN_ENABLED_KEY);
|
||||
response.setSmsLoginEnabled("true".equalsIgnoreCase(smsEnabled));
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 实体转响应对象
|
||||
*/
|
||||
private SystemConfigResponse convertToResponse(SystemConfig config) {
|
||||
SystemConfigResponse response = new SystemConfigResponse();
|
||||
response.setConfigKey(config.getConfigKey());
|
||||
response.setConfigValue(config.getConfigValue());
|
||||
response.setValueType(config.getValueType());
|
||||
response.setConfigGroup(config.getConfigGroup());
|
||||
response.setConfigName(config.getConfigName());
|
||||
response.setDescription(config.getDescription());
|
||||
response.setSortOrder(config.getSortOrder());
|
||||
return response;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user