Skip to content

Refactor profile item config#8659

Merged
2dust merged 22 commits into2dust:masterfrom
DHR60:jsondata
Feb 5, 2026
Merged

Refactor profile item config#8659
2dust merged 22 commits into2dust:masterfrom
DHR60:jsondata

Conversation

@DHR60
Copy link
Copy Markdown
Contributor

@DHR60 DHR60 commented Jan 17, 2026

Related to #7887
Fixes #8249


  • 弃用 ProfileItem 的 Ports AlterId Flow,弃用 ProfileGroupItem 和 ProfileGroupItemManager
  • 将独属于部分 ConfigTypy 的字段迁移到 ProtocolExtraItem,如组配置的子项,vless 的 flow,vmess 的 aid,hy2 的多端口等
  • ProfileItem 添加 SetExtraItem GetExtraItem 方法
  • 迁移是直接在 InitComponents() 调用了个简单粗暴的异步迁移函数 MigrateProfileExtra(),将弃用字段迁移到 ProtocolExtraItem,如果遍历过程中查询到任意一个 JsonData 不为空,则直接退出迁移函数
  • 然后就是为 hy2 协议添加了 upMbps 等字段和界面

@2dust
Copy link
Copy Markdown
Owner

2dust commented Jan 17, 2026

感谢 PR。
这个 PR 感觉步子不够大啊。
参考
https://github.com/2dust/v2rayNG/blob/master/V2rayNG/app/src/main/java/com/v2ray/ang/dto/ProfileItem.kt

只保留大概下面的属性,其他全部用json 字符串,基本上json 字符串中都是各个 outbound 的特有属性。

val configVersion: Int = 4,
    val configType: EConfigType,
    var subscriptionId: String = "",
    var addedTime: Long = System.currentTimeMillis(),

    var remarks: String = "",
    var server: String? = null,
    var serverPort: String? = null,

    var network: String? = null,
  var host: String? = null,
    var path: String? = null,

  var tlssecurity: String? = null,
  var sni: String? = null,
    var alpn: String? = null,
    var fingerPrint: String? = null,
    var insecure: Boolean? = null,

迁移的时候给数据版本+1 即可,这个过程可能需要半年到一年的时间,然后可以删除迁移代码。

@2dust
Copy link
Copy Markdown
Owner

2dust commented Jan 17, 2026

原来的 ProfileItem 和 ProfileGroupItem 需要保留很长时间,方便迁移。
这个 PR 暂时不合并,等讨论清楚些再说

@2dust
Copy link
Copy Markdown
Owner

2dust commented Jan 17, 2026

https://github.com/2dust/v2rayNG/blob/master/V2rayNG/app/src/main/java/com/v2ray/ang/dto/ProfileItem.kt
参考这个的原因,是想把原来一些属性共用存储的问题也处理下,增加可读性。

@DHR60
Copy link
Copy Markdown
Contributor Author

DHR60 commented Jan 17, 2026

这个 PR 感觉步子不够大啊。

因为全部迁移到 json 我个人感觉没什么好处,反而增加了序列化反序列化的性能损耗

这个是为了给个别协议添加他们独属字段,放到 ProfileItem sql table 里未免有点过于浪费空间了

把原来一些属性共用存储的问题也处理下,增加可读性。

这个确实,密码有的用 id 有的用 security。我的想法是传输层的字段分开,放到 sql table 里;协议独属的放到 json 里

@2dust
Copy link
Copy Markdown
Owner

2dust commented Jan 18, 2026

因为全部迁移到 json 我个人感觉没什么好处,反而增加了序列化反序列化的性能损耗

确实不应该全部迁移,不过这不是序列化的性能问题,这个不值一提。 主要是开发麻烦了些。

大概下面的留下


  val configVersion: Int = 4,
    val configType: EConfigType,
    var subscriptionId: String = "",
    var addedTime: Long = System.currentTimeMillis(),

    var remarks: String = "",
    var server: String? = null,
    var serverPort: String? = null,

  var password: String? = null,
    var method: String? = null,  
    var username: String? = null,

    var network: String? = null,
    var headerType: String? = null,
    var host: String? = null,
    var path: String? = null,
    var seed: String? = null,
    var quicSecurity: String? = null,
    var quicKey: String? = null,
    var mode: String? = null,
    var serviceName: String? = null,
    var authority: String? = null,
    var xhttpMode: String? = null,
    var xhttpExtra: String? = null,

    var security: String? = null,
    var sni: String? = null,
    var alpn: String? = null,
    var fingerPrint: String? = null,
    var insecure: Boolean? = null,
    var echConfigList: String? = null,
    var echForceQuery: String? = null,

    var publicKey: String? = null,
    var shortId: String? = null,
    var spiderX: String? = null,
    var mldsa65Verify: String? = null,

其中这些也可以考虑放 json ,毕竟是会随着 network 变化

    var seed: String? = null,
    var quicSecurity: String? = null,
    var quicKey: String? = null,
    var mode: String? = null,
    var serviceName: String? = null,
    var authority: String? = null,
    var xhttpMode: String? = null,
    var xhttpExtra: String? = null,

@2dust
Copy link
Copy Markdown
Owner

2dust commented Jan 18, 2026

这个确实,密码有的用 id 有的用 security。我的想法是传输层的字段分开,放到 sql table 里;协议独属的放到 json 里

还有一个考虑,大概以 Vless 协议能用的为准

@DHR60
Copy link
Copy Markdown
Contributor Author

DHR60 commented Jan 18, 2026

和现在的差别也就是 network 传输层?

也就是说要重构传输层那边,重构的工作量肯定不小,从 GUI 到分享链接生成都得改。

不如未来在加一个 NetworkTranExtraItem,又不是不能放两个 json,ProtocolExtraItem 膨胀了也不好

并且和这个 pr 不冲突吧,先重构协议再重构传输层,总不能一口吃成胖子吧

@2dust
Copy link
Copy Markdown
Owner

2dust commented Jan 18, 2026

NetworkTranExtraItem ,这个可行,毕竟Network这里太多属性了。
每次重构都可考虑迁移数据,所以想讨论清楚再做;如果不考虑数据迁移,随便改就行了

@2dust
Copy link
Copy Markdown
Owner

2dust commented Jan 18, 2026

把前几次你 PR 的先发一个版本后,再合并这个 pr。
然后把这些重构的在一个版本里面完成了,慢慢来

@DHR60
Copy link
Copy Markdown
Contributor Author

DHR60 commented Jan 19, 2026

行,那我这里就继续重构协议 Extra 部分了

看了下 sql 里面可以只保留 password / id;
method 只有 vmess 和 ss 会用,username 只有 socks 和 http 用,这两个就放到 json 里面吧

@DHR60
Copy link
Copy Markdown
Contributor Author

DHR60 commented Jan 21, 2026

感觉差不多了,协议部分已经拆出来了

https://github.com/2dust/v2rayN/pull/8659/changes#v2rayN/ServiceLib/Models/ProtocolExtraItem.cs

然后 ProfileItem 这些被弃用了

    [Obsolete("Use ProtocolExtraItem.Ports instead.")]
    public string Ports { get; set; }

    [Obsolete("Use ProtocolExtraItem.AlterId instead.")]
    public int AlterId { get; set; }

    [Obsolete("Use ProtocolExtraItem.Flow instead.")]
    public string Flow { get; set; }

    [Obsolete("Use ProfileItem.Password instead.")]
    public string Id { get; set; }

    [Obsolete("Use ProtocolExtraItem.xxx instead.")]
    public string Security { get; set; }

ProtoExtra = JsonUtils.Serialize(extraItem, false);
}

public ProtocolExtraItem GetProtocolExtra()
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里在内部维护一个缓存 _protocolExtraCache ,这样不用每次都转换了。并且在 保存的时候,直接在存储到数据库之前 调用一次就可以了。不要在每个地方都频繁的转换

    public void SetProtocolExtra(  )
    {
        ProtoExtra = JsonUtils.Serialize(_protocolExtraCache, false);
    }
    private ProtocolExtraItem? _protocolExtraCache;
    public ProtocolExtraItem GetProtocolExtra()
    {
        return _protocolExtraCache ??= JsonUtils.Deserialize<ProtocolExtraItem>(ProtoExtra) ?? new ProtocolExtraItem();
    }

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public void SetProtocolExtra(ProtocolExtraItem extraItem)
这个函数还保留,在一些地方还用的到

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

当时写到一半的时候就考虑过做成属性+缓存的形式

后面是怕未来哪个地方可能直接对 raw jsondata str 读写,所以就没做

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

好像也行,jsondata 只读暴露出去,里面只存 ProtocolExtraItem 私有变量

我写写试试

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个函数再这样改造下,这样保存调用 SetProtocolExtra()的时候行为也统一了

 public void SetProtocolExtra(ProtocolExtraItem extraItem)
    {
        _protocolExtraCache = extraItem;
        ProtoExtra = JsonUtils.Serialize(extraItem, false);

    }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

我是这样处理了,内部只存了个对象,c8922dd

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这样的话,感觉还可以再优化

[Ignore]
public ProtocolExtraItem ProtocolExtraItem
{
get => ...
set => ...
}

然后 ProtocolExtraItem 用 record class,可以直接

item.ProtocolExtraItem = item.ProtocolExtraItem with { Flow = "xtls" }

就不用担心忘写回了

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Ignore]
public ProtocolExtraItem ProtocolExtraItem
{
get => ...
set => ...
}

这个方案 get get 挺方便的;但是 get set 里面如果继续每次都 转换json 不合适

@DHR60
Copy link
Copy Markdown
Contributor Author

DHR60 commented Jan 21, 2026

okok,现在写法大概是这样,仅在 set 时更改两值:
不是,犯傻了,我再想想

这样,用时初始化,初始为 null

    public string ProtoExtra
    {
        get => _protocolExtraJsonStr;
        set
        {
            _protocolExtraJsonStr = value;
            if (_protocolExtraCache is not null)
            {
                _protocolExtraCache = JsonUtils.Deserialize<ProtocolExtraItem>(_protocolExtraJsonStr) ?? new ProtocolExtraItem();
            }
        }
    }

    [Ignore]
    public ProtocolExtraItem ProtocolExtraItem
    {
        get => _protocolExtraCache ??= JsonUtils.Deserialize<ProtocolExtraItem>(_protocolExtraJsonStr) ?? new ProtocolExtraItem();
        set
        {
            _protocolExtraCache = value;
            _protocolExtraJsonStr = JsonUtils.Serialize(_protocolExtraCache);
        }
    }

@2dust
Copy link
Copy Markdown
Owner

2dust commented Jan 21, 2026

public string ProtoExtra
    {
        get => _protocolExtraJsonStr;
        set
        {
            _protocolExtraJsonStr = value;
            _protocolExtraCache = JsonUtils.Deserialize<ProtocolExtraItem>(_protocolExtraJsonStr) ?? new ProtocolExtraItem();
        }
    }

只要 ProtoExtra set 里面有 json 处理,从数据库中查询出来会执行 N 次 ,即使不用到也会处理这样很浪费

@2dust
Copy link
Copy Markdown
Owner

2dust commented Jan 21, 2026

所以还是保持你第一版的, public string ProtoExtra ,
加上函数中处理json ,加缓存应该就可以了。

@2dust
Copy link
Copy Markdown
Owner

2dust commented Jan 21, 2026

你先不动,要不我把我的想法在你的这个基础上改下,先只改部分代码。

@DHR60
Copy link
Copy Markdown
Contributor Author

DHR60 commented Jan 21, 2026

还是避不开 string 改了,cache 没改这个问题吧

@DHR60
Copy link
Copy Markdown
Contributor Author

DHR60 commented Jan 21, 2026

如果不考虑这个的话直接这样就行

    public string ProtoExtra { get; set;}

    [Ignore]
    public ProtocolExtraItem ProtocolExtraItem
    {
        get => _protocolExtraCache ??= JsonUtils.Deserialize<ProtocolExtraItem>(_protocolExtraJsonStr) ?? new ProtocolExtraItem();
        set
        {
            _protocolExtraCache = value;
            ProtoExtra = JsonUtils.Serialize(_protocolExtraCache);
        }
    }

@2dust
Copy link
Copy Markdown
Owner

2dust commented Jan 21, 2026

如果不考虑这个的话直接这样就行

    public string ProtoExtra { get; set;}

    [Ignore]
    public ProtocolExtraItem ProtocolExtraItem
    {
        get => _protocolExtraCache ??= JsonUtils.Deserialize<ProtocolExtraItem>(_protocolExtraJsonStr) ?? new ProtocolExtraItem();
        set
        {
            _protocolExtraCache = value;
            ProtoExtra = JsonUtils.Serialize(_protocolExtraCache);
        }
    }

这个可能行,但是对你原来的改动就有点大了。
ProtocolExtraItem 这个其实就相当于两个函数 ,get 和 set 了;
我来测试下

@2dust
Copy link
Copy Markdown
Owner

2dust commented Jan 21, 2026

    public string ProtoExtra { get; set;}

    [Ignore]
    public ProtocolExtraItem ProtocolExtraItem
    {
        get => _protocolExtraCache ??= JsonUtils.Deserialize<ProtocolExtraItem>(_protocolExtraJsonStr) ?? new ProtocolExtraItem();
        set
        {
            _protocolExtraCache = value;
            ProtoExtra = JsonUtils.Serialize(_protocolExtraCache);
        }
    }

实测这个不行。
只要是属性,构造这个实体的时候,属性里面的 set 会自动执行;
所以还是得是第一版的 函数更好,只有用的时候才执行

@DHR60
Copy link
Copy Markdown
Contributor Author

DHR60 commented Jan 21, 2026

4ef6b2d 用 record 代替了 class
用法从

var protocolExtra = item.GetProtocolExtra();
protocolExtra.VlessEncryption = GetQueryValue(query, "encryption", Global.None);
protocolExtra.Flow = GetQueryValue(query, "flow");
item.SetProtocolExtra(protocolExtra);

简化成了

item.SetProtocolExtra(item.GetProtocolExtra() with
{
    VlessEncryption = GetQueryValue(query, "encryption", Global.None),
    Flow = GetQueryValue(query, "flow")
});

@2dust
Copy link
Copy Markdown
Owner

2dust commented Jan 22, 2026

4ef6b2d 用 record 代替了 class 用法从

var protocolExtra = item.GetProtocolExtra();
protocolExtra.VlessEncryption = GetQueryValue(query, "encryption", Global.None);
protocolExtra.Flow = GetQueryValue(query, "flow");
item.SetProtocolExtra(protocolExtra);

简化成了

item.SetProtocolExtra(item.GetProtocolExtra() with
{
    VlessEncryption = GetQueryValue(query, "encryption", Global.None),
    Flow = GetQueryValue(query, "flow")
});

个人更喜欢用原来的写法,代码虽然多了,但是更清晰明了。

@DHR60
Copy link
Copy Markdown
Contributor Author

DHR60 commented Jan 22, 2026

因为前一种开发时老是忘写 SetProtocolExtra 写回

不过现在应该也不用写回了

总感觉 GetProtocolExtra 返回的是深拷贝,不写回不太放心

@2dust
Copy link
Copy Markdown
Owner

2dust commented Jan 22, 2026

因为前一种开发时老是忘写 SetProtocolExtra 写回

不过现在应该也不用写回了

总感觉 GetProtocolExtra 返回的是深拷贝,不写回不太放心

public ProtocolExtraItem GetProtocolExtra()
{
    return _protocolExtraCache ??= JsonUtils.Deserialize<ProtocolExtraItem>(ProtoExtra) ?? new ProtocolExtraItem();
}

从这个代码分析,只要 ProtocolExtraItem 是从 GetProtocolExtra() 获取的,那外面使用的 Item 就是 _protocolExtraCache 的引用,
只要在保存之前的 common 代码里面有 SetProtocolExtra() 就没有问题。

如果外面是 new ProtocolExtraItem() 出来的,就需要自己额外处理下;
所以没有特殊情况,都应该从 GetProtocolExtra() 获取,即使原来是没有的

@DHR60
Copy link
Copy Markdown
Contributor Author

DHR60 commented Jan 22, 2026

第一种也可以优化 public ProtocolExtraItem ProtocolExtra => GetProtocolExtra();
仅get的应该不会被自动调用,然后就可以直接写 item.ProtocolExtra.Flow = GetQueryValue(query, "flow");

但是第二种写都写了,懒得改回去了

@2dust
Copy link
Copy Markdown
Owner

2dust commented Feb 5, 2026

准备合并了,还有要改的吗?

@2dust
Copy link
Copy Markdown
Owner

2dust commented Feb 5, 2026

Username 和 Password 放一块吧,虽然目前只有 http 和 socks 用到,你觉得如何?

@DHR60
Copy link
Copy Markdown
Contributor Author

DHR60 commented Feb 5, 2026

不用吧
Password 我是做成协议密码这种意思,比如 vless 的 uuid 和 ss 的 password
Username 就是 http, socks 和 TUIC 的

@DHR60
Copy link
Copy Markdown
Contributor Author

DHR60 commented Feb 5, 2026

应该没问题了

@DHR60
Copy link
Copy Markdown
Contributor Author

DHR60 commented Feb 5, 2026

等下,改个 HopInterval

@DHR60
Copy link
Copy Markdown
Contributor Author

DHR60 commented Feb 5, 2026

好了

@2dust
Copy link
Copy Markdown
Owner

2dust commented Feb 5, 2026

不用吧 Password 我是做成协议密码这种意思,比如 vless 的 uuid 和 ss 的 password Username 就是 http, socks 和 TUIC 的

确实 Username 放json 合理,但是看到太多这样的代码,感觉有点过度设计了。

var pw = Utils.Base64Encode($"{item.GetProtocolExtra().Username}:{item.Password}", true);

@DHR60
Copy link
Copy Markdown
Contributor Author

DHR60 commented Feb 5, 2026

@DHR60
Copy link
Copy Markdown
Contributor Author

DHR60 commented Feb 5, 2026

好了

@2dust 2dust merged commit 677e81f into 2dust:master Feb 5, 2026
@DHR60 DHR60 deleted the jsondata branch February 7, 2026 13:16
@DHR60 DHR60 mentioned this pull request Mar 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature request]:支持为hysteria2单个节点配置上下行速度和支持修改端口跳跃时间

2 participants