feat(account): 添加账户交易记录分页查询功能
- 新增充值记录、消费记录和赠送记录的分页查询接口 - 实现消费记录按callId分组查询功能 - 添加账户交易记录DTO和分组查询DTO定义 - 在AccountController中添加日志注解和相关API端点 - 优化AccountServiceImpl中的数据库操作方法调用 - 修改AccountTransactionMapper.xml增加相应的SQL查询语句 - 添加创建时间范围筛选功能到交易记录查询中 - 实现CMS内容管理的路径更新功能和标签查询优化
This commit is contained in:
parent
0d934f7287
commit
891f60d5a5
|
|
@ -0,0 +1,46 @@
|
|||
-- 更新 sys_log 表结构以支持新的日志功能
|
||||
-- 作者: 王志维
|
||||
-- 创建时间: 2026-04-14
|
||||
|
||||
-- 备份旧数据(可选)
|
||||
-- CREATE TABLE sys_log_backup AS SELECT * FROM sys_log;
|
||||
|
||||
-- 删除旧表(如果存在)
|
||||
DROP TABLE IF EXISTS `sys_log`;
|
||||
|
||||
-- 创建新表
|
||||
CREATE TABLE `sys_log` (
|
||||
`log_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`trace_id` varchar(255) DEFAULT NULL COMMENT '链路ID',
|
||||
`description` varchar(255) DEFAULT NULL COMMENT '日志描述',
|
||||
`module` varchar(50) DEFAULT NULL COMMENT '所属模块',
|
||||
`request_url` varchar(512) DEFAULT NULL COMMENT '请求URL',
|
||||
`request_method` varchar(10) DEFAULT NULL COMMENT '请求方式',
|
||||
`request_headers` text COMMENT '请求头',
|
||||
`request_body` text COMMENT '请求体',
|
||||
`status_code` int(11) DEFAULT NULL COMMENT '状态码',
|
||||
`response_headers` text COMMENT '响应头',
|
||||
`response_body` mediumtext COMMENT '响应体',
|
||||
`time_taken` bigint(20) DEFAULT NULL COMMENT '耗时(ms)',
|
||||
`ip` varchar(100) DEFAULT NULL COMMENT 'IP',
|
||||
`address` varchar(255) DEFAULT NULL COMMENT 'IP归属地',
|
||||
`browser` varchar(100) DEFAULT NULL COMMENT '浏览器',
|
||||
`os` varchar(100) DEFAULT NULL COMMENT '操作系统',
|
||||
`status` tinyint(1) DEFAULT '1' COMMENT '状态(1:成功;2:失败)',
|
||||
`error_msg` text COMMENT '错误信息',
|
||||
`create_user` bigint(20) DEFAULT NULL COMMENT '创建人',
|
||||
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`delete_flag` tinyint(1) DEFAULT '0' COMMENT '是否删除 :0 未删除,1已删除',
|
||||
`create_by` varchar(50) DEFAULT NULL COMMENT '创建人',
|
||||
`update_by` varchar(50) DEFAULT NULL COMMENT '更新人',
|
||||
PRIMARY KEY (`log_id`) USING BTREE,
|
||||
KEY `idx_module` (`module`) USING BTREE COMMENT '模块查询优化',
|
||||
KEY `idx_ip` (`ip`) USING BTREE COMMENT 'IP查询优化',
|
||||
KEY `idx_create_time` (`create_time`) USING BTREE COMMENT '时间范围查询优化',
|
||||
KEY `idx_status` (`status`) USING BTREE COMMENT '状态查询优化'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='系统操作日志表';
|
||||
|
||||
-- 插入测试数据(可选)
|
||||
-- INSERT INTO `sys_log` (`description`, `module`, `request_url`, `request_method`, `status_code`, `time_taken`, `ip`, `status`, `create_time`)
|
||||
-- VALUES ('用户登录', '登录认证', '/api/login/accountLogin', 'POST', 200, 150, '127.0.0.1', 1, NOW());
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package com.kexue.skills.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 操作日志注解
|
||||
* 用于记录Controller层方法的请求和响应信息
|
||||
*
|
||||
* @author 王志维
|
||||
* @since 2026-04-14
|
||||
*/
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Log {
|
||||
|
||||
/**
|
||||
* 模块名称
|
||||
* 例如:用户管理、登录认证、内容管理等
|
||||
*/
|
||||
String module() default "";
|
||||
|
||||
/**
|
||||
* 日志描述
|
||||
* 如果不填写,将根据方法签名自动生成
|
||||
*/
|
||||
String description() default "";
|
||||
|
||||
/**
|
||||
* 是否忽略记录日志
|
||||
* 默认false,设置为true时不记录该接口的日志
|
||||
*/
|
||||
boolean ignore() default false;
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package com.kexue.skills.config;
|
||||
|
||||
import com.kexue.skills.interceptor.LogInterceptor;
|
||||
import com.kexue.skills.mapper.SysLogMapper;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 日志配置类
|
||||
* 注册日志拦截器,启用异步支持
|
||||
*
|
||||
* @author 王志维
|
||||
* @since 2026-04-14
|
||||
*/
|
||||
@Configuration
|
||||
@EnableAsync // 启用异步支持
|
||||
public class LogConfiguration implements WebMvcConfigurer {
|
||||
|
||||
@Resource
|
||||
private SysLogMapper sysLogMapper;
|
||||
|
||||
/**
|
||||
* 注册拦截器
|
||||
*/
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
LogInterceptor logInterceptor = new LogInterceptor();
|
||||
logInterceptor.setSysLogMapper(sysLogMapper);
|
||||
registry.addInterceptor(logInterceptor)
|
||||
.addPathPatterns("/api/**") // 拦截所有 API 请求
|
||||
.excludePathPatterns(
|
||||
"/api/login/validateToken", // 排除 token 验证接口
|
||||
"/api/captcha/**" // 排除验证码接口
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.kexue.skills.controller;
|
|||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.kexue.skills.annotation.Log;
|
||||
import com.kexue.skills.annotation.RequireAuth;
|
||||
import com.kexue.skills.annotation.RequireRole;
|
||||
import com.kexue.skills.common.CommonResult;
|
||||
|
|
@ -10,6 +11,8 @@ import com.kexue.skills.entity.Account;
|
|||
import com.kexue.skills.entity.dto.AccountDto;
|
||||
import com.kexue.skills.entity.dto.TokenConsumptionDto;
|
||||
import com.kexue.skills.entity.dto.GiftBalanceDto;
|
||||
import com.kexue.skills.entity.dto.AccountTransactionDto;
|
||||
import com.kexue.skills.entity.dto.ConsumptionGroupedDto;
|
||||
import com.kexue.skills.service.AccountService;
|
||||
import com.kexue.skills.service.SysUserService;
|
||||
import com.kexue.skills.entity.SysUser;
|
||||
|
|
@ -32,6 +35,7 @@ import java.util.List;
|
|||
* @author 王志维
|
||||
* @since 2025-02-21 23:01:48
|
||||
*/
|
||||
@Log(module = "账户管理")
|
||||
@Tag(name = "账户管理 api")
|
||||
@RestController
|
||||
@RequestMapping("/api/account")
|
||||
|
|
@ -164,5 +168,44 @@ public class AccountController {
|
|||
return CommonResult.success(this.accountService.getTransactions(userId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询充值记录
|
||||
*
|
||||
* @param queryDto 查询条件
|
||||
* @return 分页结果
|
||||
*/
|
||||
@Operation(summary = "分页查询充值记录", description = "分页查询所有充值记录,默认根据时间倒序")
|
||||
@PostMapping("/getRechargePageList")
|
||||
@RequireAuth
|
||||
public CommonResult<PageInfo<com.kexue.skills.entity.AccountTransaction>> getRechargePageList(@RequestBody AccountTransactionDto queryDto) {
|
||||
return CommonResult.success(this.accountService.getRechargePageList(queryDto));
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询消费记录(按callId分组)
|
||||
*
|
||||
* @param queryDto 查询条件
|
||||
* @return 分页结果
|
||||
*/
|
||||
@Operation(summary = "分页查询消费记录", description = "分页查询所有消费记录,按callId分组,question取最早入库的记录,默认根据时间倒序")
|
||||
@PostMapping("/getConsumptionGroupedPageList")
|
||||
@RequireAuth
|
||||
public CommonResult<PageInfo<ConsumptionGroupedDto>> getConsumptionGroupedPageList(@RequestBody AccountTransactionDto queryDto) {
|
||||
return CommonResult.success(this.accountService.getConsumptionGroupedPageList(queryDto));
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询赠送记录
|
||||
*
|
||||
* @param queryDto 查询条件
|
||||
* @return 分页结果
|
||||
*/
|
||||
@Operation(summary = "分页查询赠送记录", description = "分页查询所有赠送记录,默认根据时间倒序")
|
||||
@PostMapping("/getGiftPageList")
|
||||
@RequireAuth
|
||||
public CommonResult<PageInfo<com.kexue.skills.entity.AccountTransaction>> getGiftPageList(@RequestBody AccountTransactionDto queryDto) {
|
||||
return CommonResult.success(this.accountService.getGiftPageList(queryDto));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.kexue.skills.controller;
|
||||
|
||||
import com.kexue.skills.annotation.Log;
|
||||
import com.kexue.skills.common.CommonResult;
|
||||
import com.kexue.skills.entity.AccountFrozen;
|
||||
import com.kexue.skills.entity.dto.AccountFrozenDto;
|
||||
|
|
@ -22,6 +23,7 @@ import java.io.IOException;
|
|||
* @author 系统生成
|
||||
* @since 2026-04-11
|
||||
*/
|
||||
@Log(module = "账户冻结")
|
||||
@RestController
|
||||
@RequestMapping("/api/accountFrozen")
|
||||
@CrossOrigin(origins = "*")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.kexue.skills.controller;
|
||||
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.kexue.skills.annotation.Log;
|
||||
import com.kexue.skills.annotation.RequireAuth;
|
||||
import com.kexue.skills.common.Assert;
|
||||
import com.kexue.skills.common.CommonResult;
|
||||
|
|
@ -354,4 +355,24 @@ public class CmsContentController {
|
|||
return CommonResult.failed("导入失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从指定目录读取Excel数据并更新CmsContent
|
||||
*
|
||||
* @param importPathDto 导入路径请求参数
|
||||
* @param updateBy 更新人
|
||||
* @return 更新结果
|
||||
*/
|
||||
@PostMapping("/updateFromPath")
|
||||
@Operation(summary = "从目录更新Excel数据", description = "从指定目录读取Excel数据并更新CmsContent")
|
||||
@RequireAuth
|
||||
public CommonResult<Integer> updateFromPath(@RequestBody ImportPathDto importPathDto, @RequestParam("updateBy") String updateBy) {
|
||||
try {
|
||||
int successCount = cmsContentService.updateFromPath(importPathDto, updateBy);
|
||||
return CommonResult.success(successCount);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return CommonResult.failed("更新失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.kexue.skills.controller;
|
||||
|
||||
import com.kexue.skills.annotation.Log;
|
||||
import com.kexue.skills.annotation.PreventDuplicateSubmission;
|
||||
import com.kexue.skills.common.CacheManager;
|
||||
import com.kexue.skills.common.CommonResult;
|
||||
|
|
@ -22,6 +23,7 @@ import static cn.dev33.satoken.SaManager.log;
|
|||
* @author 王志维
|
||||
* @since 2024-04-13 01:25:22
|
||||
*/
|
||||
@Log(module = "登录认证")
|
||||
@RestController
|
||||
@RequestMapping("api/login")
|
||||
@CrossOrigin(origins = "*")
|
||||
|
|
|
|||
|
|
@ -16,6 +16,11 @@ import com.github.pagehelper.PageInfo;
|
|||
import com.kexue.skills.common.CommonResult;
|
||||
import com.kexue.skills.entity.base.IdDto;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
|
||||
/**
|
||||
* (SysUser)表控制层
|
||||
|
|
@ -222,4 +227,44 @@ public class SysUserController {
|
|||
|
||||
return CommonResult.success(loginUserDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传用户头像
|
||||
*
|
||||
* @param file 头像文件
|
||||
* @param request HTTP请求
|
||||
* @return 上传结果
|
||||
*/
|
||||
@PostMapping("/uploadAvatar")
|
||||
@Operation(summary = "上传用户头像", description = "上传用户头像并更新用户信息")
|
||||
@RequireAuth
|
||||
public CommonResult<String> uploadAvatar(@RequestParam("file") MultipartFile file, HttpServletRequest request) {
|
||||
// 从请求头中获取token
|
||||
String token = request.getHeader("Authorization");
|
||||
if (token == null || token.isEmpty()) {
|
||||
throw new BizException("请先登录认证后操作");
|
||||
}
|
||||
|
||||
// 从Redis中获取当前登录用户信息
|
||||
String loginUserJson = (String)redissonClient.getBucket("loginUser:" + token).get();
|
||||
if (loginUserJson == null || loginUserJson.isEmpty()) {
|
||||
throw new BizException("无效的token,请重新登录");
|
||||
}
|
||||
|
||||
// 解析JSON字符串为LoginUser对象
|
||||
com.kexue.skills.entity.request.LoginUser loginUser = cn.hutool.json.JSONUtil.toBean(loginUserJson, com.kexue.skills.entity.request.LoginUser.class);
|
||||
if (loginUser == null || loginUser.getUserInfo() == null) {
|
||||
throw new BizException("无效的token,请重新登录");
|
||||
}
|
||||
|
||||
SysUser user = loginUser.getUserInfo();
|
||||
if (user == null) {
|
||||
throw new BizException("用户不存在");
|
||||
}
|
||||
|
||||
// 调用服务层方法上传头像
|
||||
String fileName = sysUserService.uploadAvatar(file, user.getUserId(), token);
|
||||
|
||||
return CommonResult.success(fileName);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,120 @@
|
|||
package com.kexue.skills.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 日志记录对象
|
||||
* 用于在拦截器和持久层之间传递日志数据
|
||||
*
|
||||
* @author 王志维
|
||||
* @since 2026-04-14
|
||||
*/
|
||||
@Data
|
||||
public class LogRecord implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 模块名称
|
||||
*/
|
||||
private String module;
|
||||
|
||||
/**
|
||||
* 日志描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 请求信息
|
||||
*/
|
||||
private LogRequest request;
|
||||
|
||||
/**
|
||||
* 响应信息
|
||||
*/
|
||||
private LogResponse response;
|
||||
|
||||
/**
|
||||
* 执行耗时(毫秒)
|
||||
*/
|
||||
private Long timeTaken;
|
||||
|
||||
/**
|
||||
* 时间戳
|
||||
*/
|
||||
private Instant timestamp;
|
||||
|
||||
/**
|
||||
* 日志请求对象
|
||||
*/
|
||||
@Data
|
||||
public static class LogRequest implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 请求方法(GET、POST等)
|
||||
*/
|
||||
private String method;
|
||||
|
||||
/**
|
||||
* 请求URL
|
||||
*/
|
||||
private String url;
|
||||
|
||||
/**
|
||||
* 请求头
|
||||
*/
|
||||
private Map<String, String> headers;
|
||||
|
||||
/**
|
||||
* 请求体
|
||||
*/
|
||||
private String body;
|
||||
|
||||
/**
|
||||
* 客户端IP
|
||||
*/
|
||||
private String ip;
|
||||
|
||||
/**
|
||||
* IP归属地
|
||||
*/
|
||||
private String address;
|
||||
|
||||
/**
|
||||
* 浏览器信息
|
||||
*/
|
||||
private String browser;
|
||||
|
||||
/**
|
||||
* 操作系统
|
||||
*/
|
||||
private String os;
|
||||
}
|
||||
|
||||
/**
|
||||
* 日志响应对象
|
||||
*/
|
||||
@Data
|
||||
public static class LogResponse implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 响应头
|
||||
*/
|
||||
private Map<String, String> headers;
|
||||
|
||||
/**
|
||||
* 响应体
|
||||
*/
|
||||
private String body;
|
||||
|
||||
/**
|
||||
* 状态码
|
||||
*/
|
||||
private Integer status;
|
||||
}
|
||||
}
|
||||
|
|
@ -22,29 +22,59 @@ public class SysLog extends BaseEntity implements Serializable {
|
|||
@Schema(description ="主键ID")
|
||||
private Long logId;
|
||||
|
||||
@Schema(description ="用户ID")
|
||||
private String userId;
|
||||
@Schema(description ="链路ID")
|
||||
private String traceId;
|
||||
|
||||
@Schema(description ="用户名称")
|
||||
private String userName;
|
||||
@Schema(description ="日志描述")
|
||||
private String description;
|
||||
|
||||
@Schema(description ="日志类型")
|
||||
private String logType;
|
||||
@Schema(description ="所属模块")
|
||||
private String module;
|
||||
|
||||
@Schema(description ="日志类容")
|
||||
private String logContent;
|
||||
@Schema(description ="请求URL")
|
||||
private String requestUrl;
|
||||
|
||||
@Schema(description ="服务端IP")
|
||||
private String serverIp;
|
||||
@Schema(description ="请求方式")
|
||||
private String requestMethod;
|
||||
|
||||
@Schema(description ="客户端IP")
|
||||
private String clientIp;
|
||||
@Schema(description ="请求头")
|
||||
private String requestHeaders;
|
||||
|
||||
@Schema(description ="yyyyMMddHHmmss")
|
||||
private String logTime;
|
||||
@Schema(description ="请求体")
|
||||
private String requestBody;
|
||||
|
||||
@Schema(description ="备注")
|
||||
private String note;
|
||||
@Schema(description ="状态码")
|
||||
private Integer statusCode;
|
||||
|
||||
@Schema(description ="响应头")
|
||||
private String responseHeaders;
|
||||
|
||||
@Schema(description ="响应体")
|
||||
private String responseBody;
|
||||
|
||||
@Schema(description ="耗时(ms)")
|
||||
private Long timeTaken;
|
||||
|
||||
@Schema(description ="IP")
|
||||
private String ip;
|
||||
|
||||
@Schema(description ="IP归属地")
|
||||
private String address;
|
||||
|
||||
@Schema(description ="浏览器")
|
||||
private String browser;
|
||||
|
||||
@Schema(description ="操作系统")
|
||||
private String os;
|
||||
|
||||
@Schema(description ="状态(1:成功;2:失败)")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description ="错误信息")
|
||||
private String errorMsg;
|
||||
|
||||
@Schema(description ="创建人")
|
||||
private Long createUser;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@Schema(description ="创建时间")
|
||||
|
|
|
|||
|
|
@ -75,4 +75,7 @@ public class SysUser extends BaseEntity implements Serializable {
|
|||
@Schema(description ="邀请人用户ID(邀请我注册的用户ID)")
|
||||
private Long invitedBy;
|
||||
|
||||
@Schema(description ="用户头像")
|
||||
private String userIcon = "defaultUserIcon.png";
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ package com.kexue.skills.entity.dto;
|
|||
import com.kexue.skills.entity.base.BaseQueryDto;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* (AccountTransaction)查询DTO类
|
||||
*
|
||||
|
|
@ -32,4 +34,8 @@ public class AccountTransactionDto extends BaseQueryDto {
|
|||
|
||||
private Integer deleteFlag;
|
||||
|
||||
private Date createTimeStart;
|
||||
|
||||
private Date createTimeEnd;
|
||||
|
||||
}
|
||||
|
|
@ -55,4 +55,19 @@ public class CmsContentDto extends BaseQueryDto {
|
|||
*/
|
||||
private String keyword;
|
||||
|
||||
/**
|
||||
* 来源
|
||||
*/
|
||||
private String origin;
|
||||
|
||||
/**
|
||||
* 标签
|
||||
*/
|
||||
private String tags;
|
||||
|
||||
/**
|
||||
* 图标
|
||||
*/
|
||||
private String icon;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,101 @@
|
|||
package com.kexue.skills.entity.dto;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 消费记录分组DTO类(按callId分组)
|
||||
*
|
||||
* @author 王志维
|
||||
* @since 2025-04-15
|
||||
*/
|
||||
@Data
|
||||
public class ConsumptionGroupedDto implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description ="主键ID(取最早记录的ID)")
|
||||
private Long transactionId;
|
||||
|
||||
@Schema(description ="用户ID")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description ="用户名")
|
||||
private String userName;
|
||||
|
||||
@Schema(description ="交易类型:1.充值 2.提现 3.购买内容 4.退款 5.签到奖励 6.赠送 7.其他")
|
||||
private Integer transactionType;
|
||||
|
||||
@Schema(description ="交易金额")
|
||||
private BigDecimal amount;
|
||||
|
||||
@Schema(description ="交易前余额")
|
||||
private BigDecimal beforeBalance;
|
||||
|
||||
@Schema(description ="交易后余额")
|
||||
private BigDecimal afterBalance;
|
||||
|
||||
@Schema(description ="交易状态:1.成功 2.失败 3.处理中")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description ="交易单号")
|
||||
private String transactionNo;
|
||||
|
||||
@Schema(description ="支付方式:1.微信 2.支付宝 3.余额支付")
|
||||
private Integer payType;
|
||||
|
||||
@Schema(description ="关联业务ID")
|
||||
private Long businessId;
|
||||
|
||||
@Schema(description ="业务类型")
|
||||
private String businessType;
|
||||
|
||||
@Schema(description ="调用ID,关联冻结单")
|
||||
private String callId;
|
||||
|
||||
@Schema(description ="交易备注")
|
||||
private String remark;
|
||||
|
||||
@Schema(description ="是否支出:1.是 0.否")
|
||||
private Integer isExpense;
|
||||
|
||||
@Schema(description ="输入token")
|
||||
private Integer inputToken;
|
||||
|
||||
@Schema(description ="输出token")
|
||||
private Integer outputToken;
|
||||
|
||||
@Schema(description ="合计tokens")
|
||||
private Integer totalTokens;
|
||||
|
||||
@Schema(description ="处理的模型名称")
|
||||
private String modelName;
|
||||
|
||||
@Schema(description ="对应回答的问题或需求(取最早入库的question)")
|
||||
private String question;
|
||||
|
||||
@Schema(description ="收入类型:recharge(充值)、sign_in(签到奖励)")
|
||||
private String incomeType;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@Schema(description ="创建时间(取最早记录的创建时间)")
|
||||
private Date createTime;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@Schema(description ="更新时间")
|
||||
private Date updateTime;
|
||||
|
||||
@Schema(description ="创建人")
|
||||
private String createBy;
|
||||
|
||||
@Schema(description ="更新人")
|
||||
private String updateBy;
|
||||
|
||||
@Schema(description ="是否删除 :0 未删除,1已删除")
|
||||
private Integer deleteFlag;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package com.kexue.skills.interceptor;
|
||||
|
||||
import jakarta.servlet.*;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 请求响应体缓存过滤器
|
||||
* 用于包装 HttpServletRequest 和 HttpServletResponse,使其可以重复读取请求体和捕获响应体
|
||||
*
|
||||
* @author 王志维
|
||||
* @since 2026-04-14
|
||||
*/
|
||||
@Component
|
||||
@Order(1) // 确保在最前面执行
|
||||
public class CachedBodyFilter implements Filter {
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
|
||||
if (request instanceof HttpServletRequest && response instanceof HttpServletResponse) {
|
||||
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
||||
HttpServletResponse httpResponse = (HttpServletResponse) response;
|
||||
|
||||
// 跳过对 multipart/form-data 格式请求的包装,因为文件上传请求不能被重复读取
|
||||
String contentType = httpRequest.getContentType();
|
||||
if (contentType != null && contentType.startsWith("multipart/")) {
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
// 包装请求,使其可以重复读取请求体
|
||||
CachedBodyHttpServletRequest cachedRequest = new CachedBodyHttpServletRequest(httpRequest);
|
||||
|
||||
// 包装响应,使其可以捕获响应体
|
||||
CachedBodyHttpServletResponse cachedResponse = new CachedBodyHttpServletResponse(httpResponse);
|
||||
|
||||
// 继续过滤链
|
||||
chain.doFilter(cachedRequest, cachedResponse);
|
||||
|
||||
// 将缓存的响应体写入真实响应
|
||||
cachedResponse.flushBuffer();
|
||||
} else {
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
package com.kexue.skills.interceptor;
|
||||
|
||||
import jakarta.servlet.ReadListener;
|
||||
import jakarta.servlet.ServletInputStream;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletRequestWrapper;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* 可重复读取请求体的 HttpServletRequest 包装类
|
||||
*
|
||||
* @author 王志维
|
||||
* @since 2026-04-14
|
||||
*/
|
||||
public class CachedBodyHttpServletRequest extends HttpServletRequestWrapper {
|
||||
|
||||
private final byte[] cachedBody;
|
||||
|
||||
public CachedBodyHttpServletRequest(HttpServletRequest request) throws IOException {
|
||||
super(request);
|
||||
// 读取并缓存请求体
|
||||
this.cachedBody = readBytes(request.getInputStream());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServletInputStream getInputStream() {
|
||||
return new CachedBodyServletInputStream(this.cachedBody);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedReader getReader() {
|
||||
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.cachedBody);
|
||||
return new BufferedReader(new InputStreamReader(byteArrayInputStream));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存的请求体字符串
|
||||
*/
|
||||
public String getCachedBodyString() {
|
||||
return new String(cachedBody);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从输入流读取字节数组
|
||||
*/
|
||||
private byte[] readBytes(InputStream inputStream) throws IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
int nRead;
|
||||
byte[] data = new byte[1024];
|
||||
while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
|
||||
buffer.write(data, 0, nRead);
|
||||
}
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 可重复读取的 ServletInputStream
|
||||
*/
|
||||
private static class CachedBodyServletInputStream extends ServletInputStream {
|
||||
|
||||
private final ByteArrayInputStream inputStream;
|
||||
|
||||
public CachedBodyServletInputStream(byte[] cachedBody) {
|
||||
this.inputStream = new ByteArrayInputStream(cachedBody);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFinished() {
|
||||
return inputStream.available() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReady() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadListener(ReadListener listener) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() {
|
||||
return inputStream.read();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
package com.kexue.skills.interceptor;
|
||||
|
||||
import jakarta.servlet.ServletOutputStream;
|
||||
import jakarta.servlet.WriteListener;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponseWrapper;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* 可捕获响应体的 HttpServletResponse 包装类
|
||||
*/
|
||||
public class CachedBodyHttpServletResponse extends HttpServletResponseWrapper {
|
||||
|
||||
private final ByteArrayOutputStream cachedBody = new ByteArrayOutputStream();
|
||||
private ServletOutputStream outputStream;
|
||||
private PrintWriter writer;
|
||||
|
||||
public CachedBodyHttpServletResponse(HttpServletResponse response) {
|
||||
super(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServletOutputStream getOutputStream() throws IOException {
|
||||
if (outputStream == null) {
|
||||
outputStream = new CachedBodyServletOutputStream(super.getOutputStream(), cachedBody);
|
||||
}
|
||||
return outputStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrintWriter getWriter() throws IOException {
|
||||
if (writer == null) {
|
||||
writer = new PrintWriter(getOutputStream(), true);
|
||||
}
|
||||
return writer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushBuffer() throws IOException {
|
||||
if (writer != null) {
|
||||
writer.flush();
|
||||
}
|
||||
if (outputStream != null) {
|
||||
outputStream.flush();
|
||||
}
|
||||
super.flushBuffer();
|
||||
}
|
||||
|
||||
public String getCachedBodyString() {
|
||||
return cachedBody.toString();
|
||||
}
|
||||
|
||||
private static class CachedBodyServletOutputStream extends ServletOutputStream {
|
||||
|
||||
private final ServletOutputStream outputStream;
|
||||
private final ByteArrayOutputStream cachedBody;
|
||||
|
||||
public CachedBodyServletOutputStream(ServletOutputStream outputStream, ByteArrayOutputStream cachedBody) {
|
||||
this.outputStream = outputStream;
|
||||
this.cachedBody = cachedBody;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReady() {
|
||||
return outputStream.isReady();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWriteListener(WriteListener listener) {
|
||||
outputStream.setWriteListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
outputStream.write(b);
|
||||
cachedBody.write(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b) throws IOException {
|
||||
outputStream.write(b);
|
||||
cachedBody.write(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
outputStream.write(b, off, len);
|
||||
cachedBody.write(b, off, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,412 @@
|
|||
package com.kexue.skills.interceptor;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.useragent.UserAgent;
|
||||
import cn.hutool.http.useragent.UserAgentUtil;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.kexue.skills.annotation.Log;
|
||||
import com.kexue.skills.entity.LogRecord;
|
||||
import com.kexue.skills.mapper.SysLogMapper;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 操作日志拦截器
|
||||
* 基于 HandlerInterceptor 实现,捕获请求和响应信息
|
||||
*
|
||||
* @author 王志维
|
||||
* @since 2026-04-14
|
||||
*/
|
||||
public class LogInterceptor implements HandlerInterceptor {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(LogInterceptor.class);
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
private SysLogMapper sysLogMapper;
|
||||
|
||||
// 使用 ThreadLocal 存储请求开始时间和日志记录
|
||||
private static final ThreadLocal<Long> START_TIME = new ThreadLocal<>();
|
||||
private static final ThreadLocal<LogRecord> LOG_RECORD = new ThreadLocal<>();
|
||||
|
||||
/**
|
||||
* 设置 SysLogMapper
|
||||
*/
|
||||
public void setSysLogMapper(SysLogMapper sysLogMapper) {
|
||||
this.sysLogMapper = sysLogMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||
// 只处理方法级别的请求
|
||||
if (!(handler instanceof HandlerMethod)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
HandlerMethod handlerMethod = (HandlerMethod) handler;
|
||||
|
||||
// 检查是否有 @Log 注解
|
||||
Log methodLog = handlerMethod.getMethodAnnotation(Log.class);
|
||||
Log classLog = handlerMethod.getBeanType().getAnnotation(Log.class);
|
||||
|
||||
// 如果方法和类都没有 @Log 注解,不记录日志
|
||||
if (methodLog == null && classLog == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 如果方法级别设置了 ignore=true,不记录日志
|
||||
if (methodLog != null && methodLog.ignore()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 记录开始时间
|
||||
START_TIME.set(System.currentTimeMillis());
|
||||
|
||||
// 构建日志记录对象
|
||||
LogRecord logRecord = new LogRecord();
|
||||
|
||||
// 设置模块和描述
|
||||
String module = "";
|
||||
String description = "";
|
||||
if (classLog != null) {
|
||||
module = classLog.module();
|
||||
}
|
||||
if (methodLog != null) {
|
||||
if (StrUtil.isNotBlank(methodLog.module())) {
|
||||
module = methodLog.module();
|
||||
}
|
||||
description = methodLog.description();
|
||||
}
|
||||
logRecord.setModule(module);
|
||||
logRecord.setDescription(description);
|
||||
|
||||
// 设置请求信息
|
||||
LogRecord.LogRequest logRequest = new LogRecord.LogRequest();
|
||||
logRequest.setMethod(request.getMethod());
|
||||
logRequest.setUrl(request.getRequestURL().toString());
|
||||
logRequest.setHeaders(getRequestHeaders(request));
|
||||
logRequest.setIp(getClientIp(request));
|
||||
logRequest.setBrowser(getBrowserInfo(request));
|
||||
logRequest.setOs(getOsInfo(request));
|
||||
|
||||
// 读取请求体(从包装对象中获取)
|
||||
if (request instanceof CachedBodyHttpServletRequest) {
|
||||
CachedBodyHttpServletRequest cachedRequest = (CachedBodyHttpServletRequest) request;
|
||||
String requestBody = cachedRequest.getCachedBodyString();
|
||||
// 限制请求体大小,避免过大
|
||||
if (requestBody.length() > 10000) {
|
||||
requestBody = requestBody.substring(0, 10000) + "... [truncated]";
|
||||
}
|
||||
logRequest.setBody(requestBody);
|
||||
}
|
||||
|
||||
logRecord.setRequest(logRequest);
|
||||
logRecord.setTimestamp(Instant.now());
|
||||
|
||||
// 存储到 ThreadLocal
|
||||
LOG_RECORD.set(logRecord);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
|
||||
Object handler, Exception ex) {
|
||||
// 只处理方法级别的请求
|
||||
if (!(handler instanceof HandlerMethod)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取日志记录
|
||||
LogRecord logRecord = LOG_RECORD.get();
|
||||
Long startTime = START_TIME.get();
|
||||
|
||||
if (logRecord == null || startTime == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 计算耗时
|
||||
long timeTaken = System.currentTimeMillis() - startTime;
|
||||
logRecord.setTimeTaken(timeTaken);
|
||||
|
||||
// 设置响应信息
|
||||
LogRecord.LogResponse logResponse = new LogRecord.LogResponse();
|
||||
logResponse.setStatus(response.getStatus());
|
||||
logResponse.setHeaders(getResponseHeaders(response));
|
||||
|
||||
// 读取响应体(从包装对象中获取)
|
||||
if (response instanceof CachedBodyHttpServletResponse) {
|
||||
CachedBodyHttpServletResponse cachedResponse = (CachedBodyHttpServletResponse) response;
|
||||
String responseBody = cachedResponse.getCachedBodyString();
|
||||
// 限制响应体大小,避免过大
|
||||
if (responseBody.length() > 10000) {
|
||||
responseBody = responseBody.substring(0, 10000) + "... [truncated]";
|
||||
}
|
||||
logResponse.setBody(responseBody);
|
||||
}
|
||||
|
||||
logRecord.setResponse(logResponse);
|
||||
|
||||
// 异步保存日志
|
||||
saveLogAsync(logRecord);
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("记录操作日志失败: {}", e.getMessage(), e);
|
||||
} finally {
|
||||
// 清理 ThreadLocal
|
||||
START_TIME.remove();
|
||||
LOG_RECORD.remove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求头
|
||||
*/
|
||||
private Map<String, String> getRequestHeaders(HttpServletRequest request) {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
Enumeration<String> headerNames = request.getHeaderNames();
|
||||
while (headerNames.hasMoreElements()) {
|
||||
String headerName = headerNames.nextElement();
|
||||
headers.put(headerName, request.getHeader(headerName));
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取响应头
|
||||
*/
|
||||
private Map<String, String> getResponseHeaders(HttpServletResponse response) {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
for (String headerName : response.getHeaderNames()) {
|
||||
headers.put(headerName, response.getHeader(headerName));
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端IP
|
||||
*/
|
||||
private String getClientIp(HttpServletRequest request) {
|
||||
String ip = request.getHeader("X-Forwarded-For");
|
||||
if (StrUtil.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("X-Real-IP");
|
||||
}
|
||||
if (StrUtil.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("Proxy-Client-IP");
|
||||
}
|
||||
if (StrUtil.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("WL-Proxy-Client-IP");
|
||||
}
|
||||
if (StrUtil.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getRemoteAddr();
|
||||
}
|
||||
// 多个代理时,第一个IP为真实IP
|
||||
if (StrUtil.isNotBlank(ip) && ip.contains(",")) {
|
||||
ip = ip.split(",")[0].trim();
|
||||
}
|
||||
return ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取浏览器信息
|
||||
*/
|
||||
private String getBrowserInfo(HttpServletRequest request) {
|
||||
String userAgent = request.getHeader("User-Agent");
|
||||
if (StrUtil.isBlank(userAgent)) {
|
||||
return "Unknown";
|
||||
}
|
||||
UserAgent ua = UserAgentUtil.parse(userAgent);
|
||||
String browserName = ua.getBrowser().getName();
|
||||
String version = ua.getVersion();
|
||||
// 避免显示 "Unknown null"
|
||||
if (StrUtil.isBlank(version) || "null".equals(version)) {
|
||||
return StrUtil.blankToDefault(browserName, "Unknown");
|
||||
}
|
||||
return browserName + " " + version;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取操作系统信息
|
||||
*/
|
||||
private String getOsInfo(HttpServletRequest request) {
|
||||
String userAgent = request.getHeader("User-Agent");
|
||||
if (StrUtil.isBlank(userAgent)) {
|
||||
return "Unknown";
|
||||
}
|
||||
UserAgent ua = UserAgentUtil.parse(userAgent);
|
||||
String osName = ua.getOs().getName();
|
||||
return StrUtil.blankToDefault(osName, "Unknown");
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步保存日志
|
||||
*/
|
||||
@Async
|
||||
public void saveLogAsync(LogRecord logRecord) {
|
||||
try {
|
||||
// 转换为 SysLog 实体
|
||||
com.kexue.skills.entity.SysLog sysLog = new com.kexue.skills.entity.SysLog();
|
||||
|
||||
// 设置基本信息
|
||||
sysLog.setDescription(StrUtil.blankToDefault(logRecord.getDescription(), ""));
|
||||
sysLog.setModule(StrUtil.blankToDefault(logRecord.getModule(), ""));
|
||||
sysLog.setTimeTaken(logRecord.getTimeTaken() != null ? logRecord.getTimeTaken() : 0L);
|
||||
sysLog.setCreateTime(new java.util.Date());
|
||||
sysLog.setUpdateTime(new java.util.Date());
|
||||
sysLog.setDeleteFlag(0);
|
||||
|
||||
// 设置请求信息
|
||||
LogRecord.LogRequest request = logRecord.getRequest();
|
||||
if (request != null) {
|
||||
sysLog.setRequestMethod(StrUtil.blankToDefault(request.getMethod(), "UNKNOWN"));
|
||||
sysLog.setRequestUrl(StrUtil.blankToDefault(request.getUrl(), ""));
|
||||
try {
|
||||
sysLog.setRequestHeaders(objectMapper.writeValueAsString(request.getHeaders()));
|
||||
} catch (Exception e) {
|
||||
logger.warn("序列化请求头失败: {}", e.getMessage());
|
||||
sysLog.setRequestHeaders("{}");
|
||||
}
|
||||
sysLog.setRequestBody(StrUtil.blankToDefault(request.getBody(), ""));
|
||||
sysLog.setIp(StrUtil.blankToDefault(request.getIp(), "127.0.0.1"));
|
||||
sysLog.setAddress(StrUtil.blankToDefault(request.getAddress(), ""));
|
||||
sysLog.setBrowser(StrUtil.blankToDefault(request.getBrowser(), "Unknown"));
|
||||
sysLog.setOs(StrUtil.blankToDefault(request.getOs(), "Unknown"));
|
||||
} else {
|
||||
// 设置默认值
|
||||
sysLog.setRequestMethod("UNKNOWN");
|
||||
sysLog.setRequestUrl("");
|
||||
sysLog.setRequestHeaders("{}");
|
||||
sysLog.setRequestBody("");
|
||||
sysLog.setIp("127.0.0.1");
|
||||
sysLog.setAddress("");
|
||||
sysLog.setBrowser("Unknown");
|
||||
sysLog.setOs("Unknown");
|
||||
}
|
||||
|
||||
// 设置响应信息
|
||||
LogRecord.LogResponse response = logRecord.getResponse();
|
||||
if (response != null) {
|
||||
sysLog.setStatusCode(response.getStatus() != null ? response.getStatus() : 200);
|
||||
try {
|
||||
sysLog.setResponseHeaders(objectMapper.writeValueAsString(response.getHeaders()));
|
||||
} catch (Exception e) {
|
||||
logger.warn("序列化响应头失败: {}", e.getMessage());
|
||||
sysLog.setResponseHeaders("{}");
|
||||
}
|
||||
String responseBody = StrUtil.blankToDefault(response.getBody(), "");
|
||||
sysLog.setResponseBody(responseBody);
|
||||
|
||||
// 判断成功/失败状态
|
||||
Integer statusCode = response.getStatus();
|
||||
if (statusCode != null) {
|
||||
sysLog.setStatus(statusCode >= 400 ? 2 : 1);
|
||||
} else {
|
||||
sysLog.setStatus(1);
|
||||
}
|
||||
|
||||
// 解析响应体中的错误信息
|
||||
if (StrUtil.isNotBlank(responseBody)) {
|
||||
try {
|
||||
// 尝试解析 JSON 响应体
|
||||
com.fasterxml.jackson.databind.JsonNode jsonNode = objectMapper.readTree(responseBody);
|
||||
|
||||
// 检查是否有错误标识(status、code、success等字段)
|
||||
boolean hasError = false;
|
||||
String errorMsg = "";
|
||||
|
||||
// 方式1: 检查 status 字段(如 {"status":500,"message":"密码不正确"})
|
||||
if (jsonNode.has("status")) {
|
||||
int statusValue = jsonNode.get("status").asInt();
|
||||
if (statusValue >= 400 || statusValue == 0) {
|
||||
hasError = true;
|
||||
if (jsonNode.has("message")) {
|
||||
errorMsg = jsonNode.get("message").asText();
|
||||
} else if (jsonNode.has("msg")) {
|
||||
errorMsg = jsonNode.get("msg").asText();
|
||||
} else if (jsonNode.has("error")) {
|
||||
errorMsg = jsonNode.get("error").asText();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 方式2: 检查 code 字段(如 {"code":500,"message":"密码不正确"})
|
||||
if (!hasError && jsonNode.has("code")) {
|
||||
int codeValue = jsonNode.get("code").asInt();
|
||||
if (codeValue != 200 && codeValue != 0) {
|
||||
hasError = true;
|
||||
if (jsonNode.has("message")) {
|
||||
errorMsg = jsonNode.get("message").asText();
|
||||
} else if (jsonNode.has("msg")) {
|
||||
errorMsg = jsonNode.get("msg").asText();
|
||||
} else if (jsonNode.has("error")) {
|
||||
errorMsg = jsonNode.get("error").asText();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 方式3: 检查 success 字段(如 {"success":false,"message":"密码不正确"})
|
||||
if (!hasError && jsonNode.has("success")) {
|
||||
boolean success = jsonNode.get("success").asBoolean();
|
||||
if (!success) {
|
||||
hasError = true;
|
||||
if (jsonNode.has("message")) {
|
||||
errorMsg = jsonNode.get("message").asText();
|
||||
} else if (jsonNode.has("msg")) {
|
||||
errorMsg = jsonNode.get("msg").asText();
|
||||
} else if (jsonNode.has("error")) {
|
||||
errorMsg = jsonNode.get("error").asText();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果检测到错误,更新状态和错误信息
|
||||
if (hasError) {
|
||||
sysLog.setStatus(2); // 失败
|
||||
sysLog.setErrorMsg(StrUtil.blankToDefault(errorMsg, "业务操作失败"));
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
// 不是 JSON 格式或解析失败,忽略
|
||||
logger.debug("响应体不是JSON格式或解析失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 设置默认值
|
||||
sysLog.setStatusCode(200);
|
||||
sysLog.setResponseHeaders("{}");
|
||||
sysLog.setResponseBody("");
|
||||
sysLog.setStatus(1);
|
||||
}
|
||||
|
||||
// 设置错误信息默认值
|
||||
sysLog.setErrorMsg("");
|
||||
|
||||
// TODO: 从 Token 中解析用户ID
|
||||
// 这里需要根据实际的认证框架来实现
|
||||
// sysLog.setCreateUser(userId);
|
||||
sysLog.setCreateUser(null);
|
||||
|
||||
// 插入数据库
|
||||
sysLogMapper.insert(sysLog);
|
||||
|
||||
logger.debug("操作日志保存成功: {}", sysLog.getDescription());
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("保存操作日志失败: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.kexue.skills.mapper;
|
|||
|
||||
import com.kexue.skills.entity.AccountTransaction;
|
||||
import com.kexue.skills.entity.dto.AccountTransactionDto;
|
||||
import com.kexue.skills.entity.dto.ConsumptionGroupedDto;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
|
|
@ -78,4 +79,36 @@ public interface AccountTransactionMapper {
|
|||
* @return 交易记录列表
|
||||
*/
|
||||
List<AccountTransaction> queryByUserId(Long userId);
|
||||
|
||||
/**
|
||||
* 获取消费原始记录列表(用于内存分组)
|
||||
*
|
||||
* @param queryDto 筛选条件
|
||||
* @return 原始记录列表
|
||||
*/
|
||||
List<AccountTransaction> getConsumptionRawList(AccountTransactionDto queryDto);
|
||||
|
||||
/**
|
||||
* 分页查询充值记录(transactionType=1)
|
||||
*
|
||||
* @param queryDto 筛选条件
|
||||
* @return 查询结果
|
||||
*/
|
||||
List<AccountTransaction> getRechargePageList(AccountTransactionDto queryDto);
|
||||
|
||||
/**
|
||||
* 分页查询赠送记录(transactionType=6)
|
||||
*
|
||||
* @param queryDto 筛选条件
|
||||
* @return 查询结果
|
||||
*/
|
||||
List<AccountTransaction> getGiftPageList(AccountTransactionDto queryDto);
|
||||
|
||||
/**
|
||||
* 分页查询消费记录并按callId分组
|
||||
*
|
||||
* @param queryDto 筛选条件
|
||||
* @return 查询结果
|
||||
*/
|
||||
List<ConsumptionGroupedDto> getConsumptionGroupedPageList(AccountTransactionDto queryDto);
|
||||
}
|
||||
|
|
@ -2,7 +2,10 @@ package com.kexue.skills.service;
|
|||
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.kexue.skills.entity.Account;
|
||||
import com.kexue.skills.entity.AccountTransaction;
|
||||
import com.kexue.skills.entity.dto.AccountDto;
|
||||
import com.kexue.skills.entity.dto.AccountTransactionDto;
|
||||
import com.kexue.skills.entity.dto.ConsumptionGroupedDto;
|
||||
import com.kexue.skills.entity.dto.TokenConsumptionDto;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
|
@ -160,4 +163,28 @@ public interface AccountService extends BaseService {
|
|||
* @return 交易记录列表
|
||||
*/
|
||||
List<com.kexue.skills.entity.AccountTransaction> getTransactions(Long userId);
|
||||
|
||||
/**
|
||||
* 分页查询充值记录
|
||||
*
|
||||
* @param queryDto 查询条件
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageInfo<AccountTransaction> getRechargePageList(AccountTransactionDto queryDto);
|
||||
|
||||
/**
|
||||
* 分页查询消费记录(按callId分组)
|
||||
*
|
||||
* @param queryDto 查询条件
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageInfo<ConsumptionGroupedDto> getConsumptionGroupedPageList(AccountTransactionDto queryDto);
|
||||
|
||||
/**
|
||||
* 分页查询赠送记录
|
||||
*
|
||||
* @param queryDto 查询条件
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageInfo<AccountTransaction> getGiftPageList(AccountTransactionDto queryDto);
|
||||
}
|
||||
|
|
@ -210,4 +210,13 @@ public interface CmsContentService extends BaseService {
|
|||
* @return 成功导入的记录数
|
||||
*/
|
||||
int importFromZip(byte[] zipFileBytes, String createBy);
|
||||
|
||||
/**
|
||||
* 从指定目录读取Excel数据并更新CmsContent
|
||||
*
|
||||
* @param importPathDto 导入路径请求参数
|
||||
* @param updateBy 更新人
|
||||
* @return 成功更新的记录数
|
||||
*/
|
||||
int updateFromPath(ImportPathDto importPathDto, String updateBy);
|
||||
}
|
||||
|
|
@ -5,6 +5,8 @@ import com.kexue.skills.entity.SysUser;
|
|||
import com.kexue.skills.entity.dto.SysUserDto;
|
||||
import com.kexue.skills.entity.request.*;
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
|
@ -137,4 +139,23 @@ public interface SysUserService extends BaseService {
|
|||
* @return 角色编码列表
|
||||
*/
|
||||
List<String> queryUserRoles(Long userId);
|
||||
|
||||
/**
|
||||
* 更新用户缓存
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param user 更新后的用户信息
|
||||
* @param token 用户的认证token
|
||||
*/
|
||||
void updateUserCache(Long userId, SysUser user, String token);
|
||||
|
||||
/**
|
||||
* 上传用户头像
|
||||
*
|
||||
* @param file 头像文件
|
||||
* @param userId 用户ID
|
||||
* @param token 用户的认证token
|
||||
* @return 上传成功的文件名
|
||||
*/
|
||||
String uploadAvatar(MultipartFile file, Long userId, String token);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import com.kexue.skills.entity.Account;
|
|||
import com.kexue.skills.entity.AccountTransaction;
|
||||
import com.kexue.skills.entity.SysUser;
|
||||
import com.kexue.skills.entity.dto.AccountDto;
|
||||
import com.kexue.skills.entity.dto.AccountTransactionDto;
|
||||
import com.kexue.skills.entity.dto.ConsumptionGroupedDto;
|
||||
import com.kexue.skills.common.Assert;
|
||||
import com.kexue.skills.entity.dto.TokenConsumptionDto;
|
||||
import com.kexue.skills.exception.BizException;
|
||||
|
|
@ -17,13 +19,18 @@ import com.kexue.skills.service.ModelPriceService;
|
|||
import com.kexue.skills.service.PackageConfigService;
|
||||
import com.kexue.skills.entity.ModelPrice;
|
||||
import com.kexue.skills.entity.PackageConfig;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* (Account)表服务实现类
|
||||
|
|
@ -34,6 +41,8 @@ import java.util.List;
|
|||
@Service("accountService")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public class AccountServiceImpl implements AccountService {
|
||||
private static final Logger log = LoggerFactory.getLogger(AccountServiceImpl.class);
|
||||
|
||||
@Resource
|
||||
private AccountMapper accountMapper;
|
||||
@Resource
|
||||
|
|
@ -57,7 +66,7 @@ public class AccountServiceImpl implements AccountService {
|
|||
@Override
|
||||
public PageInfo<Account> getPageList(AccountDto queryDto) {
|
||||
PageHelper.startPage(queryDto.getPageNum(), queryDto.getPageSize());
|
||||
List<Account> list = this.accountMapper.getPageList(queryDto);
|
||||
List<Account> list = accountMapper.getPageList(queryDto);
|
||||
return new PageInfo<>(list);
|
||||
}
|
||||
|
||||
|
|
@ -69,7 +78,7 @@ public class AccountServiceImpl implements AccountService {
|
|||
*/
|
||||
@Override
|
||||
public List<Account> getList(AccountDto queryDto) {
|
||||
return this.accountMapper.getList(queryDto);
|
||||
return accountMapper.getList(queryDto);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -80,7 +89,7 @@ public class AccountServiceImpl implements AccountService {
|
|||
*/
|
||||
@Override
|
||||
public Account queryById(Long accountId) {
|
||||
return this.accountMapper.queryById(accountId);
|
||||
return accountMapper.queryById(accountId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -91,7 +100,7 @@ public class AccountServiceImpl implements AccountService {
|
|||
*/
|
||||
@Override
|
||||
public Account queryByUserId(Long userId) {
|
||||
return this.accountMapper.queryByUserId(userId);
|
||||
return accountMapper.queryByUserId(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -121,7 +130,7 @@ public class AccountServiceImpl implements AccountService {
|
|||
account.setFrozenAmount(BigDecimal.ZERO);
|
||||
}
|
||||
// 保存数据
|
||||
this.accountMapper.insert(account);
|
||||
accountMapper.insert(account);
|
||||
return account;
|
||||
}
|
||||
|
||||
|
|
@ -136,8 +145,8 @@ public class AccountServiceImpl implements AccountService {
|
|||
// 设置更新时间
|
||||
account.setUpdateTime(new Date());
|
||||
// 更新数据
|
||||
this.accountMapper.update(account);
|
||||
return this.queryById(account.getAccountId());
|
||||
accountMapper.update(account);
|
||||
return queryById(account.getAccountId());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -155,7 +164,7 @@ public class AccountServiceImpl implements AccountService {
|
|||
@Override
|
||||
public int addBalance(Long userId, BigDecimal amount, boolean isWithdrawable, String transactionNo, Long businessId, String businessType, String remark) {
|
||||
// 1. 查询账户信息
|
||||
Account account = this.queryByUserId(userId);
|
||||
Account account = queryByUserId(userId);
|
||||
if (account == null) {
|
||||
// 创建账户
|
||||
account = new Account();
|
||||
|
|
@ -164,7 +173,7 @@ public class AccountServiceImpl implements AccountService {
|
|||
account.setWithdrawableBalance(BigDecimal.ZERO);
|
||||
account.setNonWithdrawableBalance(BigDecimal.ZERO);
|
||||
account.setFrozenAmount(BigDecimal.ZERO);
|
||||
this.insert(account);
|
||||
insert(account);
|
||||
}
|
||||
|
||||
// 2. 计算积分
|
||||
|
|
@ -212,7 +221,7 @@ public class AccountServiceImpl implements AccountService {
|
|||
transaction.setRemark(rechargeRemark);
|
||||
transaction.setIsExpense(0); // 收入
|
||||
transaction.setIncomeType("recharge"); // 充值
|
||||
this.accountTransactionMapper.insert(transaction);
|
||||
accountTransactionMapper.insert(transaction);
|
||||
|
||||
// 4. 更新账户余额(使用积分)
|
||||
if (isWithdrawable) {
|
||||
|
|
@ -225,7 +234,7 @@ public class AccountServiceImpl implements AccountService {
|
|||
BigDecimal balance = account.getBalance() == null ? BigDecimal.ZERO : account.getBalance();
|
||||
account.setBalance(balance.add(points));
|
||||
account.setUpdateTime(new Date());
|
||||
this.update(account);
|
||||
update(account);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -259,7 +268,7 @@ public class AccountServiceImpl implements AccountService {
|
|||
@Override
|
||||
public int reduceBalance(Long userId, BigDecimal amount, String transactionNo, Long businessId, String businessType, String remark) {
|
||||
// 1. 查询账户信息
|
||||
Account account = this.queryByUserId(userId);
|
||||
Account account = queryByUserId(userId);
|
||||
Assert.notNull(account, "账户不存在");
|
||||
|
||||
// 2. 检查余额是否足够
|
||||
|
|
@ -281,10 +290,10 @@ public class AccountServiceImpl implements AccountService {
|
|||
transaction.setBusinessType(businessType);
|
||||
transaction.setRemark(remark);
|
||||
transaction.setIsExpense(1); // 支出
|
||||
this.accountTransactionMapper.insert(transaction);
|
||||
accountTransactionMapper.insert(transaction);
|
||||
|
||||
// 4. 更新账户余额
|
||||
return this.accountMapper.updateBalance(userId, amount, 2);
|
||||
return accountMapper.updateBalance(userId, amount, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -301,7 +310,7 @@ public class AccountServiceImpl implements AccountService {
|
|||
throw new BizException("会话ID不存在");
|
||||
}
|
||||
userId = bySessionId.getUserId();
|
||||
Account account = this.queryByUserId(userId);
|
||||
Account account = queryByUserId(userId);
|
||||
Assert.notNull(account, "账户不存在");
|
||||
|
||||
// 2. 查询模型价格信息
|
||||
|
|
@ -348,10 +357,10 @@ public class AccountServiceImpl implements AccountService {
|
|||
transaction.setTotalTokens(dto.getTotalTokens());
|
||||
transaction.setModelName(dto.getModelName());
|
||||
transaction.setQuestion(dto.getQuestion());
|
||||
this.accountTransactionMapper.insert(transaction);
|
||||
accountTransactionMapper.insert(transaction);
|
||||
|
||||
// 6. 更新账户余额
|
||||
this.accountMapper.updateBalance(userId, amount, 2);
|
||||
accountMapper.updateBalance(userId, amount, 2);
|
||||
return amount;
|
||||
}
|
||||
|
||||
|
|
@ -377,7 +386,7 @@ public class AccountServiceImpl implements AccountService {
|
|||
@Override
|
||||
public int addSignInBalance(Long userId, BigDecimal amount, String transactionNo, Long businessId, String businessType, String remark) {
|
||||
// 1. 查询账户信息
|
||||
Account account = this.queryByUserId(userId);
|
||||
Account account = queryByUserId(userId);
|
||||
if (account == null) {
|
||||
// 创建账户
|
||||
account = new Account();
|
||||
|
|
@ -386,7 +395,7 @@ public class AccountServiceImpl implements AccountService {
|
|||
account.setWithdrawableBalance(BigDecimal.ZERO);
|
||||
account.setNonWithdrawableBalance(BigDecimal.ZERO);
|
||||
account.setFrozenAmount(BigDecimal.ZERO);
|
||||
this.insert(account);
|
||||
insert(account);
|
||||
}
|
||||
|
||||
// 2. 保存交易记录
|
||||
|
|
@ -410,7 +419,7 @@ public class AccountServiceImpl implements AccountService {
|
|||
transaction.setRemark(signInRemark);
|
||||
transaction.setIsExpense(0); // 收入
|
||||
transaction.setIncomeType("sign_in"); // 签到奖励
|
||||
this.accountTransactionMapper.insert(transaction);
|
||||
accountTransactionMapper.insert(transaction);
|
||||
|
||||
// 3. 更新账户余额(签到奖励不可提现)
|
||||
BigDecimal nonWithdrawableBalance = account.getNonWithdrawableBalance() == null ? BigDecimal.ZERO : account.getNonWithdrawableBalance();
|
||||
|
|
@ -418,7 +427,7 @@ public class AccountServiceImpl implements AccountService {
|
|||
BigDecimal balance = account.getBalance() == null ? BigDecimal.ZERO : account.getBalance();
|
||||
account.setBalance(balance.add(amount));
|
||||
account.setUpdateTime(new Date());
|
||||
this.update(account);
|
||||
update(account);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -436,7 +445,7 @@ public class AccountServiceImpl implements AccountService {
|
|||
@Override
|
||||
public int addGiftBalance(Long userId, BigDecimal amount, String transactionNo, Long businessId, String businessType, String remark) {
|
||||
// 1. 查询账户信息
|
||||
Account account = this.queryByUserId(userId);
|
||||
Account account = queryByUserId(userId);
|
||||
if (account == null) {
|
||||
// 创建账户
|
||||
account = new Account();
|
||||
|
|
@ -445,7 +454,7 @@ public class AccountServiceImpl implements AccountService {
|
|||
account.setWithdrawableBalance(BigDecimal.ZERO);
|
||||
account.setNonWithdrawableBalance(BigDecimal.ZERO);
|
||||
account.setFrozenAmount(BigDecimal.ZERO);
|
||||
this.insert(account);
|
||||
insert(account);
|
||||
}
|
||||
|
||||
// 2. 保存交易记录
|
||||
|
|
@ -469,7 +478,7 @@ public class AccountServiceImpl implements AccountService {
|
|||
transaction.setRemark(giftRemark);
|
||||
transaction.setIsExpense(0); // 收入
|
||||
transaction.setIncomeType("gift"); // 赠送
|
||||
this.accountTransactionMapper.insert(transaction);
|
||||
accountTransactionMapper.insert(transaction);
|
||||
|
||||
// 3. 更新账户余额(赠送积分不可提现)
|
||||
if (account.getNonWithdrawableBalance() == null){
|
||||
|
|
@ -481,7 +490,7 @@ public class AccountServiceImpl implements AccountService {
|
|||
}
|
||||
account.setBalance(account.getBalance().add(amount));
|
||||
account.setUpdateTime(new Date());
|
||||
this.update(account);
|
||||
update(account);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -494,7 +503,7 @@ public class AccountServiceImpl implements AccountService {
|
|||
*/
|
||||
@Override
|
||||
public int logicDeleteById(Long accountId, String updateBy) {
|
||||
return this.accountMapper.logicDeleteById(accountId, updateBy);
|
||||
return accountMapper.logicDeleteById(accountId, updateBy);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -505,7 +514,7 @@ public class AccountServiceImpl implements AccountService {
|
|||
*/
|
||||
@Override
|
||||
public int deleteById(Long accountId) {
|
||||
return this.accountMapper.deleteById(accountId);
|
||||
return accountMapper.deleteById(accountId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -516,6 +525,134 @@ public class AccountServiceImpl implements AccountService {
|
|||
*/
|
||||
@Override
|
||||
public List<com.kexue.skills.entity.AccountTransaction> getTransactions(Long userId) {
|
||||
return this.accountTransactionMapper.queryByUserId(userId);
|
||||
return accountTransactionMapper.queryByUserId(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询充值记录
|
||||
*
|
||||
* @param queryDto 查询条件
|
||||
* @return 分页结果
|
||||
*/
|
||||
@Override
|
||||
public PageInfo<AccountTransaction> getRechargePageList(AccountTransactionDto queryDto) {
|
||||
PageHelper.startPage(queryDto.getPageNum(), queryDto.getPageSize());
|
||||
List<AccountTransaction> list = accountTransactionMapper.getRechargePageList(queryDto);
|
||||
return new PageInfo<>(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询消费记录(按callId分组)
|
||||
*
|
||||
* @param queryDto 查询条件
|
||||
* @return 分页结果
|
||||
*/
|
||||
@Override
|
||||
public PageInfo<ConsumptionGroupedDto> getConsumptionGroupedPageList(AccountTransactionDto queryDto) {
|
||||
int pageNum = queryDto.getPageNum() == null || queryDto.getPageNum() < 1 ? 1 : queryDto.getPageNum();
|
||||
int pageSize = queryDto.getPageSize() == null || queryDto.getPageSize() < 1 ? 10 : queryDto.getPageSize();
|
||||
|
||||
log.info("开始查询消费记录(内存分组模式),参数:pageNum={}, pageSize={}, userId={}", pageNum, pageSize, queryDto.getUserId());
|
||||
|
||||
// 1. 查询所有原始数据(不分页,获取全量)
|
||||
List<AccountTransaction> rawList = accountTransactionMapper.getConsumptionRawList(queryDto);
|
||||
if (rawList == null || rawList.isEmpty()) {
|
||||
return new PageInfo<>(new ArrayList<>());
|
||||
}
|
||||
|
||||
// 2. 内存分组与聚合
|
||||
// 使用 LinkedHashMap 保持按 callId 首次出现的顺序(即按时间倒序)
|
||||
Map<String, ConsumptionGroupedDto> groupedMap = new LinkedHashMap<>();
|
||||
|
||||
for (AccountTransaction tx : rawList) {
|
||||
// 处理 callId 为 null 或空字符串的情况,使用 transactionId 作为唯一标识
|
||||
String callId = tx.getCallId();
|
||||
if (callId == null || callId.isEmpty()) {
|
||||
callId = "tx_" + tx.getTransactionId();
|
||||
}
|
||||
ConsumptionGroupedDto group = groupedMap.get(callId);
|
||||
|
||||
if (group == null) {
|
||||
// 第一次遇到该 callId,创建新组
|
||||
group = new ConsumptionGroupedDto();
|
||||
// 复制基础信息
|
||||
group.setTransactionId(tx.getTransactionId());
|
||||
group.setUserId(tx.getUserId());
|
||||
group.setUserName(tx.getUserName());
|
||||
group.setTransactionType(tx.getTransactionType());
|
||||
group.setBeforeBalance(tx.getBeforeBalance());
|
||||
group.setAfterBalance(tx.getAfterBalance());
|
||||
group.setStatus(tx.getStatus());
|
||||
group.setTransactionNo(tx.getTransactionNo());
|
||||
group.setPayType(tx.getPayType());
|
||||
group.setBusinessId(tx.getBusinessId());
|
||||
group.setBusinessType(tx.getBusinessType());
|
||||
group.setCallId(tx.getCallId());
|
||||
group.setRemark(tx.getRemark());
|
||||
group.setIsExpense(tx.getIsExpense());
|
||||
group.setModelName(tx.getModelName());
|
||||
group.setQuestion(tx.getQuestion());
|
||||
group.setIncomeType(tx.getIncomeType());
|
||||
group.setCreateTime(tx.getCreateTime());
|
||||
group.setUpdateTime(tx.getUpdateTime());
|
||||
group.setCreateBy(tx.getCreateBy());
|
||||
group.setUpdateBy(tx.getUpdateBy());
|
||||
group.setDeleteFlag(tx.getDeleteFlag());
|
||||
// 初始化聚合字段
|
||||
group.setAmount(BigDecimal.ZERO);
|
||||
group.setInputToken(0);
|
||||
group.setOutputToken(0);
|
||||
group.setTotalTokens(0);
|
||||
groupedMap.put(callId, group);
|
||||
}
|
||||
|
||||
// 累加数值字段
|
||||
if (tx.getAmount() != null) {
|
||||
group.setAmount(group.getAmount().add(tx.getAmount()));
|
||||
}
|
||||
if (tx.getInputToken() != null) {
|
||||
group.setInputToken(group.getInputToken() + tx.getInputToken());
|
||||
}
|
||||
if (tx.getOutputToken() != null) {
|
||||
group.setOutputToken(group.getOutputToken() + tx.getOutputToken());
|
||||
}
|
||||
if (tx.getTotalTokens() != null) {
|
||||
group.setTotalTokens(group.getTotalTokens() + tx.getTotalTokens());
|
||||
}
|
||||
}
|
||||
|
||||
List<ConsumptionGroupedDto> allGroupedList = new ArrayList<>(groupedMap.values());
|
||||
log.info("分组聚合完成,总组数:{}", allGroupedList.size());
|
||||
|
||||
// 3. 内存分页
|
||||
int total = allGroupedList.size();
|
||||
int fromIndex = (pageNum - 1) * pageSize;
|
||||
int toIndex = Math.min(fromIndex + pageSize, total);
|
||||
|
||||
List<ConsumptionGroupedDto> pageList = fromIndex < total ? allGroupedList.subList(fromIndex, toIndex) : new ArrayList<>();
|
||||
|
||||
// 4. 构建 PageInfo
|
||||
PageInfo<ConsumptionGroupedDto> pageInfo = new PageInfo<>(pageList);
|
||||
pageInfo.setTotal((long) total);
|
||||
pageInfo.setPageNum(pageNum);
|
||||
pageInfo.setPageSize(pageSize);
|
||||
pageInfo.setPages((total + pageSize - 1) / pageSize);
|
||||
|
||||
log.info("分页成功:total={}, pages={}, 当前页数据量={}", total, pageInfo.getPages(), pageList.size());
|
||||
return pageInfo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 分页查询赠送记录
|
||||
*
|
||||
* @param queryDto 查询条件
|
||||
* @return 分页结果
|
||||
*/
|
||||
@Override
|
||||
public PageInfo<AccountTransaction> getGiftPageList(AccountTransactionDto queryDto) {
|
||||
PageHelper.startPage(queryDto.getPageNum(), queryDto.getPageSize());
|
||||
List<AccountTransaction> list = accountTransactionMapper.getGiftPageList(queryDto);
|
||||
return new PageInfo<>(list);
|
||||
}
|
||||
}
|
||||
|
|
@ -14,6 +14,9 @@ import com.kexue.skills.entity.request.ImportPathDto;
|
|||
import com.kexue.skills.mapper.CmsContentMapper;
|
||||
import com.kexue.skills.mapper.CmsContentViewMapper;
|
||||
import com.kexue.skills.mapper.CmsContentLikeMapper;
|
||||
import com.kexue.skills.mapper.CmsTagMapper;
|
||||
import com.kexue.skills.entity.CmsTag;
|
||||
import com.kexue.skills.entity.dto.CmsTagDto;
|
||||
import com.kexue.skills.service.CmsContentService;
|
||||
import cn.hutool.poi.excel.ExcelReader;
|
||||
import cn.hutool.poi.excel.ExcelUtil;
|
||||
|
|
@ -51,6 +54,9 @@ public class CmsContentServiceImpl implements CmsContentService {
|
|||
@Resource
|
||||
private CmsContentLikeMapper cmsContentLikeMapper;
|
||||
|
||||
@Resource
|
||||
private CmsTagMapper cmsTagMapper;
|
||||
|
||||
@Resource
|
||||
private LoginUserCacheUtil loginUserCacheUtil;
|
||||
|
||||
|
|
@ -77,94 +83,7 @@ public class CmsContentServiceImpl implements CmsContentService {
|
|||
queryDto.setLanguageType(0);
|
||||
}
|
||||
List<CmsContent> list = this.cmsContentMapper.getPageList(queryDto);
|
||||
PageInfo<CmsContent> pageInfo = new PageInfo<>(list);
|
||||
|
||||
|
||||
if (queryDto.getTitle() != null && !queryDto.getTitle().trim().isEmpty() && list.size() <= 3 && !list.isEmpty()) {
|
||||
// 获取第一个 skill 的标签
|
||||
CmsContent firstSkill = list.get(0);
|
||||
String tagsStr = firstSkill.getTags();
|
||||
if (tagsStr != null && !tagsStr.trim().isEmpty()) {
|
||||
// 解析标签(逗号分隔)
|
||||
String[] tags = tagsStr.split(",");
|
||||
if (tags.length > 0) {
|
||||
// 构建标签 ID 列表
|
||||
Set<Long> tagIdSet = new HashSet<>();
|
||||
for (String tag : tags) {
|
||||
if (tag != null && !tag.trim().isEmpty()) {
|
||||
try {
|
||||
Long tagId = Long.parseLong(tag.trim());
|
||||
tagIdSet.add(tagId);
|
||||
} catch (NumberFormatException e) {
|
||||
// 忽略格式不正确的标签
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//如果传入的tagId不为空,则添加到tagIdSet中
|
||||
if (queryDto.getTagId() != null) {
|
||||
tagIdSet.add(queryDto.getTagId());
|
||||
}
|
||||
|
||||
// 如果有有效的标签 ID,查询相关内容
|
||||
if (!tagIdSet.isEmpty()) {
|
||||
// 排除已返回的 contentId
|
||||
Set<Long> existingIds = list.stream()
|
||||
.map(CmsContent::getContentId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// 构建查询条件,使用 tagIdList 一次性查询
|
||||
CmsContentDto tagQueryDto = new CmsContentDto();
|
||||
tagQueryDto.setDeleteFlag(0);
|
||||
tagQueryDto.setPublishStatus(2); // 已发布
|
||||
tagQueryDto.setTagIdList(new ArrayList<>(tagIdSet));
|
||||
tagQueryDto.setPageNum(queryDto.getPageNum());
|
||||
tagQueryDto.setPageSize(1000+queryDto.getPageSize());
|
||||
|
||||
// 查询包含这些标签的所有 skill
|
||||
List<CmsContent> taggedContents = this.cmsContentMapper.getPageList(tagQueryDto);
|
||||
|
||||
//如果 tag 传参,则根据 tag 再次过滤
|
||||
if (queryDto.getTagId() != null) {
|
||||
taggedContents = taggedContents.stream().filter(content -> content.getTags().contains(queryDto.getTagId().toString())).toList() ;
|
||||
}
|
||||
|
||||
// 过滤掉已返回的内容,避免重复
|
||||
List<CmsContent> finalList = taggedContents.stream()
|
||||
.filter(content -> !existingIds.contains(content.getContentId()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 如果有相关 skill,添加到结果中
|
||||
if (!finalList.isEmpty()) {
|
||||
// 将外层查询结果和内层查询结果合并
|
||||
List<CmsContent> allContents = new ArrayList<>(list);
|
||||
allContents.addAll(finalList);
|
||||
|
||||
// 按 view_count 和 create_time 排序
|
||||
allContents.sort((a, b) -> {
|
||||
int sortCompare = Integer.compare(
|
||||
a.getViewCount() != null ? a.getViewCount() : 0,
|
||||
b.getViewCount() != null ? b.getViewCount() : 0);
|
||||
if (sortCompare != 0) {
|
||||
return sortCompare;
|
||||
}
|
||||
if (a.getCreateTime() == null || b.getCreateTime() == null) {
|
||||
return 0;
|
||||
}
|
||||
return b.getCreateTime().compareTo(a.getCreateTime());
|
||||
});
|
||||
|
||||
// 计算需要添加的数量,确保总数量不超过 pageSize
|
||||
PageInfo<CmsContent> newPageInfo = new PageInfo<>(memoryPagination(allContents, queryDto.getPageNum(), queryDto.getPageSize()));
|
||||
newPageInfo.setTotal(allContents.size());
|
||||
pageInfo = newPageInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pageInfo;
|
||||
return new PageInfo<>(list);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1102,5 +1021,179 @@ public class CmsContentServiceImpl implements CmsContentService {
|
|||
return totalSuccessCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int updateFromPath(ImportPathDto importPathDto, String updateBy) {
|
||||
int totalSuccessCount = 0;
|
||||
|
||||
try {
|
||||
// 检查目录是否存在
|
||||
File directory = new File(importPathDto.getFilePath());
|
||||
if (!directory.exists() || !directory.isDirectory()) {
|
||||
System.err.println("目录不存在或不是有效目录: " + importPathDto.getFilePath());
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 读取目录下所有 Excel 文件(排除 Office 临时锁定文件)
|
||||
File[] files = directory.listFiles((dir, name) -> {
|
||||
// 跳过以 ~$ 开头的临时锁定文件
|
||||
if (name.startsWith("~$")) {
|
||||
return false;
|
||||
}
|
||||
return name.endsWith(".xls") || name.endsWith(".xlsx");
|
||||
});
|
||||
if (files == null || files.length == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 记录文件总数
|
||||
int totalFiles = files.length;
|
||||
System.out.println("总共发现 " + totalFiles + " 个 Excel 文件需要处理");
|
||||
|
||||
// 遍历所有 Excel 文件并处理
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
File file = files[i];
|
||||
System.out.println("当前处理第 " + (i + 1) + " 个文件,文件名称是:" + file.getName());
|
||||
try (FileInputStream fis = new FileInputStream(file)) {
|
||||
// 读取文件内容到字节数组
|
||||
byte[] fileBytes = new byte[(int) file.length()];
|
||||
fis.read(fileBytes);
|
||||
|
||||
// 调用 updateFromExcel 方法进行更新
|
||||
int successCount = updateFromExcel(fileBytes, updateBy);
|
||||
totalSuccessCount += successCount;
|
||||
System.out.println("第 " + (i + 1) + " 个文件处理成功,更新了 " + successCount + " 条记录");
|
||||
} catch (Exception e) {
|
||||
System.err.println("处理文件失败: " + file.getAbsolutePath());
|
||||
e.printStackTrace();
|
||||
// 单个文件处理失败不影响其他文件
|
||||
continue;
|
||||
}
|
||||
}
|
||||
System.out.println("处理完成,共处理 " + totalFiles + " 个文件,成功更新 " + totalSuccessCount + " 条记录");
|
||||
} catch (Exception e) {
|
||||
System.err.println("更新操作失败: " + importPathDto.getFilePath());
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return totalSuccessCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从Excel文件读取数据并更新CmsContent
|
||||
*
|
||||
* @param fileBytes Excel文件字节数组
|
||||
* @param updateBy 更新人
|
||||
* @return 成功更新的记录数
|
||||
*/
|
||||
private int updateFromExcel(byte[] fileBytes, String updateBy) {
|
||||
int successCount = 0;
|
||||
|
||||
try (InputStream inputStream = new ByteArrayInputStream(fileBytes);
|
||||
ExcelReader reader = ExcelUtil.getReader(inputStream)) {
|
||||
|
||||
// 获取总行数(包括标题行)
|
||||
int totalRows = reader.getRowCount();
|
||||
if (totalRows <= 1) {
|
||||
// 只有标题行或空文件
|
||||
return 0;
|
||||
}
|
||||
|
||||
Date now = new Date();
|
||||
|
||||
// 读取标题行
|
||||
List<Object> headerObjList = reader.readRow(0);
|
||||
if (headerObjList == null || headerObjList.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
// 转换为List<String>
|
||||
List<String> headerList = new ArrayList<>();
|
||||
for (Object obj : headerObjList) {
|
||||
headerList.add(obj != null ? obj.toString() : "");
|
||||
}
|
||||
|
||||
// 从第二行开始读取数据(第一行为标题行)
|
||||
for (int rowIndex = 1; rowIndex < totalRows; rowIndex++) {
|
||||
try {
|
||||
List<Object> rowList = reader.readRow(rowIndex);
|
||||
if (rowList == null || rowList.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 转换为Map<String, Object>
|
||||
Map<String, Object> row = new HashMap<>();
|
||||
for (int i = 0; i < headerList.size() && i < rowList.size(); i++) {
|
||||
row.put(headerList.get(i), rowList.get(i));
|
||||
}
|
||||
if (row.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 读取关键字段用于查询
|
||||
String title = getStringValue(row, "title");
|
||||
String origin = getStringValue(row, "origin");
|
||||
String tags = getStringValue(row, "tags");
|
||||
// 去掉tags中的空格
|
||||
if (tags != null) {
|
||||
tags = tags.replaceAll(" ", "");
|
||||
}
|
||||
String icon = getStringValue(row, "icon");
|
||||
|
||||
// 根据关键字段查询记录
|
||||
CmsContentDto queryDto = new CmsContentDto();
|
||||
queryDto.setTitle(title);
|
||||
queryDto.setOrigin(origin);
|
||||
// queryDto.setTags(tags);
|
||||
// queryDto.setIcon(icon);
|
||||
queryDto.setDeleteFlag(0);
|
||||
|
||||
List<CmsContent> existingList = cmsContentMapper.getList(queryDto);
|
||||
if (existingList != null && !existingList.isEmpty()) {
|
||||
// 找到匹配的记录,进行更新
|
||||
CmsContent existingContent = existingList.get(0);
|
||||
|
||||
// 更新字段
|
||||
existingContent.setTitle(getStringValue(row, "title"));
|
||||
existingContent.setTitleEn(getStringValue(row, "title_en"));
|
||||
existingContent.setOrigin(getStringValue(row, "origin"));
|
||||
existingContent.setTags(tags);
|
||||
existingContent.setIcon(getStringValue(row, "icon"));
|
||||
existingContent.setPrice(getBigDecimalValue(row, "price"));
|
||||
existingContent.setContentType(getIntegerValue(row, "content_type"));
|
||||
existingContent.setContent(getStringValue(row, "content"));
|
||||
existingContent.setContentEn(getStringValue(row, "content_en"));
|
||||
existingContent.setAuditStatus(getIntegerValue(row, "audit_status"));
|
||||
existingContent.setPublishStatus(getIntegerValue(row, "publish_status"));
|
||||
existingContent.setViewCount(getIntegerValue(row, "view_count"));
|
||||
existingContent.setLikeCount(getIntegerValue(row, "like_count"));
|
||||
existingContent.setCommentCount(getIntegerValue(row, "comment_count"));
|
||||
existingContent.setIsPaid(getIntegerValue(row, "is_paid"));
|
||||
existingContent.setSupportPointsPay(getIntegerValue(row, "support_points_pay"));
|
||||
existingContent.setDescription(getStringValue(row, "description"));
|
||||
existingContent.setDescriptionEn(getStringValue(row, "description_en"));
|
||||
existingContent.setIntroduce(getStringValue(row, "introduce"));
|
||||
existingContent.setIntroduceEn(getStringValue(row, "introduce_en"));
|
||||
|
||||
// 设置更新时间和更新人
|
||||
existingContent.setUpdateTime(now);
|
||||
existingContent.setUpdateBy(updateBy);
|
||||
|
||||
// 执行更新
|
||||
cmsContentMapper.update(existingContent);
|
||||
successCount++;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("处理第 " + rowIndex + " 行数据时出错: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
// 跳过当前行,继续处理下一行
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return successCount;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -34,11 +34,15 @@ import org.dromara.sms4j.api.SmsBlend;
|
|||
import org.dromara.sms4j.core.factory.SmsFactory;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* (SysUser)表服务实现类
|
||||
|
|
@ -81,6 +85,12 @@ public class SysUserServiceImpl implements SysUserService {
|
|||
@Resource
|
||||
private AccountService accountService;
|
||||
|
||||
/**
|
||||
* 用户头像上传目录
|
||||
*/
|
||||
@Value("${web.upload.userIconPath}")
|
||||
private String userIconPath;
|
||||
|
||||
/**
|
||||
* 分页查询数据
|
||||
*
|
||||
|
|
@ -281,7 +291,9 @@ public class SysUserServiceImpl implements SysUserService {
|
|||
// 判断更新的用户是否是当前登录用户,如果是则更新用户缓存
|
||||
Long currentUserId = loginUserCacheUtil.getCurrentUserId();
|
||||
if (currentUserId != null && currentUserId.equals(sysUserUpdateDto.getUserId())) {
|
||||
updateUserCache(currentUserId, sysUser);
|
||||
// 获取当前登录用户的 token
|
||||
String token = loginUserCacheUtil.getTokenFromRequest();
|
||||
updateUserCache(currentUserId, sysUser, token);
|
||||
}
|
||||
}else {
|
||||
// 如果没有用户ID,创建新用户
|
||||
|
|
@ -1153,13 +1165,14 @@ public class SysUserServiceImpl implements SysUserService {
|
|||
*
|
||||
* @param userId 用户ID
|
||||
* @param updatedUser 更新后的用户信息
|
||||
* @param token 用户的认证token
|
||||
*/
|
||||
private void updateUserCache(Long userId, SysUser updatedUser) {
|
||||
@Override
|
||||
public void updateUserCache(Long userId, SysUser updatedUser, String token) {
|
||||
try {
|
||||
// 获取当前登录用户的 token
|
||||
String token = loginUserCacheUtil.getTokenFromRequest();
|
||||
// 验证 token 是否有效
|
||||
if (token == null || token.isEmpty()) {
|
||||
log.warn("无法获取当前用户的 token,跳过缓存更新");
|
||||
log.warn("无效的 token,跳过缓存更新");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1191,4 +1204,59 @@ public class SysUserServiceImpl implements SysUserService {
|
|||
log.error("更新用户缓存失败:{}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传用户头像
|
||||
*
|
||||
* @param file 头像文件
|
||||
* @param userId 用户ID
|
||||
* @param token 用户的认证token
|
||||
* @return 上传成功的文件名
|
||||
*/
|
||||
@Override
|
||||
public String uploadAvatar(MultipartFile file, Long userId, String token) {
|
||||
// 检查文件是否为空
|
||||
if (file.isEmpty()) {
|
||||
throw new BizException("请选择要上传的头像文件");
|
||||
}
|
||||
|
||||
// 检查文件类型
|
||||
String contentType = file.getContentType();
|
||||
if (contentType == null || !contentType.startsWith("image/")) {
|
||||
throw new BizException("只能上传图片文件");
|
||||
}
|
||||
|
||||
// 生成唯一文件名
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
String extension = originalFilename != null ? originalFilename.substring(originalFilename.lastIndexOf(".")) : ".jpg";
|
||||
String fileName = UUID.randomUUID().toString() + extension;
|
||||
|
||||
// 定义上传目录
|
||||
File dir = new File(userIconPath);
|
||||
if (!dir.exists()) {
|
||||
dir.mkdirs();
|
||||
}
|
||||
|
||||
// 保存文件
|
||||
File dest = new File(dir, fileName);
|
||||
try {
|
||||
file.transferTo(dest);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
throw new BizException("上传失败:" + e.getMessage());
|
||||
}
|
||||
|
||||
// 更新用户头像
|
||||
SysUser user = sysUserMapper.queryById(userId);
|
||||
if (user == null) {
|
||||
throw new BizException("用户不存在");
|
||||
}
|
||||
user.setUserIcon(fileName);
|
||||
sysUserMapper.update(user);
|
||||
|
||||
// 更新缓存中的用户信息
|
||||
updateUserCache(userId, user, token);
|
||||
|
||||
return fileName;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -230,4 +230,230 @@
|
|||
and delete_flag = 0
|
||||
order by create_time desc
|
||||
</select>
|
||||
|
||||
<!-- 分页查询充值记录(transactionType=1) -->
|
||||
<select id="getRechargePageList" resultMap="AccountTransactionMap">
|
||||
select
|
||||
transaction_id, user_id, user_name, transaction_type, amount, before_balance, after_balance, status,
|
||||
transaction_no, pay_type, business_id, business_type, call_id, remark, is_expense, input_token, output_token,
|
||||
total_tokens, model_name, question, income_type, create_time, update_time, create_by, update_by, delete_flag
|
||||
from account_transaction
|
||||
<where>
|
||||
and transaction_type = 1
|
||||
<if test="userId != null">
|
||||
and user_id = #{userId}
|
||||
</if>
|
||||
<if test="status != null">
|
||||
and status = #{status}
|
||||
</if>
|
||||
<if test="transactionNo != null and transactionNo != ''">
|
||||
and transaction_no = #{transactionNo}
|
||||
</if>
|
||||
<if test="createTimeStart != null">
|
||||
and create_time <![CDATA[ >= ]]> #{createTimeStart}
|
||||
</if>
|
||||
<if test="createTimeEnd != null">
|
||||
and create_time <![CDATA[ <= ]]> #{createTimeEnd}
|
||||
</if>
|
||||
<if test="deleteFlag != null">
|
||||
and delete_flag = #{deleteFlag}
|
||||
</if>
|
||||
</where>
|
||||
order by create_time desc
|
||||
</select>
|
||||
|
||||
<!-- 分页查询赠送记录(transactionType=6) -->
|
||||
<select id="getGiftPageList" resultMap="AccountTransactionMap">
|
||||
select
|
||||
transaction_id, user_id, user_name, transaction_type, amount, before_balance, after_balance, status,
|
||||
transaction_no, pay_type, business_id, business_type, call_id, remark, is_expense, input_token, output_token,
|
||||
total_tokens, model_name, question, income_type, create_time, update_time, create_by, update_by, delete_flag
|
||||
from account_transaction
|
||||
<where>
|
||||
and transaction_type = 6
|
||||
<if test="userId != null">
|
||||
and user_id = #{userId}
|
||||
</if>
|
||||
<if test="status != null">
|
||||
and status = #{status}
|
||||
</if>
|
||||
<if test="transactionNo != null and transactionNo != ''">
|
||||
and transaction_no = #{transactionNo}
|
||||
</if>
|
||||
<if test="createTimeStart != null">
|
||||
and create_time <![CDATA[ >= ]]> #{createTimeStart}
|
||||
</if>
|
||||
<if test="createTimeEnd != null">
|
||||
and create_time <![CDATA[ <= ]]> #{createTimeEnd}
|
||||
</if>
|
||||
<if test="deleteFlag != null">
|
||||
and delete_flag = #{deleteFlag}
|
||||
</if>
|
||||
</where>
|
||||
order by create_time desc
|
||||
</select>
|
||||
|
||||
<!-- 分页查询消费记录并按callId分组 -->
|
||||
<resultMap type="com.kexue.skills.entity.dto.ConsumptionGroupedDto" id="ConsumptionGroupedMap">
|
||||
<result property="transactionId" column="transaction_id" jdbcType="BIGINT"/>
|
||||
<result property="userId" column="user_id" jdbcType="BIGINT"/>
|
||||
<result property="userName" column="user_name" jdbcType="VARCHAR"/>
|
||||
<result property="transactionType" column="transaction_type" jdbcType="INTEGER"/>
|
||||
<result property="amount" column="amount" jdbcType="DECIMAL"/>
|
||||
<result property="beforeBalance" column="before_balance" jdbcType="DECIMAL"/>
|
||||
<result property="afterBalance" column="after_balance" jdbcType="DECIMAL"/>
|
||||
<result property="status" column="status" jdbcType="INTEGER"/>
|
||||
<result property="transactionNo" column="transaction_no" jdbcType="VARCHAR"/>
|
||||
<result property="payType" column="pay_type" jdbcType="INTEGER"/>
|
||||
<result property="businessId" column="business_id" jdbcType="BIGINT"/>
|
||||
<result property="businessType" column="business_type" jdbcType="VARCHAR"/>
|
||||
<result property="callId" column="call_id" jdbcType="VARCHAR"/>
|
||||
<result property="remark" column="remark" jdbcType="VARCHAR"/>
|
||||
<result property="isExpense" column="is_expense" jdbcType="INTEGER"/>
|
||||
<result property="inputToken" column="input_token" jdbcType="INTEGER"/>
|
||||
<result property="outputToken" column="output_token" jdbcType="INTEGER"/>
|
||||
<result property="totalTokens" column="total_tokens" jdbcType="INTEGER"/>
|
||||
<result property="modelName" column="model_name" jdbcType="VARCHAR"/>
|
||||
<result property="question" column="question" jdbcType="LONGVARCHAR"/>
|
||||
<result property="incomeType" column="income_type" jdbcType="VARCHAR"/>
|
||||
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
|
||||
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
|
||||
<result property="createBy" column="create_by" jdbcType="VARCHAR"/>
|
||||
<result property="updateBy" column="update_by" jdbcType="VARCHAR"/>
|
||||
<result property="deleteFlag" column="delete_flag" jdbcType="INTEGER"/>
|
||||
</resultMap>
|
||||
|
||||
<select id="getConsumptionGroupedPageList" resultMap="ConsumptionGroupedMap">
|
||||
SELECT
|
||||
t.transaction_id,
|
||||
t.user_id,
|
||||
t.user_name,
|
||||
t.transaction_type,
|
||||
amount_sum.amount,
|
||||
t.before_balance,
|
||||
t.after_balance,
|
||||
t.status,
|
||||
t.transaction_no,
|
||||
t.pay_type,
|
||||
t.business_id,
|
||||
t.business_type,
|
||||
t.call_id,
|
||||
t.remark,
|
||||
t.is_expense,
|
||||
token_sum.input_token,
|
||||
token_sum.output_token,
|
||||
token_sum.total_tokens,
|
||||
t.model_name,
|
||||
first_record.question,
|
||||
t.income_type,
|
||||
t.create_time,
|
||||
t.update_time,
|
||||
t.create_by,
|
||||
t.update_by,
|
||||
t.delete_flag
|
||||
FROM (
|
||||
SELECT
|
||||
MIN(transaction_id) as min_transaction_id,
|
||||
call_id,
|
||||
MIN(create_time) as min_create_time,
|
||||
SUM(amount) as amount
|
||||
FROM account_transaction
|
||||
WHERE transaction_type IN (3, 7)
|
||||
AND call_id IS NOT NULL
|
||||
AND call_id != ''
|
||||
AND delete_flag = 0
|
||||
<if test="userId != null">
|
||||
AND user_id = #{userId}
|
||||
</if>
|
||||
<if test="status != null">
|
||||
AND status = #{status}
|
||||
</if>
|
||||
<if test="createTimeStart != null">
|
||||
AND create_time <![CDATA[ >= ]]> #{createTimeStart}
|
||||
</if>
|
||||
<if test="createTimeEnd != null">
|
||||
AND create_time <![CDATA[ <= ]]> #{createTimeEnd}
|
||||
</if>
|
||||
GROUP BY call_id
|
||||
) amount_sum
|
||||
INNER JOIN account_transaction t ON t.transaction_id = amount_sum.min_transaction_id
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
call_id,
|
||||
SUM(input_token) as input_token,
|
||||
SUM(output_token) as output_token,
|
||||
SUM(total_tokens) as total_tokens
|
||||
FROM account_transaction
|
||||
WHERE transaction_type IN (3, 7)
|
||||
AND call_id IS NOT NULL
|
||||
AND call_id != ''
|
||||
AND delete_flag = 0
|
||||
<if test="userId != null">
|
||||
AND user_id = #{userId}
|
||||
</if>
|
||||
<if test="status != null">
|
||||
AND status = #{status}
|
||||
</if>
|
||||
<if test="createTimeStart != null">
|
||||
AND create_time <![CDATA[ >= ]]> #{createTimeStart}
|
||||
</if>
|
||||
<if test="createTimeEnd != null">
|
||||
AND create_time <![CDATA[ <= ]]> #{createTimeEnd}
|
||||
</if>
|
||||
GROUP BY call_id
|
||||
) token_sum ON t.call_id = token_sum.call_id
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
at.call_id,
|
||||
at.question
|
||||
FROM account_transaction at
|
||||
INNER JOIN (
|
||||
SELECT call_id, MIN(create_time) as earliest_time
|
||||
FROM account_transaction
|
||||
WHERE transaction_type IN (3, 7)
|
||||
AND call_id IS NOT NULL
|
||||
AND call_id != ''
|
||||
AND delete_flag = 0
|
||||
<if test="userId != null">
|
||||
AND user_id = #{userId}
|
||||
</if>
|
||||
<if test="status != null">
|
||||
AND status = #{status}
|
||||
</if>
|
||||
<if test="createTimeStart != null">
|
||||
AND create_time <![CDATA[ >= ]]> #{createTimeStart}
|
||||
</if>
|
||||
<if test="createTimeEnd != null">
|
||||
AND create_time <![CDATA[ <= ]]> #{createTimeEnd}
|
||||
</if>
|
||||
GROUP BY call_id
|
||||
) earliest ON at.call_id = earliest.call_id AND at.create_time = earliest.earliest_time
|
||||
) first_record ON t.call_id = first_record.call_id
|
||||
ORDER BY t.create_time desc
|
||||
</select>
|
||||
<!-- 获取消费原始记录列表(用于内存分组) -->
|
||||
<select id="getConsumptionRawList" resultMap="AccountTransactionMap">
|
||||
select
|
||||
transaction_id, user_id, user_name, transaction_type, amount, before_balance, after_balance, status,
|
||||
transaction_no, pay_type, business_id, business_type, call_id, remark, is_expense, input_token, output_token,
|
||||
total_tokens, model_name, question, income_type, create_time, update_time, create_by, update_by, delete_flag
|
||||
from account_transaction
|
||||
where transaction_type IN (3, 7)
|
||||
AND delete_flag = 0
|
||||
And call_id IS NOT NULL
|
||||
AND call_id != ''
|
||||
<if test="userId != null">
|
||||
AND user_id = #{userId}
|
||||
</if>
|
||||
<if test="status != null">
|
||||
AND status = #{status}
|
||||
</if>
|
||||
<if test="createTimeStart != null">
|
||||
AND create_time <![CDATA[ >= ]]> #{createTimeStart}
|
||||
</if>
|
||||
<if test="createTimeEnd != null">
|
||||
AND create_time <![CDATA[ <= ]]> #{createTimeEnd}
|
||||
</if>
|
||||
ORDER BY create_time DESC
|
||||
</select>
|
||||
</mapper>
|
||||
|
|
@ -121,7 +121,7 @@
|
|||
<if test="isOfficial != null">
|
||||
and is_official = #{isOfficial}
|
||||
</if>
|
||||
<if test="tagId != null">
|
||||
<if test="tagId != null ">
|
||||
and find_in_set(#{tagId}, tags)
|
||||
</if>
|
||||
<if test="tagIdList != null and tagIdList.size() > 0">
|
||||
|
|
@ -131,12 +131,15 @@
|
|||
</foreach>
|
||||
)
|
||||
</if>
|
||||
<if test="tags != null and tags != ''">
|
||||
and find_in_set(#{tags}, tags)
|
||||
</if>
|
||||
</where>
|
||||
<if test="sortBy != null and sortBy != ''">
|
||||
order by ${sortBy} ${sortDesc ? 'desc' : 'asc'}
|
||||
order by ${sortBy} ${sortDesc ? 'desc' : 'asc'}, content_id desc
|
||||
</if>
|
||||
<if test="sortBy == null or sortBy == ''">
|
||||
order by sort asc, create_time desc
|
||||
order by sort asc, create_time desc, content_id desc
|
||||
</if>
|
||||
</select>
|
||||
|
||||
|
|
@ -203,10 +206,10 @@
|
|||
</if>
|
||||
</where>
|
||||
<if test="queryDto.sortBy != null and queryDto.sortBy != ''">
|
||||
order by ${queryDto.sortBy} ${queryDto.sortDesc ? 'desc' : 'asc'}
|
||||
order by ${queryDto.sortBy} ${queryDto.sortDesc ? 'desc' : 'asc'}, content_id desc
|
||||
</if>
|
||||
<if test="queryDto.sortBy == null or queryDto.sortBy == ''">
|
||||
order by sort asc, create_time desc
|
||||
order by sort asc, create_time desc, content_id desc
|
||||
</if>
|
||||
limit #{offset}, #{limit}
|
||||
</select>
|
||||
|
|
@ -249,6 +252,16 @@
|
|||
<if test="tagId != null">
|
||||
and find_in_set(#{tagId}, tags)
|
||||
</if>
|
||||
<if test="tagIdList != null and tagIdList.size() > 0">
|
||||
and (
|
||||
<foreach collection="tagIdList" item="tagId" separator=" or ">
|
||||
find_in_set(#{tagId}, tags)
|
||||
</foreach>
|
||||
)
|
||||
</if>
|
||||
<if test="tags != null and tags != ''">
|
||||
and find_in_set(#{tags}, tags)
|
||||
</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
|
|
@ -307,8 +320,17 @@
|
|||
</foreach>
|
||||
)
|
||||
</if>
|
||||
<if test="origin != null and origin != ''">
|
||||
and origin = #{origin}
|
||||
</if>
|
||||
<if test="tags != null and tags != ''">
|
||||
and tags = #{tags}
|
||||
</if>
|
||||
<if test="icon != null and icon != ''">
|
||||
and icon = #{icon}
|
||||
</if>
|
||||
</where>
|
||||
order by sort asc, create_time desc
|
||||
order by sort asc, create_time desc, content_id desc
|
||||
</select>
|
||||
|
||||
<!--新增所有列-->
|
||||
|
|
|
|||
|
|
@ -3,21 +3,39 @@
|
|||
<mapper namespace="com.kexue.skills.mapper.SysLogMapper">
|
||||
|
||||
<resultMap type="com.kexue.skills.entity.SysLog" id="SysLogMap">
|
||||
<result property="logId" column="LOG_ID" jdbcType="INTEGER"/>
|
||||
<result property="userId" column="USER_ID" jdbcType="VARCHAR"/>
|
||||
<result property="userName" column="USER_NAME" jdbcType="VARCHAR"/>
|
||||
<result property="logType" column="LOG_TYPE" jdbcType="VARCHAR"/>
|
||||
<result property="logContent" column="LOG_CONTENT" jdbcType="VARCHAR"/>
|
||||
<result property="serverIp" column="SERVER_IP" jdbcType="VARCHAR"/>
|
||||
<result property="clientIp" column="CLIENT_IP" jdbcType="VARCHAR"/>
|
||||
<result property="logTime" column="LOG_TIME" jdbcType="VARCHAR"/>
|
||||
<result property="note" column="NOTE" jdbcType="VARCHAR"/>
|
||||
<result property="logId" column="LOG_ID" jdbcType="BIGINT"/>
|
||||
<result property="traceId" column="TRACE_ID" jdbcType="VARCHAR"/>
|
||||
<result property="description" column="DESCRIPTION" jdbcType="VARCHAR"/>
|
||||
<result property="module" column="MODULE" jdbcType="VARCHAR"/>
|
||||
<result property="requestUrl" column="REQUEST_URL" jdbcType="VARCHAR"/>
|
||||
<result property="requestMethod" column="REQUEST_METHOD" jdbcType="VARCHAR"/>
|
||||
<result property="requestHeaders" column="REQUEST_HEADERS" jdbcType="VARCHAR"/>
|
||||
<result property="requestBody" column="REQUEST_BODY" jdbcType="VARCHAR"/>
|
||||
<result property="statusCode" column="STATUS_CODE" jdbcType="INTEGER"/>
|
||||
<result property="responseHeaders" column="RESPONSE_HEADERS" jdbcType="VARCHAR"/>
|
||||
<result property="responseBody" column="RESPONSE_BODY" jdbcType="VARCHAR"/>
|
||||
<result property="timeTaken" column="TIME_TAKEN" jdbcType="BIGINT"/>
|
||||
<result property="ip" column="IP" jdbcType="VARCHAR"/>
|
||||
<result property="address" column="ADDRESS" jdbcType="VARCHAR"/>
|
||||
<result property="browser" column="BROWSER" jdbcType="VARCHAR"/>
|
||||
<result property="os" column="OS" jdbcType="VARCHAR"/>
|
||||
<result property="status" column="STATUS" jdbcType="TINYINT"/>
|
||||
<result property="errorMsg" column="ERROR_MSG" jdbcType="VARCHAR"/>
|
||||
<result property="createUser" column="CREATE_USER" jdbcType="BIGINT"/>
|
||||
<result property="createTime" column="CREATE_TIME" jdbcType="TIMESTAMP"/>
|
||||
<result property="updateTime" column="UPDATE_TIME" jdbcType="TIMESTAMP"/>
|
||||
<result property="deleteFlag" column="DELETE_FLAG" jdbcType="TINYINT"/>
|
||||
<result property="createBy" column="CREATE_BY" jdbcType="VARCHAR"/>
|
||||
<result property="updateBy" column="UPDATE_BY" jdbcType="VARCHAR"/>
|
||||
</resultMap>
|
||||
|
||||
<!--查询单个-->
|
||||
<select id="queryById" resultMap="SysLogMap">
|
||||
select
|
||||
LOG_ID, USER_ID, USER_NAME, LOG_TYPE, LOG_CONTENT, SERVER_IP, CLIENT_IP, LOG_TIME, NOTE
|
||||
LOG_ID, TRACE_ID, DESCRIPTION, MODULE, REQUEST_URL, REQUEST_METHOD,
|
||||
REQUEST_HEADERS, REQUEST_BODY, STATUS_CODE, RESPONSE_HEADERS, RESPONSE_BODY,
|
||||
TIME_TAKEN, IP, ADDRESS, BROWSER, OS, STATUS, ERROR_MSG, CREATE_USER,
|
||||
CREATE_TIME, UPDATE_TIME, DELETE_FLAG, CREATE_BY, UPDATE_BY
|
||||
from sys_log
|
||||
where LOG_ID = #{logId}
|
||||
</select>
|
||||
|
|
@ -25,43 +43,44 @@
|
|||
<!--查询指定行数据-->
|
||||
<select id="getPageList" resultMap="SysLogMap">
|
||||
select
|
||||
LOG_ID, USER_ID, USER_NAME, LOG_TYPE, LOG_CONTENT, SERVER_IP, CLIENT_IP, LOG_TIME, NOTE
|
||||
LOG_ID, TRACE_ID, DESCRIPTION, MODULE, REQUEST_URL, REQUEST_METHOD,
|
||||
REQUEST_HEADERS, REQUEST_BODY, STATUS_CODE, RESPONSE_HEADERS, RESPONSE_BODY,
|
||||
TIME_TAKEN, IP, ADDRESS, BROWSER, OS, STATUS, ERROR_MSG, CREATE_USER,
|
||||
CREATE_TIME, UPDATE_TIME, DELETE_FLAG, CREATE_BY, UPDATE_BY
|
||||
from sys_log
|
||||
<where>
|
||||
<if test="logId != null">
|
||||
and LOG_ID = #{logId}
|
||||
</if>
|
||||
<if test="userId != null and userId != ''">
|
||||
and USER_ID = #{userId}
|
||||
<if test="module != null and module != ''">
|
||||
and MODULE = #{module}
|
||||
</if>
|
||||
<if test="userName != null and userName != ''">
|
||||
and USER_NAME = #{userName}
|
||||
<if test="description != null and description != ''">
|
||||
and DESCRIPTION like concat('%', #{description}, '%')
|
||||
</if>
|
||||
<if test="logType != null and logType != ''">
|
||||
and LOG_TYPE = #{logType}
|
||||
<if test="ip != null and ip != ''">
|
||||
and IP = #{ip}
|
||||
</if>
|
||||
<if test="logContent != null and logContent != ''">
|
||||
and LOG_CONTENT = #{logContent}
|
||||
<if test="status != null">
|
||||
and STATUS = #{status}
|
||||
</if>
|
||||
<if test="serverIp != null and serverIp != ''">
|
||||
and SERVER_IP = #{serverIp}
|
||||
<if test="startTime != null">
|
||||
and CREATE_TIME >= #{startTime}
|
||||
</if>
|
||||
<if test="clientIp != null and clientIp != ''">
|
||||
and CLIENT_IP = #{clientIp}
|
||||
</if>
|
||||
<if test="logTime != null and logTime != ''">
|
||||
and LOG_TIME = #{logTime}
|
||||
</if>
|
||||
<if test="note != null and note != ''">
|
||||
and NOTE = #{note}
|
||||
<if test="endTime != null">
|
||||
and CREATE_TIME <= #{endTime}
|
||||
</if>
|
||||
</where>
|
||||
order by CREATE_TIME desc
|
||||
</select>
|
||||
|
||||
<!--查询指定行数据-->
|
||||
<select id="queryAllByLimit" resultMap="SysLogMap">
|
||||
select
|
||||
LOG_ID, USER_ID, USER_NAME, LOG_TYPE, LOG_CONTENT, SERVER_IP, CLIENT_IP, LOG_TIME, NOTE
|
||||
LOG_ID, TRACE_ID, DESCRIPTION, MODULE, REQUEST_URL, REQUEST_METHOD,
|
||||
REQUEST_HEADERS, REQUEST_BODY, STATUS_CODE, RESPONSE_HEADERS, RESPONSE_BODY,
|
||||
TIME_TAKEN, IP, ADDRESS, BROWSER, OS, STATUS, ERROR_MSG, CREATE_USER,
|
||||
CREATE_TIME, UPDATE_TIME, DELETE_FLAG, CREATE_BY, UPDATE_BY
|
||||
from sys_log
|
||||
limit #{offset}, #{limit}
|
||||
</select>
|
||||
|
|
@ -69,43 +88,45 @@
|
|||
<!--通过实体作为筛选条件查询-->
|
||||
<select id="queryAll" resultMap="SysLogMap">
|
||||
select
|
||||
LOG_ID, USER_ID, USER_NAME, LOG_TYPE, LOG_CONTENT, SERVER_IP, CLIENT_IP, LOG_TIME, NOTE
|
||||
LOG_ID, TRACE_ID, DESCRIPTION, MODULE, REQUEST_URL, REQUEST_METHOD,
|
||||
REQUEST_HEADERS, REQUEST_BODY, STATUS_CODE, RESPONSE_HEADERS, RESPONSE_BODY,
|
||||
TIME_TAKEN, IP, ADDRESS, BROWSER, OS, STATUS, ERROR_MSG, CREATE_USER,
|
||||
CREATE_TIME, UPDATE_TIME, DELETE_FLAG, CREATE_BY, UPDATE_BY
|
||||
from sys_log
|
||||
<where>
|
||||
<if test="logId != null">
|
||||
and LOG_ID = #{logId}
|
||||
</if>
|
||||
<if test="userId != null and userId != ''">
|
||||
and USER_ID = #{userId}
|
||||
<if test="module != null and module != ''">
|
||||
and MODULE = #{module}
|
||||
</if>
|
||||
<if test="userName != null and userName != ''">
|
||||
and USER_NAME = #{userName}
|
||||
<if test="description != null and description != ''">
|
||||
and DESCRIPTION like concat('%', #{description}, '%')
|
||||
</if>
|
||||
<if test="logType != null and logType != ''">
|
||||
and LOG_TYPE = #{logType}
|
||||
<if test="ip != null and ip != ''">
|
||||
and IP = #{ip}
|
||||
</if>
|
||||
<if test="logContent != null and logContent != ''">
|
||||
and LOG_CONTENT = #{logContent}
|
||||
</if>
|
||||
<if test="serverIp != null and serverIp != ''">
|
||||
and SERVER_IP = #{serverIp}
|
||||
</if>
|
||||
<if test="clientIp != null and clientIp != ''">
|
||||
and CLIENT_IP = #{clientIp}
|
||||
</if>
|
||||
<if test="logTime != null and logTime != ''">
|
||||
and LOG_TIME = #{logTime}
|
||||
</if>
|
||||
<if test="note != null and note != ''">
|
||||
and NOTE = #{note}
|
||||
<if test="status != null">
|
||||
and STATUS = #{status}
|
||||
</if>
|
||||
</where>
|
||||
order by CREATE_TIME desc
|
||||
</select>
|
||||
|
||||
<!--新增所有列-->
|
||||
<insert id="insert" keyProperty="logId" useGeneratedKeys="true">
|
||||
insert into sys_log(USER_ID, USER_NAME, LOG_TYPE, LOG_CONTENT, SERVER_IP, CLIENT_IP, LOG_TIME, NOTE)
|
||||
values (#{userId}, #{userName}, #{logType}, #{logContent}, #{serverIp}, #{clientIp}, #{logTime}, #{note})
|
||||
insert into sys_log(
|
||||
TRACE_ID, DESCRIPTION, MODULE, REQUEST_URL, REQUEST_METHOD,
|
||||
REQUEST_HEADERS, REQUEST_BODY, STATUS_CODE, RESPONSE_HEADERS, RESPONSE_BODY,
|
||||
TIME_TAKEN, IP, ADDRESS, BROWSER, OS, STATUS, ERROR_MSG, CREATE_USER,
|
||||
CREATE_TIME, UPDATE_TIME, DELETE_FLAG, CREATE_BY, UPDATE_BY
|
||||
)
|
||||
values (
|
||||
#{traceId}, #{description}, #{module}, #{requestUrl}, #{requestMethod},
|
||||
#{requestHeaders}, #{requestBody}, #{statusCode}, #{responseHeaders}, #{responseBody},
|
||||
#{timeTaken}, #{ip}, #{address}, #{browser}, #{os}, #{status}, #{errorMsg}, #{createUser},
|
||||
#{createTime}, #{updateTime}, #{deleteFlag}, #{createBy}, #{updateBy}
|
||||
)
|
||||
</insert>
|
||||
|
||||
|
||||
|
|
@ -134,29 +155,62 @@
|
|||
<update id="update">
|
||||
update sys_log
|
||||
<set>
|
||||
<if test="userId != null and userId != ''">
|
||||
USER_ID = #{userId},
|
||||
<if test="traceId != null and traceId != ''">
|
||||
TRACE_ID = #{traceId},
|
||||
</if>
|
||||
<if test="userName != null and userName != ''">
|
||||
USER_NAME = #{userName},
|
||||
<if test="description != null and description != ''">
|
||||
DESCRIPTION = #{description},
|
||||
</if>
|
||||
<if test="logType != null and logType != ''">
|
||||
LOG_TYPE = #{logType},
|
||||
<if test="module != null and module != ''">
|
||||
MODULE = #{module},
|
||||
</if>
|
||||
<if test="logContent != null and logContent != ''">
|
||||
LOG_CONTENT = #{logContent},
|
||||
<if test="requestUrl != null and requestUrl != ''">
|
||||
REQUEST_URL = #{requestUrl},
|
||||
</if>
|
||||
<if test="serverIp != null and serverIp != ''">
|
||||
SERVER_IP = #{serverIp},
|
||||
<if test="requestMethod != null and requestMethod != ''">
|
||||
REQUEST_METHOD = #{requestMethod},
|
||||
</if>
|
||||
<if test="clientIp != null and clientIp != ''">
|
||||
CLIENT_IP = #{clientIp},
|
||||
<if test="requestHeaders != null and requestHeaders != ''">
|
||||
REQUEST_HEADERS = #{requestHeaders},
|
||||
</if>
|
||||
<if test="logTime != null and logTime != ''">
|
||||
LOG_TIME = #{logTime},
|
||||
<if test="requestBody != null and requestBody != ''">
|
||||
REQUEST_BODY = #{requestBody},
|
||||
</if>
|
||||
<if test="note != null and note != ''">
|
||||
NOTE = #{note},
|
||||
<if test="statusCode != null">
|
||||
STATUS_CODE = #{statusCode},
|
||||
</if>
|
||||
<if test="responseHeaders != null and responseHeaders != ''">
|
||||
RESPONSE_HEADERS = #{responseHeaders},
|
||||
</if>
|
||||
<if test="responseBody != null and responseBody != ''">
|
||||
RESPONSE_BODY = #{responseBody},
|
||||
</if>
|
||||
<if test="timeTaken != null">
|
||||
TIME_TAKEN = #{timeTaken},
|
||||
</if>
|
||||
<if test="ip != null and ip != ''">
|
||||
IP = #{ip},
|
||||
</if>
|
||||
<if test="address != null and address != ''">
|
||||
ADDRESS = #{address},
|
||||
</if>
|
||||
<if test="browser != null and browser != ''">
|
||||
BROWSER = #{browser},
|
||||
</if>
|
||||
<if test="os != null and os != ''">
|
||||
OS = #{os},
|
||||
</if>
|
||||
<if test="status != null">
|
||||
STATUS = #{status},
|
||||
</if>
|
||||
<if test="errorMsg != null and errorMsg != ''">
|
||||
ERROR_MSG = #{errorMsg},
|
||||
</if>
|
||||
<if test="createUser != null">
|
||||
CREATE_USER = #{createUser},
|
||||
</if>
|
||||
<if test="updateBy != null and updateBy != ''">
|
||||
UPDATE_BY = #{updateBy},
|
||||
</if>
|
||||
</set>
|
||||
where LOG_ID = #{logId}
|
||||
|
|
|
|||
|
|
@ -18,12 +18,13 @@
|
|||
<result property="inviteCode" column="invite_code" jdbcType="VARCHAR"/>
|
||||
<result property="invitedCode" column="invited_code" jdbcType="VARCHAR"/>
|
||||
<result property="invitedBy" column="invited_by" jdbcType="BIGINT"/>
|
||||
<result property="userIcon" column="user_icon" jdbcType="VARCHAR"/>
|
||||
</resultMap>
|
||||
|
||||
<!--查询单个-->
|
||||
<select id="queryById" resultMap="SysUserMap">
|
||||
select
|
||||
user_id, user_name, pwd, real_name, tel, email, salt, remark, create_time, enable, delete_flag, session_id, invite_code, invited_code, invited_by
|
||||
user_id, user_name, pwd, real_name, tel, email, salt, remark, create_time, enable, delete_flag, session_id, invite_code, invited_code, invited_by, user_icon
|
||||
from sys_user
|
||||
where user_id = #{userId}
|
||||
</select>
|
||||
|
|
@ -31,7 +32,7 @@
|
|||
<!--查询指定行数据-->
|
||||
<select id="getPageList" resultMap="SysUserMap">
|
||||
select
|
||||
user_id, user_name, pwd, real_name, tel, email, salt, remark, create_time, enable, delete_flag, session_id, invite_code, invited_code, invited_by
|
||||
user_id, user_name, pwd, real_name, tel, email, salt, remark, create_time, enable, delete_flag, session_id, invite_code, invited_code, invited_by, user_icon
|
||||
from sys_user
|
||||
<where>
|
||||
<if test="userId != null">
|
||||
|
|
@ -73,7 +74,7 @@
|
|||
<!--查询指定行数据-->
|
||||
<select id="queryAllByLimit" resultMap="SysUserMap">
|
||||
select
|
||||
user_id, user_name, pwd, real_name, tel, email, salt, remark, create_time, enable, delete_flag, session_id, invite_code, invited_code, invited_by
|
||||
user_id, user_name, pwd, real_name, tel, email, salt, remark, create_time, enable, delete_flag, session_id, invite_code, invited_code, invited_by, user_icon
|
||||
from sys_user
|
||||
limit #{offset}, #{limit}
|
||||
</select>
|
||||
|
|
@ -81,7 +82,7 @@
|
|||
<!--通过实体作为筛选条件查询-->
|
||||
<select id="queryAll" resultMap="SysUserMap">
|
||||
select
|
||||
user_id, user_name, pwd, real_name, tel, email, salt, remark, create_time, enable, delete_flag, session_id, invite_code, invited_code, invited_by
|
||||
user_id, user_name, pwd, real_name, tel, email, salt, remark, create_time, enable, delete_flag, session_id, invite_code, invited_code, invited_by, user_icon
|
||||
from sys_user
|
||||
<where>
|
||||
<if test="userId != null">
|
||||
|
|
@ -122,8 +123,8 @@
|
|||
|
||||
<!--新增所有列-->
|
||||
<insert id="insert" keyProperty="userId" useGeneratedKeys="true">
|
||||
insert into sys_user(user_name, pwd, real_name, tel, email, salt, remark, create_time, enable, delete_flag, session_id, invite_code, invited_code, invited_by)
|
||||
values (#{userName}, #{pwd}, #{realName}, #{tel}, #{email}, #{salt}, #{remark}, #{createTime}, #{enable}, #{deleteFlag}, #{sessionId}, #{inviteCode}, #{invitedCode}, #{invitedBy})
|
||||
insert into sys_user(user_name, pwd, real_name, tel, email, salt, remark, create_time, enable, delete_flag, session_id, invite_code, invited_code, invited_by, user_icon)
|
||||
values (#{userName}, #{pwd}, #{realName}, #{tel}, #{email}, #{salt}, #{remark}, #{createTime}, #{enable}, #{deleteFlag}, #{sessionId}, #{inviteCode}, #{invitedCode}, #{invitedBy}, #{userIcon})
|
||||
</insert>
|
||||
|
||||
|
||||
|
|
@ -194,6 +195,9 @@
|
|||
<if test="invitedBy != null">
|
||||
invited_by = #{invitedBy},
|
||||
</if>
|
||||
<if test="userIcon != null and userIcon != ''">
|
||||
user_icon = #{userIcon},
|
||||
</if>
|
||||
</set>
|
||||
where user_id = #{userId}
|
||||
</update>
|
||||
|
|
@ -205,7 +209,7 @@
|
|||
|
||||
<select id="getByUsername" resultMap="SysUserMap">
|
||||
select
|
||||
user_id, user_name, pwd, real_name, tel, email, salt, remark, create_time, enable, delete_flag, session_id, invite_code, invited_code, invited_by
|
||||
user_id, user_name, pwd, real_name, tel, email, salt, remark, create_time, enable, delete_flag, session_id, invite_code, invited_code, invited_by, user_icon
|
||||
from sys_user
|
||||
where (user_name = #{userName} or tel = #{userName})
|
||||
and delete_flag = 0
|
||||
|
|
@ -214,7 +218,7 @@
|
|||
|
||||
<select id="getByTel" resultMap="SysUserMap">
|
||||
select
|
||||
user_id, user_name, pwd, real_name, tel, email, salt, remark, create_time, enable, delete_flag, session_id, invite_code, invited_code, invited_by
|
||||
user_id, user_name, pwd, real_name, tel, email, salt, remark, create_time, enable, delete_flag, session_id, invite_code, invited_code, invited_by, user_icon
|
||||
from sys_user
|
||||
where tel = #{tel}
|
||||
and delete_flag = 0
|
||||
|
|
@ -222,7 +226,7 @@
|
|||
</select>
|
||||
<select id="getBySessionId" resultMap="SysUserMap">
|
||||
select
|
||||
user_id, user_name, pwd, real_name, tel, email, salt, remark, create_time, enable, delete_flag, session_id, invite_code, invited_code, invited_by
|
||||
user_id, user_name, pwd, real_name, tel, email, salt, remark, create_time, enable, delete_flag, session_id, invite_code, invited_code, invited_by, user_icon
|
||||
from sys_user
|
||||
where session_id = #{sessionId}
|
||||
and delete_flag = 0
|
||||
|
|
@ -231,7 +235,7 @@
|
|||
|
||||
<select id="getByInviteCode" resultMap="SysUserMap">
|
||||
select
|
||||
user_id, user_name, pwd, real_name, tel, email, salt, remark, create_time, enable, delete_flag, session_id, invite_code, invited_code, invited_by
|
||||
user_id, user_name, pwd, real_name, tel, email, salt, remark, create_time, enable, delete_flag, session_id, invite_code, invited_code, invited_by, user_icon
|
||||
from sys_user
|
||||
where invite_code = #{inviteCode}
|
||||
and delete_flag = 0
|
||||
|
|
|
|||
Loading…
Reference in New Issue