chore: 更新 .gitignore 文件,移除不必要的包文件类型;新增邮箱验证码登录流程和 OAuth/Ticket 认证系统 API 文档;更新勋章相关逻辑,优化用户信息获取失败处理;调整勋章展示逻辑,支持多等级高亮功能。

This commit is contained in:
edwinQQQ
2025-07-14 17:37:10 +08:00
parent 99db078b62
commit 16f9041ba9
33 changed files with 15384 additions and 60390 deletions

6
.gitignore vendored
View File

@@ -82,12 +82,6 @@ iOSInjectionProject/
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.war
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*

View File

@@ -0,0 +1,434 @@
# 邮箱验证码登录流程文档
## 概述
本文档详细描述了 YuMi iOS 应用中 `LoginTypesViewController``LoginDisplayType_email` 模式下的邮箱验证码登录流程。该流程实现了基于邮箱和验证码的用户认证机制。
## 系统架构
### 核心组件
- **LoginTypesViewController**: 登录类型控制器,负责 UI 展示和用户交互
- **LoginPresenter**: 登录业务逻辑处理器,负责与 API 交互
- **LoginInputItemView**: 输入组件,提供邮箱和验证码输入界面
- **Api+Login**: 登录相关 API 接口封装
- **AccountInfoStorage**: 账户信息本地存储管理
### 数据模型
#### LoginDisplayType 枚举
```objc
typedef NS_ENUM(NSUInteger, LoginDisplayType) {
LoginDisplayType_id, // ID 登录
LoginDisplayType_email, // 邮箱登录 ✓
LoginDisplayType_phoneNum, // 手机号登录
LoginDisplayType_email_forgetPassword, // 邮箱忘记密码
LoginDisplayType_phoneNum_forgetPassword, // 手机号忘记密码
};
```
#### LoginInputType 枚举
```objc
typedef NS_ENUM(NSUInteger, LoginInputType) {
LoginInputType_email, // 邮箱输入
LoginInputType_verificationCode, // 验证码输入
LoginInputType_login, // 登录按钮
// ... 其他类型
};
```
#### GetSmsType 验证码类型
```objc
typedef NS_ENUM(NSUInteger, GetSmsType) {
GetSmsType_Regist = 1, // 注册(邮箱登录使用此类型)
GetSmsType_Login = 2, // 登录
GetSmsType_Reset_Password = 3, // 重设密码
// ... 其他类型
};
```
## 登录流程详解
### 1. 界面初始化流程
#### 1.1 控制器初始化
```objc
// 在 LoginViewController 中点击邮箱登录按钮
- (void)didTapEntrcyButton:(UIButton *)sender {
if (sender.tag == LoginType_Email) {
LoginTypesViewController *vc = [[LoginTypesViewController alloc] init];
[self.navigationController pushViewController:vc animated:YES];
[vc updateLoginType:LoginDisplayType_email]; // 设置为邮箱登录模式
}
}
```
#### 1.2 输入区域设置
```objc
- (void)setupEmailInputArea {
[self setupInpuArea:LoginInputType_email // 第一行:邮箱输入
second:LoginInputType_verificationCode // 第二行:验证码输入
third:LoginInputType_none // 第三行:无
action:LoginInputType_login // 操作按钮:登录
showForgetPassword:NO]; // 不显示忘记密码
}
```
#### 1.3 UI 组件配置
- **第一行输入框**: 邮箱地址输入
- 占位符: "请输入邮箱地址"
- 键盘类型: `UIKeyboardTypeEmailAddress`
- 回调: `handleFirstInputContentUpdate`
- **第二行输入框**: 验证码输入
- 占位符: "请输入验证码"
- 键盘类型: `UIKeyboardTypeDefault`
- 附带"获取验证码"按钮
- 回调: `handleSecondInputContentUpdate`
### 2. 验证码获取流程
#### 2.1 用户交互触发
```objc
// 用户点击"获取验证码"按钮
[self.secondLineInputView setHandleItemAction:^(LoginInputType inputType) {
if (inputType == LoginInputType_verificationCode) {
if (self.type == LoginDisplayType_email) {
[self handleTapGetMailVerificationCode];
}
}
}];
```
#### 2.2 邮箱验证码获取处理
```objc
- (void)handleTapGetMailVerificationCode {
NSString *email = [self.firstLineInputView inputContent];
// 邮箱地址验证
if (email.length == 0) {
[self.secondLineInputView endVerificationCountDown];
return;
}
// 调用 Presenter 发送验证码
[self.presenter sendMailVerificationCode:email type:GetSmsType_Regist];
}
```
#### 2.3 Presenter 层处理
```objc
- (void)sendMailVerificationCode:(NSString *)emailAddress type:(NSInteger)type {
// DES 加密邮箱地址
NSString *desEmail = [DESEncrypt encryptUseDES:emailAddress
key:KeyWithType(KeyType_PasswordEncode)];
@kWeakify(self);
[Api emailGetCode:[self createHttpCompletion:^(BaseModel *data) {
@kStrongify(self);
if ([[self getView] respondsToSelector:@selector(emailCodeSucess:type:)]) {
[[self getView] emailCodeSucess:@"" type:type];
}
} fail:^(NSInteger code, NSString *msg) {
@kStrongify(self);
if ([[self getView] respondsToSelector:@selector(emailCodeFailure)]) {
[[self getView] emailCodeFailure];
}
} showLoading:YES errorToast:YES]
emailAddress:desEmail
type:@(type)];
}
```
#### 2.4 API 接口调用
```objc
+ (void)emailGetCode:(HttpRequestHelperCompletion)completion
emailAddress:(NSString *)emailAddress
type:(NSNumber *)type {
[self makeRequest:@"email/getCode"
method:HttpRequestHelperMethodPOST
completion:completion, __FUNCTION__, emailAddress, type, nil];
}
```
**API 详情**:
- **接口路径**: `POST /email/getCode`
- **请求参数**:
- `emailAddress`: 邮箱地址DES 加密)
- `type`: 验证码类型1=注册)
#### 2.5 获取验证码成功处理
```objc
- (void)emailCodeSucess:(NSString *)message type:(GetSmsType)type {
[self showSuccessToast:YMLocalizedString(@"XPLoginPhoneViewController2")]; // "验证码已发送"
[self.secondLineInputView startVerificationCountDown]; // 开始倒计时
[self.secondLineInputView displayKeyboard]; // 显示键盘
}
```
#### 2.6 获取验证码失败处理
```objc
- (void)emailCodeFailure {
[self.secondLineInputView endVerificationCountDown]; // 结束倒计时
}
```
### 3. 邮箱登录流程
#### 3.1 登录按钮状态检查
```objc
- (void)checkActionButtonStatus {
switch (self.type) {
case LoginDisplayType_email: {
NSString *accountString = [self.firstLineInputView inputContent]; // 邮箱
NSString *codeString = [self.secondLineInputView inputContent]; // 验证码
// 只有当邮箱和验证码都不为空时才启用登录按钮
if (![NSString isEmpty:accountString] && ![NSString isEmpty:codeString]) {
self.bottomActionButton.enabled = YES;
} else {
self.bottomActionButton.enabled = NO;
}
}
break;
}
}
```
#### 3.2 登录按钮点击处理
```objc
- (void)didTapActionButton {
[self.view endEditing:true];
switch (self.type) {
case LoginDisplayType_email: {
// 调用 Presenter 进行邮箱登录
[self.presenter loginWithEmail:[self.firstLineInputView inputContent]
code:[self.secondLineInputView inputContent]];
}
break;
}
}
```
#### 3.3 Presenter 层登录处理
```objc
- (void)loginWithEmail:(NSString *)email code:(NSString *)code {
// DES 加密邮箱地址
NSString *desMail = [DESEncrypt encryptUseDES:email
key:KeyWithType(KeyType_PasswordEncode)];
@kWeakify(self);
[Api loginWithCode:[self createHttpCompletion:^(BaseModel *data) {
@kStrongify(self);
// 解析账户模型
AccountModel *accountModel = [AccountModel modelWithDictionary:data.data];
// 保存账户信息
if (accountModel && accountModel.access_token.length > 0) {
[[AccountInfoStorage instance] saveAccountInfo:accountModel];
}
// 通知登录成功
if ([[self getView] respondsToSelector:@selector(loginSuccess)]) {
[[self getView] loginSuccess];
}
} fail:^(NSInteger code, NSString *msg) {
@kStrongify(self);
[[self getView] loginFailWithMsg:msg];
} errorToast:NO]
email:desMail
code:code
client_secret:clinet_s // 客户端密钥
version:@"1"
client_id:@"erban-client"
grant_type:@"email"]; // 邮箱登录类型
}
```
#### 3.4 API 接口调用
```objc
+ (void)loginWithCode:(HttpRequestHelperCompletion)completion
email:(NSString *)email
code:(NSString *)code
client_secret:(NSString *)client_secret
version:(NSString *)version
client_id:(NSString *)client_id
grant_type:(NSString *)grant_type {
NSString *fang = [NSString stringFromBase64String:@"b2F1dGgvdG9rZW4="]; // oauth/token
[self makeRequest:fang
method:HttpRequestHelperMethodPOST
completion:completion, __FUNCTION__, email, code, client_secret,
version, client_id, grant_type, nil];
}
```
**API 详情**:
- **接口路径**: `POST /oauth/token`
- **请求参数**:
- `email`: 邮箱地址DES 加密)
- `code`: 验证码
- `client_secret`: 客户端密钥
- `version`: 版本号 "1"
- `client_id`: 客户端ID "erban-client"
- `grant_type`: 授权类型 "email"
#### 3.5 登录成功处理
```objc
- (void)loginSuccess {
[self showSuccessToast:YMLocalizedString(@"XPLoginPhoneViewController1")]; // "登录成功"
[PILoginManager loginWithVC:self isLoginPhone:NO]; // 执行登录后续处理
}
```
#### 3.6 登录失败处理
```objc
- (void)loginFailWithMsg:(NSString *)msg {
[self showSuccessToast:msg]; // 显示错误信息
}
```
## 数据流时序图
```mermaid
sequenceDiagram
participant User as 用户
participant VC as LoginTypesViewController
participant IV as LoginInputItemView
participant P as LoginPresenter
participant API as Api+Login
participant Storage as AccountInfoStorage
Note over User,Storage: 1. 初始化邮箱登录界面
User->>VC: 选择邮箱登录
VC->>VC: updateLoginType(LoginDisplayType_email)
VC->>VC: setupEmailInputArea()
VC->>IV: 创建邮箱输入框
VC->>IV: 创建验证码输入框
Note over User,Storage: 2. 获取邮箱验证码
User->>IV: 输入邮箱地址
User->>IV: 点击"获取验证码"
IV->>VC: handleTapGetMailVerificationCode
VC->>VC: 验证邮箱地址非空
VC->>P: sendMailVerificationCode(email, GetSmsType_Regist)
P->>P: DES加密邮箱地址
P->>API: emailGetCode(encryptedEmail, type=1)
API-->>P: 验证码发送结果
P-->>VC: emailCodeSucess / emailCodeFailure
VC->>IV: startVerificationCountDown / endVerificationCountDown
VC->>User: 显示成功/失败提示
Note over User,Storage: 3. 邮箱验证码登录
User->>IV: 输入验证码
IV->>VC: 输入内容变化回调
VC->>VC: checkActionButtonStatus()
VC->>User: 启用/禁用登录按钮
User->>VC: 点击登录按钮
VC->>VC: didTapActionButton()
VC->>P: loginWithEmail(email, code)
P->>P: DES加密邮箱地址
P->>API: loginWithCode(email, code, ...)
API-->>P: OAuth Token 响应
P->>P: 解析 AccountModel
P->>Storage: saveAccountInfo(accountModel)
P-->>VC: loginSuccess / loginFailWithMsg
VC->>User: 显示登录结果
VC->>User: 跳转到主界面
```
## 安全机制
### 1. 数据加密
- **邮箱地址加密**: 使用 DES 算法加密邮箱地址后传输
```objc
NSString *desEmail = [DESEncrypt encryptUseDES:email key:KeyWithType(KeyType_PasswordEncode)];
```
### 2. 输入验证
- **邮箱格式验证**: 通过 `UIKeyboardTypeEmailAddress` 键盘类型引导正确输入
- **非空验证**: 邮箱和验证码都必须非空才能执行登录
### 3. 验证码安全
- **时效性**: 验证码具有倒计时机制,防止重复获取
- **类型标识**: 使用 `GetSmsType_Regist = 1` 标识登录验证码
### 4. 网络安全
- **错误处理**: 完整的成功/失败回调机制
- **加载状态**: `showLoading:YES` 防止重复请求
- **错误提示**: `errorToast:YES` 显示网络错误
## 错误处理机制
### 1. 邮箱验证码获取错误
```objc
- (void)emailCodeFailure {
[self.secondLineInputView endVerificationCountDown]; // 停止倒计时
// 用户可以重新获取验证码
}
```
### 2. 登录失败处理
```objc
- (void)loginFailWithMsg:(NSString *)msg {
[self showSuccessToast:msg]; // 显示具体错误信息
// 用户可以重新尝试登录
}
```
### 3. 网络请求错误
- **自动重试**: 用户可以手动重新点击获取验证码或登录
- **错误提示**: 通过 Toast 显示具体错误信息
- **状态恢复**: 失败后恢复按钮可点击状态
## 本地化支持
### 关键文本资源
- `@"20.20.51_text_1"`: "邮箱登录"
- `@"20.20.51_text_4"`: "请输入邮箱地址"
- `@"20.20.51_text_7"`: "请输入验证码"
- `@"XPLoginPhoneViewController2"`: "验证码已发送"
- `@"XPLoginPhoneViewController1"`: "登录成功"
### 多语言支持
- 简体中文 (`zh-Hant.lproj`)
- 英文 (`en.lproj`)
- 阿拉伯语 (`ar.lproj`)
- 土耳其语 (`tr.lproj`)
## 依赖组件
### 外部框架
- **MASConstraintMaker**: 自动布局
- **ReactiveObjC**: 响应式编程(部分组件使用)
### 内部组件
- **YMLocalizedString**: 本地化字符串管理
- **DESEncrypt**: DES 加密工具
- **AccountInfoStorage**: 账户信息存储
- **HttpRequestHelper**: 网络请求管理
## 扩展和维护
### 新增功能建议
1. **邮箱格式验证**: 添加正则表达式验证邮箱格式
2. **验证码长度限制**: 限制验证码输入长度
3. **自动填充**: 支持系统邮箱自动填充
4. **记住邮箱**: 保存最近使用的邮箱地址
### 性能优化
1. **请求去重**: 防止短时间内重复请求验证码
2. **缓存机制**: 缓存验证码倒计时状态
3. **网络优化**: 添加请求超时和重试机制
### 代码维护
1. **常量管理**: 将硬编码字符串提取为常量
2. **错误码统一**: 统一管理API错误码
3. **日志记录**: 添加详细的操作日志
## 总结
邮箱验证码登录流程是一个完整的用户认证系统,包含了界面展示、验证码获取、用户登录、数据存储等完整环节。该流程具有良好的安全性、用户体验和错误处理机制,符合现代移动应用的认证标准。
通过本文档,开发人员可以全面了解邮箱登录的实现细节,便于后续的功能扩展和维护工作。

View File

@@ -0,0 +1,262 @@
# OAuth/Ticket 认证系统 API 文档
## 概述
本文档描述了 YuMi 应用中 OAuth 认证和 Ticket 会话管理的完整流程。系统采用两阶段认证机制:
1. **OAuth 阶段**:用户登录获取 `access_token`
2. **Ticket 阶段**:使用 `access_token` 获取业务会话 `ticket`
## 认证流程架构
### 核心组件
- **AccountInfoStorage**: 负责账户信息和 ticket 的本地存储
- **HttpRequestHelper**: 网络请求管理,自动添加认证头
- **Api+Login**: 登录相关 API 接口
- **Api+Main**: Ticket 获取相关 API 接口
### 认证数据模型
#### AccountModel
```objc
@interface AccountModel : PIBaseModel
@property (nonatomic, assign) NSString *uid; // 用户唯一标识
@property (nonatomic, copy) NSString *jti; // JWT ID
@property (nonatomic, copy) NSString *token_type; // Token 类型
@property (nonatomic, copy) NSString *refresh_token; // 刷新令牌
@property (nonatomic, copy) NSString *netEaseToken; // 网易云信令牌
@property (nonatomic, copy) NSString *access_token; // OAuth 访问令牌
@property (nonatomic, assign) NSNumber *expires_in; // 过期时间
@end
```
## API 接口详情
### 1. OAuth 登录接口
#### 1.1 手机验证码登录
```objc
+ (void)loginWithCode:(HttpRequestHelperCompletion)completion
phone:(NSString *)phone
code:(NSString *)code
client_secret:(NSString *)client_secret
version:(NSString *)version
client_id:(NSString *)client_id
grant_type:(NSString *)grant_type
phoneAreaCode:(NSString *)phoneAreaCode;
```
**接口路径**: `POST /oauth/token`
**请求参数**:
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| phone | String | 是 | 手机号DES加密 |
| code | String | 是 | 验证码 |
| client_secret | String | 是 | 客户端密钥,固定值:"uyzjdhds" |
| version | String | 是 | 版本号,固定值:"1" |
| client_id | String | 是 | 客户端ID固定值"erban-client" |
| grant_type | String | 是 | 授权类型,验证码登录为:"sms_code" |
| phoneAreaCode | String | 是 | 手机区号 |
**返回数据**: AccountModel 对象
#### 1.2 手机密码登录
```objc
+ (void)loginWithPassword:(HttpRequestHelperCompletion)completion
phone:(NSString *)phone
password:(NSString *)password
client_secret:(NSString *)client_secret
version:(NSString *)version
client_id:(NSString *)client_id
grant_type:(NSString *)grant_type;
```
**接口路径**: `POST /oauth/token`
**请求参数**:
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| phone | String | 是 | 手机号DES加密 |
| password | String | 是 | 密码DES加密 |
| client_secret | String | 是 | 客户端密钥 |
| version | String | 是 | 版本号 |
| client_id | String | 是 | 客户端ID |
| grant_type | String | 是 | 授权类型,密码登录为:"password" |
#### 1.3 第三方登录
```objc
+ (void)loginWithThirdPart:(HttpRequestHelperCompletion)completion
openid:(NSString *)openid
unionid:(NSString *)unionid
access_token:(NSString *)access_token
type:(NSString *)type;
```
**接口路径**: `POST /acc/third/login`
**请求参数**:
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| openid | String | 是 | 第三方平台用户唯一标识 |
| unionid | String | 是 | 第三方平台联合ID |
| access_token | String | 是 | 第三方平台访问令牌 |
| type | String | 是 | 第三方平台类型1:Apple, 2:Facebook, 3:Google等 |
### 2. Ticket 获取接口
#### 2.1 获取 Ticket
```objc
+ (void)requestTicket:(HttpRequestHelperCompletion)completion
access_token:(NSString *)accessToken
issue_type:(NSString *)issueType;
```
**接口路径**: `POST /oauth/ticket`
**请求参数**:
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| access_token | String | 是 | OAuth 登录获取的访问令牌 |
| issue_type | String | 是 | 签发类型,固定值:"multi" |
**返回数据**:
```json
{
"code": 200,
"data": {
"tickets": [
{
"ticket": "eyJhbGciOiJIUzI1NiJ9..."
}
]
}
}
```
### 3. HTTP 请求头配置
所有业务 API 请求都会自动添加以下请求头:
```objc
// 在 HttpRequestHelper 中自动配置
- (void)setupHeader {
AFHTTPSessionManager *client = [HttpRequestHelper requestManager];
// 用户ID头
if ([[AccountInfoStorage instance] getUid].length > 0) {
[client.requestSerializer setValue:[[AccountInfoStorage instance] getUid]
forHTTPHeaderField:@"pub_uid"];
}
// Ticket 认证头
if ([[AccountInfoStorage instance] getTicket].length > 0) {
[client.requestSerializer setValue:[[AccountInfoStorage instance] getTicket]
forHTTPHeaderField:@"pub_ticket"];
}
// 其他公共头
[client.requestSerializer setValue:[NSBundle uploadLanguageText]
forHTTPHeaderField:@"Accept-Language"];
[client.requestSerializer setValue:PI_App_Version
forHTTPHeaderField:@"App-Version"];
}
```
## 使用流程
### 完整登录流程示例
```objc
// 1. 用户登录获取 access_token
[Api loginWithCode:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) {
if (code == 200) {
// 保存账户信息
AccountModel *accountModel = [AccountModel modelWithDictionary:data.data];
[[AccountInfoStorage instance] saveAccountInfo:accountModel];
// 2. 使用 access_token 获取 ticket
[Api requestTicket:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) {
if (code == 200) {
NSArray *tickets = [data.data valueForKey:@"tickets"];
NSString *ticket = [tickets[0] valueForKey:@"ticket"];
// 保存 ticket
[[AccountInfoStorage instance] saveTicket:ticket];
// 3. 登录成功,可以进行业务操作
[self navigateToMainPage];
}
} access_token:accountModel.access_token issue_type:@"multi"];
}
} phone:encryptedPhone
code:verificationCode
client_secret:@"uyzjdhds"
version:@"1"
client_id:@"erban-client"
grant_type:@"sms_code"
phoneAreaCode:areaCode];
```
### 自动登录流程
```objc
- (void)autoLogin {
// 检查本地是否有账户信息
AccountModel *accountModel = [[AccountInfoStorage instance] getCurrentAccountInfo];
if (accountModel == nil || accountModel.access_token == nil) {
[self tokenInvalid]; // 跳转到登录页
return;
}
// 检查是否有有效的 ticket
if ([[AccountInfoStorage instance] getTicket].length > 0) {
[[self getView] autoLoginSuccess];
return;
}
// 使用 access_token 重新获取 ticket
[Api requestTicket:^(BaseModel * _Nonnull data) {
NSArray *tickets = [data.data valueForKey:@"tickets"];
NSString *ticket = [tickets[0] valueForKey:@"ticket"];
[[AccountInfoStorage instance] saveTicket:ticket];
[[self getView] autoLoginSuccess];
} fail:^(NSInteger code, NSString * _Nullable msg) {
[self logout]; // ticket 获取失败,重新登录
} access_token:accountModel.access_token issue_type:@"multi"];
}
```
## 错误处理
### 401 未授权错误
当接收到 401 状态码时,系统会自动处理:
```objc
// 在 HttpRequestHelper 中
if (response && response.statusCode == 401) {
failure(response.statusCode, YMLocalizedString(@"HttpRequestHelper7"));
// 通常需要重新登录
}
```
### Ticket 过期处理
- Ticket 过期时服务器返回 401 错误
- 客户端应该使用保存的 `access_token` 重新获取 ticket
- 如果 `access_token` 也过期,则需要用户重新登录
## 安全注意事项
1. **数据加密**: 敏感信息(手机号、密码)使用 DES 加密传输
2. **本地存储**:
- `access_token` 存储在文件系统中
- `ticket` 存储在内存中,应用重启需重新获取
3. **请求头**: 所有业务请求自动携带 `pub_uid``pub_ticket`
4. **错误处理**: 建立完善的 401 错误重试机制
## 相关文件
- `YuMi/Structure/MVP/Model/AccountInfoStorage.h/m` - 账户信息存储管理
- `YuMi/Modules/YMLogin/Api/Api+Login.h/m` - 登录相关接口
- `YuMi/Modules/YMTabbar/Api/Api+Main.h/m` - Ticket 获取接口
- `YuMi/Network/HttpRequestHelper.h/m` - 网络请求管理
- `YuMi/Structure/MVP/Model/AccountModel.h/m` - 账户数据模型

File diff suppressed because it is too large Load Diff

View File

@@ -46,7 +46,7 @@ isPhoneXSeries = [[UIApplication sharedApplication] delegate].window.safeAreaIns
#define kFontHeavy(font) [UIFont systemFontOfSize:kGetScaleWidth(font) weight:UIFontWeightHeavy]
///内置版本号
#define PI_App_Version @"1.0.28.1"
#define PI_App_Version @"1.0.29"
///渠道
#define PI_App_Source @"appstore"
#define PI_Test_Flight @"TestFlight"

Binary file not shown.

View File

@@ -25,7 +25,9 @@
UserInfoModel * infoModel = [UserInfoModel modelWithDictionary:data.data];
[[self getView] onGetUserInfoSuccess:infoModel];
} fail:^(NSInteger code, NSString * _Nullable msg) {
if ([self.getView respondsToSelector:@selector(onGetUserFailure:)]) {
[self.getView onGetUserFailure:code];
}
}] uid:uid];
}

View File

@@ -17,6 +17,9 @@ NS_ASSUME_NONNULL_BEGIN
///获取用户信息成功
- (void)onGetSessionUserInfoSuccess:(UserInfoModel *)userInfo;
- (void)onGetUserFailure:(NSInteger)code;
///获取粉丝喜欢成功
- (void)getFansLikeSuccess:(BOOL)isLike;
///关注成功

View File

@@ -573,10 +573,27 @@
make.top.mas_equalTo(self.sessionNavView.mas_bottom).mas_offset(0);
}];
}
}
}
- (void)onGetUserFailure:(NSInteger)code {
if (code == 3009) {
if (self.openType == SessionListOpenTypeRoom) {
CATransition *transition = [CATransition animation];
transition.duration = 0.3f;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromLeft;
[self.view.superview.layer addAnimation:transition forKey:nil];
[self willMoveToParentViewController:nil]; //1
[self.view removeFromSuperview]; //2
[self removeFromParentViewController]; //3
return;
}
[self.navigationController popViewControllerAnimated:YES];
}
}
- (void)getFansLikeSuccess:(BOOL)isLike {
self.sessionNavView.isLike = isLike;
if(self.isAttention == YES && isLike == NO){

View File

@@ -26,7 +26,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)rankListFailure;
- (void)useMedalSuccess;
- (void)userMedalsFailure;
- (void)useMedalFailure;
@end

View File

@@ -14,7 +14,7 @@ NS_ASSUME_NONNULL_BEGIN
+ (void)registerTo:(UICollectionView *)collectionView;
+ (instancetype)cellFor:(UICollectionView *)collectionView atIndexPath:(NSIndexPath *)index;
- (void)updateCell:(MedalSeriesItemVo *)model isForSquare:(BOOL)isSquare;
- (void)updateCell:(MedalSeriesItemVo *)model isForSquare:(BOOL)isSquare isForMine:(BOOL)isMine;
/**
* 当 cell 将要显示时调用

View File

@@ -162,7 +162,6 @@
self.displayModel = nil; //
self.currentItemVo = nil; //
//
self.titleLabel.text = @"";
self.subLabel.text = @"";
@@ -175,8 +174,7 @@
self.imageView.imageUrl = @"";
}
- (void)updateCell:(MedalSeriesItemVo *)model isForSquare:(BOOL)isSquare {
// MedalSeriesItemVo *itemVos = [model.medalSeries xpSafeObjectAtIndex:0];
- (void)updateCell:(MedalSeriesItemVo *)model isForSquare:(BOOL)isSquare isForMine:(BOOL)isMine {
self.currentItemVo = model;
//
@@ -190,13 +188,16 @@
self.displayModel = [model.medalVos xpSafeObjectAtIndex:model.medalVos.count - 1];
[self.levelIndicatorView setSelectedLevel:model.medalLevel animated:NO];
} else {
self.subLabel.hidden = !isMine;
self.levelIndicatorView.userInteractionEnabled = YES;
self.displayModel = [model.medalVos xpSafeObjectAtIndex:0];
NSMutableArray *arr = @[].mutableCopy;
for (MedalVo *vo in model.medalVos) {
[arr addObject:@(vo.level)];
if (vo.level > self.displayModel.level) {
self.displayModel = vo;
if (vo.hasGain) {
[arr addObject:@(vo.level)];
if (vo.level > self.displayModel.level) {
self.displayModel = vo;
}
}
}
[self.levelIndicatorView setHighlightLevels:arr animated:NO];

View File

@@ -11,7 +11,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface MedalsDetailView : UIView
@property (nonatomic, strong) MedalSeriesItemVo *detailItemVo;
- (void)updateSeriesItem:(MedalSeriesItemVo *)detailItem isMine:(BOOL)isMine;
@end

View File

@@ -26,6 +26,8 @@
@property (nonatomic, strong) MedalSeriesItemVo *currentSeriesItemVO;
@property (nonatomic, strong) MedalVo *displayModel;
@property (nonatomic, strong) MedalSeriesItemVo *detailItemVo;
@end
@implementation MedalsDetailView
@@ -129,6 +131,23 @@
}];
}
- (void)updateSeriesItem:(MedalSeriesItemVo *)detailItem isMine:(BOOL)isMine {
self.detailItemVo = detailItem;
if (isMine) {
MedalVo *tempVo = [detailItem.medalVos xpSafeObjectAtIndex:0];
for (MedalVo *vo in detailItem.medalVos) {
if (vo.hasGain) {
if (vo.level > tempVo.level) {
tempVo = vo;
}
}
}
self.displayModel = tempVo;
[self.levelIndicatorView setSelectedLevel:self.displayModel.level animated:NO];
[self updateDisplayWithCurrentModel];
}
}
- (void)setDetailItemVo:(MedalSeriesItemVo *)detailItemVo {
// _detailItemVo = detailItemVo;
self.currentSeriesItemVO = detailItemVo;

View File

@@ -425,11 +425,11 @@
header.lastUpdatedTimeLabel.textColor = [DJDKMIMOMColor secondTextColor];
self.rankTableView.mj_header = header;
//
MJRefreshBackNormalFooter *footer = [MJRefreshBackNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(footerRefresh)];
footer.stateLabel.textColor = [DJDKMIMOMColor secondTextColor];
footer.stateLabel.font = [UIFont systemFontOfSize:10.0];
self.rankTableView.mj_footer = footer;
// //
// MJRefreshBackNormalFooter *footer = [MJRefreshBackNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(footerRefresh)];
// footer.stateLabel.textColor = [DJDKMIMOMColor secondTextColor];
// footer.stateLabel.font = [UIFont systemFontOfSize:10.0];
// self.rankTableView.mj_footer = footer;
}
#pragma mark -
@@ -450,7 +450,7 @@
self.mineRankModel = model.mine;
self.avatarImageView.imageUrl = self.mineRankModel.avatar;
self.nameLabel.text = self.mineRankModel.nick;
self.indexLabel.text = self.mineRankModel.rank == 0 ? @"-" : @(self.mineRankModel.rank).stringValue;
self.indexLabel.text = self.mineRankModel.rank == 0 ? @"10+" : @(self.mineRankModel.rank).stringValue;
self.countLabel.text = @(self.mineRankModel.medalCount).stringValue;
self.rankList = [NSMutableArray array];

View File

@@ -37,7 +37,7 @@ typedef enum : NSInteger {
@property (nonatomic, strong) UIButton *emptyUserMedalButton;
@property (nonatomic, strong) TYCyclePagerView *medalsCyclePagerView;
@property (nonatomic, strong) UIView *emptyView;
@property (nonatomic, copy) UICollectionView *medalsCollectionView;
@property (nonatomic, strong) UICollectionView *medalsCollectionView;
@property (nonatomic, strong) UIStackView *centerTabsStackView;
@property (nonatomic, strong) NSMutableArray <MedalSeriesItemVo *>*datasourceTaskMedals;
@@ -53,9 +53,9 @@ typedef enum : NSInteger {
@property (nonatomic, assign) MedalsCenterDisplayType displayType;
@property (nonatomic, strong) UserMedalsModel *userMedalsModel;
@property (nonatomic, copy) NSMutableArray <MedalSeriesVo *>*taskSquareMedalVo;
@property (nonatomic, copy) NSMutableArray <MedalSeriesVo *>*activitySquareMedalVo;
@property (nonatomic, copy) NSMutableArray <MedalSeriesVo *>*glorySquareMedalVo;
@property (nonatomic, strong) NSMutableArray <MedalSeriesVo *>*taskSquareMedalVo;
@property (nonatomic, strong) NSMutableArray <MedalSeriesVo *>*activitySquareMedalVo;
@property (nonatomic, strong) NSMutableArray <MedalSeriesVo *>*glorySquareMedalVo;
@property (nonatomic, strong) UIImageView *otherBG;
@property (nonatomic, strong) NetImageView *otherAvatar;
@@ -807,21 +807,25 @@ typedef enum : NSInteger {
- (void)_updateDataSource:(NSArray <MedalSeriesVo *>*)models {
if (models.count < [self.presenter pageSize]) {
NSArray *targetArr = models;
NSMutableArray *itemDataSources = [NSMutableArray array];
MedalSeriesVo *series = [models xpSafeObjectAtIndex:0];
if (series) {
targetArr = series.medalSeries;
}
itemDataSources = targetArr.mutableCopy;
if (targetArr.count < [self.presenter pageSize]) {
[self.medalsCollectionView.mj_footer endRefreshingWithNoMoreData];
} else {
[self.medalsCollectionView.mj_footer resetNoMoreData];
}
NSMutableArray *itemDataSources = [NSMutableArray array];
for (MedalSeriesVo *vo in models) {
[itemDataSources addObjectsFromArray:vo.medalSeries];
}
switch (self.currentTabType) {
case MedalsCenterTab_TaskMedals:
if (self.currentPageTaskMedals == 1) {
self.emptyView.hidden = (models.count != 0);
self.emptyView.hidden = (targetArr.count != 0);
self.datasourceTaskMedals = itemDataSources;
} else {
[self.datasourceTaskMedals addObjectsFromArray:itemDataSources];
@@ -829,7 +833,7 @@ typedef enum : NSInteger {
break;
case MedalsCenterTab_ActivityMedals:
if (self.currentPageActivityMedals == 1) {
self.emptyView.hidden = (models.count != 0);
self.emptyView.hidden = (targetArr.count != 0);
self.datasourceActivityMedals = itemDataSources;
} else {
[self.datasourceActivityMedals addObjectsFromArray:itemDataSources];
@@ -837,7 +841,7 @@ typedef enum : NSInteger {
break;
case MedalsCenterTab_GloryMedals:
if (self.currentPageGloryMedals == 1) {
self.emptyView.hidden = (models.count != 0);
self.emptyView.hidden = (targetArr.count != 0);
self.datasourceGloryMedals = itemDataSources;
} else {
[self.datasourceGloryMedals addObjectsFromArray:itemDataSources];
@@ -938,7 +942,9 @@ typedef enum : NSInteger {
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
MedalsCollectionViewCell *cell = [MedalsCollectionViewCell cellFor:collectionView atIndexPath:indexPath];
[cell updateCell:[self loadModel:indexPath.item] isForSquare:self.displayType == MedalsCenterDisplayType_Square];
[cell updateCell:[self loadModel:indexPath.item]
isForSquare:self.displayType == MedalsCenterDisplayType_Square
isForMine:self.displayType == MedalsCenterDisplayType_Mine];
return cell;
}
@@ -964,7 +970,8 @@ typedef enum : NSInteger {
return;
}
MedalsDetailView *view = [[MedalsDetailView alloc] initWithFrame:self.view.bounds];
view.detailItemVo = [self loadModel:indexPath.item];
[view updateSeriesItem:[self loadModel:indexPath.item]
isMine:self.displayType == MedalsCenterDisplayType_Mine || self.displayType == MedalsCenterDisplayType_Other];
[self.view addSubview:view];
}

View File

@@ -31,7 +31,9 @@
@property (nonatomic, copy) void(^contactCustomerService)(NSString *uid);
//
@property (nonatomic, strong) NSMutableDictionary *retryCountMap;
@property (nonatomic, strong) NSMutableDictionary *retryCountDict;
// 线
@property (nonatomic, strong) dispatch_queue_t retryCountQueue;
@end
@@ -46,7 +48,10 @@
proxy.recheckIndex = 0;
proxy.recheckInterval = 1.0;
proxy.retryCountMap = [NSMutableDictionary dictionary];
proxy.retryCountDict = [NSMutableDictionary dictionary];
proxy.retryCountQueue = dispatch_queue_create("com.iapmanager.retrycount.queue", DISPATCH_QUEUE_CONCURRENT);
proxy.isProcessing = NO;
proxy.isLogin = NO;
});
return proxy;
@@ -96,6 +101,9 @@
self.recheckTimer = nil;
}
//
[self cleanupStaleTransactions];
//
NSTimeInterval interval = MIN(self.recheckInterval, 300.0);
@@ -105,112 +113,178 @@
selector:@selector(handleRetryCheckReceipt)
userInfo:nil
repeats:NO];
//
[[NSRunLoop mainRunLoop] addTimer:self.recheckTimer forMode:NSRunLoopCommonModes];
}
//
- (void)handleRetryCheckReceipt {
// NSLog(@"[YuMi IAP] 用户触发补单检查 - Retry checking receipts");
NSLog(@"[YuMi IAP] 用户触发补单检查 - Retry checking receipts");
//
if (self.isProcessing || !self.isLogin) {
//
if (self.recheckTimer) {
[self.recheckTimer invalidate];
self.recheckTimer = nil;
}
//
self.recheckInterval = MIN(self.recheckInterval * 1.5, 300.0);
[self retryCheckAllReceipt];
return;
}
NSArray *array = [RechargeStorage getAllReceiptsWithUid:[AccountInfoStorage instance].getUid];
// NSLog(@" ------------.------------ 尝试:%@", array);
@synchronized (array) {
if (array.count == 0 || self.isProcessing) {
return;
}
if (!self.isLogin) {
return;
}
if (self.recheckIndex >= array.count) {
self.recheckIndex = 0;
}
#if DEBUG
// [self requestAPPOrderData:@"com.hflighting.yumi.gold.1_7000" isFroRecheck:YES];
#endif
self.isProcessing = YES;
NSDictionary *dic = [array xpSafeObjectAtIndex:self.recheckIndex];
NSString *transactionId = dic[@"transactionId"];
[self _logToBugly:transactionId oID:dic[@"orderId"] status:0];
NSInteger retryCount = [self getRetryCountForTransaction:transactionId];
if (retryCount > MAX_RETRY_COUNT) {
//
[self _logToBugly:transactionId oID:dic[@"orderId"] status:4]; //
[RechargeStorage delegateTransactionId:transactionId uid:[AccountInfoStorage instance].getUid];
self.isProcessing = NO;
return;
}
//
[self incrementRetryCountForTransaction:transactionId];
@kWeakify(self);
[self backgroundCheckReceiptWithTransactionID:transactionId
orderID:dic[@"orderId"]
next:^(BOOL isSuccess){
@kStrongify(self);
if (isSuccess) {
[RechargeStorage delegateTransactionId:transactionId
uid:[AccountInfoStorage instance].getUid];
self.recheckInterval = MIN(self.recheckInterval * 2, 300.0);
[self _logToBugly:transactionId oID:dic[@"orderId"] status:1];
//
[self removeRetryCountForTransaction:transactionId];
} else {
self.recheckInterval = self.recheckInterval * 2;
[self _logToBugly:transactionId oID:dic[@"orderId"] status:2];
}
self.recheckIndex += 1;
self.isProcessing = NO;
NSLog(@" ------------.------------ 尝试:%@", array);
//
if (array.count == 0) {
NSLog(@" ------------.------------ 没有需要验单的数据");
if (self.recheckTimer) {
[self.recheckTimer invalidate];
[self retryCheckAllReceipt];
}];
self.recheckTimer = nil;
}
// 10
self.recheckInterval = 600.0;
[self retryCheckAllReceipt];
return;
}
//
if (self.recheckIndex >= array.count) {
self.recheckIndex = 0;
}
//
self.isProcessing = YES;
//
NSDictionary *dic = [array xpSafeObjectAtIndex:self.recheckIndex];
NSString *transactionId = dic[@"transactionId"];
NSString *orderId = dic[@"orderId"];
//
[self _logToBugly:transactionId oID:orderId status:0];
//
NSInteger retryCount = [self getRetryCountForTransaction:transactionId];
if (retryCount > MAX_RETRY_COUNT) {
//
[self _logToBugly:transactionId oID:orderId status:4]; //
[RechargeStorage delegateTransactionId:transactionId uid:[AccountInfoStorage instance].getUid];
[self removeRetryCountForTransaction:transactionId];
//
self.isProcessing = NO;
//
self.recheckIndex = (self.recheckIndex + 1) % array.count;
if (self.recheckTimer) {
[self.recheckTimer invalidate];
self.recheckTimer = nil;
}
[self retryCheckAllReceipt];
return;
}
//
[self incrementRetryCountForTransaction:transactionId];
@kWeakify(self);
[self backgroundCheckReceiptWithTransactionID:transactionId
orderID:orderId
next:^(BOOL isSuccess){
@kStrongify(self);
if (isSuccess) {
//
[RechargeStorage delegateTransactionId:transactionId
uid:[AccountInfoStorage instance].getUid];
//
self.recheckInterval = MAX(self.recheckInterval / 2, 1.0);
[self _logToBugly:transactionId oID:orderId status:1];
//
[self removeRetryCountForTransaction:transactionId];
} else {
//
self.recheckInterval = MIN(self.recheckInterval * 1.5, 300.0);
[self _logToBugly:transactionId oID:orderId status:2];
}
//
self.recheckIndex = (self.recheckIndex + 1) % array.count;
//
self.isProcessing = NO;
//
if (self.recheckTimer) {
[self.recheckTimer invalidate];
self.recheckTimer = nil;
}
[self retryCheckAllReceipt];
}];
}
- (void)_logToBugly:(NSString *)tid oID:(NSString *)oid status:(NSInteger)status {
NSMutableDictionary *logDic = [NSMutableDictionary dictionary];
[logDic setObject:tid forKey:@"内购 transactionId"];
[logDic setObject:oid forKey:@"内购 orderId"];
[logDic setObject:[AccountInfoStorage instance].getUid forKey:@"内购 用户id"];
// ID
if ([NSString isEmpty:tid]) {
[logDic setObject:@"" forKey:@"内购 transactionId"];
tid = @"";
} else {
[logDic setObject:tid forKey:@"内购 transactionId"];
}
// ID
if ([NSString isEmpty:oid]) {
[logDic setObject:@"" forKey:@"内购 orderId"];
} else {
[logDic setObject:oid forKey:@"内购 orderId"];
}
// ID
NSString *uid = [AccountInfoStorage instance].getUid ?: @"未知用户";
[logDic setObject:uid forKey:@"内购 用户id"];
//
[logDic setObject:@([self getRetryCountForTransaction:tid]) forKey:@"重试次数"];
[logDic setObject:@(self.recheckInterval) forKey:@"重试间隔"];
[logDic setObject:@(self.recheckIndex) forKey:@"当前索引"];
[logDic setObject:@(self.isProcessing) forKey:@"处理中状态"];
[logDic setObject:@(self.isLogin) forKey:@"登录状态"];
NSString *statusMsg = @"";
NSInteger code = -20000;
switch (status) {
case 0:
statusMsg = [NSString stringWithFormat:@"UID: %@, 尝试验单",
[AccountInfoStorage instance].getUid];
statusMsg = [NSString stringWithFormat:@"UID: %@, 尝试验单", uid];
break;
case 1:
statusMsg = [NSString stringWithFormat:@"UID: %@, 验单-补单成功",
[AccountInfoStorage instance].getUid];
statusMsg = [NSString stringWithFormat:@"UID: %@, 验单-补单成功", uid];
code = -20001;
break;
case 2:
statusMsg = [NSString stringWithFormat:@"UID: %@, 验单-补单失败",
[AccountInfoStorage instance].getUid];
statusMsg = [NSString stringWithFormat:@"UID: %@, 验单-补单失败", uid];
code = -20002;
break;
case 3:
statusMsg = [NSString stringWithFormat:@"UID: %@, 验单-补单 id 异常",
[AccountInfoStorage instance].getUid];
statusMsg = [NSString stringWithFormat:@"UID: %@, 验单-补单 id 异常", uid];
code = -20002;
break;
case 4:
statusMsg = [NSString stringWithFormat:@"UID: %@, 重试次数过多",
[AccountInfoStorage instance].getUid];
statusMsg = [NSString stringWithFormat:@"UID: %@, 重试次数过多", uid];
code = -20003;
break;
case 5:
statusMsg = [NSString stringWithFormat:@"UID: %@, 过期交易清理", uid];
code = -20004;
break;
default:
break;
}
@@ -299,29 +373,29 @@
@kStrongify(self);
switch (state) {
case StoreConditionResultStart:
// NSLog(@"~~~```~~~ Purchase started");
NSLog(@"~~~```~~~ Purchase started");
break;
case StoreConditionResultPay:
// NSLog(@"~~~```~~~ Processing payment");
NSLog(@"~~~```~~~ Processing payment");
break;
case StoreConditionResultVerifiedServer:
// NSLog(@"~~~```~~~ Verified by server");
NSLog(@"~~~```~~~ Verified by server");
[self handleIAPSuccess:param];
break;
case StoreConditionResultUserCancelled:
// NSLog(@"~~~```~~~ User cancelled purchase");
NSLog(@"~~~```~~~ User cancelled purchase");
[self handleFailurePurchase:@""];
break;
case StoreConditionResultNoProduct:
// NSLog(@"~~~```~~~ No product found");
NSLog(@"~~~```~~~ No product found");
[self handleFailurePurchase:[NSString stringWithFormat:@"%@ No product found", YMLocalizedString(@"XPIAPRechargeViewController1")]];
break;
case StoreConditionResultFailedVerification:
// NSLog(@"~~~```~~~ Verification failed");
NSLog(@"~~~```~~~ Verification failed");
[self handleFailurePurchase:YMLocalizedString(@"XPIAPRechargeViewController1")];
break;
case StoreConditionResultUnowned:
// NSLog(@"~~~```~~~ Result Unowned");
NSLog(@"~~~```~~~ Result Unowned");
[self handleFailurePurchase:YMLocalizedString(@"XPIAPRechargeViewController1")];
break;
default:
@@ -421,7 +495,7 @@
- (void)checkReceiptWithTransactionID:(NSString *)tID
orderID:(NSString *)orderID {
// NSLog(@" ------------.------------ 后端验单:%@ | %@", tID, orderID);
NSLog(@" ------------.------------ 后端验单:%@ | %@", tID, orderID);
@kWeakify(self);
[Api checkReceipt:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) {
@@ -435,7 +509,7 @@
//
}
}
// NSLog(@" ------------.------------ 后端验单结果:%@ ",msg);
NSLog(@" ------------.------------ 后端验单结果:%@ ",msg);
}
chooseEnv:@"true"
chargeRecordId:orderID
@@ -449,6 +523,14 @@
orderID:(NSString *)orderID
next:(void(^)(BOOL isSuccess))next {
//
if (![self isValidTransactionID:tID orderID:orderID]) {
if (next) {
next(NO);
}
return;
}
//
UIBackgroundTaskIdentifier backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithName:@"VerifyReceipt"
expirationHandler:^{
@@ -459,13 +541,24 @@
[[UIApplication sharedApplication] endBackgroundTask:backgroundTask];
}];
// ...
//
@kWeakify(self);
[Api checkReceipt:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) {
@kStrongify(self);
// ...
BOOL isSuccess = (code == 200);
if (isSuccess) {
[self handleCheckReceiptSuccess:tID isFromRecheck:YES];
NSLog(@" ------------.------------ 后台验单成功:%@ | %@", tID, orderID);
} else {
NSLog(@" ------------.------------ 后台验单失败:%@ | %@ | %@", tID, orderID, msg);
}
//
if (next) {
next(isSuccess);
}
//
[[UIApplication sharedApplication] endBackgroundTask:backgroundTask];
@@ -480,13 +573,19 @@
// transactionID apple
- (void)handleCheckReceiptSuccess:(NSString *)tID
isFromRecheck:(BOOL)isFromRecheck {
//
if (tID.length == 0) {
NSLog(@" ------------.------------ apple 验单失败交易ID为空");
return;
}
if (@available(iOS 15.0, *)) {
@kWeakify(self);
[[PIIAPRegulate shared] verifyBusinessAccomplishWithTransactionID:tID
completionHandler:^(BOOL success, NSError * _Nullable error) {
@kStrongify(self);
if (success) {
// NSLog(@" ------------.------------ apple 验单成功");
NSLog(@" ------------.------------ apple 验单成功");
//
[RechargeStorage delegateTransactionId:tID
uid:[AccountInfoStorage instance].getUid];
@@ -494,7 +593,7 @@
[self removeRetryCountForTransaction:tID];
} else {
//
// NSLog(@" ------------.------------ apple 验单成功%@ ",error);
NSLog(@" ------------.------------ apple 验单失败%@ ",error);
if (error == nil) {
// appstore
[RechargeStorage delegateTransactionId:tID
@@ -502,7 +601,10 @@
//
[self removeRetryCountForTransaction:tID];
} else {
[self retryCheckAllReceipt];
//
if (isFromRecheck) {
//
}
}
}
@@ -549,48 +651,92 @@
//
- (void)cleanupStaleTransactions {
NSArray *receipts = [RechargeStorage getAllReceiptsWithUid:[AccountInfoStorage instance].getUid];
if (!receipts || receipts.count == 0) {
return;
}
NSDate *now = [NSDate date];
NSInteger cleanupDays = 70; // 70
for (NSDictionary *receipt in receipts) {
// 7
//
NSString *timestamp = receipt[@"timestamp"];
if (timestamp) {
if (!timestamp || timestamp.length == 0) {
continue;
}
NSDate *transactionDate = nil;
//
NSArray *dateFormats = @[
@"yyyy-MM-dd HH:mm:ss Z",
@"yyyy-MM-dd'T'HH:mm:ssZ",
@"EEE MMM dd HH:mm:ss Z yyyy"
];
for (NSString *format in dateFormats) {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss Z"]; //
NSDate *transactionDate = [dateFormatter dateFromString:timestamp];
[dateFormatter setDateFormat:format];
[dateFormatter setLocale:[NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]];
if (transactionDate && [now timeIntervalSinceDate:transactionDate] > 70 * 24 * 60 * 60) {
NSString *tID = receipt[@"transactionId"];
[RechargeStorage delegateTransactionId:tID uid:[AccountInfoStorage instance].getUid];
[self _logToBugly:tID oID:receipt[@"orderId"] status:5]; //
//
[self removeRetryCountForTransaction:tID];
transactionDate = [dateFormatter dateFromString:timestamp];
if (transactionDate) {
break;
}
}
// 使60
if (!transactionDate) {
transactionDate = [now dateByAddingTimeInterval:-60 * 24 * 60 * 60];
}
//
if ([now timeIntervalSinceDate:transactionDate] > cleanupDays * 24 * 60 * 60) {
NSString *tID = receipt[@"transactionId"];
NSString *oID = receipt[@"orderId"];
//
[self _logToBugly:tID oID:oID status:5]; // 5
//
[RechargeStorage delegateTransactionId:tID uid:[AccountInfoStorage instance].getUid];
//
[self removeRetryCountForTransaction:tID];
}
}
}
//
- (NSInteger)getRetryCountForTransaction:(NSString *)transactionId {
NSNumber *countNumber = self.retryCountMap[transactionId];
return countNumber ? [countNumber integerValue] : 0;
__block NSInteger count = 0;
dispatch_sync(self.retryCountQueue, ^{
NSNumber *countNumber = self.retryCountDict[transactionId];
count = countNumber ? [countNumber integerValue] : 0;
});
return count;
}
//
- (void)incrementRetryCountForTransaction:(NSString *)transactionId {
NSInteger currentCount = [self getRetryCountForTransaction:transactionId];
self.retryCountMap[transactionId] = @(currentCount + 1);
dispatch_barrier_async(self.retryCountQueue, ^{
NSInteger currentCount = [self.retryCountDict[transactionId] integerValue];
self.retryCountDict[transactionId] = @(currentCount + 1);
});
}
//
- (void)removeRetryCountForTransaction:(NSString *)transactionId {
[self.retryCountMap removeObjectForKey:transactionId];
dispatch_barrier_async(self.retryCountQueue, ^{
[self.retryCountDict removeObjectForKey:transactionId];
});
}
//
- (void)cleanAllRetryCount {
[self.retryCountMap removeAllObjects];
dispatch_barrier_async(self.retryCountQueue, ^{
[self.retryCountDict removeAllObjects];
});
}
@end

View File

@@ -195,10 +195,23 @@ XPIAPRechargeHeadCellDelegate>
@kStrongify(self);
[self hideHUD];
[self getUserWalletBalanceInfo];
//
FirstRechargeManager *manager = [FirstRechargeManager sharedManager];
// 使
[manager updateChargeStatusToCompleted];
[self.collectionView reloadData];
//
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[[FirstRechargeManager sharedManager] updateChargeStatusToCompleted];
});
if(self.delegate && [self.delegate respondsToSelector:@selector(paySuccess)]){
[self.delegate paySuccess];
}
self.rechargeBtn.userInteractionEnabled = YES;
} failure:^(NSError * _Nonnull error) {
@kStrongify(self);
[self hideHUD];
@@ -280,9 +293,6 @@ XPIAPRechargeHeadCellDelegate>
return CGSizeMake(kGetScaleWidth(108), kGetScaleWidth(121));
}
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
XPIAPRechargeCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass(XPIAPRechargeCollectionViewCell.self) forIndexPath:indexPath];
NSInteger count = indexPath.item;
cell.rechargeModel = [self.dataSource xpSafeObjectAtIndex:count];
@@ -300,7 +310,6 @@ XPIAPRechargeHeadCellDelegate>
}
#pragma mark -
- (UICollectionView *)collectionView {
if (!_collectionView) {
MSBaseRTLFlowLayout *layout = [[MSBaseRTLFlowLayout alloc] init];
@@ -317,10 +326,7 @@ XPIAPRechargeHeadCellDelegate>
_collectionView.delegate = self;
_collectionView.dataSource = self;
_collectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
// _collectionView.contentInset = UIEdgeInsetsMake(kHeaderViewHeight, 0, 0, 0);
_collectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
return _collectionView;
}

View File

@@ -284,16 +284,16 @@
@kStrongify(self);
[UIView animateWithDuration:0.5 animations:^{
self.firstChargeBackgroundView.alpha = 1;
} completion:^(BOOL finished) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self removeFirstChargePopup];
});
}];
}];
[self.firstChargeWebVC setDidTapCharge:^{
@kStrongify(self);
[self removeFirstChargePopup];
}];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self removeFirstChargePopup];
});
}
//

View File

@@ -353,4 +353,16 @@
return _goButton;
}
// 穿
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (!self.userInteractionEnabled || self.hidden || self.alpha <= 0.01) {
return nil;
}
CGPoint goButtonPoint = [self.goButton convertPoint:point fromView:self];
if ([self.goButton pointInside:goButtonPoint withEvent:event]) {
return self.goButton;
}
return nil;
}
@end

View File

@@ -401,4 +401,16 @@
return _goButton;
}
// ========== 穿 ==========
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (!self.userInteractionEnabled || self.hidden || self.alpha <= 0.01) {
return nil;
}
CGPoint goButtonPoint = [self.goButton convertPoint:point fromView:self];
if ([self.goButton pointInside:goButtonPoint withEvent:event]) {
return self.goButton;
}
return nil;
}
@end

View File

@@ -52,11 +52,13 @@ static const NSInteger kItemsPerRow = 5;
[self.blurEffectView mas_makeConstraints:^(MASConstraintMaker *make) {
make.leading.trailing.equalTo(self).inset(0);
make.bottom.equalTo(self).offset(12);
make.height.mas_equalTo(self.collectionView.mas_height).multipliedBy(1.05);
make.height.mas_equalTo(KScreenHeight*2/3);
}];
[self.collectionView mas_makeConstraints:^(MASConstraintMaker *make) {
make.bottom.leading.trailing.equalTo(self).inset(kGetScaleWidth(0));
make.height.mas_equalTo(kGetScaleWidth(246));
make.bottom.equalTo(self);
make.leading.trailing.equalTo(self).inset(16);
// make.height.mas_equalTo(kGetScaleWidth(246));
make.height.mas_equalTo(KScreenHeight*2/3 - 30);
}];
}
@@ -69,7 +71,6 @@ static const NSInteger kItemsPerRow = 5;
-(void)setPlayList:(NSMutableArray *)playList {
_playList = playList;
[self.dataSource addObjectsFromArray:playList];
[self updateViewHeightWithItemCount:self.dataSource.count];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.collectionView reloadData];
});
@@ -78,7 +79,6 @@ static const NSInteger kItemsPerRow = 5;
- (void)setLittleGameList:(NSMutableArray<LittleGameInfoModel *> *)littleGameList {
_littleGameList = littleGameList;
[self.dataSource addObjectsFromArray:littleGameList];
[self updateViewHeightWithItemCount:self.dataSource.count];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.collectionView reloadData];
});
@@ -90,39 +90,18 @@ static const NSInteger kItemsPerRow = 5;
self.dataSource = [NSMutableArray array];
[self.dataSource addObjectsFromArray:playList];
[self.dataSource addObjectsFromArray:littleGameList];
[self updateViewHeightWithItemCount:self.dataSource.count];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.collectionView reloadData];
});
}
- (void)updateViewHeightWithItemCount:(NSInteger)count {
if (count == 0) {
return;
}
NSInteger lineNum = (count + kItemsPerRow - 1)/kItemsPerRow;// MIN(3, (count + kItemsPerRow - 1)/kItemsPerRow);
CGFloat calHeight = self.itemHeight;
CGFloat height = 20 + 56 * lineNum + 44 * (lineNum - 1) + 70;
height = calHeight * lineNum;
// [self.ms_bgView mas_updateConstraints:^(MASConstraintMaker *make) {
// make.height.mas_equalTo(kGetScaleWidth(height + 12));
// }];
if (height > KScreenHeight*2/3) {
height = KScreenHeight*2/3;
}
[self.collectionView mas_updateConstraints:^(MASConstraintMaker *make) {
make.height.mas_equalTo(kGetScaleWidth(height));
}];
}
- (NSInteger)countOfCurrentType {
return self.dataSource.count;// self.littleGameList.count > 0 ? self.littleGameList.count : self.playList.count;
}
#pragma mark- UICollectionViewDelegate,UICollectionViewDataSource,UICollectionViewDelegateFlowLayout
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{
CGFloat width = (KScreenWidth-kGetScaleWidth(6))/4;
CGFloat width = (KScreenWidth - 64)/4;
return [self countOfCurrentType] > 0 ? CGSizeMake(width, self.itemHeight) : CGSizeMake(KScreenWidth, self.emptyHeight);
}
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section{
@@ -183,16 +162,16 @@ static const NSInteger kItemsPerRow = 5;
if (!_collectionView) {
MSBaseRTLFlowLayout *layout = [[MSBaseRTLFlowLayout alloc] init];
layout.minimumLineSpacing = 10;
layout.minimumInteritemSpacing = 0;
layout.minimumLineSpacing = 0;
layout.minimumInteritemSpacing = 8;
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
_collectionView.showsHorizontalScrollIndicator = NO;
_collectionView.showsVerticalScrollIndicator = NO;
_collectionView.dataSource = self;
_collectionView.delegate = self;
_collectionView.backgroundColor = [UIColor clearColor];
[_collectionView registerClass:[MSRoomMenuGameCell class] forCellWithReuseIdentifier:NSStringFromClass([MSRoomMenuGameCell class])];
[_collectionView registerClass:[MSRoomMenuGameEmptyCell class] forCellWithReuseIdentifier:NSStringFromClass([MSRoomMenuGameEmptyCell class])];
}
return _collectionView;
}

View File

@@ -70,7 +70,7 @@
}
- (void)gameSetUp {
// nslog(@"小游戏的版本号是:%@", [SudMGP getVersion]);
NSLog(@"小游戏的版本号是:%@", [SudMGP getVersion]);
BOOL isTestEnv = NO;
#ifdef DEBUG
isTestEnv = YES;
@@ -90,7 +90,7 @@
}
NSString * userId = [AccountInfoStorage instance].getUid;
NSString * roomId = self.gameModel.roomId;
// nslog(@"用户ID%@房间ID%@, 游戏ID%lld, code:%@", userId, roomId, self.currentmgId, self.code);
NSLog(@"用户ID%@房间ID%@, 游戏ID%lld, code:%@", userId, roomId, self.currentmgId, self.code);
NSString *language = [NSBundle getLanguageText];
if ([language hasPrefix:@"zh"]) {
@@ -105,7 +105,7 @@
self.fsmAPP2MG = [SudMGP loadMG:userId roomId:roomId code:self.code mgId:self.currentmgId language:language fsmMG:self rootView:self];
} else {
/// ,
// nslog(@"ISudFSMMG:initGameSDKWithAppID:初始化sdk失败 :%@",retMsg);
NSLog(@"ISudFSMMG:initGameSDKWithAppID:初始化sdk失败 :%@",retMsg);
}
}];
}
@@ -157,7 +157,7 @@
*
*/
-(void)onGameLog:(NSString*)dataJson {
// nslog(@"ISudFSMMG:onGameLog:%@", dataJson);
NSLog(@"ISudFSMMG:onGameLog:%@", dataJson);
NSDictionary * dic = [SudCommon turnStringToDictionary:dataJson];
NSString * msg_string = [dic objectForKey:@"msg"];
if (!msg_string) {
@@ -169,7 +169,7 @@
*
*/
-(void)onGameStarted {
// nslog(@"ISudFSMMG:onGameStarted:游戏开始");
NSLog(@"ISudFSMMG:onGameStarted:游戏开始");
[self notifySelfInState:YES seatIndex:-1];///
[self handleSelfReadyEvent];///
}
@@ -178,7 +178,7 @@
*
*/
-(void)onGameDestroyed {
// nslog(@"ISudFSMMG:onGameDestroyed:游戏销毁");
NSLog(@"ISudFSMMG:onGameDestroyed:游戏销毁");
}
/**
@@ -186,14 +186,14 @@
* @param dataJson {"code":"value"}
*/
-(void)onExpireCode:(id<ISudFSMStateHandle>)handle dataJson:(NSString*)dataJson {
// nslog(@"ISudFSMMG:onExpireCode:Code过期");
NSLog(@"ISudFSMMG:onExpireCode:Code过期");
//
[Api getSudGameCode:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) {
if (code == 200) {
NSString * gameCode = data.data[@"code"];
self.code = gameCode;
[self.fsmAPP2MG updateCode:gameCode listener:^(int retCode, const NSString *retMsg, const NSString *dataJson) {
// nslog(@"ISudFSMMG:updateGameCode retCode=%@ retMsg=%@ dataJson=%@", @(retCode), retMsg, dataJson);
NSLog(@"ISudFSMMG:updateGameCode retCode=%@ retMsg=%@ dataJson=%@", @(retCode), retMsg, dataJson);
}];
//
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:@(0), @"ret_code", @"return form APP onExpireCode", @"ret_msg", nil];
@@ -251,15 +251,15 @@
//
if (error) {
// nslog(@"JSON 解析失败: %@", error.localizedDescription);
NSLog(@"JSON 解析失败: %@", error.localizedDescription);
} else {
// nslog(@"解析后的字典: %@", dictionary);
NSLog(@"解析后的字典: %@", dictionary);
//
NSMutableDictionary *ui = [[NSMutableDictionary alloc] initWithDictionary:dictionary[@"ui"]];
[ui setObject: @{@"hide" : @(YES)}
forKey:@"lobby_game_setting"];
[dictionary setObject:ui forKey:@"ui"];
// nslog(@"更新后的字典: %@", dictionary);
NSLog(@"更新后的字典: %@", dictionary);
}
// dict[@"ui"] = @{
@@ -290,11 +290,11 @@
* @param dataJson json
*/
-(void)onGameStateChange:(id<ISudFSMStateHandle>) handle state:(NSString*) state dataJson:(NSString*) dataJson {
// nslog(@"onGameStateChange 回调: %@ \n%@", state, dataJson);
NSLog(@"onGameStateChange 回调: %@ \n%@", state, dataJson);
if ([state isEqualToString:MG_COMMON_SELF_CLICK_READY_BTN]) {
// [self addAI];
} else if ([state isEqualToString:@"mg_common_game_add_ai_players"]) {
// nslog(@" ????????????????????????????????????????????? ");
NSLog(@" ????????????????????????????????????????????? ");
} else if([state isEqualToString:MG_COMMON_GAME_SETTLE]){
NSDictionary *data = @{@"value0":self.gameModel.data.matchRoundId ?: @""};
NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:[data mj_JSONString] , @"reportGameInfoExtras",@"value0" , @"reportGameInfoKey" ,nil];
@@ -308,7 +308,7 @@
[self.delegate getGameRsultsWithList:list];
}
}else if ([state isEqualToString:MG_COMMON_PUBLIC_MESSAGE]) {
// nslog(@"ISudFSMMG:onGameStateChange:游戏->APP:公屏消息");
NSLog(@"ISudFSMMG:onGameStateChange:游戏->APP:公屏消息");
} else if ([state isEqualToString:MG_COMMON_KEY_WORD_TO_HIT]) {
}else if ([state isEqualToString:MG_COMMON_SELF_CLICK_JOIN_BTN]) {//
@@ -318,9 +318,9 @@
seatIndex = [[dic objectForKey:@"seatIndex"] intValue];
}
if (seatIndex == -1) {
// nslog(@"来自加入按钮%d",seatIndex);
NSLog(@"来自加入按钮%d",seatIndex);
}else {
// nslog(@"来自麦位+入%d",seatIndex);
NSLog(@"来自麦位+入%d",seatIndex);
}
[self notifySelfInState:YES seatIndex:-1];
} else if([state isEqualToString:MG_COMMON_SELF_CLICK_START_BTN]) {//
@@ -330,7 +330,7 @@
[self handleSelfInExitEvent];
} else {
///
// nslog(@"ISudFSMMG:onGameStateChange:游戏->APP:state:%@",MG_COMMON_PUBLIC_MESSAGE);
NSLog(@"ISudFSMMG:onGameStateChange:游戏->APP:state:%@",MG_COMMON_PUBLIC_MESSAGE);
}
}
@@ -342,7 +342,7 @@
* @param dataJson JSON
*/
-(void)onPlayerStateChange:(nullable id<ISudFSMStateHandle>) handle userId:(NSString*) userId state:(NSString*) state dataJson:(NSString*) dataJson {
// nslog(@"ISudFSMMG:onPlayerStateChange:游戏->APP:游戏玩家状态变化:userId: %@ --state: %@ --dataJson: %@", userId, state, dataJson);
NSLog(@"ISudFSMMG:onPlayerStateChange:游戏->APP:游戏玩家状态变化:userId: %@ --state: %@ --dataJson: %@", userId, state, dataJson);
///
NSString *dataStr = @"";
if ([state isEqualToString:MG_COMMON_PLAYER_IN]) {
@@ -382,9 +382,9 @@
dataStr = YMLocalizedString(@"XPRoomLittleGameContainerView8");
[self handleState_MG_DG_SCORE_WithUserId:userId dataJson:dataJson];
}else {
// nslog(@"ISudFSMMG:onPlayerStateChange:未做解析状态:%@", MG_DG_SCORE);
NSLog(@"ISudFSMMG:onPlayerStateChange:未做解析状态:%@", MG_DG_SCORE);
}
// nslog(@"ISudFSMMG:onPlayerStateChange:dataStr:%@", dataStr);
NSLog(@"ISudFSMMG:onPlayerStateChange:dataStr:%@", dataStr);
///
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:@(0), @"ret_code", @"return form APP onPlayerStateChange", @"ret_msg", nil];
[handle success:[SudCommon dictionaryToJson:dict]];
@@ -443,9 +443,9 @@
/// @param state
/// @param dataJson json
- (void)notifyStateChange:(NSString *) state dataJson:(NSString*) dataJson {
// nslog(@"ISudFSMMG: START notifyStateChange:state=%@ \ndataJson=%@", state, dataJson);
NSLog(@"ISudFSMMG: START notifyStateChange:state=%@ \ndataJson=%@", state, dataJson);
[self.fsmAPP2MG notifyStateChange:state dataJson:dataJson listener:^(int retCode, const NSString *retMsg, const NSString *dataJson) {
// nslog(@"ISudFSMMG:notifyStateChange:retCode=%@ retMsg=%@ dataJson=%@", @(retCode), retMsg, dataJson);
NSLog(@"ISudFSMMG:notifyStateChange:retCode=%@ retMsg=%@ dataJson=%@", @(retCode), retMsg, dataJson);
if (retCode == 0 && [state isEqualToString:APP_COMMON_SELF_PLAYING]) {//
//
@@ -606,11 +606,11 @@
}
- (void)handleState_MG_DG_SELECTING_WithUserId:(NSString *)userId dataJson:(NSString *)dataJson {
// nslog(@"handleState_MG_DG_SELECTING_WithUserId%@",dataJson);
NSLog(@"handleState_MG_DG_SELECTING_WithUserId%@",dataJson);
}
- (void)handleState_MG_DG_PAINTING_WithUserId:(NSString *)userId dataJson:(NSString *)dataJson {
// nslog(@"handleState_MG_DG_PAINTING_WithUserId%@",dataJson);
NSLog(@"handleState_MG_DG_PAINTING_WithUserId%@",dataJson);
///
NSDictionary * dic = [SudCommon turnStringToDictionary:dataJson];
bool isPainting = NO;
@@ -621,18 +621,18 @@
- (void)handleState_MG_DG_ERRORANSWER_WithUserId:(NSString *)userId dataJson:(NSString *)dataJson {
///
// nslog(@"handleState_MG_DG_ERRORANSWER_WithUserId%@",dataJson);
NSLog(@"handleState_MG_DG_ERRORANSWER_WithUserId%@",dataJson);
}
- (void)handleState_MG_DG_TOTALSCORE_WithUserId:(NSString *)userId dataJson:(NSString *)dataJson {
///
// nslog(@"handleState_MG_DG_TOTALSCORE_WithUserId%@",dataJson);
NSLog(@"handleState_MG_DG_TOTALSCORE_WithUserId%@",dataJson);
}
- (void)handleState_MG_DG_SCORE_WithUserId:(NSString *)userId dataJson:(NSString *)dataJson {
///
// nslog(@"handleState_MG_DG_SCORE_WithUserId%@",dataJson);
NSLog(@"handleState_MG_DG_SCORE_WithUserId%@",dataJson);
}
/// MG

View File

@@ -1447,13 +1447,13 @@ XPCandyTreeInsufficientBalanceViewDelegate>
self.userInfo = userInfo;
[self requestBoomData];
[self getOnlineCount];
@kWeakify(self);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0), ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0), ^{
@kStrongify(self);
//
[self.presenter getRedPacket:[NSString stringWithFormat:@"%ld", self.roomInfo.uid]];
[self getOnlineCount];
});
switch (roomInfo.type) {
@@ -1842,7 +1842,7 @@ XPCandyTreeInsufficientBalanceViewDelegate>
continue;
}
NSLog(@" --- Message Raw Attach Content: %@, %@, %ld", @(message.senderClientType), message.rawAttachContent, (long)message.messageType);
// NSLog(@" --- Message Raw Attach Content: %@, %@, %ld", @(message.senderClientType), message.rawAttachContent, (long)message.messageType);
if (message.messageType == NIMMessageTypeNotification) {
NIMNotificationObject *notiMsg = (NIMNotificationObject *)message.messageObject;

View File

@@ -52,7 +52,9 @@
return manager;
}
+(NSString *)getHostUrl{
#ifdef DEBUG
#if 0
// return @"https://api.epartylive.com";
NSString *isProduction = [[NSUserDefaults standardUserDefaults]valueForKey:@"kIsProductionEnvironment"];
if([isProduction isEqualToString:@"YES"]){
return API_HOST_URL;
@@ -93,13 +95,31 @@
editParam = [MSParamsDecode msDecodeParams:editParam];
params = [self configBaseParmars:editParam];
#ifdef DEBUG
NSLog(@"\nmethod:\n%@\nparameter:\n%@\n", method, params);
#if 0
// URL
NSString *baseUrl = [HttpRequestHelper getHostUrl];
NSString *fullUrl = [NSString stringWithFormat:@"%@/%@", baseUrl, method];
//
NSMutableArray *queryItems = [NSMutableArray array];
[params enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
[queryItems addObject:[NSString stringWithFormat:@"%@=%@", key, obj]];
}];
NSString *queryString = [queryItems componentsJoinedByString:@"&"];
if (queryString.length > 0) {
fullUrl = [NSString stringWithFormat:@"%@?%@", fullUrl, queryString];
}
NSLog(@"\n🌐 API Request Info:");
NSLog(@"📍 Full URL: %@", fullUrl);
NSLog(@"🔧 Method: %@", method);
NSLog(@"📦 Parameter Type: queryParameters");
NSLog(@"📋 Parameters: %@\n", params);
#endif
@kWeakify(self);
[manager GET:method parameters:params headers:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
BaseModel *baseModel = [BaseModel modelWithDictionary:responseObject];
#ifdef DEBUG
#if 0
NSLog(@"%@ - \n%@\n", method, [baseModel toJSONString]);
#else
#endif
@@ -226,14 +246,22 @@
params = [self configBaseParmars:params];
#ifdef DEBUG
NSLog(@"\nmethod:\n%@\nparameter:\n%@", method, params);
#if 0
// URL
NSString *baseUrl = [HttpRequestHelper getHostUrl];
NSString *fullUrl = [NSString stringWithFormat:@"%@/%@", baseUrl, method];
NSLog(@"\n🌐 API Request Info:");
NSLog(@"📍 Full URL: %@", fullUrl);
NSLog(@"🔧 Method: %@", method);
NSLog(@"📦 Parameter Type: bodyParameters");
NSLog(@"📋 Parameters: %@\n", params);
#else
#endif
[manager POST:method parameters:params headers:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
BaseModel *baseModel = [BaseModel modelWithDictionary:responseObject];
#ifdef DEBUG
#if 0
NSLog(@"\n%@", [baseModel toJSONString]);
#else
#endif
@@ -259,7 +287,7 @@
params = [MSParamsDecode msDecodeParams:[params mutableCopy] ];
params = [self configBaseParmars:params];
#ifdef DEBUG
#if 0
NSLog(@"\nmethod:\n%@\nparameter:\n%@\n 超時:%@",
method,
params,
@@ -282,13 +310,13 @@ constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
progress:nil
success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
BaseModel *baseModel = [BaseModel modelWithDictionary:responseObject];
#ifdef DEBUG
#if 0
NSLog(@"%@ - \n%@\n", method, [baseModel toJSONString]);
#else
#endif
success(baseModel);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
#ifdef DEBUG
#if 0
NSLog(@"%@ - \n%@\n", method, error);
#else
#endif
@@ -311,14 +339,32 @@ constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
params = [MSParamsDecode msDecodeParams:[params mutableCopy] ];
params = [self configBaseParmars:params];
AFHTTPSessionManager *manager = [HttpRequestHelper requestManager];
#ifdef DEBUG
NSLog(@"\nmethod:\n%@\nparameter:\n%@\n", method, params);
#if 0
// URL
NSString *baseUrl = [HttpRequestHelper getHostUrl];
NSString *fullUrl = [NSString stringWithFormat:@"%@/%@", baseUrl, method];
//
NSMutableArray *queryItems = [NSMutableArray array];
[params enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
[queryItems addObject:[NSString stringWithFormat:@"%@=%@", key, obj]];
}];
NSString *queryString = [queryItems componentsJoinedByString:@"&"];
if (queryString.length > 0) {
fullUrl = [NSString stringWithFormat:@"%@?%@", fullUrl, queryString];
}
NSLog(@"\n🌐 API Request Info:");
NSLog(@"📍 Full URL: %@", fullUrl);
NSLog(@"🔧 Method: %@", method);
NSLog(@"📦 Parameter Type: queryParameters");
NSLog(@"📋 Parameters: %@\n", params);
#else
#endif
@kWeakify(self);
[manager DELETE:method parameters:params headers:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
BaseModel *baseModel = [BaseModel modelWithDictionary:responseObject];
#ifdef DEBUG
#if 0
NSLog(@"\n%@\n", [baseModel toJSONString]);
#else
#endif
@@ -456,7 +502,7 @@ constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
failure(error.code, error.localizedDescription.length > 0 ? error.localizedDescription : YMLocalizedString(@"HttpRequestHelper4"));
}
}
#ifdef DEBUG
#if 0
NSLog(@"\n%@", error);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSDictionary *allHeaders = response.allHeaderFields;
@@ -491,7 +537,7 @@ constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
AFHTTPSessionManager *manager = [HttpRequestHelper requestManager];
NSString *url = [self getHostUrl];
NSString *urlPath = [NSString stringWithFormat:@"%@/%@", url ,path];
#ifdef DEBUG
#if 0
NSLog(@"\nmethod:\n%@\nparameter:\n%@", path, params);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[BSNetListenModel addHttpReq:urlPath header:manager.requestSerializer.HTTPRequestHeaders param:[params copy] time:[NSDate getCurrentTimeWithFormat:@"yyyy-MM-dd HH:mm:ss"]];
@@ -536,12 +582,12 @@ constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
if (responseObject) {
BaseModel *baseModel = [BaseModel modelWithDictionary:responseObject];
#ifdef DEBUG
#if 0
NSLog(@"\n%@", [baseModel toJSONString]);
#else
#endif
if (baseModel.code == 200) {
#ifdef DEBUG
#if 0
NSHTTPURLResponse *urlresponse = (NSHTTPURLResponse *)response;
NSDictionary *allHeaders = urlresponse.allHeaderFields;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

View File

@@ -11,13 +11,13 @@ NS_ASSUME_NONNULL_BEGIN
@interface AccountModel : PIBaseModel
@property (nonatomic , assign) NSString *uid;
@property (nonatomic , copy) NSString *uid;
@property (nonatomic , copy) NSString *jti;
@property (nonatomic , copy) NSString *token_type;
@property (nonatomic , copy) NSString *refresh_token;
@property (nonatomic , copy) NSString *netEaseToken;
@property (nonatomic , copy) NSString *access_token;
@property (nonatomic , assign) NSNumber *expires_in;
@property (nonatomic , copy) NSNumber *expires_in;
@end
@interface HomeUserModel : PIBaseModel

View File

@@ -98,8 +98,8 @@
[[self getView] completeUserInfo];
return;
case 3009: //
[[self getView] accountCanceled:data.model2dictionary];
return;
// [[self getView] accountCanceled:data.model2dictionary];
// return;
case 31005: {//
}
break;

View File

@@ -13,6 +13,7 @@
- (void)stopMonitoring;
- (void)manualCheckFirstRecharge;
- (void)markTodayShown;
- (void)updateChargeStatusToCompleted;
- (FirstRechargeModel *)loadCurrentModel;
@@ -29,4 +30,4 @@
- (void)firstRechargeManager:(FirstRechargeManager *)manager
didFailWithError:(NSError *)error;
@end
@end

View File

@@ -106,6 +106,19 @@ static NSString * const kCurrentUserIdKey = @"FirstRechargeCurrentUserId";
[self setTodayShownForCurrentUser:today];
}
- (void)updateChargeStatusToCompleted {
if (self.currentFirstRechargeData && !self.currentFirstRechargeData.chargeStatus) {
//
self.currentFirstRechargeData.chargeStatus = YES;
//
[self saveCachedModel:self.currentFirstRechargeData];
//
[self notifyDelegatesWithModel:self.currentFirstRechargeData shouldShow:NO];
}
}
#pragma mark - Private Methods
- (void)setupDailyTimer {

View File

@@ -127,7 +127,7 @@
"LoginBindPhoneViewController2" = "ربط رقم الجوال";
"MvpViewController0" = "وقت تسجيل الخروج: %@";
"MvpViewController1" = "%@\n\nيرجى الاتصال بخدمة العملاء (WeChat: mxyz2050) للاستفسارات.";
"MvpViewController1" = "%@\n\nيرجى الاتصال بخدمة العملاء للاستفسارات.";
"MvpViewController2" = "تم تسجيل الخروج من هذا الحساب";
"MvpViewController3" = "من أجل خلق بيئة عمل
@@ -2941,8 +2941,8 @@ ineHeadView12" = "الحمل";
"LoginBindPhoneViewController2" = "ربط رقم الهاتف المحمول";
"MvpViewController0" = "وقت الإلغاء: %@";
"MvpViewController1.1" = "@%\n\nيرجى الاتصال بخدمة العملاء (WeChat: mxyz2050) للحصول على استفسارات";
"MvpViewController1.2" = "يرجى الاتصال بخدمة العملاء (WeChat: mxyz2050) للحصول على استفسارات";
"MvpViewController1.1" = "@%\n\nيرجى الاتصال بخدمة العملاء للحصول على استفسارات";
"MvpViewController1.2" = "يرجى الاتصال بخدمة العملاء للحصول على استفسارات";
"MvpViewController3" = "من أجل خلق بيئة إنترنت آمنة أكثر\nحماية ممتلكاتك وممتلكات الآخرين\nيرجى تقديم التحقق الهوية أولا";
"MvpViewController5" = "التحقق من الهوية";

View File

@@ -2548,9 +2548,9 @@
"LoginBindPhoneViewController2" = "Bind Mobile Number";
"MvpViewController0" = "Logoff time: %@";
"MvpViewController1" = "%@\n\nPlease contact customer service (WeChat: mxyz2050) for inquiries.";
"MvpViewController1.1" = "%@\n\nPlease contact customer service (WeChat: mxyz2050) for inquiries.";
"MvpViewController1.2" = "Please contact customer service (WeChat: mxyz2050) for inquiries.";
"MvpViewController1" = "%@\n\nPlease contact customer service for inquiries.";
"MvpViewController1.1" = "%@\n\nPlease contact customer service for inquiries.";
"MvpViewController1.2" = "Please contact customer service for inquiries.";
"MvpViewController2" = "This account has been logged off";
"MvpViewController3" = "In order to create a safer online environment\nand protect the property safety of you and others,\nplease complete real-name authentication first";

View File

@@ -2223,9 +2223,9 @@
"LoginBindPhoneViewController2" = "Telefon numarasını bağla";
"MvpViewController0" = "Oturumu kapatma zamanı: %@";
"MvpViewController1" = "%@\n\nMüşteri hizmetleriyle iletişime geçin (WeChat: mxyz2050) danışın";
"MvpViewController1.1" = "%@\n\nMüşteri hizmetleriyle iletişime geçin (WeChat: mxyz2050) danışın";
"MvpViewController1.2" = "Müşteri hizmetleriyle iletişime geçin (WeChat: mxyz2050) danışın";
"MvpViewController1" = "%@\n\nMüşteri hizmetleriyle iletişime geçin danışın";
"MvpViewController1.1" = "%@\n\nMüşteri hizmetleriyle iletişime geçin danışın";
"MvpViewController1.2" = "Müşteri hizmetleriyle iletişime geçin danışın";
"MvpViewController2" = "Bu hesap kapatıldı";
"MvpViewController3" = "Daha güvenli bir ağ ortamı oluşturmak ve sizin ve diğerlerinin mülk güvenliğini korumak için\nlütfen önce kimlik doğrulaması yapın";

View File

@@ -2263,8 +2263,8 @@
"LoginBindPhoneViewController2" = "綁定手機號";
"MvpViewController0" = "註銷時間: %@";
"MvpViewController1.1" = "%@\n\n請聯繫客服微信mxyz2050咨詢哦";
"MvpViewController1.2" = "請聯繫客服微信mxyz2050咨詢哦";
"MvpViewController1.1" = "%@\n\n請聯繫客服咨詢哦";
"MvpViewController1.2" = "請聯繫客服咨詢哦";
"MvpViewController2" = "該賬號已註銷";
"MvpViewController3" = "為了營造更安全的網絡環境\n保護您和他人的財產安全\n請先進行實名認證";