Compare commits

...

22 Commits

Author SHA1 Message Date
49a0022ee8 feat(login): 新增网页AI登录场景WEB_AI
- 在LoginSceneEnum中增加WEB_AI枚举,绑定路径webAi-doLogin
- LoginService扩展WEB_AI登录逻辑:缓存登录态、创建w.tenant.{tenantId}队列并绑定webAiHeadersExchange
- 新增webAiHeadersExchange Bean,交换机名web.ai.headers.exchange
- 用户实体与Service增加web_ai字段及checkWebAILoginRole权限校验
- 提供/webAi-doLogin接口,支持网页端AI独立登录与Token管理
2025-12-15 20:17:50 +08:00
e2eb5f9ad0 feat(login): 新增网页AI登录场景WEB_AI
- 在LoginSceneEnum中增加WEB_AI枚举,绑定路径webAi-doLogin
- LoginService扩展WEB_AI登录逻辑:缓存登录态、创建w.tenant.{tenantId}队列并绑定webAiHeadersExchange
- 新增webAiHeadersExchange Bean,交换机名web.ai.headers.exchange
- 用户实体与Service增加web_ai字段及checkWebAILoginRole权限校验
- 提供/webAi-doLogin接口,支持网页端AI独立登录与Token管理
2025-12-05 13:57:16 +08:00
cd7aba7aaa fix(mapper): 补充 country_name_english 字段映射 2025-12-02 19:22:49 +08:00
b69e36391e fix(country): 移除冗余花括号并补全国家英文名字段 2025-12-02 19:20:07 +08:00
a9ea0d1ebf 优化 import 2025-11-17 22:08:44 +08:00
a2cdb813c7 删除微信依赖 2025-11-17 22:06:23 +08:00
3e66d8461b Revert "chore(config): 调整token超时与并发配置并清理注释"
This reverts commit 5ed0e5aaf9.
2025-11-17 22:03:37 +08:00
5ed0e5aaf9 chore(config): 调整token超时与并发配置并清理注释 2025-11-17 21:59:01 +08:00
ed980e953a chore(config): 调整token超时与并发配置并清理注释 2025-11-17 21:55:46 +08:00
a12cf82833 feat(user): 新增用户智能回复开关字段
在 SystemUsers 实体、DTO、VO 及 Mapper 中统一增加 aiReplay 字段,用于控制是否开启智能回复功能。
2025-11-07 13:57:56 +08:00
a630ae7b23 fix(controller): 重构 HostInfoController 并新增健康检查端点 2025-11-04 14:08:14 +08:00
6b3aa1203c 1.添加主播信息批量修改接口 2025-10-15 21:06:22 +08:00
8b5b7719f1 1.大哥添加粉丝团字段 2025-09-23 15:56:50 +08:00
40ae31786d 1.修复数据拼装错误的 bug 2025-09-16 20:11:56 +08:00
edfd9a083f 1.设置用户登录 Token 到期时间为租户对应权限到期时间 2025-09-16 20:04:36 +08:00
e86e561c37 1.设置用户登录 Token 到期时间为租户对应权限到期时间 2025-09-16 16:54:19 +08:00
22a1a8b963 1.修改用户登出逻辑
2.添加拦截器动态刷新用户 token 有效期
2025-09-09 15:36:47 +08:00
a44651dd2f 1.修改判断用户退出状态逻辑 2025-08-27 21:39:56 +08:00
43cbd262ea 1.修改AI退出登录后的逻辑 2025-08-27 21:12:50 +08:00
07a4142818 1.添加 rabbitmq
2.在 AI 登录时 创建消息队列
2025-08-27 16:42:53 +08:00
5ea65a8d25 1.新增 log 日志上传功能 2025-08-25 15:17:41 +08:00
3f1c4df78a 1.租户到期时间查询 2025-08-25 14:28:17 +08:00
41 changed files with 862 additions and 95 deletions

17
pom.xml
View File

@@ -70,12 +70,6 @@
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.binarywang/wx-java-mp-spring-boot-starter -->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java-mp-spring-boot-starter</artifactId>
<version>4.4.0</version>
</dependency>
<!-- https://doc.xiaominfo.com/docs/quick-start#openapi2 -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
@@ -125,6 +119,17 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.dromara.x-file-storage</groupId>
<artifactId>x-file-storage-spring</artifactId>
<version>2.3.0</version>
</dependency>
</dependencies>
<build>

View File

@@ -0,0 +1,38 @@
//package com.yupi.springbootinit.Interceptor;
//
//import cn.dev33.satoken.stp.StpUtil;
//
//import org.springframework.web.servlet.HandlerInterceptor;
//import org.springframework.web.servlet.ModelAndView;
//
//import javax.servlet.http.HttpServletRequest;
//import javax.servlet.http.HttpServletResponse;
//
///**
// * 自定义拦截器token续期 和 token定期刷新
// */
//public class TokenInterceptor implements HandlerInterceptor {
//
// @Override
// public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler){
// response.setHeader( "Content-Security-Policy" , "default-src 'self'; script-src 'self'; frame-ancestors 'self'");
// response.setHeader("Access-Control-Allow-Origin", (request).getHeader("Origin"));
// response.setHeader("Access-Control-Allow-Credentials", "true");
// response.setHeader("Referrer-Policy","no-referrer");
// response.setContentType("application/json");
// response.setCharacterEncoding("UTF-8");
// // 登录校验 -- 拦截所有请求,只有登录后才可以访问
// StpUtil.checkLogin();
// String tokenValue = StpUtil.getTokenValue();
// StpUtil.renewTimeout(tokenValue,259200);
// return true;
// }
//
// @Override
// public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,ModelAndView modelAndView) throws Exception {
// }
//
// @Override
// public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// }
//}

View File

@@ -19,7 +19,7 @@ public enum ErrorCode {
NOT_FOUND_ERROR(40800, "请求数据不存在"),
FORBIDDEN_ERROR(40300, "禁止访问"),
TENANT_NAME_NOT_EXISTS(40600, "租户不存在"),
LOGIN_NOW_ALLOWED(40700, "当前账号没有登录权限"),
LOGIN_NOT_ALLOWED(40700, "当前账号没有登录权限"),
SYSTEM_ERROR(50000, "系统内部异常"),
OPERATION_ERROR(50001, "操作失败"),
QUEUE_ERROR(60001, "队列消息添加失败"),

View File

@@ -0,0 +1,89 @@
package com.yupi.springbootinit.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.amqp.core.ExchangeBuilder;
import org.springframework.amqp.core.HeadersExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitMQConfig {
private static final String QUEUE = "HOST_INFO_QUEUE";
public static final String EXCHANGE_NAME = "user.headers.exchange";
public static final String AI_CHAT_EXCHANGE_NAME = "ai.chat.headers.exchange";
public static final String BIG_BROTHER_EXCHANGE_NAME = "big.brother.headers.exchange";
public static final String WEB_AI_EXCHANGE_NAME = "web.ai.headers.exchange";
//创建队列
//true:表示持久化
//队列在默认情况下放到内存rabbitmq重启后就丢失了如果希望重启后队列
//数据还能使用,就需要持久化
@Bean
public Queue hostInfoQueue(){
return new Queue(QUEUE,true);
}
//
// @Bean
// public MessageConverter messageConverter(){
// return new Jackson2JsonMessageConverter();
// }
@Bean
public HeadersExchange userHeadersExchange() {
return ExchangeBuilder.headersExchange(EXCHANGE_NAME)
.durable(true)
.build();
}
@Bean
public HeadersExchange aiChatHeadersExchange() {
return ExchangeBuilder.headersExchange(AI_CHAT_EXCHANGE_NAME)
.durable(true)
.build();
}
@Bean
public HeadersExchange bigBrotherHeadersExchange() {
return ExchangeBuilder.headersExchange(BIG_BROTHER_EXCHANGE_NAME)
.durable(true)
.build();
}
/** 网页AI使用的 HeadersExchange */
@Bean
public HeadersExchange webAiHeadersExchange() {
return ExchangeBuilder.headersExchange(WEB_AI_EXCHANGE_NAME)
.durable(true)
.build();
}
@Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory cf) {
return new RabbitAdmin(cf);
}
@Bean
public MessageConverter messageConverter() {
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
om.registerModule(new JavaTimeModule());
return new Jackson2JsonMessageConverter(om);
}
}

View File

@@ -1,37 +1,49 @@
package com.yupi.springbootinit.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean(name="redisTemplate")
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, String> template = new RedisTemplate<>();
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
// @Bean(name="redisTemplate")
// public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
// RedisTemplate<String, String> template = new RedisTemplate<>();
// RedisSerializer<String> redisSerializer = new StringRedisSerializer();
// template.setConnectionFactory(factory);
// //key序列化方式
// template.setKeySerializer(redisSerializer);
// //value序列化
// template.setValueSerializer(redisSerializer);
// //value hashmap序列化
// template.setHashValueSerializer(redisSerializer);
// //key haspmap序列化
// template.setHashKeySerializer(redisSerializer);
// //
// return template;
// }
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
//key序列化方式
template.setKeySerializer(redisSerializer);
//value序列化
template.setValueSerializer(redisSerializer);
//value hashmap序列化
template.setHashValueSerializer(redisSerializer);
//key haspmap序列化
template.setHashKeySerializer(redisSerializer);
//
// 使用 JSON 序列化器
GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer();
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(jsonSerializer);
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(jsonSerializer);
template.afterPropertiesSet();
return template;
}
}

View File

@@ -5,8 +5,6 @@ import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.router.SaHttpMethod;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil;
import com.yupi.springbootinit.common.ErrorCode;
import com.yupi.springbootinit.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -25,7 +23,11 @@ public class SaTokenConfigure implements WebMvcConfigurer {
registry.addInterceptor(new SaInterceptor(handle -> StpUtil.checkLogin()))
.addPathPatterns("/**")
.excludePathPatterns(getExcludePaths());
// registry.addInterceptor(new TokenInterceptor())
// .addPathPatterns("/**")
// .excludePathPatterns(getExcludePaths());
}
/**
* 获取需要放行的路径
@@ -46,7 +48,10 @@ public class SaTokenConfigure implements WebMvcConfigurer {
"/user/doLogin",
"/tenant/get-id-by-name",
"/user/bigbrother-doLogin",
"/user/aiChat-doLogin"
"/user/aiChat-doLogin",
"/user/aiChat-logout",
"/user/webAi-doLogin",
"/error",
};
}
@@ -67,6 +72,5 @@ public class SaTokenConfigure implements WebMvcConfigurer {
SaRouter.match(SaHttpMethod.OPTIONS)
.back();
};
}
}

View File

@@ -69,4 +69,9 @@ public class CommonController {
});
return ResultUtils.success(commentList);
}
@GetMapping("health")
public BaseResponse<String> health(){
return ResultUtils.success("ok");
}
}

View File

@@ -0,0 +1,33 @@
package com.yupi.springbootinit.controller;
/*
* @author: ziin
* @date: 2025/8/25 13:36
*/
import com.yupi.springbootinit.common.BaseResponse;
import com.yupi.springbootinit.common.ResultUtils;
import com.yupi.springbootinit.service.ServerAiLogService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
@RestController
@RequestMapping("/log")
@Slf4j
@CrossOrigin
public class FileController {
@Resource
private ServerAiLogService serverAiLogService;
@PostMapping("upload")
public BaseResponse<Boolean> upload(@RequestParam("file") MultipartFile file,
@RequestParam Long tenantId,
@RequestParam Long userId) {
return ResultUtils.success(serverAiLogService.saveLog(file,tenantId,userId));
}
}

View File

@@ -1,16 +1,11 @@
package com.yupi.springbootinit.controller;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yupi.springbootinit.common.BaseResponse;
import com.yupi.springbootinit.common.ResultUtils;
import com.yupi.springbootinit.model.dto.host.HistoryDataDTO;
import com.yupi.springbootinit.model.dto.host.HostInfoDTO;
import com.yupi.springbootinit.model.entity.NewHosts;
import com.yupi.springbootinit.model.entity.ServerEmployeeHosts;
import com.yupi.springbootinit.model.vo.hosts.NewHostsVO;
import com.yupi.springbootinit.model.vo.hosts.SevenDaysData;
import com.yupi.springbootinit.service.HostInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
@@ -44,4 +39,11 @@ public class HostInfoController {
return ResultUtils.success(b);
}
@PostMapping("/updates")
public BaseResponse<Boolean> updates(@RequestBody List<NewHosts> newHosts){
boolean b = hostInfoService.updateBatchById(newHosts);
return ResultUtils.success(b);
}
}

View File

@@ -4,12 +4,15 @@ import com.yupi.springbootinit.common.BaseResponse;
import com.yupi.springbootinit.common.ErrorCode;
import com.yupi.springbootinit.common.ResultUtils;
import com.yupi.springbootinit.exception.BusinessException;
import com.yupi.springbootinit.model.entity.SystemTenant;
import com.yupi.springbootinit.model.vo.user.SystemTenantVO;
import com.yupi.springbootinit.model.vo.user.SystemUsersVO;
import com.yupi.springbootinit.service.SystemTenantService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.annotation.security.PermitAll;
/*
* @author: ziin
@@ -30,7 +33,12 @@ public class TenantController {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
return ResultUtils.success( systemTenantService.getTenantIdByName(name));
}
@GetMapping("/get-expired-time")
public BaseResponse<SystemTenantVO> getTenantIdExpired(@RequestParam("tenantId") Long tenantId) {
return ResultUtils.success(systemTenantService.getTenantIdExpired(tenantId));
}
}

View File

@@ -30,8 +30,6 @@ import javax.annotation.Resource;
public class UserController {
// @Resource
// private SystemUsersService usersService;
@Resource
private LoginService loginService;
@@ -40,7 +38,6 @@ public class UserController {
@PostMapping("doLogin")
public BaseResponse<SystemUsersVO> doLogin(@RequestBody SystemUsersDTO usersDTO) {
return ResultUtils.success(loginService.login(LoginSceneEnum.HOST, usersDTO));
// return ResultUtils.success(systemUsersVO);
}
@@ -54,27 +51,21 @@ public class UserController {
@PostMapping("aiChat-doLogin")
public BaseResponse<SystemUsersVO> aiChatDoLogin(@RequestBody SystemUsersDTO usersDTO) {
return ResultUtils.success(loginService.login(LoginSceneEnum.AI_CHAT, usersDTO));
// return ResultUtils.success(systemUsersVO);
}
@PostMapping("aiChat-logout")
public BaseResponse<Boolean> aiChatLogout(@RequestBody SystemUsersDTO usersDTO){
return ResultUtils.success(loginService.aiChatLogout(usersDTO));
}
//
// private SystemUsers getUserByName(@RequestBody SystemUsersDTO usersDTO) {
// SystemUsers user = usersService.getUserByUserName(usersDTO.getUsername(),usersDTO.getTenantId());
// if (user == null) {
// throw new BusinessException(ErrorCode.USERNAME_OR_PASSWORD_ERROR);
// }
// if (!usersService.isPasswordMatch(usersDTO.getPassword(), user.getPassword())) {
// throw new BusinessException(ErrorCode.USERNAME_OR_PASSWORD_ERROR);
// }
//
// if (CommonStatusEnum.isDisable(Integer.valueOf(user.getStatus()))) {
// throw new BusinessException(ErrorCode.USER_DISABLE);
// }
// if (usersService.isExpired(usersDTO.getTenantId())){
// throw new BusinessException(ErrorCode.PACKAGE_EXPIRED);
// }
// return user;
// }
@PostMapping("webAi-doLogin")
public BaseResponse<SystemUsersVO> webAiDoLogin(@RequestBody SystemUsersDTO usersDTO) {
return ResultUtils.success(loginService.login(LoginSceneEnum.WEB_AI, usersDTO));
}
@GetMapping("/logout")
public BaseResponse<Boolean> logout(){
return ResultUtils.success(loginService.logout());
}
}

View File

@@ -15,7 +15,4 @@ public interface CountryInfoMapper extends BaseMapper<CountryInfo> {
List<CountryInfoVO> selectByCountryGroupName();
}

View File

@@ -0,0 +1,12 @@
package com.yupi.springbootinit.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yupi.springbootinit.model.entity.ServerAiLog;
/*
* @author: ziin
* @date: 2025/8/25 13:37
*/
public interface ServerAiLogMapper extends BaseMapper<ServerAiLog> {
}

View File

@@ -0,0 +1,32 @@
package com.yupi.springbootinit.model.dto.file;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
/*
* @author: ziin
* @date: 2025/8/25 14:32
*/
@Data
public class FileDTO {
/**
* 日志租户
*/
@TableField(value = "log_tenant_id")
@ApiModelProperty(value="日志租户")
private Long logTenantId;
/**
* 用户 Id
*/
@TableField(value = "log_user_id")
@ApiModelProperty(value="用户 Id")
private Long logUserId;
@ApiModelProperty(value="日志文件")
private MultipartFile file;
}

View File

@@ -165,4 +165,8 @@ public class ServerBigBrotherDTO extends PageRequest implements Serializable {
@TableField(value = "hostcoins")
@ApiModelProperty(value="大哥打赏的金币")
private Integer hostcoins;
@TableField(value = "fans_level")
@ApiModelProperty(value = "大哥粉丝团等级")
private Integer fansLevel;
}

View File

@@ -29,4 +29,8 @@ public class SystemUsersDTO {
* 租户编号
*/
private Long tenantId;
private Long userId;
private Byte aiReplay;
}

View File

@@ -0,0 +1,71 @@
package com.yupi.springbootinit.model.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.util.Date;
import lombok.Data;
/*
* @author: ziin
* @date: 2025/8/25 13:37
*/
@ApiModel(description="server_ai_log")
@Data
@TableName(value = "server_ai_log")
public class ServerAiLog {
/**
* 日志主键
*/
@TableId(value = "log_id", type = IdType.AUTO)
@ApiModelProperty(value="日志主键")
private Long logId;
/**
* 日志主键
*/
@TableField(value = "log_name")
@ApiModelProperty(value="日志主键")
private String logName;
/**
* 日志路径
*/
@TableField(value = "log_file_path")
@ApiModelProperty(value="日志路径")
private String logFilePath;
/**
* 日志租户
*/
@TableField(value = "log_tenant_id")
@ApiModelProperty(value="日志租户")
private Long logTenantId;
/**
* 用户 Id
*/
@TableField(value = "log_user_id")
@ApiModelProperty(value="用户 Id")
private Long logUserId;
@TableField(value = "creator")
@ApiModelProperty(value="")
private String creator;
@TableField(value = "create_time")
@ApiModelProperty(value="")
private Date createTime;
@TableField(value = "updater")
@ApiModelProperty(value="")
private String updater;
@TableField(value = "update_time")
@ApiModelProperty(value="")
private Date updateTime;
}

View File

@@ -150,4 +150,7 @@ public class ServerBigBrother {
@TableField(value = "tenant_id")
@ApiModelProperty(value="租户 Id")
private Long tenantId;
@TableField(value = "sec_uid")
private String secUid;
}

View File

@@ -6,6 +6,8 @@ import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.time.LocalDateTime;
import java.util.Date;
import lombok.Data;
@@ -84,6 +86,20 @@ public class SystemTenant {
@ApiModelProperty(value="过期时间")
private Date expireTime;
/**
* ai过期时间
*/
@TableField(value = "expire_time")
@ApiModelProperty(value="ai过期时间")
private Date aiExpireTime;
/**
* 大哥过期时间
*/
@TableField(value = "expire_time")
@ApiModelProperty(value="大哥过期时间")
private Date brotherExpireTime;
/**
* 账号数量
*/

View File

@@ -181,4 +181,18 @@ public class SystemUsers {
@TableField(value = "ai_chat")
@ApiModelProperty(value = "能否登录大哥爬虫客户端")
private Byte aiChat;
/**
* 是否开启智能回复
*/
@TableField(value = "ai_replay")
@ApiModelProperty(value = "是否开启智能回复")
private Byte aiReplay;
/**
* 能否登录智能回复客户端
*/
@TableField(value = "web_ai")
@ApiModelProperty(value = "能否登录智能回复客户端")
private Byte webAi;
}

View File

@@ -8,7 +8,8 @@ import lombok.Getter;
public enum LoginSceneEnum {
HOST("doLogin", "host", "checkCrawlRole"),
BIG_BROTHER("bigbrother-doLogin", "bigbrother", "checkBigBrotherRole"),
AI_CHAT("aiChat-doLogin", "aiChat", "checkAiChatLoginRole");
AI_CHAT("aiChat-doLogin", "aiChat", "checkAiChatLoginRole"),
WEB_AI("webAiChat-doLogin", "webAiChat", "checkWebAiChatLoginRole");
private final String path; // 对应 @PostMapping
private final String saMode; // Sa-Token 登录模式

View File

@@ -109,4 +109,10 @@ public class ServerBigBrotherVO {
@ApiModelProperty(value="创建时间")
private LocalDateTime createTime;
@TableField(value = "fans_level")
@ApiModelProperty(value = "大哥粉丝团等级")
private Integer fansLevel;
@TableField(value = "sec_uid")
private String secUid;
}

View File

@@ -64,4 +64,11 @@ public class CountryInfoVO {
@ApiModelProperty(value = "语言中文")
private String languageName;
/**
* 国家名称英文
*/
@TableField(value = "country_name_english")
@ApiModelProperty(value = "国家名称英文")
private String countryNameEnglish;
}

View File

@@ -0,0 +1,15 @@
package com.yupi.springbootinit.model.vo.user;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.Date;
/*
* @author: ziin
* @date: 2025/8/25 14:12
*/
@Data
public class SystemTenantVO {
private Date expiredTime;
}

View File

@@ -29,4 +29,12 @@ public class SystemUsersVO {
private String tokenName;
private String tokenValue;
private Date expireTime;
private Date brotherExpireTime;
private Date aiExpireTime;
private Byte aiReplay;
}

View File

@@ -0,0 +1,15 @@
package com.yupi.springbootinit.service;
import com.yupi.springbootinit.model.entity.ServerAiLog;
import com.baomidou.mybatisplus.extension.service.IService;
import org.springframework.web.multipart.MultipartFile;
/*
* @author: ziin
* @date: 2025/8/25 13:37
*/
public interface ServerAiLogService extends IService<ServerAiLog>{
Boolean saveLog(MultipartFile file, Long tenantId, Long userId);
}

View File

@@ -3,7 +3,8 @@ package com.yupi.springbootinit.service;
import com.yupi.springbootinit.common.BaseResponse;
import com.yupi.springbootinit.model.entity.SystemTenant;
import com.baomidou.mybatisplus.extension.service.IService;
/*
import com.yupi.springbootinit.model.vo.user.SystemTenantVO;
/*
* @author: ziin
* @date: 2025/6/20 14:50
*/
@@ -12,4 +13,6 @@ public interface SystemTenantService extends IService<SystemTenant>{
Long getTenantIdByName(String name);
SystemTenantVO getTenantIdExpired(Long tenantId);
}

View File

@@ -24,4 +24,6 @@ public interface SystemUsersService extends IService<SystemUsers> {
boolean checkbigBrotherlRole(Long userId);
boolean checkAiCHatLoginRole(Long userId);
boolean checkWebAILoginRole(Long userId);
}

View File

@@ -1,30 +1,17 @@
package com.yupi.springbootinit.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.yupi.springbootinit.common.BaseResponse;
import com.yupi.springbootinit.common.ResultUtils;
import com.yupi.springbootinit.mapper.NewHostsMapper;
import com.yupi.springbootinit.model.dto.host.HistoryDataDTO;
import com.yupi.springbootinit.model.dto.host.HostInfoDTO;
import com.yupi.springbootinit.model.entity.NewHosts;
import com.yupi.springbootinit.model.vo.hosts.NewHostsVO;
import com.yupi.springbootinit.model.vo.hosts.SevenDaysData;
import com.yupi.springbootinit.service.HostInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StopWatch;
import org.springframework.web.bind.annotation.RequestBody;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
/*
* @author: ziin

View File

@@ -2,55 +2,261 @@ package com.yupi.springbootinit.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yupi.springbootinit.common.ErrorCode;
import com.yupi.springbootinit.exception.BusinessException;
import com.yupi.springbootinit.mapper.SystemTenantMapper;
import com.yupi.springbootinit.model.dto.user.SystemUsersDTO;
import com.yupi.springbootinit.model.entity.SystemTenant;
import com.yupi.springbootinit.model.entity.SystemUsers;
import com.yupi.springbootinit.model.enums.CommonStatusEnum;
import com.yupi.springbootinit.model.enums.LoginSceneEnum;
import com.yupi.springbootinit.model.vo.user.SystemUsersVO;
import com.yupi.springbootinit.service.SystemUsersService;
import com.yupi.springbootinit.utils.DateUtils;
import com.yupi.springbootinit.utils.RedisUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* 登录相关业务实现类
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class LoginService {
/** 用户业务服务 */
private final SystemUsersService usersService;
/** 用于缓存 AI 登录状态的 RedisTemplate布尔值 */
@Resource
private RedisTemplate<String, Boolean> redisTemplate;
/** AI聊天使用的 HeadersExchange */
private final HeadersExchange aiChatHeadersExchange;
/** 大哥使用的 HeadersExchange */
private final HeadersExchange bigBrotherHeadersExchange;
/** 网页AI使用的 HeadersExchange */
private final HeadersExchange webAiHeadersExchange;
/** RabbitMQ 管理组件 */
@Resource
private RabbitAdmin rabbitAdmin;
/** 通用 Redis 工具类 */
@Resource
private RedisUtils redisUtils;
@Resource
private SystemTenantMapper tenantMapper;
/**
* 统一登录入口
*
* @param scene 登录场景HOST / BIG_BROTHER / AI_CHAT
* @param dto 登录参数(用户名、密码、租户 ID
* @return 登录成功后的用户信息 + Token
*/
public SystemUsersVO login(LoginSceneEnum scene, SystemUsersDTO dto) {
SystemUsers user = validateUser(dto); // 校验用户名、密码、状态、租户过期
checkRole(scene, user.getId()); // 按场景做角色校验
Long second = usersService.getTenantExpiredTime(dto.getTenantId());
// Sa-Token 登录
StpUtil.login(user.getId(), scene.getSaMode());
StpUtil.renewTimeout(second);
// 1. 校验用户名、密码、状态、租户过期
SystemUsers user = validateUser(dto);
// 2. 按场景校验角色权限
checkRole(scene, user.getId());
// 3. AI_CHAT 场景专属逻辑:缓存登录状态并动态创建 RabbitMQ 队列
if (scene.equals(LoginSceneEnum.AI_CHAT)) {
// 记录该用户已登录 AI_CHAT
redisTemplate.opsForValue().set("ai_login:" + user.getTenantId() + ":" + user.getId(), true);
String queueName = "q.tenant." + user.getTenantId();
// 若该租户队列尚未创建,则创建队列并绑定到 HeadersExchange
Queue queue = QueueBuilder.durable(queueName).build();
rabbitAdmin.declareQueue(queue);
Map<String, Object> headers = Map.of("tenantId", user.getTenantId(), "x-match", "all");
Binding binding = BindingBuilder
.bind(queue)
.to(aiChatHeadersExchange) // 使用AI聊天专用交换机
.whereAll(headers)
.match();
rabbitAdmin.declareBinding(binding);
}
// 3. 大哥场景专属逻辑:缓存登录状态并动态创建 RabbitMQ 队列
if (scene.equals(LoginSceneEnum.BIG_BROTHER)) {
// 记录该用户已登录 BIG_BROTHER
redisTemplate.opsForValue().set("bigbrother_login:" + user.getTenantId() + ":" + user.getId(), true);
String queueName = "b.tenant." + user.getTenantId();
// 若该租户队列尚未创建,则创建队列并绑定到 HeadersExchange
Queue queue = QueueBuilder.durable(queueName).build();
rabbitAdmin.declareQueue(queue);
Map<String, Object> headers = Map.of("tenantId", user.getTenantId(), "x-match", "all");
Binding binding = BindingBuilder
.bind(queue)
.to(bigBrotherHeadersExchange) // 使用大哥专用交换机
.whereAll(headers)
.match();
rabbitAdmin.declareBinding(binding);
}
if (scene.equals(LoginSceneEnum.WEB_AI)) {
redisTemplate.opsForValue().set("webAI_login:" + user.getTenantId() + ":" + user.getId(), true);
String queueName = "w.tenant." + user.getTenantId();
// 若该租户队列尚未创建,则创建队列并绑定到 HeadersExchange
Queue queue = QueueBuilder.durable(queueName).build();
rabbitAdmin.declareQueue(queue);
Map<String, Object> headers = Map.of("tenantId", user.getTenantId(), "x-match", "all");
Binding binding = BindingBuilder
.bind(queue)
.to(webAiHeadersExchange) // 使用webAi专用交换机
.whereAll(headers)
.match();
rabbitAdmin.declareBinding(binding);
}
SystemTenant systemTenant = tenantMapper.selectById(user.getTenantId());
// 封装返回数据
SystemUsersVO vo = new SystemUsersVO();
BeanUtil.copyProperties(user, vo);
vo.setTokenName(StpUtil.getTokenName());
vo.setTokenValue(StpUtil.getTokenValue());
return vo;
// 5. Sa-Token 登录
StpUtil.login(user.getId(), scene.getSaMode());
switch (scene) {
case AI_CHAT:
StpUtil.logout(user.getId(), LoginSceneEnum.WEB_AI.getSaMode());
StpUtil.renewTimeout(DateUtils.dateBetween(systemTenant.getAiExpireTime(),DateUtil.date()));
BeanUtil.copyProperties(user, vo);
vo.setTokenName(StpUtil.getTokenName());
vo.setTokenValue(StpUtil.getTokenValue());
vo.setAiExpireTime(systemTenant.getAiExpireTime());
return vo;
case HOST:
StpUtil.renewTimeout(DateUtils.dateBetween(systemTenant.getExpireTime(),DateUtil.date()));
BeanUtil.copyProperties(user, vo);
vo.setTokenName(StpUtil.getTokenName());
vo.setTokenValue(StpUtil.getTokenValue());
vo.setExpireTime(systemTenant.getExpireTime());
return vo;
case BIG_BROTHER:
StpUtil.renewTimeout(DateUtils.dateBetween(systemTenant.getBrotherExpireTime(),DateUtil.date()));
BeanUtil.copyProperties(user, vo);
vo.setTokenName(StpUtil.getTokenName());
vo.setTokenValue(StpUtil.getTokenValue());
vo.setBrotherExpireTime(systemTenant.getBrotherExpireTime());
return vo;
case WEB_AI:
StpUtil.logout(user.getId(), LoginSceneEnum.AI_CHAT.getSaMode());
StpUtil.renewTimeout(DateUtils.dateBetween(systemTenant.getAiExpireTime(),DateUtil.date()));
BeanUtil.copyProperties(user, vo);
vo.setTokenName(StpUtil.getTokenName());
vo.setTokenValue(StpUtil.getTokenValue());
vo.setAiExpireTime(systemTenant.getAiExpireTime());
return vo;
}
return null;
}
/**
* 校验用户登录信息
*
* @param dto 登录参数
* @return 校验通过的用户实体
* @throws BusinessException 校验失败时抛出
*/
private SystemUsers validateUser(SystemUsersDTO dto) {
SystemUsers user = usersService.getUserByUserName(dto.getUsername(), dto.getTenantId());
if (user == null) throw new BusinessException(ErrorCode.USERNAME_OR_PASSWORD_ERROR);
if (!usersService.isPasswordMatch(dto.getPassword(), user.getPassword()))
if (user == null) {
throw new BusinessException(ErrorCode.USERNAME_OR_PASSWORD_ERROR);
if (CommonStatusEnum.isDisable(Integer.valueOf(user.getStatus())))
}
if (!usersService.isPasswordMatch(dto.getPassword(), user.getPassword())) {
throw new BusinessException(ErrorCode.USERNAME_OR_PASSWORD_ERROR);
}
if (CommonStatusEnum.isDisable(Integer.valueOf(user.getStatus()))) {
throw new BusinessException(ErrorCode.USER_DISABLE);
if (usersService.isExpired(dto.getTenantId()))
}
if (usersService.isExpired(dto.getTenantId())) {
throw new BusinessException(ErrorCode.PACKAGE_EXPIRED);
}
return user;
}
/**
* 按登录场景校验角色权限
*
* @param scene 登录场景
* @param userId 用户 ID
* @throws BusinessException 无权限时抛出
*/
private void checkRole(LoginSceneEnum scene, Long userId) {
Boolean pass = switch (scene) {
case HOST -> usersService.checkCrawlRole(userId);
case BIG_BROTHER -> usersService.checkbigBrotherlRole(userId);
case AI_CHAT -> usersService.checkAiCHatLoginRole(userId);
case WEB_AI -> usersService.checkWebAILoginRole(userId);
};
if (!pass) throw new BusinessException(ErrorCode.LOGIN_NOW_ALLOWED);
if (!pass) {
throw new BusinessException(ErrorCode.LOGIN_NOT_ALLOWED);
}
}
}
/**
* AI_CHAT 场景专属登出
*
* @param usersDTO 包含租户 ID 与用户 ID
* @return 固定返回 true
*/
public Boolean aiChatLogout(SystemUsersDTO usersDTO) {
// 1. 删除 Redis 中该用户的 AI_CHAT 登录标记
Boolean ai_login = redisTemplate.delete("ai_login:" + usersDTO.getTenantId() + ":" + usersDTO.getUserId());
Boolean webAi_login = redisTemplate.delete("webAI_login:" + usersDTO.getTenantId() + ":" + usersDTO.getUserId());
// 2. 使当前 Token 失效
String tokenValue = StpUtil.getTokenValue();
StpUtil.logoutByTokenValue(tokenValue);
log.info("删除租户:{} 登录状态:{}", usersDTO.getTenantId(), ai_login);
log.info("删除租户:{} 登录状态:{}", usersDTO.getTenantId(), webAi_login);
// 3. 若该租户下已无 AI_CHAT 在线用户,则删除队列
if (!redisUtils.hasKeyByPrefix("ai_login:" + usersDTO.getTenantId())) {
boolean b = rabbitAdmin.deleteQueue("q.tenant." + usersDTO.getTenantId());
log.info("删除Ai_CHAT租户:{} 队列删除状态:{}", usersDTO.getTenantId(), b);
}
if (!redisUtils.hasKeyByPrefix("webAI_login:" + usersDTO.getTenantId())) {
boolean b = rabbitAdmin.deleteQueue("w.tenant." + usersDTO.getTenantId());
log.info("删除Web_AI租户:{} 队列删除状态:{}", usersDTO.getTenantId(), b);
}
return true;
}
/**
* 通用登出(不区分场景)
*
* @return 固定返回 true
*/
public Boolean logout() {
String tokenValue = StpUtil.getTokenValue();
Long loginId = (Long) StpUtil.getLoginId();
StpUtil.logoutByTokenValue(tokenValue);
log.info("用户:{} 登出成功", loginId);
return true;
}
}

View File

@@ -0,0 +1,70 @@
package com.yupi.springbootinit.service.impl;
import com.yupi.springbootinit.common.ErrorCode;
import com.yupi.springbootinit.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.time.LocalDateTime;
import java.util.List;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yupi.springbootinit.mapper.ServerAiLogMapper;
import com.yupi.springbootinit.model.entity.ServerAiLog;
import com.yupi.springbootinit.service.ServerAiLogService;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
/*
* @author: ziin
* @date: 2025/8/25 13:37
*/
@Service
@Slf4j
public class ServerAiLogServiceImpl extends ServiceImpl<ServerAiLogMapper, ServerAiLog> implements ServerAiLogService{
@Resource
private ServerAiLogMapper serverAiLogMapper;
@Value("${ai_log_path}")
private String basePath;
@Override
public Boolean saveLog(MultipartFile file, Long tenantId, Long userId) {
if (file.isEmpty()) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR,"上传文件为空");
}
Path dir = Paths.get(basePath, tenantId.toString(), userId.toString());
if (!Files.exists(dir)) {
try {
Files.createDirectories(dir);
} catch (IOException e) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR,"创建目录失败");
}
}
String originalFilename = file.getOriginalFilename();
Path target = dir.resolve(originalFilename);
try {
Files.copy(file.getInputStream(), target, StandardCopyOption.REPLACE_EXISTING);
log.info("文件名{},文件上传路径{},",originalFilename,target);
} catch (IOException e) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR,"保存文件失败");
}
ServerAiLog log = new ServerAiLog();
log.setLogName(originalFilename);
log.setLogFilePath(target.toString());
log.setLogTenantId(tenantId);
log.setLogUserId(userId);
log.setCreator(String.valueOf(userId)); // 可替换为当前登录用户
return serverAiLogMapper.insert(log) == 1;
}
}

View File

@@ -1,9 +1,11 @@
package com.yupi.springbootinit.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yupi.springbootinit.common.BaseResponse;
import com.yupi.springbootinit.common.ErrorCode;
import com.yupi.springbootinit.exception.BusinessException;
import com.yupi.springbootinit.model.vo.user.SystemTenantVO;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
@@ -11,6 +13,8 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yupi.springbootinit.model.entity.SystemTenant;
import com.yupi.springbootinit.mapper.SystemTenantMapper;
import com.yupi.springbootinit.service.SystemTenantService;
import javax.annotation.Resource;
/*
* @author: ziin
* @date: 2025/6/20 14:50
@@ -19,6 +23,9 @@ import com.yupi.springbootinit.service.SystemTenantService;
@Service
public class SystemTenantServiceImpl extends ServiceImpl<SystemTenantMapper, SystemTenant> implements SystemTenantService{
@Resource
private SystemTenantMapper systemTenantMapper;
@Override
public Long getTenantIdByName(String name) {
QueryWrapper<SystemTenant> queryWrapper = new QueryWrapper<>();
@@ -31,4 +38,12 @@ public class SystemTenantServiceImpl extends ServiceImpl<SystemTenantMapper, Sys
return systemTenant.getId();
}
@Override
public SystemTenantVO getTenantIdExpired(Long tenantId) {
SystemTenant systemTenant = systemTenantMapper.selectById(tenantId);
SystemTenantVO systemTenantVO = new SystemTenantVO();
systemTenantVO.setExpiredTime(systemTenant.getExpireTime());
return systemTenantVO;
}
}

View File

@@ -83,5 +83,12 @@ public class SystemUsersServiceImpl extends ServiceImpl<SystemUsersMapper,System
return systemUsers.getAiChat() == 1;
}
@Override
public boolean checkWebAILoginRole(Long userId) {
SystemUsers systemUsers = baseMapper.selectById(userId);
return systemUsers.getWebAi() == 1;
}
}

View File

@@ -0,0 +1,17 @@
package com.yupi.springbootinit.utils;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import java.util.Date;
/*
* @author: ziin
* @date: 2025/9/16 16:31
*/
public class DateUtils {
public static Long dateBetween(Date date1, Date date2) {
return DateUtil.between(date1, date2, DateUnit.SECOND);
}
}

View File

@@ -0,0 +1,40 @@
package com.yupi.springbootinit.utils;
/*
* @author: ziin
* @date: 2025/8/27 20:35
*/
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Set;
@Component
public class RedisUtils {
@Resource
private RedisTemplate<String,Object> redisTemplate;
public boolean hasAiLoginKeys(String prefix) {
Set<String> keys = redisTemplate.keys(prefix); // 获取匹配的键集合
return !keys.isEmpty(); // 如果有键匹配返回true否则返回false
}
public boolean hasKeyByPrefix(String prefix) {
return Boolean.TRUE.equals(
redisTemplate.execute((RedisCallback<Boolean>) conn -> {
try (Cursor<byte[]> cursor = conn.scan(
ScanOptions.scanOptions()
.match(prefix + ":*")
.count(100) // 每次返回条数,可调整
.build())) {
return cursor.hasNext(); // 只要存在一条就返回 true
}
})
);
}
}

View File

@@ -93,9 +93,7 @@ sa-token:
# token 名称(同时也是 cookie 名称)
token-name: vvtoken
# token 有效期(单位:秒) 默认30天-1 代表永久有效
timeout: 2592000
# token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
active-timeout: -1
timeout: 172800
# 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
is-concurrent: false
# 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token
@@ -107,4 +105,6 @@ sa-token:
md5:
salt: (-FhqvXO,wMz
salt: (-FhqvXO,wMz
ai_log_path: /test/ai_log

View File

@@ -26,6 +26,7 @@
<result column="country_name" jdbcType="VARCHAR" property="countryName" />
<result column="language" jdbcType="VARCHAR" property="language" />
<result column="language_name" jdbcType="VARCHAR" property="languageName" />
<result column="country_name_english" jdbcType="VARCHAR" property="countryNameEnglish" />
</resultMap>
<sql id="Base_Column_List">
<!--@mbg.generated-->

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yupi.springbootinit.mapper.ServerAiLogMapper">
<resultMap id="BaseResultMap" type="com.yupi.springbootinit.model.entity.ServerAiLog">
<!--@mbg.generated-->
<!--@Table server_ai_log-->
<id column="log_id" jdbcType="BIGINT" property="logId" />
<result column="log_name" jdbcType="VARCHAR" property="logName" />
<result column="log_file_path" jdbcType="VARCHAR" property="logFilePath" />
<result column="log_tenant_id" jdbcType="BIGINT" property="logTenantId" />
<result column="log_user_id" jdbcType="BIGINT" property="logUserId" />
<result column="creator" jdbcType="VARCHAR" property="creator" />
<result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
<result column="updater" jdbcType="VARCHAR" property="updater" />
<result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
</resultMap>
<sql id="Base_Column_List">
<!--@mbg.generated-->
log_id, log_name, log_file_path, log_tenant_id, log_user_id, creator, create_time,
updater, update_time
</sql>
</mapper>

View File

@@ -23,12 +23,13 @@
<result column="updater" jdbcType="VARCHAR" property="updater" />
<result column="deleted" jdbcType="BOOLEAN" property="deleted" />
<result column="tenant_id" jdbcType="BIGINT" property="tenantId" />
<result column="secUid" jdbcType="VARCHAR" property="secUid" />
</resultMap>
<sql id="Base_Column_List">
<!--@mbg.generated-->
id, display_id, user_id_str, nickname, `level`, hostcoins, follower_count, following_count,
region, historic_high_coins, total_gift_coins, host_display_id, owner_id, create_time,
update_time, creator, updater, deleted, tenant_id
update_time, creator, updater, deleted, tenant_id,secUid
</sql>
<resultMap id="BaseResultMapCopy" type="com.yupi.springbootinit.model.vo.bigbrother.ServerBigBrotherVO">
@@ -46,6 +47,8 @@
<result column="total_gift_coins" jdbcType="INTEGER" property="totalGiftCoins" />
<result column="host_display_id" jdbcType="VARCHAR" property="hostDisplayId" />
<result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
<result column="fans_level" jdbcType="INTEGER" property="fansLevel" />
<result column="secUid" jdbcType="VARCHAR" property="secUid" />
</resultMap>
@@ -53,7 +56,7 @@
<select id="selectPageByCondition" resultMap="BaseResultMapCopy">
select sbr.display_id, sbr.user_id_str,sbr.nickname,sbr.level,sbr.hostcoins,
sbr.follower_count, sbr.following_count, sbr.region, sbr.historic_high_coins, sbr.total_gift_coins,
sbr.host_display_id,sbr.create_time from
sbr.host_display_id,sbr.create_time,sbr.fans_level,sbr.secUid from
server_big_brother sbr left join server_country_info ci ON sbr.region = ci.country_name
where sbr.tenant_id=#{dto.tenantId}
<!-- 大哥所属国家筛选 -->

View File

@@ -25,6 +25,7 @@
<result column="deleted" jdbcType="BIT" property="deleted" />
<result column="tenant_id" jdbcType="BIGINT" property="tenantId" />
<result column="crawl" jdbcType="TINYINT" property="crawl" />
<result column="ai_replay" jdbcType="TINYINT" property="aiReplay" />
<result column="big_brother" jdbcType="TINYINT" property="bigBrother" />
</resultMap>
<sql id="Base_Column_List">

View File

@@ -10,7 +10,8 @@ import org.springframework.boot.test.context.SpringBootTest;
* @from <a href="https://yupi.icu">编程导航知识星球</a>
*/
@SpringBootTest
class MainApplicationTests {
class
MainApplicationTests {
@Test
void contextLoads() {