定时任务流量上报

This commit is contained in:
lys 2026-03-24 11:52:00 +08:00
parent a850899638
commit 73852fc5bd
10 changed files with 461 additions and 6 deletions

View File

@ -16,28 +16,28 @@ dataSources:
rules:
- !SHARDING
tables: # 数据分片规则配置
# 节点流量记录表(按天分表)
tables:
net_node_flow_record:
actualDataNodes: net_flow_record.net_node_flow_record_${['20260323']}
actualDataNodes: net_flow_record.net_node_flow_record_${['20260324','20260325','20260326','20260327','20260328','20260329','20260330','20260331','20260401','20260402','20260403','20260404','20260405','20260406','20260407','20260408','20260409','20260410','20260411','20260412','20260413','20260414','20260415','20260416','20260417','20260418','20260419','20260420','20260421','20260422','20260423','20260424','20260425','20260426','20260427','20260428','20260429','20260430','20260501','20260502','20260503','20260504','20260505','20260506','20260507','20260508','20260509','20260510','20260511','20260512','20260513','20260514','20260515','20260516','20260517','20260518','20260519','20260520','20260521','20260522','20260523','20260524','20260525','20260526','20260527','20260528','20260529','20260530','20260531','20260601','20260602','20260603','20260604','20260605','20260606','20260607','20260608','20260609','20260610','20260611','20260612','20260613','20260614','20260615','20260616','20260617','20260618','20260619','20260620','20260621','20260622','20260623','20260624','20260625','20260626','20260627','20260628','20260629','20260630','20260701','20260702','20260703','20260704','20260705','20260706','20260707','20260708','20260709','20260710','20260711','20260712','20260713','20260714','20260715','20260716','20260717','20260718','20260719','20260720','20260721','20260722','20260723','20260724','20260725','20260726','20260727','20260728','20260729','20260730','20260731','20260801','20260802','20260803','20260804','20260805','20260806','20260807','20260808','20260809','20260810','20260811','20260812','20260813','20260814','20260815','20260816','20260817','20260818','20260819','20260820','20260821','20260822','20260823','20260824','20260825','20260826','20260827','20260828','20260829','20260830','20260831','20260901','20260902','20260903','20260904','20260905','20260906','20260907','20260908','20260909','20260910','20260911','20260912','20260913','20260914','20260915','20260916','20260917','20260918','20260919','20260920','20260921','20260922','20260923','20260924','20260925','20260926','20260927','20260928','20260929','20260930','20261001','20261002','20261003','20261004','20261005','20261006','20261007','20261008','20261009','20261010','20261011','20261012','20261013','20261014','20261015','20261016','20261017','20261018','20261019','20261020','20261021','20261022','20261023','20261024','20261025','20261026','20261027','20261028','20261029','20261030','20261031','20261101','20261102','20261103','20261104','20261105','20261106','20261107','20261108','20261109','20261110','20261111','20261112','20261113','20261114','20261115','20261116','20261117','20261118','20261119','20261120','20261121','20261122','20261123','20261124','20261125','20261126','20261127','20261128','20261129','20261130','20261201','20261202','20261203','20261204','20261205','20261206','20261207','20261208','20261209','20261210','20261211','20261212','20261213','20261214','20261215','20261216','20261217','20261218','20261219','20261220','20261221','20261222','20261223','20261224','20261225','20261226','20261227','20261228','20261229','20261230','20261231','20270101','20270102','20270103','20270104','20270105','20270106','20270107','20270108','20270109','20270110','20270111','20270112','20270113','20270114','20270115','20270116','20270117','20270118','20270119','20270120','20270121','20270122','20270123','20270124','20270125','20270126','20270127','20270128','20270129','20270130','20270131','20270201','20270202','20270203','20270204','20270205','20270206','20270207','20270208','20270209','20270210','20270211','20270212','20270213','20270214','20270215','20270216','20270217','20270218','20270219','20270220','20270221','20270222','20270223','20270224','20270225','20270226','20270227','20270228','20270301','20270302','20270303','20270304','20270305','20270306','20270307','20270308','20270309','20270310','20270311','20270312','20270313','20270314','20270315','20270316','20270317','20270318','20270319','20270320','20270321','20270322','20270323','20270324']}
tableStrategy:
standard:
shardingColumn: create_time
shardingAlgorithmName: daily_sharding
# 用户流量记录表(按天分表)
net_user_flow_record:
actualDataNodes: net_flow_record.net_user_flow_record_${['20260323']}
actualDataNodes: net_flow_record.net_user_flow_record_${['20260324','20260325','20260326','20260327','20260328','20260329','20260330','20260331','20260401','20260402','20260403','20260404','20260405','20260406','20260407','20260408','20260409','20260410','20260411','20260412','20260413','20260414','20260415','20260416','20260417','20260418','20260419','20260420','20260421','20260422','20260423','20260424','20260425','20260426','20260427','20260428','20260429','20260430','20260501','20260502','20260503','20260504','20260505','20260506','20260507','20260508','20260509','20260510','20260511','20260512','20260513','20260514','20260515','20260516','20260517','20260518','20260519','20260520','20260521','20260522','20260523','20260524','20260525','20260526','20260527','20260528','20260529','20260530','20260531','20260601','20260602','20260603','20260604','20260605','20260606','20260607','20260608','20260609','20260610','20260611','20260612','20260613','20260614','20260615','20260616','20260617','20260618','20260619','20260620','20260621','20260622','20260623','20260624','20260625','20260626','20260627','20260628','20260629','20260630','20260701','20260702','20260703','20260704','20260705','20260706','20260707','20260708','20260709','20260710','20260711','20260712','20260713','20260714','20260715','20260716','20260717','20260718','20260719','20260720','20260721','20260722','20260723','20260724','20260725','20260726','20260727','20260728','20260729','20260730','20260731','20260801','20260802','20260803','20260804','20260805','20260806','20260807','20260808','20260809','20260810','20260811','20260812','20260813','20260814','20260815','20260816','20260817','20260818','20260819','20260820','20260821','20260822','20260823','20260824','20260825','20260826','20260827','20260828','20260829','20260830','20260831','20260901','20260902','20260903','20260904','20260905','20260906','20260907','20260908','20260909','20260910','20260911','20260912','20260913','20260914','20260915','20260916','20260917','20260918','20260919','20260920','20260921','20260922','20260923','20260924','20260925','20260926','20260927','20260928','20260929','20260930','20261001','20261002','20261003','20261004','20261005','20261006','20261007','20261008','20261009','20261010','20261011','20261012','20261013','20261014','20261015','20261016','20261017','20261018','20261019','20261020','20261021','20261022','20261023','20261024','20261025','20261026','20261027','20261028','20261029','20261030','20261031','20261101','20261102','20261103','20261104','20261105','20261106','20261107','20261108','20261109','20261110','20261111','20261112','20261113','20261114','20261115','20261116','20261117','20261118','20261119','20261120','20261121','20261122','20261123','20261124','20261125','20261126','20261127','20261128','20261129','20261130','20261201','20261202','20261203','20261204','20261205','20261206','20261207','20261208','20261209','20261210','20261211','20261212','20261213','20261214','20261215','20261216','20261217','20261218','20261219','20261220','20261221','20261222','20261223','20261224','20261225','20261226','20261227','20261228','20261229','20261230','20261231','20270101','20270102','20270103','20270104','20270105','20270106','20270107','20270108','20270109','20270110','20270111','20270112','20270113','20270114','20270115','20270116','20270117','20270118','20270119','20270120','20270121','20270122','20270123','20270124','20270125','20270126','20270127','20270128','20270129','20270130','20270131','20270201','20270202','20270203','20270204','20270205','20270206','20270207','20270208','20270209','20270210','20270211','20270212','20270213','20270214','20270215','20270216','20270217','20270218','20270219','20270220','20270221','20270222','20270223','20270224','20270225','20270226','20270227','20270228','20270301','20270302','20270303','20270304','20270305','20270306','20270307','20270308','20270309','20270310','20270311','20270312','20270313','20270314','20270315','20270316','20270317','20270318','20270319','20270320','20270321','20270322','20270323','20270324']}
tableStrategy:
standard:
shardingColumn: create_time
shardingAlgorithmName: daily_sharding
shardingAlgorithms:
# 新增: 按天分表算法
daily_sharding:
type: CLASS_BASED
props:
strategy: standard
algorithmClassName: org.dromara.common.mybatis.sharding.DailyShardingAlgorithm
props:
sql-show: true

View File

@ -0,0 +1,87 @@
package org.dromara.common.mybatis.sharding;
import cn.hutool.core.date.DateUtil;
import com.google.common.collect.Range;
import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm;
import java.util.*;
/**
* 按天分表算法
* 表名后缀格式: _yyyyMMdd
*
* @author lys
* @date 2025-03-23
*/
public class DailyShardingAlgorithm implements StandardShardingAlgorithm<Comparable<?>> {
private static final String DATE_PATTERN = "yyyyMMdd";
@Override
public void init(Properties props) {
// 无需初始化
}
/**
* 从分片值中提取日期
*
* @param shardingValue 分片值
* @return 日期
*/
private Date extractDate(Comparable<?> shardingValue) {
if (shardingValue instanceof Date) {
return (Date) shardingValue;
}
throw new UnsupportedOperationException("分片值必须是 Date 类型,当前值: " + shardingValue);
}
/**
* 从可用表名集合中获取逻辑表名
*
* @param availableTargetNames 可用表名集合
* @return 逻辑表名不带后缀
*/
private String getLogicTableName(Collection<String> availableTargetNames) {
// 从第一个可用表名中提取逻辑表名
String firstTableName = availableTargetNames.iterator().next();
if (firstTableName != null) {
int underscoreIndex = firstTableName.lastIndexOf('_');
if (underscoreIndex > 0) {
return firstTableName.substring(0, underscoreIndex);
}
}
throw new IllegalStateException("无法从可用表名中提取逻辑表名");
}
@Override
public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Comparable<?>> shardingValue) {
// 获取分片值对应的日期
Date date = extractDate(shardingValue.getValue());
// 格式化为 yyyyMMdd
String suffix = DateUtil.format(date, DATE_PATTERN);
// 构建目标表名
String logicTableName = getLogicTableName(availableTargetNames);
return logicTableName + "_" + suffix;
}
@Override
public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Comparable<?>> shardingValue) {
// 获取范围查询的日期范围
Range<Comparable<?>> range = shardingValue.getValueRange();
Date lower = extractDate(range.lowerEndpoint());
Date upper = extractDate(range.upperEndpoint());
String logicTableName = getLogicTableName(availableTargetNames);
// 生成日期范围内的所有表名
Set<String> result = new LinkedHashSet<>();
Date current = lower;
while (!current.after(upper)) {
result.add(logicTableName + "_" + DateUtil.format(current, DATE_PATTERN));
current = DateUtil.offsetDay(current, 1);
}
// 包含上边界
result.add(logicTableName + "_" + DateUtil.format(upper, DATE_PATTERN));
return result;
}
}

View File

@ -0,0 +1,55 @@
package org.dromara.net.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 节点流量记录表 net_node_flow_record
* 记录每个节点的流量汇总数据
*
* @author lys
* @date 2025-03-23
*/
@Data
@TableName("net_node_flow_record")
public class NetNodeFlowRecord implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id")
private Long id;
/**
* 节点ID
*/
private Long nodeId;
/**
* 上传流量字节
*/
private Long upNum;
/**
* 下载流量字节
*/
private Long downNum;
/**
* 总流量字节
*/
private Long totalNum;
/**
* 记录时间
*/
private Date createTime;
}

View File

@ -0,0 +1,55 @@
package org.dromara.net.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 用户流量记录表 net_user_flow_record
* 记录每个用户的流量汇总数据
*
* @author lys
* @date 2025-03-23
*/
@Data
@TableName("net_user_flow_record")
public class NetUserFlowRecord implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id")
private Long id;
/**
* 用户ID
*/
private Long userId;
/**
* 上传流量字节
*/
private Long upNum;
/**
* 下载流量字节
*/
private Long downNum;
/**
* 总流量字节
*/
private Long totalNum;
/**
* 记录时间
*/
private Date createTime;
}

View File

@ -0,0 +1,114 @@
package org.dromara.net.dto;
import lombok.Data;
import java.util.List;
/**
* 添加用户到节点请求DTO
* 对应 POST /node/handler/add-user 接口
*
* @author lys
* @date 2025-03-20
*/
@Data
public class AddUserToNodeRequest {
/**
* 用户数据列表
*/
private List<InboundData> data;
/**
* 哈希数据
*/
private HashData hashData;
/**
* 入站数据 - 基类
*/
@Data
public static class InboundData {
/**
* 协议类型: vless, trojan, shadowsocks
*/
private String type;
/**
* 入站标签
*/
private String tag;
/**
* 用户名
*/
private String username;
}
/**
* VLESS入站数据
*/
@Data
public static class VlessInboundData extends InboundData {
/**
* UUID
*/
private String uuid;
/**
* 流控: xtls-rprx-vision 或空字符串
*/
private String flow;
}
/**
* Trojan入站数据
*/
@Data
public static class TrojanInboundData extends InboundData {
/**
* 密码
*/
private String password;
}
/**
* Shadowsocks入站数据
*/
@Data
public static class ShadowsocksInboundData extends InboundData {
/**
* 密码
*/
private String password;
/**
* 加密类型
* 5 = AES_128_GCM
* 6 = AES_256_GCM
* 7 = CHACHA20_POLY1305
*/
private Integer cipherType;
/**
* IV检查
*/
private Boolean ivCheck;
}
/**
* 哈希数据
*/
@Data
public static class HashData {
/**
* VLESS UUID
*/
private String vlessUuid;
/**
* 上一个VLESS UUID可选
*/
private String prevVlessUuid;
}
}

View File

@ -0,0 +1,32 @@
package org.dromara.net.dto;
import lombok.Data;
/**
* 添加用户到节点响应DTO
* 对应 POST /node/handler/add-user 接口响应
*
* @author lys
* @date 2025-03-20
*/
@Data
public class AddUserToNodeResponse {
/**
* 响应数据
*/
private ResponseData response;
@Data
public static class ResponseData {
/**
* 是否成功
*/
private Boolean success;
/**
* 错误信息
*/
private String error;
}
}

View File

@ -0,0 +1,26 @@
package org.dromara.net.dto;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 节点用户流量统计请求DTO
* 对应 POST /node/stats/get-users-stats 接口的请求体
*
* @author lys
* @date 2025-03-23
*/
@Data
@NoArgsConstructor
public class NodeUserStatsRequest {
/**
* 是否重置统计数据
* true - 获取数据后重置节点上的流量统计
*/
private Boolean reset;
public NodeUserStatsRequest(Boolean reset) {
this.reset = reset;
}
}

View File

@ -0,0 +1,50 @@
package org.dromara.net.dto;
import lombok.Data;
import java.util.List;
/**
* 节点用户流量统计响应DTO
* 对应 POST /node/stats/get-users-stats 接口的响应
*
* @author lys
* @date 2025-03-23
*/
@Data
public class NodeUserStatsResponse {
/**
* 响应数据
*/
private ResponseData response;
@Data
public static class ResponseData {
/**
* 用户流量列表
*/
private List<UserFlowData> users;
}
/**
* 用户流量数据
*/
@Data
public static class UserFlowData {
/**
* 用户名用户ID字符串形式
*/
private String username;
/**
* 下载流量字节
*/
private Long downlink;
/**
* 上传流量字节
*/
private Long uplink;
}
}

View File

@ -0,0 +1,18 @@
package org.dromara.net.mapper;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.dromara.net.domain.NetNodeFlowRecord;
/**
* 节点流量记录Mapper接口
*
* @author lys
* @date 2025-03-23
*/
@Mapper
@DS(value = "net_flow_record")
public interface NetNodeFlowRecordMapper extends BaseMapper<NetNodeFlowRecord> {
}

View File

@ -0,0 +1,18 @@
package org.dromara.net.mapper;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.dromara.net.domain.NetUserFlowRecord;
/**
* 用户流量记录Mapper接口
*
* @author lys
* @date 2025-03-23
*/
@Mapper
@DS(value = "net_flow_record")
public interface NetUserFlowRecordMapper extends BaseMapper<NetUserFlowRecord> {
}