Compare commits
No commits in common. "main" and "v1.0.260410" have entirely different histories.
main
...
v1.0.26041
|
|
@ -1,5 +0,0 @@
|
|||
-- 为 account_frozen 表添加 question 字段
|
||||
-- 用于记录用户的问题或需求
|
||||
|
||||
ALTER TABLE `account_frozen`
|
||||
ADD COLUMN `question` text DEFAULT NULL COMMENT '对应回答的问题或需求' AFTER `model_name`;
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
-- 为 account_transaction 表添加 call_id 字段
|
||||
-- 用于关联冻结单释放时的调用ID
|
||||
|
||||
ALTER TABLE `account_transaction`
|
||||
ADD COLUMN `call_id` varchar(100) DEFAULT NULL COMMENT '调用ID,关联冻结单' AFTER `business_type`;
|
||||
|
||||
-- 添加索引以提高查询性能
|
||||
ALTER TABLE `account_transaction`
|
||||
ADD INDEX `idx_call_id` (`call_id`);
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
-- 更新 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());
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
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;
|
||||
}
|
||||
|
|
@ -44,15 +44,6 @@ public class CommonResult<T> {
|
|||
return new CommonResult<T>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功返回结果
|
||||
*
|
||||
* @param data 获取的数据
|
||||
*/
|
||||
public static <T> CommonResult<T> success(IErrorCode errorCode,T data) {
|
||||
return new CommonResult<T>(errorCode.getCode(), errorCode.getMessage(), data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功返回结果
|
||||
*
|
||||
|
|
@ -67,11 +58,11 @@ public class CommonResult<T> {
|
|||
/**
|
||||
* 成功返回结果
|
||||
*
|
||||
* @param code 获取的数据
|
||||
* @param errorCode 获取的数据
|
||||
* @param message 提示信息
|
||||
*/
|
||||
public static <T> CommonResult<T> success(Long code, String message ) {
|
||||
return new CommonResult<T>(code, message,null);
|
||||
public static <T> CommonResult<T> success(IErrorCode errorCode, String message ) {
|
||||
return new CommonResult<T>(errorCode.getCode(), message,null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -92,19 +92,7 @@ public enum ResultCode implements IErrorCode {
|
|||
/**
|
||||
* 统一异常返回码
|
||||
* */
|
||||
EXCEPTION_HANDLER(-2500,"服务异常,请联系管理员"),
|
||||
|
||||
/**
|
||||
* 账户冻结单相关错误
|
||||
* */
|
||||
PARAMETER_EMPTY(-1100, "参数不能为空"),
|
||||
FROZEN_ID_EMPTY(-1101, "冻结单ID不能为空"),
|
||||
FROZEN_NOT_EXIST(-1102, "冻结单不存在"),
|
||||
FROZEN_STATUS_ERROR(-1103, "冻结单状态不正确,无法释放"),
|
||||
SESSION_ID_NOT_EXIST(-1104, "会话ID不存在"),
|
||||
ACCOUNT_NOT_EXIST(-1105, "用户账户不存在"),
|
||||
INSUFFICIENT_BALANCE(-1106, "账户余额不足");
|
||||
|
||||
EXCEPTION_HANDLER(-2500,"服务异常,请联系管理员");
|
||||
|
||||
private final long code;
|
||||
private final String message;
|
||||
|
|
|
|||
|
|
@ -2,119 +2,22 @@ package com.kexue.skills.common.util;
|
|||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @description: ID管理类
|
||||
**/
|
||||
@Component
|
||||
public class IDUtils {
|
||||
public static final Logger logger = LoggerFactory.getLogger(IDUtils.class);
|
||||
|
||||
@Value("${snowflake.workid:1}")
|
||||
private long workId;
|
||||
|
||||
private static SnowflakeIdGenerator snowflakeIdGenerator;
|
||||
|
||||
public static String getUUID(){
|
||||
return UUID.randomUUID().toString().replace("-","");
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
snowflakeIdGenerator = new SnowflakeIdGenerator(workId);
|
||||
logger.info("Snowflake ID generator initialized with workId: {}", workId);
|
||||
}
|
||||
|
||||
public static long getSnowflakeId() {
|
||||
if (snowflakeIdGenerator == null) {
|
||||
// 如果未初始化,使用默认workId
|
||||
snowflakeIdGenerator = new SnowflakeIdGenerator(1L);
|
||||
}
|
||||
return snowflakeIdGenerator.nextId();
|
||||
}
|
||||
|
||||
public static String getSnowflakeIdStr() {
|
||||
if (snowflakeIdGenerator == null) {
|
||||
// 如果未初始化,使用默认workId
|
||||
snowflakeIdGenerator = new SnowflakeIdGenerator(1L);
|
||||
}
|
||||
return String.valueOf(snowflakeIdGenerator.nextId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 雪花算法实现
|
||||
*/
|
||||
static class SnowflakeIdGenerator {
|
||||
// 起始时间戳 (2020-01-01 00:00:00)
|
||||
private static final long START_TIMESTAMP = 1577808000000L;
|
||||
// 机器ID位数
|
||||
private static final long MACHINE_ID_BITS = 10L;
|
||||
// 序列号位数
|
||||
private static final long SEQUENCE_BITS = 12L;
|
||||
// 机器ID最大值
|
||||
private static final long MAX_MACHINE_ID = (1L << MACHINE_ID_BITS) - 1;
|
||||
// 序列号最大值
|
||||
private static final long MAX_SEQUENCE = (1L << SEQUENCE_BITS) - 1;
|
||||
// 机器ID左移位数
|
||||
private static final long MACHINE_ID_SHIFT = SEQUENCE_BITS;
|
||||
// 时间戳左移位数
|
||||
private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + MACHINE_ID_BITS;
|
||||
|
||||
private long machineId;
|
||||
private long sequence = 0L;
|
||||
private long lastTimestamp = -1L;
|
||||
|
||||
public SnowflakeIdGenerator(long machineId) {
|
||||
if (machineId > MAX_MACHINE_ID || machineId < 0) {
|
||||
throw new IllegalArgumentException("Machine ID must be between 0 and " + MAX_MACHINE_ID);
|
||||
}
|
||||
this.machineId = machineId;
|
||||
}
|
||||
|
||||
public synchronized long nextId() {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
|
||||
if (timestamp < lastTimestamp) {
|
||||
throw new RuntimeException("Clock moved backwards. Refusing to generate id");
|
||||
}
|
||||
|
||||
if (timestamp == lastTimestamp) {
|
||||
sequence = (sequence + 1) & MAX_SEQUENCE;
|
||||
if (sequence == 0) {
|
||||
timestamp = tilNextMillis(lastTimestamp);
|
||||
}
|
||||
} else {
|
||||
sequence = 0L;
|
||||
}
|
||||
|
||||
lastTimestamp = timestamp;
|
||||
|
||||
return ((timestamp - START_TIMESTAMP) << TIMESTAMP_SHIFT) |
|
||||
(machineId << MACHINE_ID_SHIFT) |
|
||||
sequence;
|
||||
}
|
||||
|
||||
private long tilNextMillis(long lastTimestamp) {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
while (timestamp <= lastTimestamp) {
|
||||
timestamp = System.currentTimeMillis();
|
||||
}
|
||||
return timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
System.out.println(getUUID());
|
||||
logger.debug("test");
|
||||
|
||||
// 测试雪花算法
|
||||
for (int i = 0; i < 10; i++) {
|
||||
System.out.println("Snowflake ID: " + getSnowflakeId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
package com.kexue.skills.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 账户扣费配置属性
|
||||
*
|
||||
* @author 系统生成
|
||||
* @since 2026-04-13
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
public class AccountDeductionProperties {
|
||||
|
||||
/**
|
||||
* 扣费系数,默认2倍
|
||||
* 例如:系数为2时,实际消耗1积分,扣除2积分
|
||||
*/
|
||||
@Value("${account.deduction.coefficient:2}")
|
||||
private BigDecimal coefficient;
|
||||
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
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,7 +2,6 @@ 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;
|
||||
|
|
@ -11,8 +10,6 @@ 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;
|
||||
|
|
@ -35,7 +32,6 @@ import java.util.List;
|
|||
* @author 王志维
|
||||
* @since 2025-02-21 23:01:48
|
||||
*/
|
||||
@Log(module = "账户管理")
|
||||
@Tag(name = "账户管理 api")
|
||||
@RestController
|
||||
@RequestMapping("/api/account")
|
||||
|
|
@ -167,45 +163,6 @@ public class AccountController {
|
|||
Long userId = Long.parseLong(StpUtil.getLoginId().toString());
|
||||
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,73 +0,0 @@
|
|||
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;
|
||||
import com.kexue.skills.entity.dto.AccountReleaseDto;
|
||||
import com.kexue.skills.service.AccountFrozenService;
|
||||
import com.kexue.skills.common.Result;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 账户冻结单控制器
|
||||
*
|
||||
* @author 系统生成
|
||||
* @since 2026-04-11
|
||||
*/
|
||||
@Log(module = "账户冻结")
|
||||
@RestController
|
||||
@RequestMapping("/api/accountFrozen")
|
||||
@CrossOrigin(origins = "*")
|
||||
@Tag(name = "账户冻结单", description = "账户冻结单管理接口")
|
||||
public class AccountFrozenController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AccountFrozenController.class);
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
@Resource
|
||||
private AccountFrozenService accountFrozenService;
|
||||
|
||||
/**
|
||||
* 创建冻结单
|
||||
* @param accountFrozenDto 冻结单DTO
|
||||
* @return 冻结单信息
|
||||
*/
|
||||
@PostMapping("/frozen")
|
||||
@Operation(summary = "创建冻结单", description = "创建账户冻结单")
|
||||
public CommonResult<AccountFrozen> createFrozen(@RequestBody AccountFrozenDto accountFrozenDto) {
|
||||
try {
|
||||
logger.info("创建冻结单入参: {}", objectMapper.writeValueAsString(accountFrozenDto));
|
||||
} catch (IOException e) {
|
||||
logger.error("创建冻结单入参序列化失败", e);
|
||||
}
|
||||
AccountFrozen accountFrozen = accountFrozenService.createFrozen(accountFrozenDto);
|
||||
return CommonResult.success(accountFrozen);
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放冻结单
|
||||
* @param accountReleaseDto 冻结单释放DTO
|
||||
* @return 冻结单信息
|
||||
*/
|
||||
@PostMapping("/release")
|
||||
@Operation(summary = "释放冻结单", description = "释放账户冻结单")
|
||||
public CommonResult<AccountFrozen> releaseFrozen(@RequestBody AccountReleaseDto accountReleaseDto) {
|
||||
try {
|
||||
logger.info("释放冻结单入参: {}", objectMapper.writeValueAsString(accountReleaseDto));
|
||||
} catch (IOException e) {
|
||||
logger.error("释放冻结单入参序列化失败", e);
|
||||
}
|
||||
AccountFrozen accountFrozen = accountFrozenService.releaseFrozen(accountReleaseDto);
|
||||
return CommonResult.success(accountFrozen);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
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;
|
||||
|
|
@ -334,45 +333,4 @@ public class CmsContentController {
|
|||
return CommonResult.failed("导入失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从ZIP文件批量导入Excel数据到CmsContent
|
||||
*
|
||||
* @param file ZIP文件
|
||||
* @param createBy 创建人
|
||||
* @return 导入结果
|
||||
*/
|
||||
@PostMapping("/importFromZip")
|
||||
@Operation(summary = "从ZIP文件批量导入Excel数据", description = "上传ZIP文件,批量导入其中的所有Excel文件到CmsContent")
|
||||
@RequireAuth
|
||||
public CommonResult<Integer> importFromZip(@RequestParam("file") MultipartFile file, @RequestParam("createBy") String createBy) {
|
||||
try {
|
||||
byte[] zipFileBytes = file.getBytes();
|
||||
int successCount = cmsContentService.importFromZip(zipFileBytes, createBy);
|
||||
return CommonResult.success(successCount);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
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,6 +1,5 @@
|
|||
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;
|
||||
|
|
@ -23,7 +22,6 @@ import static cn.dev33.satoken.SaManager.log;
|
|||
* @author 王志维
|
||||
* @since 2024-04-13 01:25:22
|
||||
*/
|
||||
@Log(module = "登录认证")
|
||||
@RestController
|
||||
@RequestMapping("api/login")
|
||||
@CrossOrigin(origins = "*")
|
||||
|
|
|
|||
|
|
@ -66,11 +66,11 @@ public class ModelPriceController {
|
|||
* 通过模型名称查询数据
|
||||
*
|
||||
* @param modelName 模型名称
|
||||
* @return 实例对象列表
|
||||
* @return 实例对象
|
||||
*/
|
||||
@Operation(summary = "通过模型名称查询", description = "通过模型名称查询大模型Token价格表")
|
||||
@GetMapping("/queryByModelName/{modelName}")
|
||||
public CommonResult<List<ModelPrice>> queryByModelName(@PathVariable("modelName") String modelName) {
|
||||
public CommonResult<ModelPrice> queryByModelName(@PathVariable("modelName") String modelName) {
|
||||
return CommonResult.success(this.modelPriceService.queryByModelName(modelName));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,121 +0,0 @@
|
|||
package com.kexue.skills.controller;
|
||||
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.kexue.skills.annotation.RequireAuth;
|
||||
import com.kexue.skills.common.CommonResult;
|
||||
import com.kexue.skills.entity.PackageConfig;
|
||||
import com.kexue.skills.entity.base.IdDto;
|
||||
import com.kexue.skills.entity.dto.PackageConfigDto;
|
||||
import com.kexue.skills.service.PackageConfigService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* (PackageConfig)表控制层
|
||||
*
|
||||
* @author 系统生成
|
||||
* @since 2026-04-11
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("api/packageConfig")
|
||||
@Tag(name = "套餐配置管理 Api")
|
||||
@CrossOrigin(origins = "*")
|
||||
public class PackageConfigController {
|
||||
/**
|
||||
* 服务对象
|
||||
*/
|
||||
@Resource
|
||||
private PackageConfigService packageConfigService;
|
||||
|
||||
/**
|
||||
* 分页查询
|
||||
*
|
||||
* @param queryDto 筛选条件
|
||||
* @return 查询结果
|
||||
*/
|
||||
@PostMapping("/getPageList")
|
||||
@Operation(summary = "查询分页列表", description = "查询分页列表")
|
||||
public CommonResult<PageInfo<PackageConfig>> getPageList(@RequestBody PackageConfigDto queryDto) {
|
||||
return CommonResult.success(packageConfigService.getPageList(queryDto));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询列表
|
||||
*
|
||||
* @param queryDto 筛选条件
|
||||
* @return 查询结果
|
||||
*/
|
||||
@PostMapping("/getList")
|
||||
@Operation(summary = "查询列表", description = "查询列表")
|
||||
public CommonResult<PageInfo<PackageConfig>> getList(@RequestBody PackageConfigDto queryDto) {
|
||||
return CommonResult.success(new PageInfo<>(packageConfigService.getList(queryDto)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过主键查询单条数据
|
||||
*
|
||||
* @param id 主键
|
||||
* @return 单条数据
|
||||
*/
|
||||
@PostMapping("queryById/{id}")
|
||||
@Operation(summary = "通过ID查询套餐", description = "通过ID查询套餐")
|
||||
public CommonResult<PackageConfig> queryById(@PathVariable("id") Long id) {
|
||||
return CommonResult.success(packageConfigService.queryById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增数据
|
||||
*
|
||||
* @param packageConfig 实体
|
||||
* @return 新增结果
|
||||
*/
|
||||
@PostMapping("/insert")
|
||||
@Operation(summary = "新增套餐", description = "新增套餐")
|
||||
@RequireAuth
|
||||
public CommonResult<PackageConfig> insert(@RequestBody PackageConfig packageConfig) {
|
||||
return CommonResult.success(packageConfigService.insert(packageConfig));
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑数据
|
||||
*
|
||||
* @param packageConfig 实体
|
||||
* @return 编辑结果
|
||||
*/
|
||||
@PostMapping("/update")
|
||||
@Operation(summary = "更新套餐", description = "更新套餐")
|
||||
@RequireAuth
|
||||
public CommonResult<PackageConfig> update(@RequestBody PackageConfig packageConfig) {
|
||||
return CommonResult.success(packageConfigService.update(packageConfig));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过主键逻辑删除
|
||||
*
|
||||
* @param idDto 主键
|
||||
* @return 删除数据
|
||||
*/
|
||||
@PostMapping("/logicDeleteById")
|
||||
@Operation(summary = "逻辑删除套餐", description = "逻辑删除套餐")
|
||||
@RequireAuth
|
||||
public CommonResult<Boolean> logicDeleteById(@RequestBody IdDto idDto) {
|
||||
return CommonResult.success(packageConfigService.logicDeleteById(idDto.getId(), "admin") > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数据
|
||||
*
|
||||
* @param id 主键
|
||||
* @return 删除数据
|
||||
*/
|
||||
@PostMapping("deleteById/{id}")
|
||||
@Operation(summary = "物理删除套餐", description = "物理删除套餐")
|
||||
@RequireAuth
|
||||
public CommonResult<Boolean> deleteById(@PathVariable("id") Long id) {
|
||||
return CommonResult.success(packageConfigService.deleteById(id) > 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -16,11 +16,6 @@ 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)表控制层
|
||||
|
|
@ -227,44 +222,4 @@ 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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,80 +0,0 @@
|
|||
package com.kexue.skills.entity;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
import java.io.Serializable;
|
||||
import com.kexue.skills.entity.base.BaseEntity;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* (AccountFrozen)实体类
|
||||
* 账户冻结单
|
||||
*
|
||||
* @author 系统生成
|
||||
* @since 2026-04-11
|
||||
*/
|
||||
@Data
|
||||
public class AccountFrozen extends BaseEntity implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description ="冻结单ID,字符型")
|
||||
private String frozenId;
|
||||
|
||||
@Schema(description ="流水ID")
|
||||
private String accountTransactionId;
|
||||
|
||||
@Schema(description ="用户ID,关联冻结单")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description ="调用ID,关联冻结单")
|
||||
private String callId;
|
||||
|
||||
@Schema(description ="会话ID")
|
||||
private String sessionId;
|
||||
|
||||
@Schema(description ="模型名称")
|
||||
private String modelName;
|
||||
|
||||
@Schema(description ="对应回答的问题或需求")
|
||||
private String question;
|
||||
|
||||
@Schema(description ="冻结金额/张数/次数/分钟")
|
||||
private BigDecimal frozenAmount;
|
||||
|
||||
@Schema(description ="冻结类型:1.token 2.RMB(元) 99其他")
|
||||
private Integer frozenType;
|
||||
|
||||
@Schema(description ="最终扣减,0=释放")
|
||||
private BigDecimal finalAmount;
|
||||
|
||||
@Schema(description ="输入tokens")
|
||||
private Long usageInputTokens;
|
||||
|
||||
@Schema(description ="输出tokens")
|
||||
private Long usageOutputTokens;
|
||||
|
||||
@Schema(description ="总tokens")
|
||||
private Long usageTotalTokens;
|
||||
|
||||
@Schema(description ="终结原因:success/cancel/timeout/error/system_recovery")
|
||||
private String finalizeReason;
|
||||
|
||||
@Schema(description ="状态:RESERVED 已预留,FINALIZED 已终结")
|
||||
private String status;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@Schema(description ="过期时间")
|
||||
private Date expireAt;
|
||||
|
||||
@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;
|
||||
|
||||
}
|
||||
|
|
@ -56,9 +56,6 @@ public class AccountTransaction extends BaseEntity implements Serializable {
|
|||
@Schema(description ="业务类型")
|
||||
private String businessType;
|
||||
|
||||
@Schema(description ="调用ID,关联冻结单")
|
||||
private String callId;
|
||||
|
||||
@Schema(description ="交易备注")
|
||||
private String remark;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,120 +0,0 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -44,18 +44,9 @@ public class ModelPrice extends BaseEntity implements Serializable {
|
|||
@Schema(description ="价格单位")
|
||||
private String unit;
|
||||
|
||||
@Schema(description ="备注/版本信息")
|
||||
@Schema(description ="备注")
|
||||
private String remark;
|
||||
|
||||
@Schema(description ="计费区间下限(不包含)")
|
||||
private Long minTokens;
|
||||
|
||||
@Schema(description ="计费区间上限(包含,-1代表无穷大)")
|
||||
private Long maxTokens;
|
||||
|
||||
@Schema(description ="输出模式:standard=非思考模式, thinking=思考模式")
|
||||
private String outputMode;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@Schema(description ="创建时间")
|
||||
private Date createdTime;
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
package com.kexue.skills.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import com.kexue.skills.entity.base.BaseEntity;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* (PackageConfig)实体类
|
||||
* 套餐配置表
|
||||
*
|
||||
* @author 系统生成
|
||||
* @since 2026-04-11
|
||||
*/
|
||||
@Data
|
||||
public class PackageConfig extends BaseEntity implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description ="套餐ID")
|
||||
private Long id;
|
||||
|
||||
@Schema(description ="套餐名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description ="价格")
|
||||
private BigDecimal price;
|
||||
|
||||
@Schema(description ="基础额度")
|
||||
private BigDecimal baseAmount;
|
||||
|
||||
@Schema(description ="赠送额度")
|
||||
private BigDecimal giftAmount;
|
||||
|
||||
@Schema(description ="创建时间")
|
||||
private Date createTime;
|
||||
|
||||
@Schema(description ="更新时间")
|
||||
private Date updateTime;
|
||||
|
||||
}
|
||||
|
|
@ -56,7 +56,7 @@ public class PaymentOrder extends BaseEntity implements Serializable {
|
|||
@Schema(description ="商品描述")
|
||||
private String productDesc;
|
||||
|
||||
@Schema(description ="关联业务ID,比如packageId(套餐ID)")
|
||||
@Schema(description ="关联业务ID")
|
||||
private Long businessId;
|
||||
|
||||
@Schema(description ="业务类型:recharge,purchase_content")
|
||||
|
|
|
|||
|
|
@ -22,59 +22,29 @@ public class SysLog extends BaseEntity implements Serializable {
|
|||
@Schema(description ="主键ID")
|
||||
private Long logId;
|
||||
|
||||
@Schema(description ="链路ID")
|
||||
private String traceId;
|
||||
@Schema(description ="用户ID")
|
||||
private String userId;
|
||||
|
||||
@Schema(description ="日志描述")
|
||||
private String description;
|
||||
@Schema(description ="用户名称")
|
||||
private String userName;
|
||||
|
||||
@Schema(description ="所属模块")
|
||||
private String module;
|
||||
@Schema(description ="日志类型")
|
||||
private String logType;
|
||||
|
||||
@Schema(description ="请求URL")
|
||||
private String requestUrl;
|
||||
@Schema(description ="日志类容")
|
||||
private String logContent;
|
||||
|
||||
@Schema(description ="请求方式")
|
||||
private String requestMethod;
|
||||
@Schema(description ="服务端IP")
|
||||
private String serverIp;
|
||||
|
||||
@Schema(description ="请求头")
|
||||
private String requestHeaders;
|
||||
@Schema(description ="客户端IP")
|
||||
private String clientIp;
|
||||
|
||||
@Schema(description ="请求体")
|
||||
private String requestBody;
|
||||
@Schema(description ="yyyyMMddHHmmss")
|
||||
private String logTime;
|
||||
|
||||
@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;
|
||||
@Schema(description ="备注")
|
||||
private String note;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@Schema(description ="创建时间")
|
||||
|
|
|
|||
|
|
@ -66,16 +66,4 @@ public class SysUser extends BaseEntity implements Serializable {
|
|||
@Schema(description ="会话ID")
|
||||
private String sessionId;
|
||||
|
||||
@Schema(description ="邀请码(用于邀请别人)")
|
||||
private String inviteCode;
|
||||
|
||||
@Schema(description ="被邀请码(邀请我注册的邀请码)")
|
||||
private String invitedCode;
|
||||
|
||||
@Schema(description ="邀请人用户ID(邀请我注册的用户ID)")
|
||||
private Long invitedBy;
|
||||
|
||||
@Schema(description ="用户头像")
|
||||
private String userIcon = "defaultUserIcon.png";
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,45 +0,0 @@
|
|||
package com.kexue.skills.entity.dto;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 账户冻结单DTO
|
||||
*
|
||||
* @author 系统生成
|
||||
* @since 2026-04-11
|
||||
*/
|
||||
@Data
|
||||
public class AccountFrozenDto {
|
||||
|
||||
@Schema(description ="会话ID")
|
||||
private String sessionId;
|
||||
|
||||
@Schema(description ="调用ID,关联冻结单")
|
||||
private String callId;
|
||||
|
||||
@Schema(description ="模型名称")
|
||||
private String modelName;
|
||||
|
||||
@Schema(description ="对应回答的问题或需求")
|
||||
private String question;
|
||||
|
||||
@Schema(description ="冻结金额/张数/次数/分钟")
|
||||
private BigDecimal frozenAmount;
|
||||
|
||||
@Schema(description ="冻结类型:1.token 2.RMB(元) 99其他")
|
||||
private Integer frozenType;
|
||||
|
||||
@Schema(description ="预估输入tokens")
|
||||
private Long estimatedInputTokens;
|
||||
|
||||
@Schema(description ="预估输出tokens")
|
||||
private Long estimatedOutputTokens;
|
||||
|
||||
@Schema(description ="过期时间")
|
||||
private Date expireAt;
|
||||
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
package com.kexue.skills.entity.dto;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 账户冻结单释放DTO
|
||||
*
|
||||
* @author 系统生成
|
||||
* @since 2026-04-11
|
||||
*/
|
||||
@Data
|
||||
public class AccountReleaseDto {
|
||||
|
||||
@Schema(description ="冻结单ID,字符型")
|
||||
private String frozenId;
|
||||
|
||||
@Schema(description ="最终扣减,0=释放")
|
||||
private BigDecimal finalAmount;
|
||||
|
||||
@Schema(description ="输入tokens")
|
||||
private Long usageInputTokens;
|
||||
|
||||
@Schema(description ="输出tokens")
|
||||
private Long usageOutputTokens;
|
||||
|
||||
@Schema(description ="总tokens")
|
||||
private Long usageTotalTokens;
|
||||
|
||||
@Schema(description ="终结原因:success/cancel/timeout/error/system_recovery")
|
||||
private String finalizeReason;
|
||||
|
||||
}
|
||||
|
|
@ -3,8 +3,6 @@ package com.kexue.skills.entity.dto;
|
|||
import com.kexue.skills.entity.base.BaseQueryDto;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* (AccountTransaction)查询DTO类
|
||||
*
|
||||
|
|
@ -34,8 +32,4 @@ public class AccountTransactionDto extends BaseQueryDto {
|
|||
|
||||
private Integer deleteFlag;
|
||||
|
||||
private Date createTimeStart;
|
||||
|
||||
private Date createTimeEnd;
|
||||
|
||||
}
|
||||
|
|
@ -55,19 +55,4 @@ public class CmsContentDto extends BaseQueryDto {
|
|||
*/
|
||||
private String keyword;
|
||||
|
||||
/**
|
||||
* 来源
|
||||
*/
|
||||
private String origin;
|
||||
|
||||
/**
|
||||
* 标签
|
||||
*/
|
||||
private String tags;
|
||||
|
||||
/**
|
||||
* 图标
|
||||
*/
|
||||
private String icon;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,101 +0,0 @@
|
|||
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;
|
||||
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
package com.kexue.skills.entity.dto;
|
||||
|
||||
import com.kexue.skills.entity.base.BaseQueryDto;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* (PackageConfig)查询DTO类
|
||||
*
|
||||
* @author 系统生成
|
||||
* @since 2026-04-11
|
||||
*/
|
||||
@Data
|
||||
public class PackageConfigDto extends BaseQueryDto {
|
||||
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
private BigDecimal price;
|
||||
|
||||
private BigDecimal baseAmount;
|
||||
|
||||
private BigDecimal giftAmount;
|
||||
|
||||
}
|
||||
|
|
@ -2,9 +2,6 @@ package com.kexue.skills.entity.dto;
|
|||
|
||||
import com.kexue.skills.entity.base.BaseQueryDto;
|
||||
import lombok.Data;
|
||||
import org.checkerframework.checker.formatter.qual.Format;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* (PaymentOrder)查询DTO类
|
||||
|
|
@ -33,10 +30,4 @@ public class PaymentOrderDto extends BaseQueryDto {
|
|||
|
||||
private Integer deleteFlag;
|
||||
|
||||
private Long packageId;
|
||||
|
||||
private Date createTimeStart;
|
||||
|
||||
private Date createTimeEnd;
|
||||
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ package com.kexue.skills.entity.dto;
|
|||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import com.kexue.skills.entity.base.BaseQueryDto;
|
||||
import lombok.Data;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
|
@ -24,24 +23,28 @@ public class SysLogDto extends BaseQueryDto implements Serializable {
|
|||
@Schema(description ="主键ID")
|
||||
private Long logId;
|
||||
|
||||
@Schema(description ="模块")
|
||||
private String module;
|
||||
@Schema(description ="用户ID")
|
||||
private String userId;
|
||||
|
||||
@Schema(description ="描述")
|
||||
private String description;
|
||||
@Schema(description ="用户名称")
|
||||
private String userName;
|
||||
|
||||
@Schema(description ="IP地址")
|
||||
private String ip;
|
||||
@Schema(description ="日志类型")
|
||||
private String logType;
|
||||
|
||||
@Schema(description ="状态")
|
||||
private Integer status;
|
||||
@Schema(description ="日志类容")
|
||||
private String logContent;
|
||||
|
||||
@Schema(description ="开始时间")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date startTime;
|
||||
@Schema(description ="服务端IP")
|
||||
private String serverIp;
|
||||
|
||||
@Schema(description ="结束时间")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date endTime;
|
||||
@Schema(description ="客户端IP")
|
||||
private String clientIp;
|
||||
|
||||
@Schema(description ="yyyyMMddHHmmss")
|
||||
private String logTime;
|
||||
|
||||
@Schema(description ="备注")
|
||||
private String note;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,4 @@ public class TokenConsumptionDto {
|
|||
@Schema(description ="备注")
|
||||
private String remark;
|
||||
|
||||
@Schema(description ="输出模式:standard=非思考模式, thinking=思考模式")
|
||||
private String outputMode;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,4 @@ public class PhoneLoginDto implements Serializable {
|
|||
|
||||
@Schema(description ="验证码")
|
||||
private String code;
|
||||
|
||||
@Schema(description ="邀请码")
|
||||
private String inviteCode;
|
||||
}
|
||||
|
|
@ -15,9 +15,9 @@ public class GlobalExceptionHandler {
|
|||
@ExceptionHandler(BizException.class)
|
||||
public CommonResult<String> handleBizException(BizException e) {
|
||||
if (e == null) {
|
||||
return CommonResult.success("未知错误");
|
||||
return CommonResult.failed("未知错误");
|
||||
}
|
||||
return CommonResult.success(e.getErrorCode(), e.getMessage());
|
||||
return CommonResult.failed(e.getMessage());
|
||||
}
|
||||
|
||||
// 其他异常处理...
|
||||
|
|
|
|||
|
|
@ -1,52 +0,0 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,412 +0,0 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
package com.kexue.skills.mapper;
|
||||
|
||||
import com.kexue.skills.entity.AccountFrozen;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 账户冻结单Mapper
|
||||
*
|
||||
* @author 系统生成
|
||||
* @since 2026-04-11
|
||||
*/
|
||||
@Mapper
|
||||
public interface AccountFrozenMapper {
|
||||
|
||||
/**
|
||||
* 根据ID查询冻结单
|
||||
* @param frozenId 冻结单ID
|
||||
* @return 冻结单信息
|
||||
*/
|
||||
AccountFrozen selectByPrimaryKey(String frozenId);
|
||||
|
||||
/**
|
||||
* 插入冻结单
|
||||
* @param accountFrozen 冻结单信息
|
||||
* @return 影响行数
|
||||
*/
|
||||
int insert(AccountFrozen accountFrozen);
|
||||
|
||||
/**
|
||||
* 更新冻结单
|
||||
* @param accountFrozen 冻结单信息
|
||||
* @return 影响行数
|
||||
*/
|
||||
int updateByPrimaryKey(AccountFrozen accountFrozen);
|
||||
|
||||
/**
|
||||
* 根据会话ID查询冻结单
|
||||
* @param sessionId 会话ID
|
||||
* @return 冻结单信息
|
||||
*/
|
||||
AccountFrozen selectBySessionId(String sessionId);
|
||||
|
||||
/**
|
||||
* 根据用户ID和状态查询冻结单
|
||||
* @param userId 用户ID
|
||||
* @param status 状态
|
||||
* @return 冻结单列表
|
||||
*/
|
||||
List<AccountFrozen> selectByUserIdAndStatus(@Param("userId") Long userId, @Param("status") String status);
|
||||
|
||||
/**
|
||||
* 查询过期的冻结单
|
||||
* @param currentTime 当前时间
|
||||
* @return 冻结单列表
|
||||
*/
|
||||
List<AccountFrozen> selectExpiredFrozen(Date currentTime);
|
||||
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ 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;
|
||||
|
||||
|
|
@ -79,36 +78,4 @@ 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);
|
||||
}
|
||||
|
|
@ -3,7 +3,6 @@ package com.kexue.skills.mapper;
|
|||
import com.kexue.skills.entity.ModelPrice;
|
||||
import com.kexue.skills.entity.dto.ModelPriceDto;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -44,19 +43,9 @@ public interface ModelPriceMapper {
|
|||
* 通过模型名称查询数据
|
||||
*
|
||||
* @param modelName 模型名称
|
||||
* @return 实例对象列表
|
||||
*/
|
||||
List<ModelPrice> queryByModelName(String modelName);
|
||||
|
||||
/**
|
||||
* 根据模型名称、输出模式和token数量查询价格规则
|
||||
*
|
||||
* @param modelName 模型名称
|
||||
* @param outputMode 输出模式
|
||||
* @param tokens token数量
|
||||
* @return 实例对象
|
||||
*/
|
||||
ModelPrice queryByModelNameAndOutputModeAndTokens(@Param("modelName") String modelName, @Param("outputMode") String outputMode, @Param("tokens") Long tokens);
|
||||
ModelPrice queryByModelName(String modelName);
|
||||
|
||||
/**
|
||||
* 新增数据
|
||||
|
|
|
|||
|
|
@ -1,66 +0,0 @@
|
|||
package com.kexue.skills.mapper;
|
||||
|
||||
import com.kexue.skills.entity.PackageConfig;
|
||||
import com.kexue.skills.entity.dto.PackageConfigDto;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* (PackageConfig)表数据库访问层
|
||||
*
|
||||
* @author 系统生成
|
||||
* @since 2026-04-11
|
||||
*/
|
||||
@Mapper
|
||||
public interface PackageConfigMapper {
|
||||
/**
|
||||
* 分页查询
|
||||
*
|
||||
* @param queryDto 筛选条件
|
||||
* @return 查询结果
|
||||
*/
|
||||
List<PackageConfig> getPageList(PackageConfigDto queryDto);
|
||||
|
||||
/**
|
||||
* 查询列表
|
||||
*
|
||||
* @param queryDto 筛选条件
|
||||
* @return 查询结果
|
||||
*/
|
||||
List<PackageConfig> getList(PackageConfigDto queryDto);
|
||||
|
||||
/**
|
||||
* 通过主键查询单条数据
|
||||
*
|
||||
* @param id 主键
|
||||
* @return 实例对象
|
||||
*/
|
||||
PackageConfig queryById(Long id);
|
||||
|
||||
/**
|
||||
* 新增数据
|
||||
*
|
||||
* @param packageConfig 实例对象
|
||||
* @return 影响行数
|
||||
*/
|
||||
int insert(PackageConfig packageConfig);
|
||||
|
||||
/**
|
||||
* 更新数据
|
||||
*
|
||||
* @param packageConfig 实例对象
|
||||
* @return 影响行数
|
||||
*/
|
||||
int update(PackageConfig packageConfig);
|
||||
|
||||
/**
|
||||
* 通过主键删除
|
||||
*
|
||||
* @param id 主键
|
||||
* @return 影响行数
|
||||
*/
|
||||
int deleteById(Long id);
|
||||
|
||||
}
|
||||
|
|
@ -88,6 +88,4 @@ public interface SysUserMapper {
|
|||
|
||||
SysUser getBySessionId(String sessionId);
|
||||
|
||||
SysUser getByInviteCode(String inviteCode);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
package com.kexue.skills.service;
|
||||
|
||||
import com.kexue.skills.entity.AccountFrozen;
|
||||
import com.kexue.skills.entity.dto.AccountFrozenDto;
|
||||
import com.kexue.skills.entity.dto.AccountReleaseDto;
|
||||
|
||||
/**
|
||||
* 账户冻结单服务
|
||||
*
|
||||
* @author 系统生成
|
||||
* @since 2026-04-11
|
||||
*/
|
||||
public interface AccountFrozenService {
|
||||
|
||||
/**
|
||||
* 创建冻结单
|
||||
* @param accountFrozenDto 冻结单DTO
|
||||
* @return 冻结单信息
|
||||
*/
|
||||
AccountFrozen createFrozen(AccountFrozenDto accountFrozenDto);
|
||||
|
||||
/**
|
||||
* 释放冻结单
|
||||
* @param accountReleaseDto 冻结单释放DTO
|
||||
* @return 冻结单信息
|
||||
*/
|
||||
AccountFrozen releaseFrozen(AccountReleaseDto accountReleaseDto);
|
||||
|
||||
/**
|
||||
* 根据ID查询冻结单
|
||||
* @param frozenId 冻结单ID
|
||||
* @return 冻结单信息
|
||||
*/
|
||||
AccountFrozen getByFrozenId(String frozenId);
|
||||
|
||||
/**
|
||||
* 根据会话ID查询冻结单
|
||||
* @param sessionId 会话ID
|
||||
* @return 冻结单信息
|
||||
*/
|
||||
AccountFrozen getBySessionId(String sessionId);
|
||||
|
||||
}
|
||||
|
|
@ -2,10 +2,7 @@ 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;
|
||||
|
|
@ -163,28 +160,4 @@ 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);
|
||||
}
|
||||
|
|
@ -201,22 +201,4 @@ public interface CmsContentService extends BaseService {
|
|||
* @return 导入结果
|
||||
*/
|
||||
int importFromPath(ImportPathDto importPathDto, String createBy);
|
||||
|
||||
/**
|
||||
* 从ZIP文件批量导入Excel数据到CmsContent
|
||||
*
|
||||
* @param zipFileBytes ZIP文件字节数组
|
||||
* @param createBy 创建人
|
||||
* @return 成功导入的记录数
|
||||
*/
|
||||
int importFromZip(byte[] zipFileBytes, String createBy);
|
||||
|
||||
/**
|
||||
* 从指定目录读取Excel数据并更新CmsContent
|
||||
*
|
||||
* @param importPathDto 导入路径请求参数
|
||||
* @param updateBy 更新人
|
||||
* @return 成功更新的记录数
|
||||
*/
|
||||
int updateFromPath(ImportPathDto importPathDto, String updateBy);
|
||||
}
|
||||
|
|
@ -42,19 +42,9 @@ public interface ModelPriceService extends BaseService {
|
|||
* 通过模型名称查询数据
|
||||
*
|
||||
* @param modelName 模型名称
|
||||
* @return 实例对象列表
|
||||
*/
|
||||
List<ModelPrice> queryByModelName(String modelName);
|
||||
|
||||
/**
|
||||
* 根据模型名称、输出模式和token数量查询价格规则
|
||||
*
|
||||
* @param modelName 模型名称
|
||||
* @param outputMode 输出模式
|
||||
* @param tokens token数量
|
||||
* @return 实例对象
|
||||
*/
|
||||
ModelPrice queryByModelNameAndOutputModeAndTokens(String modelName, String outputMode, Long tokens);
|
||||
ModelPrice queryByModelName(String modelName);
|
||||
|
||||
/**
|
||||
* 新增数据
|
||||
|
|
|
|||
|
|
@ -1,73 +0,0 @@
|
|||
package com.kexue.skills.service;
|
||||
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.kexue.skills.entity.PackageConfig;
|
||||
import com.kexue.skills.entity.dto.PackageConfigDto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* (PackageConfig)表服务接口
|
||||
*
|
||||
* @author 系统生成
|
||||
* @since 2026-04-11
|
||||
*/
|
||||
public interface PackageConfigService extends BaseService {
|
||||
/**
|
||||
* 分页查询
|
||||
*
|
||||
* @param queryDto 筛选条件
|
||||
* @return 查询结果
|
||||
*/
|
||||
PageInfo<PackageConfig> getPageList(PackageConfigDto queryDto);
|
||||
|
||||
/**
|
||||
* 查询列表
|
||||
*
|
||||
* @param queryDto 筛选条件
|
||||
* @return 查询结果
|
||||
*/
|
||||
List<PackageConfig> getList(PackageConfigDto queryDto);
|
||||
|
||||
/**
|
||||
* 通过主键查询单条数据
|
||||
*
|
||||
* @param id 主键
|
||||
* @return 实例对象
|
||||
*/
|
||||
PackageConfig queryById(Long id);
|
||||
|
||||
/**
|
||||
* 新增数据
|
||||
*
|
||||
* @param packageConfig 实例对象
|
||||
* @return 实例对象
|
||||
*/
|
||||
PackageConfig insert(PackageConfig packageConfig);
|
||||
|
||||
/**
|
||||
* 更新数据
|
||||
*
|
||||
* @param packageConfig 实例对象
|
||||
* @return 实例对象
|
||||
*/
|
||||
PackageConfig update(PackageConfig packageConfig);
|
||||
|
||||
/**
|
||||
* 通过主键逻辑删除
|
||||
*
|
||||
* @param id 主键
|
||||
* @param updateBy 更新人
|
||||
* @return 影响行数
|
||||
*/
|
||||
int logicDeleteById(Long id, String updateBy);
|
||||
|
||||
/**
|
||||
* 通过主键物理删除
|
||||
*
|
||||
* @param id 主键
|
||||
* @return 影响行数
|
||||
*/
|
||||
int deleteById(Long id);
|
||||
|
||||
}
|
||||
|
|
@ -5,8 +5,6 @@ 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;
|
||||
|
||||
/**
|
||||
|
|
@ -139,23 +137,4 @@ 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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,667 +0,0 @@
|
|||
package com.kexue.skills.service.impl;
|
||||
|
||||
import com.kexue.skills.entity.Account;
|
||||
import com.kexue.skills.entity.AccountFrozen;
|
||||
import com.kexue.skills.entity.SysUser;
|
||||
import com.kexue.skills.entity.dto.AccountFrozenDto;
|
||||
import com.kexue.skills.entity.dto.AccountReleaseDto;
|
||||
import com.kexue.skills.entity.dto.ModelPriceDto;
|
||||
import com.kexue.skills.exception.BizException;
|
||||
import com.kexue.skills.mapper.AccountFrozenMapper;
|
||||
import com.kexue.skills.mapper.AccountMapper;
|
||||
import com.kexue.skills.mapper.SysUserMapper;
|
||||
import com.kexue.skills.service.AccountFrozenService;
|
||||
import com.kexue.skills.service.ModelPriceService;
|
||||
import com.kexue.skills.entity.ModelPrice;
|
||||
import com.kexue.skills.entity.AccountTransaction;
|
||||
import com.kexue.skills.mapper.AccountTransactionMapper;
|
||||
import com.kexue.skills.common.util.IDUtils;
|
||||
import com.kexue.skills.common.ResultCode;
|
||||
import com.kexue.skills.config.AccountDeductionProperties;
|
||||
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.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 账户冻结单服务实现
|
||||
*
|
||||
* @author 系统生成
|
||||
* @since 2026-04-11
|
||||
*/
|
||||
@Service("accountFrozenService")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public class AccountFrozenServiceImpl implements AccountFrozenService {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AccountFrozenServiceImpl.class);
|
||||
|
||||
private static final Integer FROZEN_TYPE_TOKEN = 1;
|
||||
|
||||
private static final Integer FROZEN_TYPE_RMB = 2;
|
||||
|
||||
private static final BigDecimal YUAN_TO_POINTS_COEFFICIENT = BigDecimal.valueOf(100);
|
||||
|
||||
@Resource
|
||||
private AccountFrozenMapper accountFrozenMapper;
|
||||
|
||||
@Resource
|
||||
private AccountMapper accountMapper;
|
||||
|
||||
@Resource
|
||||
private SysUserMapper sysUserMapper;
|
||||
|
||||
@Resource
|
||||
private ModelPriceService modelPriceService;
|
||||
|
||||
@Resource
|
||||
private AccountTransactionMapper accountTransactionMapper;
|
||||
|
||||
@Resource
|
||||
private AccountDeductionProperties accountDeductionProperties;
|
||||
|
||||
/**
|
||||
* 创建冻结单
|
||||
*
|
||||
* @param accountFrozenDto 冻结单DTO
|
||||
* @return 冻结单信息
|
||||
*/
|
||||
@Override
|
||||
public AccountFrozen createFrozen(AccountFrozenDto accountFrozenDto) {
|
||||
// 1. 验证创建冻结单参数
|
||||
validateCreateFrozenParams(accountFrozenDto);
|
||||
|
||||
// 2. 通过sessionId获取用户信息
|
||||
SysUser sysUser = getUserBySessionId(accountFrozenDto.getSessionId());
|
||||
|
||||
// 3. 通过用户ID获取账户信息
|
||||
Account account = getAccountByUserId(sysUser.getUserId());
|
||||
|
||||
// 4. 根据冻结类型计算冻结金额
|
||||
// - 如果是token类型,根据预估tokens和模型价格计算
|
||||
// - 如果是RMB类型,将元转换为积分(1元=100积分)
|
||||
BigDecimal finalFrozenAmount = calculateFrozenAmount(accountFrozenDto);
|
||||
|
||||
// 5. 检查账户余额是否足够冻结
|
||||
// 计算可用余额(总余额 - 已冻结金额),确保足够本次冻结
|
||||
checkBalanceSufficient(account, finalFrozenAmount);
|
||||
|
||||
// 6. 更新账户冻结金额
|
||||
// 将本次冻结金额加到账户的已冻结金额中
|
||||
updateAccountFrozenAmount(account, finalFrozenAmount);
|
||||
|
||||
// 7. 执行创建冻结单
|
||||
// 构建冻结单对象并保存到数据库
|
||||
return doCreateFrozen(accountFrozenDto, sysUser.getUserId(), finalFrozenAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证创建冻结单参数
|
||||
*
|
||||
* @param accountFrozenDto 冻结单DTO
|
||||
*/
|
||||
private void validateCreateFrozenParams(AccountFrozenDto accountFrozenDto) {
|
||||
if (accountFrozenDto == null) {
|
||||
throw new BizException(ResultCode.PARAMETER_EMPTY.getCode(), ResultCode.PARAMETER_EMPTY.getMessage());
|
||||
}
|
||||
if (accountFrozenDto.getSessionId() == null) {
|
||||
throw new BizException(ResultCode.SESSION_ID_NOT_EXIST.getCode(), ResultCode.SESSION_ID_NOT_EXIST.getMessage());
|
||||
}
|
||||
if (accountFrozenDto.getFrozenType() == null) {
|
||||
throw new BizException(ResultCode.PARAMETER_EMPTY.getCode(), "冻结类型不能为空");
|
||||
}
|
||||
|
||||
// 根据冻结类型进行不同的参数校验
|
||||
if (FROZEN_TYPE_TOKEN.equals(accountFrozenDto.getFrozenType())) {
|
||||
// token类型需要校验:estimatedInputTokens、estimatedOutputTokens、modelName
|
||||
if (accountFrozenDto.getEstimatedInputTokens() == null) {
|
||||
throw new BizException(ResultCode.PARAMETER_EMPTY.getCode(), "预估输入tokens不能为空");
|
||||
}
|
||||
if (accountFrozenDto.getEstimatedOutputTokens() == null) {
|
||||
throw new BizException(ResultCode.PARAMETER_EMPTY.getCode(), "预估输出tokens不能为空");
|
||||
}
|
||||
if (accountFrozenDto.getModelName() == null) {
|
||||
throw new BizException(ResultCode.PARAMETER_EMPTY.getCode(), "模型名称不能为空");
|
||||
}
|
||||
} else if (FROZEN_TYPE_RMB.equals(accountFrozenDto.getFrozenType())) {
|
||||
// RMB类型需要校验:frozenAmount
|
||||
if (accountFrozenDto.getFrozenAmount() == null) {
|
||||
throw new BizException(ResultCode.PARAMETER_EMPTY.getCode(), "冻结金额不能为空");
|
||||
}
|
||||
}
|
||||
// 其他类型的校验可以在此添加
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过sessionId获取用户信息
|
||||
*
|
||||
* @param sessionId 会话ID
|
||||
* @return 用户信息
|
||||
*/
|
||||
private SysUser getUserBySessionId(String sessionId) {
|
||||
SysUser sysUser = sysUserMapper.getBySessionId(sessionId);
|
||||
if (sysUser == null) {
|
||||
throw new BizException(ResultCode.SESSION_ID_NOT_EXIST.getCode(), ResultCode.SESSION_ID_NOT_EXIST.getMessage());
|
||||
}
|
||||
return sysUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过用户ID获取账户信息
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 账户信息
|
||||
*/
|
||||
private Account getAccountByUserId(Long userId) {
|
||||
Account account = accountMapper.queryByUserId(userId);
|
||||
if (account == null) {
|
||||
throw new BizException(ResultCode.ACCOUNT_NOT_EXIST.getCode(), ResultCode.ACCOUNT_NOT_EXIST.getMessage());
|
||||
}
|
||||
return account;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据冻结类型计算冻结金额
|
||||
*
|
||||
* @param accountFrozenDto 冻结单DTO
|
||||
* @return 计算后的冻结金额(单位:积分)
|
||||
*/
|
||||
private BigDecimal calculateFrozenAmount(AccountFrozenDto accountFrozenDto) {
|
||||
BigDecimal frozenAmount = accountFrozenDto.getFrozenAmount();
|
||||
if (FROZEN_TYPE_TOKEN.equals(accountFrozenDto.getFrozenType())) {
|
||||
return calculateTokenFrozenAmount(accountFrozenDto);
|
||||
} else if (FROZEN_TYPE_RMB.equals(accountFrozenDto.getFrozenType())) {
|
||||
return calculateRmbFrozenAmount(frozenAmount);
|
||||
}
|
||||
return frozenAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算token类型的冻结金额
|
||||
* 根据预估输入输出tokens和模型价格计算冻结金额,并应用扣费系数
|
||||
*
|
||||
* @param accountFrozenDto 冻结单DTO
|
||||
* @return 冻结金额(单位:积分)
|
||||
*/
|
||||
private BigDecimal calculateTokenFrozenAmount(AccountFrozenDto accountFrozenDto) {
|
||||
if (accountFrozenDto.getEstimatedInputTokens() == null ||
|
||||
accountFrozenDto.getEstimatedOutputTokens() == null ||
|
||||
accountFrozenDto.getModelName() == null) {
|
||||
return accountFrozenDto.getFrozenAmount();
|
||||
}
|
||||
String modelName = normalizeModelName(accountFrozenDto.getModelName());
|
||||
List<ModelPrice> modelPriceList = getModelPriceList(modelName);
|
||||
if (modelPriceList.isEmpty()) {
|
||||
return accountFrozenDto.getFrozenAmount();
|
||||
}
|
||||
ModelPrice inputModelPrice = findInputModelPrice(modelPriceList, accountFrozenDto.getEstimatedInputTokens());
|
||||
ModelPrice outputModelPrice = findOutputModelPrice(modelPriceList, accountFrozenDto.getEstimatedOutputTokens());
|
||||
if (inputModelPrice == null || outputModelPrice == null) {
|
||||
return accountFrozenDto.getFrozenAmount();
|
||||
}
|
||||
long totalFee = calculateTotalTokenFee(accountFrozenDto, inputModelPrice, outputModelPrice);
|
||||
BigDecimal baseAmount = BigDecimal.valueOf(totalFee);
|
||||
return baseAmount.multiply(accountDeductionProperties.getCoefficient());
|
||||
}
|
||||
|
||||
/**
|
||||
* 标准化模型名称
|
||||
* 将前端传入的模型名称转换为数据库中对应的名称
|
||||
*
|
||||
* @param modelName 前端传入的模型名称
|
||||
* @return 标准化后的模型名称
|
||||
*/
|
||||
private String normalizeModelName(String modelName) {
|
||||
if ("Qwen 3.5 Plus".equals(modelName)) {
|
||||
return "qwen3.5-plus";
|
||||
}
|
||||
return modelName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模型价格列表
|
||||
*
|
||||
* @param modelName 模型名称
|
||||
* @return 模型价格列表
|
||||
*/
|
||||
private List<ModelPrice> getModelPriceList(String modelName) {
|
||||
ModelPriceDto modelPriceDto = new ModelPriceDto();
|
||||
modelPriceDto.setModelName(modelName);
|
||||
return modelPriceService.getList(modelPriceDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据输入tokens查找匹配的输入模型价格
|
||||
* 使用standard模式过滤价格规则
|
||||
*
|
||||
* @param modelPriceList 模型价格列表
|
||||
* @param estimatedInputTokens 预估输入tokens
|
||||
* @return 匹配的输入模型价格,若无匹配返回null
|
||||
*/
|
||||
private ModelPrice findInputModelPrice(List<ModelPrice> modelPriceList, Long estimatedInputTokens) {
|
||||
return modelPriceList.stream()
|
||||
.filter(mp -> "standard".equals(mp.getOutputMode()))
|
||||
.filter(mp -> mp.getMinTokens() < estimatedInputTokens)
|
||||
.filter(mp -> mp.getMaxTokens() == -1 || mp.getMaxTokens() >= estimatedInputTokens)
|
||||
.max((mp1, mp2) -> mp1.getMinTokens().compareTo(mp2.getMinTokens()))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据输出tokens查找匹配的输出模型价格
|
||||
* 使用thinking模式过滤价格规则
|
||||
*
|
||||
* @param modelPriceList 模型价格列表
|
||||
* @param estimatedOutputTokens 预估输出tokens
|
||||
* @return 匹配的输出模型价格,若无匹配返回null
|
||||
*/
|
||||
private ModelPrice findOutputModelPrice(List<ModelPrice> modelPriceList, Long estimatedOutputTokens) {
|
||||
return modelPriceList.stream()
|
||||
.filter(mp -> "thinking".equals(mp.getOutputMode()))
|
||||
.filter(mp -> mp.getMinTokens() < estimatedOutputTokens)
|
||||
.filter(mp -> mp.getMaxTokens() == -1 || mp.getMaxTokens() >= estimatedOutputTokens)
|
||||
.max((mp1, mp2) -> mp1.getMinTokens().compareTo(mp2.getMinTokens()))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算总的token费用
|
||||
*
|
||||
* @param accountFrozenDto 冻结单DTO
|
||||
* @param inputModelPrice 输入模型价格
|
||||
* @param outputModelPrice 输出模型价格
|
||||
* @return 总费用(单位:分)
|
||||
*/
|
||||
private long calculateTotalTokenFee(AccountFrozenDto accountFrozenDto, ModelPrice inputModelPrice, ModelPrice outputModelPrice) {
|
||||
long inputFee = calculateTokenFee(accountFrozenDto.getEstimatedInputTokens(), inputModelPrice.getInputPerCent());
|
||||
long outputFee = calculateTokenFee(accountFrozenDto.getEstimatedOutputTokens(), outputModelPrice.getOutputPerCent());
|
||||
return inputFee + outputFee;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算单个token费用
|
||||
* 采用向上取整的方式计算
|
||||
*
|
||||
* @param tokens token数量
|
||||
* @param perCent 每token对应的分数
|
||||
* @return 费用(单位:分)
|
||||
*/
|
||||
private long calculateTokenFee(long tokens, long perCent) {
|
||||
long fee = tokens / perCent;
|
||||
if (tokens % perCent > 0) {
|
||||
fee += 1;
|
||||
}
|
||||
return fee;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算RMB类型的冻结金额
|
||||
* 将元转换为积分(1元=100积分)
|
||||
*
|
||||
* @param frozenAmount 冻结金额(单位:元)
|
||||
* @return 冻结金额(单位:积分)
|
||||
*/
|
||||
private BigDecimal calculateRmbFrozenAmount(BigDecimal frozenAmount) {
|
||||
if (frozenAmount == null) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
return frozenAmount.multiply(YUAN_TO_POINTS_COEFFICIENT).multiply(accountDeductionProperties.getCoefficient());
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查账户余额是否足够冻结
|
||||
*
|
||||
* @param account 账户信息
|
||||
* @param frozenAmount 本次冻结金额
|
||||
*/
|
||||
private void checkBalanceSufficient(Account account, BigDecimal frozenAmount) {
|
||||
BigDecimal balance = account.getBalance() == null ? BigDecimal.ZERO : account.getBalance();
|
||||
BigDecimal frozen = account.getFrozenAmount() == null ? BigDecimal.ZERO : account.getFrozenAmount();
|
||||
BigDecimal availableBalance = balance.subtract(frozen);
|
||||
LOG.debug("检查余额是否足够 - userId: {}, 总余额: {}, 已冻结: {}, 可用余额: {}, 本次冻结: {}",
|
||||
account.getUserId(), balance, frozen, availableBalance, frozenAmount);
|
||||
if (availableBalance.compareTo(frozenAmount) < 0) {
|
||||
LOG.warn("余额不足 - userId: {}, 可用余额: {}, 本次冻结: {}", account.getUserId(), availableBalance, frozenAmount);
|
||||
throw new BizException(ResultCode.INSUFFICIENT_BALANCE.getCode(), ResultCode.INSUFFICIENT_BALANCE.getMessage());
|
||||
}
|
||||
LOG.debug("余额检查通过 - userId: {}, 可用余额: {}, 本次冻结: {}", account.getUserId(), availableBalance, frozenAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新账户冻结金额
|
||||
*
|
||||
* @param account 账户信息
|
||||
* @param frozenAmount 本次冻结金额
|
||||
*/
|
||||
private void updateAccountFrozenAmount(Account account, BigDecimal frozenAmount) {
|
||||
BigDecimal currentFrozen = account.getFrozenAmount() == null ? BigDecimal.ZERO : account.getFrozenAmount();
|
||||
account.setFrozenAmount(currentFrozen.add(frozenAmount));
|
||||
account.setUpdateTime(new Date());
|
||||
accountMapper.update(account);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行创建冻结单
|
||||
*
|
||||
* @param accountFrozenDto 冻结单DTO
|
||||
* @param userId 用户ID
|
||||
* @param finalFrozenAmount 最终冻结金额
|
||||
* @return 冻结单信息
|
||||
*/
|
||||
private AccountFrozen doCreateFrozen(AccountFrozenDto accountFrozenDto, Long userId, BigDecimal finalFrozenAmount) {
|
||||
AccountFrozen accountFrozen = new AccountFrozen();
|
||||
accountFrozen.setFrozenId(IDUtils.getSnowflakeIdStr());
|
||||
accountFrozen.setUserId(userId);
|
||||
accountFrozen.setSessionId(accountFrozenDto.getSessionId());
|
||||
accountFrozen.setCallId(accountFrozenDto.getCallId());
|
||||
accountFrozen.setModelName(accountFrozenDto.getModelName());
|
||||
if (Objects.nonNull(accountFrozenDto.getQuestion())) {
|
||||
accountFrozen.setQuestion(accountFrozenDto.getQuestion());
|
||||
}
|
||||
accountFrozen.setFrozenAmount(finalFrozenAmount);
|
||||
accountFrozen.setFrozenType(accountFrozenDto.getFrozenType());
|
||||
accountFrozen.setStatus("RESERVED");
|
||||
accountFrozen.setExpireAt(accountFrozenDto.getExpireAt());
|
||||
accountFrozen.setCreateTime(new Date());
|
||||
accountFrozen.setUpdateTime(new Date());
|
||||
accountFrozenMapper.insert(accountFrozen);
|
||||
return accountFrozen;
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放冻结单
|
||||
*
|
||||
* @param accountReleaseDto 冻结单释放DTO
|
||||
* @return 冻结单信息
|
||||
*/
|
||||
@Override
|
||||
public AccountFrozen releaseFrozen(AccountReleaseDto accountReleaseDto) {
|
||||
// 1. 验证释放冻结单参数
|
||||
validateReleaseParams(accountReleaseDto);
|
||||
|
||||
// 2. 通过冻结单ID获取冻结单信息
|
||||
AccountFrozen accountFrozen = getFrozenById(accountReleaseDto.getFrozenId());
|
||||
|
||||
// 3. 验证冻结单状态是否为预留状态
|
||||
// 只有状态为RESERVED的冻结单才能被释放
|
||||
validateFrozenStatus(accountFrozen);
|
||||
|
||||
// 4. 通过用户ID获取账户信息
|
||||
Account account = getAccountByUserId(accountFrozen.getUserId());
|
||||
|
||||
// 5. 根据冻结类型计算最终释放金额
|
||||
// - 如果是token类型,根据实际使用的tokens和模型价格计算
|
||||
// - 如果是RMB类型,将元转换为积分(1元=100积分)
|
||||
BigDecimal finalAmount = calculateReleaseAmount(accountReleaseDto, accountFrozen);
|
||||
|
||||
// 6. 执行释放时的账户更新
|
||||
// - 扣减账户的冻结金额
|
||||
// - 如果有实际扣减金额,更新账户余额
|
||||
doUpdateAccountForRelease(account, accountFrozen, finalAmount);
|
||||
|
||||
// 7. 创建交易流水记录
|
||||
// 当有实际扣减金额时,生成交易流水记录
|
||||
doCreateTransaction(accountFrozen, account, finalAmount);
|
||||
|
||||
// 8. 执行冻结单终结
|
||||
// 更新冻结单状态为已终结,并记录最终扣减金额和使用情况
|
||||
return doFinalizeFrozen(accountFrozen, accountReleaseDto, finalAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证释放冻结单参数
|
||||
*
|
||||
* @param accountReleaseDto 冻结单释放DTO
|
||||
*/
|
||||
private void validateReleaseParams(AccountReleaseDto accountReleaseDto) {
|
||||
if (accountReleaseDto == null) {
|
||||
throw new BizException(ResultCode.PARAMETER_EMPTY.getCode(), ResultCode.PARAMETER_EMPTY.getMessage());
|
||||
}
|
||||
if (accountReleaseDto.getFrozenId() == null) {
|
||||
throw new BizException(ResultCode.FROZEN_ID_EMPTY.getCode(), ResultCode.FROZEN_ID_EMPTY.getMessage());
|
||||
}
|
||||
|
||||
// 注:释放冻结单时的frozenType需要从冻结单中获取,所以这里不做类型相关的校验
|
||||
// 具体的类型相关校验会在calculateReleaseAmount方法中处理
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过冻结单ID获取冻结单信息
|
||||
*
|
||||
* @param frozenId 冻结单ID
|
||||
* @return 冻结单信息
|
||||
*/
|
||||
private AccountFrozen getFrozenById(String frozenId) {
|
||||
AccountFrozen accountFrozen = accountFrozenMapper.selectByPrimaryKey(frozenId);
|
||||
if (accountFrozen == null) {
|
||||
throw new BizException(ResultCode.FROZEN_NOT_EXIST.getCode(), ResultCode.FROZEN_NOT_EXIST.getMessage());
|
||||
}
|
||||
return accountFrozen;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证冻结单状态是否为预留状态
|
||||
*
|
||||
* @param accountFrozen 冻结单信息
|
||||
*/
|
||||
private void validateFrozenStatus(AccountFrozen accountFrozen) {
|
||||
if (!"RESERVED".equals(accountFrozen.getStatus())) {
|
||||
throw new BizException(ResultCode.FROZEN_STATUS_ERROR.getCode(), ResultCode.FROZEN_STATUS_ERROR.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据冻结类型计算最终释放金额
|
||||
*
|
||||
* @param accountReleaseDto 冻结单释放DTO
|
||||
* @param accountFrozen 冻结单信息
|
||||
* @return 最终释放金额(单位:积分)
|
||||
*/
|
||||
private BigDecimal calculateReleaseAmount(AccountReleaseDto accountReleaseDto, AccountFrozen accountFrozen) {
|
||||
BigDecimal finalAmount = accountReleaseDto.getFinalAmount() != null ?
|
||||
accountReleaseDto.getFinalAmount() : BigDecimal.ZERO;
|
||||
if (FROZEN_TYPE_TOKEN.equals(accountFrozen.getFrozenType())) {
|
||||
return calculateTokenReleaseAmount(accountReleaseDto, accountFrozen, finalAmount);
|
||||
} else if (FROZEN_TYPE_RMB.equals(accountFrozen.getFrozenType())) {
|
||||
return calculateRmbFrozenAmount(finalAmount);
|
||||
}
|
||||
return finalAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算token类型的释放金额
|
||||
* 根据实际使用的tokens和模型价格计算释放金额,并应用扣费系数
|
||||
*
|
||||
* @param accountReleaseDto 冻结单释放DTO
|
||||
* @param accountFrozen 冻结单信息
|
||||
* @param finalAmount 当前计算的最终金额
|
||||
* @return 计算后的释放金额(单位:积分)
|
||||
*/
|
||||
private BigDecimal calculateTokenReleaseAmount(AccountReleaseDto accountReleaseDto, AccountFrozen accountFrozen,
|
||||
BigDecimal finalAmount) {
|
||||
if (finalAmount.compareTo(BigDecimal.ZERO) != 0 ||
|
||||
accountReleaseDto.getUsageInputTokens() == null ||
|
||||
accountReleaseDto.getUsageOutputTokens() == null ||
|
||||
accountFrozen.getModelName() == null) {
|
||||
return finalAmount;
|
||||
}
|
||||
List<ModelPrice> modelPriceList = getModelPriceList(accountFrozen.getModelName());
|
||||
if (modelPriceList.isEmpty()) {
|
||||
return finalAmount;
|
||||
}
|
||||
ModelPrice inputModelPrice = findInputModelPrice(modelPriceList, accountReleaseDto.getUsageInputTokens());
|
||||
ModelPrice outputModelPrice = findOutputModelPrice(modelPriceList, accountReleaseDto.getUsageOutputTokens());
|
||||
if (inputModelPrice == null || outputModelPrice == null) {
|
||||
return finalAmount;
|
||||
}
|
||||
long totalFee = calculateReleaseTokenFee(accountReleaseDto, inputModelPrice, outputModelPrice);
|
||||
BigDecimal baseAmount = BigDecimal.valueOf(totalFee);
|
||||
return baseAmount.multiply(accountDeductionProperties.getCoefficient());
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算释放时的token总费用
|
||||
*
|
||||
* @param accountReleaseDto 冻结单释放DTO
|
||||
* @param inputModelPrice 输入模型价格
|
||||
* @param outputModelPrice 输出模型价格
|
||||
* @return 总费用(单位:分)
|
||||
*/
|
||||
private long calculateReleaseTokenFee(AccountReleaseDto accountReleaseDto,
|
||||
ModelPrice inputModelPrice, ModelPrice outputModelPrice) {
|
||||
long inputFee = calculateTokenFee(accountReleaseDto.getUsageInputTokens(), inputModelPrice.getInputPerCent());
|
||||
long outputFee = calculateTokenFee(accountReleaseDto.getUsageOutputTokens(), outputModelPrice.getOutputPerCent());
|
||||
return inputFee + outputFee;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行释放时的账户更新
|
||||
* 包括扣减冻结金额和实际余额
|
||||
*
|
||||
* @param account 账户信息
|
||||
* @param accountFrozen 冻结单信息
|
||||
* @param finalAmount 最终扣减金额
|
||||
*/
|
||||
private void doUpdateAccountForRelease(Account account, AccountFrozen accountFrozen, BigDecimal finalAmount) {
|
||||
BigDecimal frozenAmount = account.getFrozenAmount() == null ? BigDecimal.ZERO : account.getFrozenAmount();
|
||||
BigDecimal balance = account.getBalance() == null ? BigDecimal.ZERO : account.getBalance();
|
||||
account.setFrozenAmount(frozenAmount.subtract(accountFrozen.getFrozenAmount()));
|
||||
if (finalAmount.compareTo(BigDecimal.ZERO) > 0) {
|
||||
balance = calculateNewBalance(balance, finalAmount, accountFrozen.getFrozenAmount());
|
||||
account.setBalance(balance);
|
||||
}
|
||||
account.setUpdateTime(new Date());
|
||||
accountMapper.update(account);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算释放后的新余额
|
||||
* 比较实际扣减金额和预扣减金额,取较大值进行扣减
|
||||
*
|
||||
* @param balance 当前余额
|
||||
* @param finalAmount 实际扣减金额
|
||||
* @param frozenAmount 预扣减金额
|
||||
* @return 新的余额
|
||||
*/
|
||||
private BigDecimal calculateNewBalance(BigDecimal balance, BigDecimal finalAmount, BigDecimal frozenAmount) {
|
||||
BigDecimal amountToDeduct = finalAmount.compareTo(frozenAmount) > 0 ? finalAmount : frozenAmount;
|
||||
if (balance.compareTo(amountToDeduct) < 0) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
return balance.subtract(amountToDeduct);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建交易流水记录
|
||||
*
|
||||
* @param accountFrozen 冻结单信息
|
||||
* @param account 账户信息
|
||||
* @param finalAmount 最终扣减金额
|
||||
*/
|
||||
private void doCreateTransaction(AccountFrozen accountFrozen, Account account, BigDecimal finalAmount) {
|
||||
if (finalAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
return;
|
||||
}
|
||||
BigDecimal beforeBalance = account.getBalance() == null ? BigDecimal.ZERO : account.getBalance();
|
||||
AccountTransaction transaction = buildTransaction(accountFrozen, account, finalAmount, beforeBalance);
|
||||
accountTransactionMapper.insert(transaction);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建交易流水对象
|
||||
*
|
||||
* @param accountFrozen 冻结单信息
|
||||
* @param account 账户信息
|
||||
* @param finalAmount 最终扣减金额
|
||||
* @param beforeBalance 扣减前余额
|
||||
* @return 交易流水对象
|
||||
*/
|
||||
private AccountTransaction buildTransaction(AccountFrozen accountFrozen, Account account,
|
||||
BigDecimal finalAmount, BigDecimal beforeBalance) {
|
||||
AccountTransaction transaction = new AccountTransaction();
|
||||
transaction.setUserId(accountFrozen.getUserId());
|
||||
transaction.setUserName(account.getUserName());
|
||||
transaction.setTransactionType(3);
|
||||
transaction.setAmount(finalAmount);
|
||||
transaction.setBeforeBalance(beforeBalance);
|
||||
transaction.setAfterBalance(account.getBalance());
|
||||
transaction.setStatus(1);
|
||||
transaction.setTransactionNo(IDUtils.getSnowflakeIdStr());
|
||||
transaction.setPayType(3);
|
||||
transaction.setBusinessType("frozen_release");
|
||||
transaction.setRemark("冻结单释放扣减: " + accountFrozen.getFrozenId());
|
||||
transaction.setIsExpense(1);
|
||||
if (Objects.nonNull(accountFrozen.getQuestion())) {
|
||||
transaction.setQuestion(accountFrozen.getQuestion());
|
||||
}
|
||||
if (Objects.nonNull(accountFrozen.getCallId())) {
|
||||
transaction.setCallId(accountFrozen.getCallId());
|
||||
}
|
||||
if (accountFrozen.getFrozenType().equals(FROZEN_TYPE_TOKEN) &&
|
||||
accountFrozen.getModelName() != null) {
|
||||
if (accountFrozen.getUsageInputTokens() != null) {
|
||||
transaction.setInputToken(accountFrozen.getUsageInputTokens().intValue());
|
||||
}
|
||||
if (accountFrozen.getUsageOutputTokens() != null) {
|
||||
transaction.setOutputToken(accountFrozen.getUsageOutputTokens().intValue());
|
||||
}
|
||||
if (accountFrozen.getUsageTotalTokens() != null) {
|
||||
transaction.setTotalTokens(accountFrozen.getUsageTotalTokens().intValue());
|
||||
}
|
||||
transaction.setModelName(accountFrozen.getModelName());
|
||||
}
|
||||
transaction.setCreateTime(new Date());
|
||||
transaction.setUpdateTime(new Date());
|
||||
return transaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行冻结单终结
|
||||
* 更新冻结单状态为已终结
|
||||
*
|
||||
* @param accountFrozen 冻结单信息
|
||||
* @param accountReleaseDto 冻结单释放DTO
|
||||
* @param finalAmount 最终扣减金额
|
||||
* @return 冻结单信息
|
||||
*/
|
||||
private AccountFrozen doFinalizeFrozen(AccountFrozen accountFrozen, AccountReleaseDto accountReleaseDto,
|
||||
BigDecimal finalAmount) {
|
||||
accountFrozen.setFinalAmount(finalAmount);
|
||||
accountFrozen.setUsageInputTokens(accountReleaseDto.getUsageInputTokens());
|
||||
accountFrozen.setUsageOutputTokens(accountReleaseDto.getUsageOutputTokens());
|
||||
accountFrozen.setUsageTotalTokens(accountReleaseDto.getUsageTotalTokens());
|
||||
accountFrozen.setFinalizeReason(accountReleaseDto.getFinalizeReason());
|
||||
accountFrozen.setStatus("FINALIZED");
|
||||
accountFrozen.setUpdateTime(new Date());
|
||||
accountFrozenMapper.updateByPrimaryKey(accountFrozen);
|
||||
return accountFrozen;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID查询冻结单
|
||||
*
|
||||
* @param frozenId 冻结单ID
|
||||
* @return 冻结单信息
|
||||
*/
|
||||
@Override
|
||||
public AccountFrozen getByFrozenId(String frozenId) {
|
||||
return accountFrozenMapper.selectByPrimaryKey(frozenId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据会话ID查询冻结单
|
||||
*
|
||||
* @param sessionId 会话ID
|
||||
* @return 冻结单信息
|
||||
*/
|
||||
@Override
|
||||
public AccountFrozen getBySessionId(String sessionId) {
|
||||
return accountFrozenMapper.selectBySessionId(sessionId);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -6,8 +6,6 @@ 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;
|
||||
|
|
@ -16,21 +14,14 @@ import com.kexue.skills.mapper.AccountTransactionMapper;
|
|||
import com.kexue.skills.mapper.SysUserMapper;
|
||||
import com.kexue.skills.service.AccountService;
|
||||
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)表服务实现类
|
||||
|
|
@ -41,8 +32,6 @@ import java.util.Map;
|
|||
@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
|
||||
|
|
@ -54,9 +43,6 @@ public class AccountServiceImpl implements AccountService {
|
|||
@Resource
|
||||
private ModelPriceService modelPriceService;
|
||||
|
||||
@Resource
|
||||
private PackageConfigService packageConfigService;
|
||||
|
||||
/**
|
||||
* 分页查询
|
||||
*
|
||||
|
|
@ -66,7 +52,7 @@ public class AccountServiceImpl implements AccountService {
|
|||
@Override
|
||||
public PageInfo<Account> getPageList(AccountDto queryDto) {
|
||||
PageHelper.startPage(queryDto.getPageNum(), queryDto.getPageSize());
|
||||
List<Account> list = accountMapper.getPageList(queryDto);
|
||||
List<Account> list = this.accountMapper.getPageList(queryDto);
|
||||
return new PageInfo<>(list);
|
||||
}
|
||||
|
||||
|
|
@ -78,7 +64,7 @@ public class AccountServiceImpl implements AccountService {
|
|||
*/
|
||||
@Override
|
||||
public List<Account> getList(AccountDto queryDto) {
|
||||
return accountMapper.getList(queryDto);
|
||||
return this.accountMapper.getList(queryDto);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -89,7 +75,7 @@ public class AccountServiceImpl implements AccountService {
|
|||
*/
|
||||
@Override
|
||||
public Account queryById(Long accountId) {
|
||||
return accountMapper.queryById(accountId);
|
||||
return this.accountMapper.queryById(accountId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -100,7 +86,7 @@ public class AccountServiceImpl implements AccountService {
|
|||
*/
|
||||
@Override
|
||||
public Account queryByUserId(Long userId) {
|
||||
return accountMapper.queryByUserId(userId);
|
||||
return this.accountMapper.queryByUserId(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -130,7 +116,7 @@ public class AccountServiceImpl implements AccountService {
|
|||
account.setFrozenAmount(BigDecimal.ZERO);
|
||||
}
|
||||
// 保存数据
|
||||
accountMapper.insert(account);
|
||||
this.accountMapper.insert(account);
|
||||
return account;
|
||||
}
|
||||
|
||||
|
|
@ -145,8 +131,8 @@ public class AccountServiceImpl implements AccountService {
|
|||
// 设置更新时间
|
||||
account.setUpdateTime(new Date());
|
||||
// 更新数据
|
||||
accountMapper.update(account);
|
||||
return queryById(account.getAccountId());
|
||||
this.accountMapper.update(account);
|
||||
return this.queryById(account.getAccountId());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -164,7 +150,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 = queryByUserId(userId);
|
||||
Account account = this.queryByUserId(userId);
|
||||
if (account == null) {
|
||||
// 创建账户
|
||||
account = new Account();
|
||||
|
|
@ -173,68 +159,39 @@ public class AccountServiceImpl implements AccountService {
|
|||
account.setWithdrawableBalance(BigDecimal.ZERO);
|
||||
account.setNonWithdrawableBalance(BigDecimal.ZERO);
|
||||
account.setFrozenAmount(BigDecimal.ZERO);
|
||||
insert(account);
|
||||
this.insert(account);
|
||||
}
|
||||
|
||||
// 2. 计算积分
|
||||
BigDecimal points = BigDecimal.ZERO;
|
||||
String rechargeRemark = "";
|
||||
|
||||
// 检查是否有套餐ID
|
||||
if (businessId != null) {
|
||||
// 查询套餐配置
|
||||
PackageConfig packageConfig = packageConfigService.queryById(businessId);
|
||||
if (packageConfig != null) {
|
||||
// 套餐的baseAmount和giftAmount已经是积分,直接使用
|
||||
points = packageConfig.getBaseAmount().add(packageConfig.getGiftAmount());
|
||||
// 根据套餐信息生成remark
|
||||
rechargeRemark = "购买套餐:" + packageConfig.getName() + ",获得" + points + "积分";
|
||||
} else {
|
||||
// 没有找到套餐,按照原来的逻辑计算积分
|
||||
points = amount.multiply(BigDecimal.valueOf(100));
|
||||
rechargeRemark = "充值" + amount + "元,获得" + points + "积分";
|
||||
}
|
||||
} else {
|
||||
// 没有套餐ID,按照原来的逻辑计算积分
|
||||
points = amount.multiply(BigDecimal.valueOf(100));
|
||||
rechargeRemark = "充值" + amount + "元,获得" + points + "积分";
|
||||
}
|
||||
|
||||
// 3. 保存交易记录
|
||||
// 2. 保存交易记录
|
||||
AccountTransaction transaction = new AccountTransaction();
|
||||
transaction.setUserId(userId);
|
||||
transaction.setUserName(account.getUserName());
|
||||
transaction.setTransactionType(1); // 充值
|
||||
transaction.setAmount(points); // 存储充值金额(积分)
|
||||
transaction.setAmount(amount);
|
||||
transaction.setBeforeBalance(account.getBalance());
|
||||
transaction.setAfterBalance(account.getBalance().add(points)); // 余额为积分
|
||||
transaction.setAfterBalance(account.getBalance().add(amount));
|
||||
transaction.setStatus(1); // 成功
|
||||
transaction.setTransactionNo(transactionNo);
|
||||
transaction.setPayType(3); // 余额支付
|
||||
transaction.setBusinessId(businessId);// 套餐ID
|
||||
transaction.setBusinessId(businessId);
|
||||
transaction.setBusinessType(businessType);
|
||||
|
||||
// 添加额外备注
|
||||
if (remark != null && !remark.isEmpty()) {
|
||||
rechargeRemark += " - " + remark;
|
||||
}
|
||||
transaction.setRemark(rechargeRemark);
|
||||
transaction.setRemark(remark);
|
||||
transaction.setIsExpense(0); // 收入
|
||||
transaction.setIncomeType("recharge"); // 充值
|
||||
accountTransactionMapper.insert(transaction);
|
||||
this.accountTransactionMapper.insert(transaction);
|
||||
|
||||
// 4. 更新账户余额(使用积分)
|
||||
// 3. 更新账户余额
|
||||
if (isWithdrawable) {
|
||||
BigDecimal withdrawableBalance = account.getWithdrawableBalance() == null ? BigDecimal.ZERO : account.getWithdrawableBalance();
|
||||
account.setWithdrawableBalance(withdrawableBalance.add(points));
|
||||
account.setWithdrawableBalance(withdrawableBalance.add(amount));
|
||||
} else {
|
||||
BigDecimal nonWithdrawableBalance = account.getNonWithdrawableBalance() == null ? BigDecimal.ZERO : account.getNonWithdrawableBalance();
|
||||
account.setNonWithdrawableBalance(nonWithdrawableBalance.add(points));
|
||||
account.setNonWithdrawableBalance(nonWithdrawableBalance.add(amount));
|
||||
}
|
||||
BigDecimal balance = account.getBalance() == null ? BigDecimal.ZERO : account.getBalance();
|
||||
account.setBalance(balance.add(points));
|
||||
account.setBalance(balance.add(amount));
|
||||
account.setUpdateTime(new Date());
|
||||
update(account);
|
||||
this.update(account);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -268,7 +225,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 = queryByUserId(userId);
|
||||
Account account = this.queryByUserId(userId);
|
||||
Assert.notNull(account, "账户不存在");
|
||||
|
||||
// 2. 检查余额是否足够
|
||||
|
|
@ -290,10 +247,10 @@ public class AccountServiceImpl implements AccountService {
|
|||
transaction.setBusinessType(businessType);
|
||||
transaction.setRemark(remark);
|
||||
transaction.setIsExpense(1); // 支出
|
||||
accountTransactionMapper.insert(transaction);
|
||||
this.accountTransactionMapper.insert(transaction);
|
||||
|
||||
// 4. 更新账户余额
|
||||
return accountMapper.updateBalance(userId, amount, 2);
|
||||
return this.accountMapper.updateBalance(userId, amount, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -310,29 +267,23 @@ public class AccountServiceImpl implements AccountService {
|
|||
throw new BizException("会话ID不存在");
|
||||
}
|
||||
userId = bySessionId.getUserId();
|
||||
Account account = queryByUserId(userId);
|
||||
Account account = this.queryByUserId(userId);
|
||||
Assert.notNull(account, "账户不存在");
|
||||
|
||||
// 2. 查询输入token的价格规则(输入token使用默认的standard模式)
|
||||
String inputOutputMode = "standard";
|
||||
ModelPrice inputModelPrice = modelPriceService.queryByModelNameAndOutputModeAndTokens(dto.getModelName(), inputOutputMode, Long.valueOf(dto.getInputToken()));
|
||||
Assert.notNull(inputModelPrice, "输入token价格信息不存在");
|
||||
// 2. 查询模型价格信息
|
||||
ModelPrice modelPrice = modelPriceService.queryByModelName(dto.getModelName());
|
||||
Assert.notNull(modelPrice, "模型价格信息不存在");
|
||||
|
||||
// 3. 查询输出token的价格规则
|
||||
String outputMode = dto.getOutputMode() != null ? dto.getOutputMode() : "standard";
|
||||
ModelPrice outputModelPrice = modelPriceService.queryByModelNameAndOutputModeAndTokens(dto.getModelName(), outputMode, Long.valueOf(dto.getOutputToken()));
|
||||
Assert.notNull(outputModelPrice, "输出token价格信息不存在");
|
||||
|
||||
// 4. 计算金额
|
||||
// 3. 计算金额
|
||||
// 输入token费用:输入token数量 / inputPerCent,不足1分按1分计算
|
||||
long inputFee = dto.getInputToken() / inputModelPrice.getInputPerCent();
|
||||
if (dto.getInputToken() % inputModelPrice.getInputPerCent() > 0) {
|
||||
long inputFee = dto.getInputToken() / modelPrice.getInputPerCent();
|
||||
if (dto.getInputToken() % modelPrice.getInputPerCent() > 0) {
|
||||
inputFee += 1;
|
||||
}
|
||||
|
||||
// 输出token费用:输出token数量 / outputPerCent,不足1分按1分计算
|
||||
long outputFee = dto.getOutputToken() / outputModelPrice.getOutputPerCent();
|
||||
if (dto.getOutputToken() % outputModelPrice.getOutputPerCent() > 0) {
|
||||
long outputFee = dto.getOutputToken() / modelPrice.getOutputPerCent();
|
||||
if (dto.getOutputToken() % modelPrice.getOutputPerCent() > 0) {
|
||||
outputFee += 1;
|
||||
}
|
||||
|
||||
|
|
@ -341,11 +292,11 @@ public class AccountServiceImpl implements AccountService {
|
|||
// 转换为元
|
||||
BigDecimal amount = BigDecimal.valueOf(totalFee).divide(BigDecimal.valueOf(100));
|
||||
|
||||
// 5. 检查余额是否足够
|
||||
// 4. 检查余额是否足够
|
||||
BigDecimal balance = account.getBalance() == null ? BigDecimal.ZERO : account.getBalance();
|
||||
Assert.isTrue(balance.compareTo(amount) >= 0, "账户余额不足");
|
||||
|
||||
// 6. 保存交易记录
|
||||
// 5. 保存交易记录
|
||||
AccountTransaction transaction = new AccountTransaction();
|
||||
transaction.setUserId(userId);
|
||||
transaction.setUserName(account.getUserName());
|
||||
|
|
@ -363,10 +314,10 @@ public class AccountServiceImpl implements AccountService {
|
|||
transaction.setTotalTokens(dto.getTotalTokens());
|
||||
transaction.setModelName(dto.getModelName());
|
||||
transaction.setQuestion(dto.getQuestion());
|
||||
accountTransactionMapper.insert(transaction);
|
||||
this.accountTransactionMapper.insert(transaction);
|
||||
|
||||
// 7. 更新账户余额
|
||||
accountMapper.updateBalance(userId, amount, 2);
|
||||
// 6. 更新账户余额
|
||||
this.accountMapper.updateBalance(userId, amount, 2);
|
||||
return amount;
|
||||
}
|
||||
|
||||
|
|
@ -376,13 +327,15 @@ public class AccountServiceImpl implements AccountService {
|
|||
Assert.notNull(dto.getQuestion(), "问题不能为空");
|
||||
Assert.notNull(dto.getModelName(), "模型名称不能为空");
|
||||
Assert.notNull(dto.getQuestion(), "问题不能为空");
|
||||
Assert.notNull(dto.getModelName(), "模型名称不能为空");
|
||||
Assert.notNull(dto.getQuestion(), "问题不能为空");
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加账户积分(签到奖励token转换)
|
||||
* 增加账户余额(签到奖励token转换)
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param amount 增加积分
|
||||
* @param amount 增加金额
|
||||
* @param transactionNo 交易单号
|
||||
* @param businessId 业务ID
|
||||
* @param businessType 业务类型
|
||||
|
|
@ -392,7 +345,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 = queryByUserId(userId);
|
||||
Account account = this.queryByUserId(userId);
|
||||
if (account == null) {
|
||||
// 创建账户
|
||||
account = new Account();
|
||||
|
|
@ -401,7 +354,7 @@ public class AccountServiceImpl implements AccountService {
|
|||
account.setWithdrawableBalance(BigDecimal.ZERO);
|
||||
account.setNonWithdrawableBalance(BigDecimal.ZERO);
|
||||
account.setFrozenAmount(BigDecimal.ZERO);
|
||||
insert(account);
|
||||
this.insert(account);
|
||||
}
|
||||
|
||||
// 2. 保存交易记录
|
||||
|
|
@ -409,23 +362,18 @@ public class AccountServiceImpl implements AccountService {
|
|||
transaction.setUserId(userId);
|
||||
transaction.setUserName(account.getUserName());
|
||||
transaction.setTransactionType(5); // 签到奖励
|
||||
transaction.setAmount(amount); // 存储签到奖励积分
|
||||
transaction.setAmount(amount);
|
||||
transaction.setBeforeBalance(account.getBalance());
|
||||
transaction.setAfterBalance(account.getBalance().add(amount)); // 余额为积分
|
||||
transaction.setAfterBalance(account.getBalance().add(amount));
|
||||
transaction.setStatus(1); // 成功
|
||||
transaction.setTransactionNo(transactionNo);
|
||||
transaction.setPayType(3); // 余额支付
|
||||
transaction.setBusinessId(businessId);
|
||||
transaction.setBusinessType(businessType);
|
||||
// 在备注中添加积分信息
|
||||
String signInRemark = "签到奖励" + amount + "积分";
|
||||
if (remark != null && !remark.isEmpty()) {
|
||||
signInRemark += " - " + remark;
|
||||
}
|
||||
transaction.setRemark(signInRemark);
|
||||
transaction.setRemark(remark);
|
||||
transaction.setIsExpense(0); // 收入
|
||||
transaction.setIncomeType("sign_in"); // 签到奖励
|
||||
accountTransactionMapper.insert(transaction);
|
||||
this.accountTransactionMapper.insert(transaction);
|
||||
|
||||
// 3. 更新账户余额(签到奖励不可提现)
|
||||
BigDecimal nonWithdrawableBalance = account.getNonWithdrawableBalance() == null ? BigDecimal.ZERO : account.getNonWithdrawableBalance();
|
||||
|
|
@ -433,15 +381,15 @@ public class AccountServiceImpl implements AccountService {
|
|||
BigDecimal balance = account.getBalance() == null ? BigDecimal.ZERO : account.getBalance();
|
||||
account.setBalance(balance.add(amount));
|
||||
account.setUpdateTime(new Date());
|
||||
update(account);
|
||||
this.update(account);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 给用户赠送积分(不可提现)
|
||||
* 给用户赠送金额(不可提现)
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param amount 赠送积分
|
||||
* @param amount 赠送金额
|
||||
* @param transactionNo 交易单号
|
||||
* @param businessId 业务ID
|
||||
* @param businessType 业务类型
|
||||
|
|
@ -451,7 +399,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 = queryByUserId(userId);
|
||||
Account account = this.queryByUserId(userId);
|
||||
if (account == null) {
|
||||
// 创建账户
|
||||
account = new Account();
|
||||
|
|
@ -460,7 +408,7 @@ public class AccountServiceImpl implements AccountService {
|
|||
account.setWithdrawableBalance(BigDecimal.ZERO);
|
||||
account.setNonWithdrawableBalance(BigDecimal.ZERO);
|
||||
account.setFrozenAmount(BigDecimal.ZERO);
|
||||
insert(account);
|
||||
this.insert(account);
|
||||
}
|
||||
|
||||
// 2. 保存交易记录
|
||||
|
|
@ -468,25 +416,20 @@ public class AccountServiceImpl implements AccountService {
|
|||
transaction.setUserId(userId);
|
||||
transaction.setUserName(account.getUserName());
|
||||
transaction.setTransactionType(6); // 赠送
|
||||
transaction.setAmount(amount); // 存储赠送积分
|
||||
transaction.setAmount(amount);
|
||||
transaction.setBeforeBalance(account.getBalance());
|
||||
transaction.setAfterBalance(account.getBalance().add(amount)); // 余额为积分
|
||||
transaction.setAfterBalance(account.getBalance().add(amount));
|
||||
transaction.setStatus(1); // 成功
|
||||
transaction.setTransactionNo(transactionNo);
|
||||
transaction.setPayType(3); // 余额支付
|
||||
transaction.setBusinessId(businessId);
|
||||
transaction.setBusinessType(businessType);
|
||||
// 在备注中添加积分信息
|
||||
String giftRemark = "赠送" + amount + "积分";
|
||||
if (remark != null && !remark.isEmpty()) {
|
||||
giftRemark += " - " + remark;
|
||||
}
|
||||
transaction.setRemark(giftRemark);
|
||||
transaction.setRemark(remark);
|
||||
transaction.setIsExpense(0); // 收入
|
||||
transaction.setIncomeType("gift"); // 赠送
|
||||
accountTransactionMapper.insert(transaction);
|
||||
this.accountTransactionMapper.insert(transaction);
|
||||
|
||||
// 3. 更新账户余额(赠送积分不可提现)
|
||||
// 3. 更新账户余额(赠送金额不可提现)
|
||||
if (account.getNonWithdrawableBalance() == null){
|
||||
account.setNonWithdrawableBalance(BigDecimal.ZERO);
|
||||
}
|
||||
|
|
@ -496,7 +439,7 @@ public class AccountServiceImpl implements AccountService {
|
|||
}
|
||||
account.setBalance(account.getBalance().add(amount));
|
||||
account.setUpdateTime(new Date());
|
||||
update(account);
|
||||
this.update(account);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -509,7 +452,7 @@ public class AccountServiceImpl implements AccountService {
|
|||
*/
|
||||
@Override
|
||||
public int logicDeleteById(Long accountId, String updateBy) {
|
||||
return accountMapper.logicDeleteById(accountId, updateBy);
|
||||
return this.accountMapper.logicDeleteById(accountId, updateBy);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -520,7 +463,7 @@ public class AccountServiceImpl implements AccountService {
|
|||
*/
|
||||
@Override
|
||||
public int deleteById(Long accountId) {
|
||||
return accountMapper.deleteById(accountId);
|
||||
return this.accountMapper.deleteById(accountId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -531,134 +474,6 @@ public class AccountServiceImpl implements AccountService {
|
|||
*/
|
||||
@Override
|
||||
public List<com.kexue.skills.entity.AccountTransaction> getTransactions(Long 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);
|
||||
return this.accountTransactionMapper.queryByUserId(userId);
|
||||
}
|
||||
}
|
||||
|
|
@ -14,9 +14,6 @@ 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;
|
||||
|
|
@ -31,8 +28,6 @@ import java.io.InputStream;
|
|||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
|
|
@ -54,9 +49,6 @@ public class CmsContentServiceImpl implements CmsContentService {
|
|||
@Resource
|
||||
private CmsContentLikeMapper cmsContentLikeMapper;
|
||||
|
||||
@Resource
|
||||
private CmsTagMapper cmsTagMapper;
|
||||
|
||||
@Resource
|
||||
private LoginUserCacheUtil loginUserCacheUtil;
|
||||
|
||||
|
|
@ -83,7 +75,94 @@ public class CmsContentServiceImpl implements CmsContentService {
|
|||
queryDto.setLanguageType(0);
|
||||
}
|
||||
List<CmsContent> list = this.cmsContentMapper.getPageList(queryDto);
|
||||
return new PageInfo<>(list);
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -967,233 +1046,5 @@ public class CmsContentServiceImpl implements CmsContentService {
|
|||
return totalSuccessCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int importFromZip(byte[] zipFileBytes, String createBy) {
|
||||
int totalSuccessCount = 0;
|
||||
int totalFiles = 0;
|
||||
|
||||
try (ZipInputStream zipInputStream = new ZipInputStream(new ByteArrayInputStream(zipFileBytes))) {
|
||||
ZipEntry entry;
|
||||
|
||||
// 遍历ZIP文件中的所有条目
|
||||
while ((entry = zipInputStream.getNextEntry()) != null) {
|
||||
String fileName = entry.getName();
|
||||
|
||||
// 跳过目录和非Excel文件
|
||||
if (entry.isDirectory() || (!fileName.endsWith(".xls") && !fileName.endsWith(".xlsx"))) {
|
||||
zipInputStream.closeEntry();
|
||||
continue;
|
||||
}
|
||||
|
||||
// 跳过Office临时锁定文件
|
||||
String simpleName = new File(fileName).getName();
|
||||
if (simpleName.startsWith("~$")) {
|
||||
zipInputStream.closeEntry();
|
||||
continue;
|
||||
}
|
||||
|
||||
totalFiles++;
|
||||
System.out.println("当前处理第 " + totalFiles + " 个文件,文件名称是:" + fileName);
|
||||
|
||||
try {
|
||||
// 读取Excel文件内容到字节数组
|
||||
byte[] fileBytes = zipInputStream.readAllBytes();
|
||||
|
||||
// 调用现有的 importFromExcel 方法进行导入
|
||||
int successCount = importFromExcel(fileBytes, createBy);
|
||||
totalSuccessCount += successCount;
|
||||
System.out.println("第 " + totalFiles + " 个文件导入成功,导入了 " + successCount + " 条记录");
|
||||
} catch (Exception e) {
|
||||
System.err.println("导入文件失败: " + fileName);
|
||||
e.printStackTrace();
|
||||
// 单个文件导入失败不影响其他文件
|
||||
} finally {
|
||||
zipInputStream.closeEntry();
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("导入完成,共处理 " + totalFiles + " 个文件,成功导入 " + totalSuccessCount + " 条记录");
|
||||
} catch (Exception e) {
|
||||
System.err.println("ZIP文件导入操作失败");
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -63,24 +63,11 @@ public class ModelPriceServiceImpl implements ModelPriceService {
|
|||
* 通过模型名称查询数据
|
||||
*
|
||||
* @param modelName 模型名称
|
||||
* @return 实例对象列表
|
||||
*/
|
||||
@Override
|
||||
public List<ModelPrice> queryByModelName(String modelName) {
|
||||
return this.modelPriceMapper.queryByModelName(modelName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据模型名称、输出模式和token数量查询价格规则
|
||||
*
|
||||
* @param modelName 模型名称
|
||||
* @param outputMode 输出模式
|
||||
* @param tokens token数量
|
||||
* @return 实例对象
|
||||
*/
|
||||
@Override
|
||||
public ModelPrice queryByModelNameAndOutputModeAndTokens(String modelName, String outputMode, Long tokens) {
|
||||
return this.modelPriceMapper.queryByModelNameAndOutputModeAndTokens(modelName, outputMode, tokens);
|
||||
public ModelPrice queryByModelName(String modelName) {
|
||||
return this.modelPriceMapper.queryByModelName(modelName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,66 +0,0 @@
|
|||
package com.kexue.skills.service.impl;
|
||||
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.kexue.skills.entity.PackageConfig;
|
||||
import com.kexue.skills.entity.dto.PackageConfigDto;
|
||||
import com.kexue.skills.mapper.PackageConfigMapper;
|
||||
import com.kexue.skills.service.PackageConfigService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* (PackageConfig)表服务实现类
|
||||
*
|
||||
* @author 系统生成
|
||||
* @since 2026-04-11
|
||||
*/
|
||||
@Service
|
||||
public class PackageConfigServiceImpl implements PackageConfigService {
|
||||
|
||||
@Resource
|
||||
private PackageConfigMapper packageConfigMapper;
|
||||
|
||||
@Override
|
||||
public PageInfo<PackageConfig> getPageList(PackageConfigDto queryDto) {
|
||||
PageHelper.startPage(queryDto.getPageNum(), queryDto.getPageSize());
|
||||
List<PackageConfig> list = packageConfigMapper.getList(queryDto);
|
||||
return new PageInfo<>(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PackageConfig> getList(PackageConfigDto queryDto) {
|
||||
return packageConfigMapper.getList(queryDto);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackageConfig queryById(Long id) {
|
||||
return packageConfigMapper.queryById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackageConfig insert(PackageConfig packageConfig) {
|
||||
packageConfigMapper.insert(packageConfig);
|
||||
return packageConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackageConfig update(PackageConfig packageConfig) {
|
||||
packageConfigMapper.update(packageConfig);
|
||||
return packageConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int logicDeleteById(Long id, String updateBy) {
|
||||
// 由于package_config表没有delete_flag字段,这里直接调用物理删除
|
||||
return packageConfigMapper.deleteById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int deleteById(Long id) {
|
||||
return packageConfigMapper.deleteById(id);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -11,8 +11,6 @@ import com.kexue.skills.entity.PaymentOrder;
|
|||
import com.kexue.skills.service.PayService;
|
||||
import com.kexue.skills.service.PaymentOrderService;
|
||||
import com.kexue.skills.service.AccountService;
|
||||
import com.kexue.skills.service.PackageConfigService;
|
||||
import com.kexue.skills.entity.PackageConfig;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
|
@ -65,9 +63,6 @@ public class PayServiceImpl implements PayService {
|
|||
@Resource
|
||||
private AccountService accountService;
|
||||
|
||||
@Resource
|
||||
private PackageConfigService packageConfigService;
|
||||
|
||||
/**
|
||||
* 生成随机字符串
|
||||
* @return 随机字符串
|
||||
|
|
@ -510,11 +505,11 @@ public class PayServiceImpl implements PayService {
|
|||
order.getAmount(),
|
||||
true, // 可提现
|
||||
transactionId,
|
||||
order.getBusinessId(),
|
||||
order.getOrderId(),
|
||||
"recharge",
|
||||
"微信支付充值"
|
||||
);
|
||||
logger.info("微信支付回调:更新账户余额成功,userId={}, amount={}, actualAmount={}", order.getUserId(), order.getAmount(), order.getAmount());
|
||||
logger.info("微信支付回调:更新账户余额成功,userId={}, amount={}", order.getUserId(), order.getAmount());
|
||||
} catch (Exception e) {
|
||||
logger.error("微信支付回调:更新账户余额失败", e);
|
||||
// 继续处理,不影响回调响应
|
||||
|
|
@ -569,7 +564,6 @@ public class PayServiceImpl implements PayService {
|
|||
bizContent.put("out_trade_no", order.getOrderNo());
|
||||
bizContent.put("total_amount", order.getAmount().toString());
|
||||
bizContent.put("subject", order.getProductName());
|
||||
bizContent.put("businessId", order.getBusinessId());
|
||||
bizContent.put("body", order.getProductDesc());
|
||||
bizContent.put("timeout_express", "30m");
|
||||
bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");
|
||||
|
|
@ -653,11 +647,11 @@ public class PayServiceImpl implements PayService {
|
|||
order.getAmount(),
|
||||
true, // 可提现
|
||||
transactionId,
|
||||
order.getBusinessId(),
|
||||
order.getOrderId(),
|
||||
"recharge",
|
||||
"支付宝支付充值"
|
||||
);
|
||||
logger.info("支付宝支付回调:更新账户余额成功,userId={}, amount={}, actualAmount={}", order.getUserId(), order.getAmount(), order.getAmount());
|
||||
logger.info("支付宝支付回调:更新账户余额成功,userId={}, amount={}", order.getUserId(), order.getAmount());
|
||||
} catch (Exception e) {
|
||||
logger.error("支付宝支付回调:更新账户余额失败", e);
|
||||
// 继续处理,不影响回调响应
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -27,22 +27,17 @@ import com.kexue.skills.mapper.CmsContentMapper;
|
|||
import com.kexue.skills.mapper.SysUserMapper;
|
||||
import com.kexue.skills.mapper.SysUserRoleMapper;
|
||||
import com.kexue.skills.service.SysUserService;
|
||||
import com.kexue.skills.service.AccountService;
|
||||
import com.kexue.skills.utils.MD5Util;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
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,15 +76,6 @@ public class SysUserServiceImpl implements SysUserService {
|
|||
|
||||
@Resource
|
||||
private CmsContentMapper cmsContentMapper;
|
||||
|
||||
@Resource
|
||||
private AccountService accountService;
|
||||
|
||||
/**
|
||||
* 用户头像上传目录
|
||||
*/
|
||||
@Value("${web.upload.userIconPath}")
|
||||
private String userIconPath;
|
||||
|
||||
/**
|
||||
* 分页查询数据
|
||||
|
|
@ -152,34 +138,6 @@ public class SysUserServiceImpl implements SysUserService {
|
|||
sysUser.setDeleteFlag(Const.DELETE_FLAG_NO);
|
||||
//写一个salt生成方法
|
||||
sysUser.setSalt(System.currentTimeMillis()+"");
|
||||
|
||||
// 生成随机邀请码(8位字母数字组合)
|
||||
if (sysUser.getInviteCode() == null || sysUser.getInviteCode().isEmpty()) {
|
||||
sysUser.setInviteCode(generateInviteCode());
|
||||
}
|
||||
|
||||
// 处理邀请码
|
||||
if (sysUser.getInvitedCode() != null && !sysUser.getInvitedCode().isEmpty()) {
|
||||
// 验证邀请码是否有效
|
||||
SysUser inviter = sysUserMapper.getByInviteCode(sysUser.getInvitedCode());
|
||||
if (inviter == null) {
|
||||
throw new BizException("邀请码无效");
|
||||
}
|
||||
|
||||
// 设置邀请人用户ID
|
||||
sysUser.setInvitedBy(inviter.getUserId());
|
||||
|
||||
// 给邀请人赠送100积分
|
||||
accountService.addGiftBalance(
|
||||
inviter.getUserId(),
|
||||
BigDecimal.valueOf(100),
|
||||
"gift_" + System.currentTimeMillis(),
|
||||
null,
|
||||
"invite",
|
||||
"邀请用户注册赠送"
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
// 假设客户端已经对密码进行了一次MD5加密,服务端使用双重加密验证
|
||||
String encryptedPwd = MD5Util.doubleEncrypt(sysUser.getPwd(), sysUser.getSalt());
|
||||
|
|
@ -199,16 +157,6 @@ public class SysUserServiceImpl implements SysUserService {
|
|||
account.setDeleteFlag(Const.DELETE_FLAG_NO); // 初始未删除
|
||||
accountMapper.insert(account);
|
||||
|
||||
// 注册赠送100积分
|
||||
accountService.addGiftBalance(
|
||||
sysUser.getUserId(),
|
||||
BigDecimal.valueOf(100),
|
||||
"gift_" + System.currentTimeMillis(),
|
||||
null,
|
||||
"register",
|
||||
"注册赠送"
|
||||
);
|
||||
|
||||
// 将返回的用户密码设置为null
|
||||
sysUser.setPwd(null);
|
||||
return sysUser;
|
||||
|
|
@ -291,9 +239,7 @@ public class SysUserServiceImpl implements SysUserService {
|
|||
// 判断更新的用户是否是当前登录用户,如果是则更新用户缓存
|
||||
Long currentUserId = loginUserCacheUtil.getCurrentUserId();
|
||||
if (currentUserId != null && currentUserId.equals(sysUserUpdateDto.getUserId())) {
|
||||
// 获取当前登录用户的 token
|
||||
String token = loginUserCacheUtil.getTokenFromRequest();
|
||||
updateUserCache(currentUserId, sysUser, token);
|
||||
updateUserCache(currentUserId, sysUser);
|
||||
}
|
||||
}else {
|
||||
// 如果没有用户ID,创建新用户
|
||||
|
|
@ -911,7 +857,7 @@ public class SysUserServiceImpl implements SysUserService {
|
|||
|
||||
// 如果用户不存在,自动创建账号
|
||||
if (sysUser == null) {
|
||||
sysUser = createUserByPhone(phone, phoneLoginDto.getInviteCode());
|
||||
sysUser = createUserByPhone(phone);
|
||||
}
|
||||
|
||||
// 检查用户是否已有 token,如果有则使旧 token 失效
|
||||
|
|
@ -1008,45 +954,15 @@ public class SysUserServiceImpl implements SysUserService {
|
|||
* 根据手机号创建用户
|
||||
*
|
||||
* @param phone 手机号
|
||||
* @param inviteCode 邀请码
|
||||
* @return 创建的用户对象
|
||||
*/
|
||||
private SysUser createUserByPhone(String phone, String inviteCode) {
|
||||
private SysUser createUserByPhone(String phone) {
|
||||
SysUser sysUser = new SysUser();
|
||||
|
||||
// 设置用户名(使用手机号作为用户名)
|
||||
sysUser.setUserName(phone);
|
||||
sysUser.setTel(phone);
|
||||
|
||||
// 生成随机邀请码(8位字母数字组合)
|
||||
String generatedInviteCode = generateInviteCode();
|
||||
sysUser.setInviteCode(generatedInviteCode);
|
||||
|
||||
// 处理邀请码
|
||||
if (inviteCode != null && !inviteCode.isEmpty()) {
|
||||
// 验证邀请码是否有效
|
||||
SysUser inviter = sysUserMapper.getByInviteCode(inviteCode);
|
||||
if (inviter == null) {
|
||||
throw new BizException("邀请码无效");
|
||||
}
|
||||
// 设置被邀请码和邀请人用户ID
|
||||
sysUser.setInvitedCode(inviteCode);
|
||||
sysUser.setInvitedBy(inviter.getUserId());
|
||||
|
||||
// 给邀请人赠送100积分
|
||||
accountService.addGiftBalance(
|
||||
inviter.getUserId(),
|
||||
BigDecimal.valueOf(100),
|
||||
"gift_" + System.currentTimeMillis(),
|
||||
null,
|
||||
"invite",
|
||||
"邀请用户注册赠送"
|
||||
);
|
||||
|
||||
// 给被邀请人(新用户)也赠送100积分作为邀请奖励
|
||||
// 注意:这里先不执行,等账户创建后再执行
|
||||
}
|
||||
|
||||
// 设置固定salt为666666
|
||||
String salt = "666666";
|
||||
sysUser.setSalt(salt);
|
||||
|
|
@ -1078,46 +994,9 @@ public class SysUserServiceImpl implements SysUserService {
|
|||
account.setDeleteFlag(Const.DELETE_FLAG_NO);
|
||||
accountMapper.insert(account);
|
||||
|
||||
// 注册赠送100积分
|
||||
accountService.addGiftBalance(
|
||||
sysUser.getUserId(),
|
||||
BigDecimal.valueOf(100),
|
||||
"gift_" + System.currentTimeMillis(),
|
||||
null,
|
||||
"register",
|
||||
"注册赠送"
|
||||
);
|
||||
|
||||
// 如果有邀请码,额外赠送100积分作为邀请奖励
|
||||
if (inviteCode != null && !inviteCode.isEmpty()) {
|
||||
accountService.addGiftBalance(
|
||||
sysUser.getUserId(),
|
||||
BigDecimal.valueOf(100),
|
||||
"gift_" + System.currentTimeMillis(),
|
||||
null,
|
||||
"invite_reward",
|
||||
"通过邀请码注册赠送"
|
||||
);
|
||||
}
|
||||
|
||||
return sysUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成8位邀请码
|
||||
*
|
||||
* @return 邀请码
|
||||
*/
|
||||
private String generateInviteCode() {
|
||||
String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
Random random = new Random();
|
||||
StringBuilder sb = new StringBuilder(8);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
sb.append(characters.charAt(random.nextInt(characters.length())));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成随机密码
|
||||
*
|
||||
|
|
@ -1165,14 +1044,13 @@ public class SysUserServiceImpl implements SysUserService {
|
|||
*
|
||||
* @param userId 用户ID
|
||||
* @param updatedUser 更新后的用户信息
|
||||
* @param token 用户的认证token
|
||||
*/
|
||||
@Override
|
||||
public void updateUserCache(Long userId, SysUser updatedUser, String token) {
|
||||
private void updateUserCache(Long userId, SysUser updatedUser) {
|
||||
try {
|
||||
// 验证 token 是否有效
|
||||
// 获取当前登录用户的 token
|
||||
String token = loginUserCacheUtil.getTokenFromRequest();
|
||||
if (token == null || token.isEmpty()) {
|
||||
log.warn("无效的 token,跳过缓存更新");
|
||||
log.warn("无法获取当前用户的 token,跳过缓存更新");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1204,59 +1082,4 @@ 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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,64 +0,0 @@
|
|||
package com.kexue.skills.task;
|
||||
|
||||
import com.kexue.skills.entity.AccountFrozen;
|
||||
import com.kexue.skills.mapper.AccountFrozenMapper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 账户冻结单定时任务
|
||||
* 扫描过期的冻结单并更新状态
|
||||
*
|
||||
* @author 系统生成
|
||||
* @since 2026-04-11
|
||||
*/
|
||||
@Component
|
||||
public class AccountFrozenTask {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AccountFrozenTask.class);
|
||||
|
||||
@Resource
|
||||
private AccountFrozenMapper accountFrozenMapper;
|
||||
|
||||
/**
|
||||
* 定时扫描过期的冻结单
|
||||
* 每隔30分钟执行一次
|
||||
*/
|
||||
@Scheduled(cron = "0 0/30 * * * ?")
|
||||
public void scanExpiredFrozen() {
|
||||
logger.info("开始扫描过期的冻结单");
|
||||
|
||||
try {
|
||||
// 查询所有状态为RESERVED且过期的冻结单
|
||||
List<AccountFrozen> expiredFrozenList = accountFrozenMapper.selectExpiredFrozen(new Date());
|
||||
|
||||
if (expiredFrozenList != null && !expiredFrozenList.isEmpty()) {
|
||||
logger.info("发现 {} 个过期的冻结单", expiredFrozenList.size());
|
||||
|
||||
for (AccountFrozen frozen : expiredFrozenList) {
|
||||
// 更新状态为FINALIZED
|
||||
frozen.setStatus("FINALIZED");
|
||||
frozen.setFinalizeReason("timeout");
|
||||
frozen.setUpdateTime(new Date());
|
||||
|
||||
// 执行更新
|
||||
accountFrozenMapper.updateByPrimaryKey(frozen);
|
||||
logger.info("已处理过期冻结单: {}", frozen.getFrozenId());
|
||||
}
|
||||
} else {
|
||||
logger.info("未发现过期的冻结单");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("扫描过期冻结单时发生错误", e);
|
||||
}
|
||||
|
||||
logger.info("扫描过期冻结单完成");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -61,7 +61,7 @@ sa-token:
|
|||
# 验证码配置
|
||||
captcha:
|
||||
# 是否启用验证码验证
|
||||
enabled: true
|
||||
enabled: false
|
||||
# 验证码有效期(秒)
|
||||
expire-time: 300
|
||||
# 验证码长度
|
||||
|
|
@ -104,7 +104,6 @@ jetcache:
|
|||
web:
|
||||
upload:
|
||||
path: /kexue/agentSkills/upload/
|
||||
userIconPath: /kexue/agentSkills/resources/userIcons
|
||||
|
||||
payment:
|
||||
# 微信支付配置
|
||||
|
|
|
|||
|
|
@ -4,9 +4,6 @@ spring:
|
|||
application:
|
||||
name: agentSkills
|
||||
version: 1.0.0
|
||||
# 禁用会话持久化,避免启动时恢复损坏的会话文件
|
||||
session:
|
||||
store-type: none
|
||||
mvc:
|
||||
pathmatch:
|
||||
matching-strategy: ANT_PATH_MATCHER
|
||||
|
|
@ -96,15 +93,4 @@ sms:
|
|||
web:
|
||||
upload:
|
||||
path: /kexue/agentSkills/upload/
|
||||
userIconPath: /kexue/agentSkills/resources/userIcons
|
||||
|
||||
# 雪花算法配置
|
||||
snowflake:
|
||||
workid: 1 # 机器ID,分布式部署时需要保证唯一
|
||||
|
||||
# 账户扣费配置
|
||||
account:
|
||||
deduction:
|
||||
# 扣费系数,默认2倍(实际消耗1积分,扣除2积分)
|
||||
coefficient: 2
|
||||
|
||||
|
|
|
|||
|
|
@ -1,93 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.kexue.skills.mapper.AccountFrozenMapper">
|
||||
|
||||
<resultMap id="BaseResultMap" type="com.kexue.skills.entity.AccountFrozen">
|
||||
<id column="frozen_id" property="frozenId" />
|
||||
<result column="account_transaction_id" property="accountTransactionId" />
|
||||
<result column="user_id" property="userId" />
|
||||
<result column="call_id" property="callId" />
|
||||
<result column="session_id" property="sessionId" />
|
||||
<result column="model_name" property="modelName" />
|
||||
<result column="question" property="question" />
|
||||
<result column="frozen_amount" property="frozenAmount" />
|
||||
<result column="frozen_type" property="frozenType" />
|
||||
<result column="final_amount" property="finalAmount" />
|
||||
<result column="usage_input_tokens" property="usageInputTokens" />
|
||||
<result column="usage_output_tokens" property="usageOutputTokens" />
|
||||
<result column="usage_total_tokens" property="usageTotalTokens" />
|
||||
<result column="finalize_reason" property="finalizeReason" />
|
||||
<result column="status" property="status" />
|
||||
<result column="expire_at" property="expireAt" />
|
||||
<result column="create_time" property="createTime" />
|
||||
<result column="update_time" property="updateTime" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="Base_Column_List">
|
||||
frozen_id, account_transaction_id, user_id, call_id, session_id, model_name, question, frozen_amount,
|
||||
frozen_type, final_amount, usage_input_tokens, usage_output_tokens, usage_total_tokens,
|
||||
finalize_reason, status, expire_at, create_time, update_time
|
||||
</sql>
|
||||
|
||||
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.String">
|
||||
select
|
||||
<include refid="Base_Column_List" />
|
||||
from account_frozen
|
||||
where frozen_id = #{frozenId}
|
||||
</select>
|
||||
|
||||
<insert id="insert" parameterType="com.kexue.skills.entity.AccountFrozen">
|
||||
insert into account_frozen
|
||||
(frozen_id, account_transaction_id, user_id, call_id, session_id, model_name, question, frozen_amount,
|
||||
frozen_type, final_amount, usage_input_tokens, usage_output_tokens, usage_total_tokens,
|
||||
finalize_reason, status, expire_at, create_time, update_time)
|
||||
values
|
||||
(#{frozenId}, #{accountTransactionId}, #{userId}, #{callId}, #{sessionId}, #{modelName}, #{question}, #{frozenAmount},
|
||||
#{frozenType}, #{finalAmount}, #{usageInputTokens}, #{usageOutputTokens}, #{usageTotalTokens},
|
||||
#{finalizeReason}, #{status}, #{expireAt}, #{createTime}, #{updateTime})
|
||||
</insert>
|
||||
|
||||
<update id="updateByPrimaryKey" parameterType="com.kexue.skills.entity.AccountFrozen">
|
||||
update account_frozen
|
||||
set
|
||||
account_transaction_id = #{accountTransactionId},
|
||||
user_id = #{userId},
|
||||
call_id = #{callId},
|
||||
session_id = #{sessionId},
|
||||
model_name = #{modelName},
|
||||
question = #{question},
|
||||
frozen_amount = #{frozenAmount},
|
||||
frozen_type = #{frozenType},
|
||||
final_amount = #{finalAmount},
|
||||
usage_input_tokens = #{usageInputTokens},
|
||||
usage_output_tokens = #{usageOutputTokens},
|
||||
usage_total_tokens = #{usageTotalTokens},
|
||||
finalize_reason = #{finalizeReason},
|
||||
status = #{status},
|
||||
expire_at = #{expireAt},
|
||||
update_time = #{updateTime}
|
||||
where frozen_id = #{frozenId}
|
||||
</update>
|
||||
|
||||
<select id="selectBySessionId" resultMap="BaseResultMap" parameterType="java.lang.String">
|
||||
select
|
||||
<include refid="Base_Column_List" />
|
||||
from account_frozen
|
||||
where session_id = #{sessionId}
|
||||
</select>
|
||||
|
||||
<select id="selectByUserIdAndStatus" resultMap="BaseResultMap">
|
||||
select
|
||||
<include refid="Base_Column_List" />
|
||||
from account_frozen
|
||||
where user_id = #{userId} and status = #{status}
|
||||
</select>
|
||||
|
||||
<select id="selectExpiredFrozen" resultMap="BaseResultMap" parameterType="java.util.Date">
|
||||
select
|
||||
<include refid="Base_Column_List" />
|
||||
from account_frozen
|
||||
where status = 'RESERVED' and expire_at is not null and expire_at < #{currentTime}
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
|
@ -15,7 +15,6 @@
|
|||
<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"/>
|
||||
|
|
@ -35,7 +34,7 @@
|
|||
<select id="queryById" 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,
|
||||
transaction_no, pay_type, business_id, business_type, 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_id = #{transactionId}
|
||||
|
|
@ -45,7 +44,7 @@
|
|||
<select id="getPageList" 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,
|
||||
transaction_no, pay_type, business_id, business_type, 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>
|
||||
|
|
@ -83,7 +82,7 @@
|
|||
<select id="getList" 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,
|
||||
transaction_no, pay_type, business_id, business_type, 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>
|
||||
|
|
@ -129,7 +128,6 @@
|
|||
<if test="payType != null">pay_type,</if>
|
||||
<if test="businessId != null">business_id,</if>
|
||||
<if test="businessType != null">business_type,</if>
|
||||
<if test="callId != null">call_id,</if>
|
||||
<if test="remark != null">remark,</if>
|
||||
<if test="isExpense != null">is_expense,</if>
|
||||
<if test="inputToken != null">input_token,</if>
|
||||
|
|
@ -156,7 +154,6 @@
|
|||
<if test="payType != null">#{payType},</if>
|
||||
<if test="businessId != null">#{businessId},</if>
|
||||
<if test="businessType != null">#{businessType},</if>
|
||||
<if test="callId != null">#{callId},</if>
|
||||
<if test="remark != null">#{remark},</if>
|
||||
<if test="isExpense != null">#{isExpense},</if>
|
||||
<if test="inputToken != null">#{inputToken},</if>
|
||||
|
|
@ -188,7 +185,6 @@
|
|||
<if test="payType != null">pay_type = #{payType},</if>
|
||||
<if test="businessId != null">business_id = #{businessId},</if>
|
||||
<if test="businessType != null">business_type = #{businessType},</if>
|
||||
<if test="callId != null">call_id = #{callId},</if>
|
||||
<if test="remark != null">remark = #{remark},</if>
|
||||
<if test="isExpense != null">is_expense = #{isExpense},</if>
|
||||
<if test="inputToken != null">input_token = #{inputToken},</if>
|
||||
|
|
@ -223,237 +219,11 @@
|
|||
<select id="queryByUserId" 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,
|
||||
transaction_no, pay_type, business_id, business_type, 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 user_id = #{userId}
|
||||
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,15 +131,12 @@
|
|||
</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'}, content_id desc
|
||||
order by ${sortBy} ${sortDesc ? 'desc' : 'asc'}
|
||||
</if>
|
||||
<if test="sortBy == null or sortBy == ''">
|
||||
order by sort asc, create_time desc, content_id desc
|
||||
order by sort asc, create_time desc
|
||||
</if>
|
||||
</select>
|
||||
|
||||
|
|
@ -206,10 +203,10 @@
|
|||
</if>
|
||||
</where>
|
||||
<if test="queryDto.sortBy != null and queryDto.sortBy != ''">
|
||||
order by ${queryDto.sortBy} ${queryDto.sortDesc ? 'desc' : 'asc'}, content_id desc
|
||||
order by ${queryDto.sortBy} ${queryDto.sortDesc ? 'desc' : 'asc'}
|
||||
</if>
|
||||
<if test="queryDto.sortBy == null or queryDto.sortBy == ''">
|
||||
order by sort asc, create_time desc, content_id desc
|
||||
order by sort asc, create_time desc
|
||||
</if>
|
||||
limit #{offset}, #{limit}
|
||||
</select>
|
||||
|
|
@ -252,16 +249,6 @@
|
|||
<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>
|
||||
|
||||
|
|
@ -320,17 +307,8 @@
|
|||
</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, content_id desc
|
||||
order by sort asc, create_time desc
|
||||
</select>
|
||||
|
||||
<!--新增所有列-->
|
||||
|
|
|
|||
|
|
@ -12,9 +12,6 @@
|
|||
<result property="outputPerCent" column="output_per_cent" jdbcType="BIGINT"/>
|
||||
<result property="unit" column="unit" jdbcType="VARCHAR"/>
|
||||
<result property="remark" column="remark" jdbcType="VARCHAR"/>
|
||||
<result property="minTokens" column="min_tokens" jdbcType="BIGINT"/>
|
||||
<result property="maxTokens" column="max_tokens" jdbcType="BIGINT"/>
|
||||
<result property="outputMode" column="output_mode" jdbcType="VARCHAR"/>
|
||||
<result property="createdTime" column="created_time" jdbcType="TIMESTAMP"/>
|
||||
<result property="updatedTime" column="updated_time" jdbcType="TIMESTAMP"/>
|
||||
</resultMap>
|
||||
|
|
@ -22,7 +19,7 @@
|
|||
<!--查询单个-->
|
||||
<select id="queryById" resultMap="ModelPriceMap">
|
||||
select
|
||||
id, vendor, model_name, input_price, output_price, input_per_cent, output_per_cent, unit, remark, min_tokens, max_tokens, output_mode, created_time, updated_time
|
||||
id, vendor, model_name, input_price, output_price, input_per_cent, output_per_cent, unit, remark, created_time, updated_time
|
||||
from model_price
|
||||
where id = #{id}
|
||||
</select>
|
||||
|
|
@ -30,7 +27,7 @@
|
|||
<!--通过模型名称查询-->
|
||||
<select id="queryByModelName" resultMap="ModelPriceMap">
|
||||
select
|
||||
id, vendor, model_name, input_price, output_price, input_per_cent, output_per_cent, unit, remark, min_tokens, max_tokens, output_mode, created_time, updated_time
|
||||
id, vendor, model_name, input_price, output_price, input_per_cent, output_per_cent, unit, remark, created_time, updated_time
|
||||
from model_price
|
||||
where model_name = #{modelName}
|
||||
</select>
|
||||
|
|
@ -38,7 +35,7 @@
|
|||
<!--分页查询-->
|
||||
<select id="getPageList" resultMap="ModelPriceMap">
|
||||
select
|
||||
id, vendor, model_name, input_price, output_price, input_per_cent, output_per_cent, unit, remark, min_tokens, max_tokens, output_mode, created_time, updated_time
|
||||
id, vendor, model_name, input_price, output_price, input_per_cent, output_per_cent, unit, remark, created_time, updated_time
|
||||
from model_price
|
||||
<where>
|
||||
<if test="vendor != null and vendor != ''">
|
||||
|
|
@ -56,7 +53,7 @@
|
|||
<!--查询列表-->
|
||||
<select id="getList" resultMap="ModelPriceMap">
|
||||
select
|
||||
id, vendor, model_name, input_price, output_price, input_per_cent, output_per_cent, unit, remark, min_tokens, max_tokens, output_mode, created_time, updated_time
|
||||
id, vendor, model_name, input_price, output_price, input_per_cent, output_per_cent, unit, remark, created_time, updated_time
|
||||
from model_price
|
||||
<where>
|
||||
<if test="vendor != null and vendor != ''">
|
||||
|
|
@ -80,9 +77,6 @@
|
|||
<if test="outputPerCent != null">output_per_cent,</if>
|
||||
<if test="unit != null">unit,</if>
|
||||
<if test="remark != null">remark,</if>
|
||||
<if test="minTokens != null">min_tokens,</if>
|
||||
<if test="maxTokens != null">max_tokens,</if>
|
||||
<if test="outputMode != null">output_mode,</if>
|
||||
<if test="createdTime != null">created_time,</if>
|
||||
<if test="updatedTime != null">updated_time,</if>
|
||||
</trim>
|
||||
|
|
@ -95,9 +89,6 @@
|
|||
<if test="outputPerCent != null">#{outputPerCent},</if>
|
||||
<if test="unit != null">#{unit},</if>
|
||||
<if test="remark != null">#{remark},</if>
|
||||
<if test="minTokens != null">#{minTokens},</if>
|
||||
<if test="maxTokens != null">#{maxTokens},</if>
|
||||
<if test="outputMode != null">#{outputMode},</if>
|
||||
<if test="createdTime != null">#{createdTime},</if>
|
||||
<if test="updatedTime != null">#{updatedTime},</if>
|
||||
</trim>
|
||||
|
|
@ -115,9 +106,6 @@
|
|||
<if test="outputPerCent != null">output_per_cent = #{outputPerCent},</if>
|
||||
<if test="unit != null">unit = #{unit},</if>
|
||||
<if test="remark != null">remark = #{remark},</if>
|
||||
<if test="minTokens != null">min_tokens = #{minTokens},</if>
|
||||
<if test="maxTokens != null">max_tokens = #{maxTokens},</if>
|
||||
<if test="outputMode != null">output_mode = #{outputMode},</if>
|
||||
<if test="createdTime != null">created_time = #{createdTime},</if>
|
||||
<if test="updatedTime != null">updated_time = #{updatedTime},</if>
|
||||
</set>
|
||||
|
|
@ -130,19 +118,4 @@
|
|||
where id = #{id}
|
||||
</delete>
|
||||
|
||||
<!--根据模型名称、输出模式和token数量查询价格规则-->
|
||||
<select id="queryByModelNameAndOutputModeAndTokens" resultMap="ModelPriceMap">
|
||||
<![CDATA[
|
||||
select
|
||||
id, vendor, model_name, input_price, output_price, input_per_cent, output_per_cent, unit, remark, min_tokens, max_tokens, output_mode, created_time, updated_time
|
||||
from model_price
|
||||
where model_name = #{modelName}
|
||||
and output_mode = #{outputMode}
|
||||
and min_tokens < #{tokens}
|
||||
and (max_tokens = -1 or max_tokens >= #{tokens})
|
||||
order by min_tokens desc
|
||||
limit 1
|
||||
]]>
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
|
|
|||
|
|
@ -1,90 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.kexue.skills.mapper.PackageConfigMapper">
|
||||
|
||||
<resultMap type="com.kexue.skills.entity.PackageConfig" id="PackageConfigMap">
|
||||
<result property="id" column="id" jdbcType="BIGINT"/>
|
||||
<result property="name" column="name" jdbcType="VARCHAR"/>
|
||||
<result property="price" column="price" jdbcType="DECIMAL"/>
|
||||
<result property="baseAmount" column="base_amount" jdbcType="DECIMAL"/>
|
||||
<result property="giftAmount" column="gift_amount" jdbcType="DECIMAL"/>
|
||||
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
|
||||
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
|
||||
</resultMap>
|
||||
|
||||
<!--查询单个-->
|
||||
<select id="queryById" resultMap="PackageConfigMap">
|
||||
select
|
||||
id, name, price, base_amount, gift_amount, create_time, update_time
|
||||
from package_config
|
||||
where id = #{id}
|
||||
</select>
|
||||
|
||||
<!--分页查询-->
|
||||
<select id="getPageList" resultMap="PackageConfigMap">
|
||||
select
|
||||
id, name, price, base_amount, gift_amount, create_time, update_time
|
||||
from package_config
|
||||
<where>
|
||||
<if test="name != null and name != ''">
|
||||
and name like concat('%', #{name}, '%')
|
||||
</if>
|
||||
</where>
|
||||
<if test="sortBy != null and sortBy != ''">
|
||||
order by ${sortBy} ${sortDesc ? 'desc' : 'asc'}
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<!--查询列表-->
|
||||
<select id="getList" resultMap="PackageConfigMap">
|
||||
select
|
||||
id, name, price, base_amount, gift_amount, create_time, update_time
|
||||
from package_config
|
||||
<where>
|
||||
<if test="name != null and name != ''">
|
||||
and name like concat('%', #{name}, '%')
|
||||
</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<!--新增数据-->
|
||||
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
|
||||
insert into package_config
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="name != null">name,</if>
|
||||
<if test="price != null">price,</if>
|
||||
<if test="baseAmount != null">base_amount,</if>
|
||||
<if test="giftAmount != null">gift_amount,</if>
|
||||
<if test="createTime != null">create_time,</if>
|
||||
<if test="updateTime != null">update_time,</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="name != null">#{name},</if>
|
||||
<if test="price != null">#{price},</if>
|
||||
<if test="baseAmount != null">#{baseAmount},</if>
|
||||
<if test="giftAmount != null">#{giftAmount},</if>
|
||||
<if test="createTime != null">#{createTime},</if>
|
||||
<if test="updateTime != null">#{updateTime},</if>
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
<!--更新数据-->
|
||||
<update id="update">
|
||||
update package_config
|
||||
<set>
|
||||
<if test="name != null">name = #{name},</if>
|
||||
<if test="price != null">price = #{price},</if>
|
||||
<if test="baseAmount != null">base_amount = #{baseAmount},</if>
|
||||
<if test="giftAmount != null">gift_amount = #{giftAmount},</if>
|
||||
<if test="updateTime != null">update_time = #{updateTime},</if>
|
||||
</set>
|
||||
where id = #{id}
|
||||
</update>
|
||||
|
||||
<!--删除-->
|
||||
<delete id="deleteById">
|
||||
delete from package_config
|
||||
where id = #{id}
|
||||
</delete>
|
||||
|
||||
</mapper>
|
||||
|
|
@ -3,39 +3,21 @@
|
|||
<mapper namespace="com.kexue.skills.mapper.SysLogMapper">
|
||||
|
||||
<resultMap type="com.kexue.skills.entity.SysLog" id="SysLogMap">
|
||||
<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"/>
|
||||
<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"/>
|
||||
</resultMap>
|
||||
|
||||
<!--查询单个-->
|
||||
<select id="queryById" resultMap="SysLogMap">
|
||||
select
|
||||
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
|
||||
LOG_ID, USER_ID, USER_NAME, LOG_TYPE, LOG_CONTENT, SERVER_IP, CLIENT_IP, LOG_TIME, NOTE
|
||||
from sys_log
|
||||
where LOG_ID = #{logId}
|
||||
</select>
|
||||
|
|
@ -43,44 +25,43 @@
|
|||
<!--查询指定行数据-->
|
||||
<select id="getPageList" resultMap="SysLogMap">
|
||||
select
|
||||
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
|
||||
LOG_ID, USER_ID, USER_NAME, LOG_TYPE, LOG_CONTENT, SERVER_IP, CLIENT_IP, LOG_TIME, NOTE
|
||||
from sys_log
|
||||
<where>
|
||||
<if test="logId != null">
|
||||
and LOG_ID = #{logId}
|
||||
</if>
|
||||
<if test="module != null and module != ''">
|
||||
and MODULE = #{module}
|
||||
<if test="userId != null and userId != ''">
|
||||
and USER_ID = #{userId}
|
||||
</if>
|
||||
<if test="description != null and description != ''">
|
||||
and DESCRIPTION like concat('%', #{description}, '%')
|
||||
<if test="userName != null and userName != ''">
|
||||
and USER_NAME = #{userName}
|
||||
</if>
|
||||
<if test="ip != null and ip != ''">
|
||||
and IP = #{ip}
|
||||
<if test="logType != null and logType != ''">
|
||||
and LOG_TYPE = #{logType}
|
||||
</if>
|
||||
<if test="status != null">
|
||||
and STATUS = #{status}
|
||||
<if test="logContent != null and logContent != ''">
|
||||
and LOG_CONTENT = #{logContent}
|
||||
</if>
|
||||
<if test="startTime != null">
|
||||
and CREATE_TIME >= #{startTime}
|
||||
<if test="serverIp != null and serverIp != ''">
|
||||
and SERVER_IP = #{serverIp}
|
||||
</if>
|
||||
<if test="endTime != null">
|
||||
and CREATE_TIME <= #{endTime}
|
||||
<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>
|
||||
</where>
|
||||
order by CREATE_TIME desc
|
||||
</select>
|
||||
|
||||
<!--查询指定行数据-->
|
||||
<select id="queryAllByLimit" resultMap="SysLogMap">
|
||||
select
|
||||
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
|
||||
LOG_ID, USER_ID, USER_NAME, LOG_TYPE, LOG_CONTENT, SERVER_IP, CLIENT_IP, LOG_TIME, NOTE
|
||||
from sys_log
|
||||
limit #{offset}, #{limit}
|
||||
</select>
|
||||
|
|
@ -88,45 +69,43 @@
|
|||
<!--通过实体作为筛选条件查询-->
|
||||
<select id="queryAll" resultMap="SysLogMap">
|
||||
select
|
||||
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
|
||||
LOG_ID, USER_ID, USER_NAME, LOG_TYPE, LOG_CONTENT, SERVER_IP, CLIENT_IP, LOG_TIME, NOTE
|
||||
from sys_log
|
||||
<where>
|
||||
<if test="logId != null">
|
||||
and LOG_ID = #{logId}
|
||||
</if>
|
||||
<if test="module != null and module != ''">
|
||||
and MODULE = #{module}
|
||||
<if test="userId != null and userId != ''">
|
||||
and USER_ID = #{userId}
|
||||
</if>
|
||||
<if test="description != null and description != ''">
|
||||
and DESCRIPTION like concat('%', #{description}, '%')
|
||||
<if test="userName != null and userName != ''">
|
||||
and USER_NAME = #{userName}
|
||||
</if>
|
||||
<if test="ip != null and ip != ''">
|
||||
and IP = #{ip}
|
||||
<if test="logType != null and logType != ''">
|
||||
and LOG_TYPE = #{logType}
|
||||
</if>
|
||||
<if test="status != null">
|
||||
and STATUS = #{status}
|
||||
<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>
|
||||
</where>
|
||||
order by CREATE_TIME desc
|
||||
</select>
|
||||
|
||||
<!--新增所有列-->
|
||||
<insert id="insert" keyProperty="logId" useGeneratedKeys="true">
|
||||
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 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>
|
||||
|
||||
|
||||
|
|
@ -155,62 +134,29 @@
|
|||
<update id="update">
|
||||
update sys_log
|
||||
<set>
|
||||
<if test="traceId != null and traceId != ''">
|
||||
TRACE_ID = #{traceId},
|
||||
<if test="userId != null and userId != ''">
|
||||
USER_ID = #{userId},
|
||||
</if>
|
||||
<if test="description != null and description != ''">
|
||||
DESCRIPTION = #{description},
|
||||
<if test="userName != null and userName != ''">
|
||||
USER_NAME = #{userName},
|
||||
</if>
|
||||
<if test="module != null and module != ''">
|
||||
MODULE = #{module},
|
||||
<if test="logType != null and logType != ''">
|
||||
LOG_TYPE = #{logType},
|
||||
</if>
|
||||
<if test="requestUrl != null and requestUrl != ''">
|
||||
REQUEST_URL = #{requestUrl},
|
||||
<if test="logContent != null and logContent != ''">
|
||||
LOG_CONTENT = #{logContent},
|
||||
</if>
|
||||
<if test="requestMethod != null and requestMethod != ''">
|
||||
REQUEST_METHOD = #{requestMethod},
|
||||
<if test="serverIp != null and serverIp != ''">
|
||||
SERVER_IP = #{serverIp},
|
||||
</if>
|
||||
<if test="requestHeaders != null and requestHeaders != ''">
|
||||
REQUEST_HEADERS = #{requestHeaders},
|
||||
<if test="clientIp != null and clientIp != ''">
|
||||
CLIENT_IP = #{clientIp},
|
||||
</if>
|
||||
<if test="requestBody != null and requestBody != ''">
|
||||
REQUEST_BODY = #{requestBody},
|
||||
<if test="logTime != null and logTime != ''">
|
||||
LOG_TIME = #{logTime},
|
||||
</if>
|
||||
<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 test="note != null and note != ''">
|
||||
NOTE = #{note},
|
||||
</if>
|
||||
</set>
|
||||
where LOG_ID = #{logId}
|
||||
|
|
|
|||
|
|
@ -15,16 +15,12 @@
|
|||
<result property="enable" column="enable" jdbcType="OTHER"/>
|
||||
<result property="deleteFlag" column="delete_flag" jdbcType="OTHER"/>
|
||||
<result property="sessionId" column="session_id" jdbcType="VARCHAR"/>
|
||||
<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_icon
|
||||
user_id, user_name, pwd, real_name, tel, email, salt, remark, create_time, enable, delete_flag, session_id
|
||||
from sys_user
|
||||
where user_id = #{userId}
|
||||
</select>
|
||||
|
|
@ -32,7 +28,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_icon
|
||||
user_id, user_name, pwd, real_name, tel, email, salt, remark, create_time, enable, delete_flag, session_id
|
||||
from sys_user
|
||||
<where>
|
||||
<if test="userId != null">
|
||||
|
|
@ -74,7 +70,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_icon
|
||||
user_id, user_name, pwd, real_name, tel, email, salt, remark, create_time, enable, delete_flag, session_id
|
||||
from sys_user
|
||||
limit #{offset}, #{limit}
|
||||
</select>
|
||||
|
|
@ -82,7 +78,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_icon
|
||||
user_id, user_name, pwd, real_name, tel, email, salt, remark, create_time, enable, delete_flag, session_id
|
||||
from sys_user
|
||||
<where>
|
||||
<if test="userId != null">
|
||||
|
|
@ -123,8 +119,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, user_icon)
|
||||
values (#{userName}, #{pwd}, #{realName}, #{tel}, #{email}, #{salt}, #{remark}, #{createTime}, #{enable}, #{deleteFlag}, #{sessionId}, #{inviteCode}, #{invitedCode}, #{invitedBy}, #{userIcon})
|
||||
insert into sys_user(user_name, pwd, real_name, tel, email, salt, remark, create_time, enable, delete_flag, session_id)
|
||||
values (#{userName}, #{pwd}, #{realName}, #{tel}, #{email}, #{salt}, #{remark}, #{createTime}, #{enable}, #{deleteFlag}, #{sessionId})
|
||||
</insert>
|
||||
|
||||
|
||||
|
|
@ -186,18 +182,6 @@
|
|||
<if test="sessionId != null and sessionId != ''">
|
||||
session_id = #{sessionId},
|
||||
</if>
|
||||
<if test="inviteCode != null and inviteCode != ''">
|
||||
invite_code = #{inviteCode},
|
||||
</if>
|
||||
<if test="invitedCode != null and invitedCode != ''">
|
||||
invited_code = #{invitedCode},
|
||||
</if>
|
||||
<if test="invitedBy != null">
|
||||
invited_by = #{invitedBy},
|
||||
</if>
|
||||
<if test="userIcon != null and userIcon != ''">
|
||||
user_icon = #{userIcon},
|
||||
</if>
|
||||
</set>
|
||||
where user_id = #{userId}
|
||||
</update>
|
||||
|
|
@ -209,7 +193,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_icon
|
||||
user_id, user_name, pwd, real_name, tel, email, salt, remark, create_time, enable, delete_flag, session_id
|
||||
from sys_user
|
||||
where (user_name = #{userName} or tel = #{userName})
|
||||
and delete_flag = 0
|
||||
|
|
@ -218,7 +202,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_icon
|
||||
user_id, user_name, pwd, real_name, tel, email, salt, remark, create_time, enable, delete_flag, session_id
|
||||
from sys_user
|
||||
where tel = #{tel}
|
||||
and delete_flag = 0
|
||||
|
|
@ -226,20 +210,11 @@
|
|||
</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_icon
|
||||
user_id, user_name, pwd, real_name, tel, email, salt, remark, create_time, enable, delete_flag, session_id
|
||||
from sys_user
|
||||
where session_id = #{sessionId}
|
||||
and delete_flag = 0
|
||||
limit 1
|
||||
</select>
|
||||
|
||||
<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_icon
|
||||
from sys_user
|
||||
where invite_code = #{inviteCode}
|
||||
and delete_flag = 0
|
||||
limit 1
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
|
|
|||
Loading…
Reference in New Issue