import 'package:json_annotation/json_annotation.dart'; part 'channel_app_model.g.dart'; /// 渠道应用信息响应模型 @JsonSerializable() class ChannelAppResponse { @JsonKey(name: 'code') final int code; @JsonKey(name: 'data') final ChannelAppData? data; @JsonKey(name: 'msg') final String msg; const ChannelAppResponse({ required this.code, this.data, required this.msg, }); factory ChannelAppResponse.fromJson(Map json) => _$ChannelAppResponseFromJson(json); Map toJson() => _$ChannelAppResponseToJson(this); } /// 渠道应用数据模型 @JsonSerializable() class ChannelAppData { @JsonKey(name: 'app') final AppInfo app; const ChannelAppData({ required this.app, }); factory ChannelAppData.fromJson(Map json) => _$ChannelAppDataFromJson(json); Map toJson() => _$ChannelAppDataToJson(this); } /// 应用信息模型 @JsonSerializable() class AppInfo { /// 下载地址(可选) @JsonKey(name: 'address') final String? address; /// 渠道标识(支持数字类型转换) @JsonKey( name: 'channel', fromJson: UserChannel.fromJson, toJson: _channelToJson, ) final UserChannel channel; /// 应用描述 @JsonKey(name: 'description') final String description; /// 最新版本号 @JsonKey(name: 'version') final String version; /// 是否显示游客登录。 /// /// 这是服务端算好的**结论**而非后台开关的原始值:服务端已按本机上报的版本号与 /// 「游客登录生效版本」比对过(见 comm.ChannelApp.ShowTourists),客户端直接照着显隐, /// 不要再拿 touristsversion 自己比一遍——版本比较各端各写一遍必然出现不一致。 @JsonKey(name: 'tourists') final bool tourists; /// 游客登录生效版本;空 = 不限定版本。仅用于排查「为什么这一版看不到入口」,不参与判定。 @JsonKey(name: 'touristsversion') final String? touristsversion; /// 这一版是否显示「实名认证」并按实名限制功能(服务端算好的结论)。 /// /// 与游客登录同一个口子(后台「游客显隐控制」):能看到游客登录的那一版才要求实名, /// 其余版本实名整个隐藏、功能不受限制。见服务端 comm.AppRelease.ShowIdVerify。 /// 老服务端不下发这个字段时退回 [tourists]——两者本来就同值。 @JsonKey(name: 'idverify', readValue: _readIdverify) final bool idverify; /// 是否允许跳过设备绑定 @JsonKey(name: 'allowskipdevicebinding') final bool allowskipdevicebinding; /// 支付渠道列表。 /// /// 服务端的 json tag 是全小写的 `paychannels`(见 db.proto 的 DBChannelApp), /// 这里两种写法都认一下:早先只认大写 `Paychannels`,实际永远解析不到。 @JsonKey(name: 'paychannels', readValue: _readPaychannels) final String? paychannels; /// 最低支持版本,低于它必须更新。空 = 后台没配,不参与判定。 @JsonKey(name: 'minversion') final String? minversion; /// 强制更新开关,配合 [minversion] 使用。 /// /// **强更判定的唯一依据就是这两个字段**(后台「渠道分发」里配)。 /// 别再拿「主版本号第一位不同」之类的土规则去猜 —— 那和后台配置完全脱节, /// 运营勾了强更也拦不住人。 @JsonKey(name: 'forceupdate') final bool forceupdate; const AppInfo({ this.address, required this.channel, required this.description, required this.version, this.tourists = false, this.touristsversion, this.idverify = false, this.allowskipdevicebinding = false, required this.paychannels, this.minversion, this.forceupdate = false, }); factory AppInfo.fromJson(Map json) => _$AppInfoFromJson(json); Map toJson() => _$AppInfoToJson(this); } // 辅助函数:将 UserChannel 转换为 JSON int _channelToJson(UserChannel channel) => channel.value; // 兼容历史大写键名的读取器:先按服务端真实的小写键取,取不到再退回大写。 Object? _readPaychannels(Map json, String key) => json[key] ?? json['Paychannels']; // 老服务端没有 idverify 字段:退回 tourists(两者同一个口子,本来就同值)。 Object? _readIdverify(Map json, String key) => json[key] ?? json['tourists']; /// 版本更新信息模型 class VersionUpdateInfo { /// 是否有新版本 final bool hasUpdate; /// 当前版本 final String currentVersion; /// 最新版本 final String latestVersion; /// 应用信息 final AppInfo? appInfo; /// 更新类型 final UpdateType updateType; const VersionUpdateInfo({ required this.hasUpdate, required this.currentVersion, required this.latestVersion, this.appInfo, required this.updateType, }); } /// 更新类型枚举 enum UpdateType { /// 无需更新 none, /// 可选更新 optional, /// 强制更新 force, } /// 应用商店类型枚举 enum AppStoreType { /// iOS App Store appStore, /// Google Play googlePlay, /// 华为应用市场 huawei, /// 小米应用商店 xiaomi, /// OPPO软件商店 oppo, /// vivo应用商店 vivo, /// 荣耀应用市场 honor, /// 官网下载 official, /// 未知 unknown, } // 用户渠道枚举 enum UserChannel { UNKNOWN(0), // 未知渠道 CHINA_HUAWEI(1), // 中国-华为 CHINA_XIAOMI(2), // 中国-小米 CHINA_OPPO(3), // 中国-OPPO CHINA_VIVO(4), // 中国-VIVO CHINA_HONOR(5), // 中国-荣耀 OFFICIAL(100), // 官方渠道 GLOBAL_APPLE(101), // 全球-苹果 GLOBAL_GOOGLE(102); // 全球-谷歌 const UserChannel(this.value); final int value; // 从数字值创建枚举 static UserChannel fromValue(int value) { return UserChannel.values.firstWhere( (channel) => channel.value == value, orElse: () => UserChannel.UNKNOWN, ); } // 从字符串名称创建枚举 static UserChannel fromName(String name) { switch (name.toUpperCase()) { case 'CHINA_HUAWEI': return UserChannel.CHINA_HUAWEI; case 'CHINA_XIAOMI': return UserChannel.CHINA_XIAOMI; case 'CHINA_OPPO': return UserChannel.CHINA_OPPO; case 'CHINA_VIVO': return UserChannel.CHINA_VIVO; case 'CHINA_HONOR': return UserChannel.CHINA_HONOR; case 'OFFICIAL': return UserChannel.OFFICIAL; case 'GLOBAL_APPLE': return UserChannel.GLOBAL_APPLE; case 'GLOBAL_GOOGLE': return UserChannel.GLOBAL_GOOGLE; default: return UserChannel.UNKNOWN; } } // JSON 序列化时返回数字值 int toJson() => value; // JSON 反序列化时从数字值创建枚举 static UserChannel fromJson(dynamic json) { if (json is int) { return fromValue(json); } else if (json is String) { final intValue = int.tryParse(json); if (intValue != null) { return fromValue(intValue); } } return UserChannel.UNKNOWN; } } /// 平台信息模型 class PlatformInfo { /// 平台类型 final String platform; /// 应用商店类型 final AppStoreType storeType; /// 渠道标识 final UserChannel channel; const PlatformInfo({ required this.platform, required this.storeType, required this.channel, }); }