Compare commits
9 Commits
codex/priz
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
749464c03e | ||
|
|
8229b41382 | ||
|
|
91dd42ca1c | ||
| d45096d13f | |||
| 2aa7cdbd61 | |||
|
|
a29669ccf6 | ||
|
|
b3b63dcba2 | ||
|
|
994a7ba0df | ||
|
|
e0db8751f3 |
613
.claude/plan/channel-user-bindding-doc.md
Normal file
613
.claude/plan/channel-user-bindding-doc.md
Normal file
@ -0,0 +1,613 @@
|
||||
# 渠道管理与用户注册绑定调用链文档
|
||||
|
||||
## 概述
|
||||
|
||||
本文档描述 Bindbox Game 项目中渠道管理模块与用户注册绑定的调用链关系。
|
||||
|
||||
**渠道绑定的三种方式:**
|
||||
1. **用户登录时绑定** - 微信/抖音登录时传入 `channel_code`
|
||||
2. **定时任务自动绑定** - 直播间奖品发放时,根据活动关联渠道自动绑定主播邀请人
|
||||
3. **抖音登录绑定** - 抖音小程序登录时传入 `channel_code`
|
||||
|
||||
---
|
||||
|
||||
## 一、数据模型
|
||||
|
||||
### 1.1 渠道表 (channels)
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | int64 | 主键ID |
|
||||
| name | string | 渠道名称 |
|
||||
| code | string | 渠道唯一标识(用于登录时绑定) |
|
||||
| type | string | 渠道类型 |
|
||||
| remarks | string | 备注 |
|
||||
| created_at | time | 创建时间 |
|
||||
| updated_at | time | 更新时间 |
|
||||
| deleted_at | time | 删除时间(软删) |
|
||||
|
||||
**文件位置**: `internal/repository/mysql/model/channels.gen.go:16-25`
|
||||
|
||||
### 1.2 用户表 (users) - 渠道相关字段
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | int64 | 主键ID |
|
||||
| channel_id | int64 | 渠道ID(关联 channels.id) |
|
||||
| invite_code | string | 用户唯一邀请码 |
|
||||
| inviter_id | int64 | 邀请人用户ID |
|
||||
| openid | string | 微信openid |
|
||||
| unionid | string | 微信unionid |
|
||||
|
||||
**文件位置**: `internal/repository/mysql/model/users.gen.go:16-33`
|
||||
|
||||
---
|
||||
|
||||
## 二、调用链架构图
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ 前端/客户端 │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ API 路由层 │
|
||||
│ internal/router/router.go │
|
||||
│ │
|
||||
│ 渠道管理路由: │
|
||||
│ - POST /api/admin/channels → CreateChannel() │
|
||||
│ - PUT /api/admin/channels/:id → ModifyChannel() │
|
||||
│ - DELETE /api/admin/channels/:id → DeleteChannel() │
|
||||
│ - GET /api/admin/channels → ListChannels() │
|
||||
│ - GET /api/admin/channels/:id/stats → ChannelStats() │
|
||||
│ │
|
||||
│ 用户登录路由: │
|
||||
│ - POST /api/app/users/weixin/login → WeixinLogin() │
|
||||
│ (携带 channel_code 参数) │
|
||||
│ - POST /api/app/users/douyin/login → DouyinLogin() │
|
||||
│ (携带 channel_code 参数) │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌─────────────────────┼─────────────────────┐
|
||||
▼ ▼ ▼
|
||||
┌─────────────────────────┐ ┌───────────────────┐ ┌───────────────────────┐
|
||||
│ 渠道管理 API 层 │ │ 用户登录 API 层 │ │ 定时任务调度器 │
|
||||
│ internal/api/admin/ │ │ internal/api/user/│ │ internal/service/ │
|
||||
│ channels.go │ │ login_app.go │ │ douyin/scheduler.go │
|
||||
│ │ │ login_douyin_app │ │ │
|
||||
│ - CreateChannel() │ │ - WeixinLogin() │ │ - GrantLivestreamPrizes()
|
||||
│ - ModifyChannel() │ │ - DouyinLogin() │ │ - bindAnchorInviter │
|
||||
│ - DeleteChannel() │ │ 接收channel_code│ │ IfNeeded() │
|
||||
│ - ListChannels() │ │ │ │ │
|
||||
│ - ChannelStats() │ │ │ │ 每5分钟自动执行 │
|
||||
└─────────────────────────┘ └───────────────────┘ └───────────────────────┘
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌─────────────────────────┐ ┌───────────────────┐ ┌───────────────────────┐
|
||||
│ 渠道 Service 层 │ │ 用户 Service 层 │ │ 用户 Service 层 │
|
||||
│ internal/service/ │ │ internal/service/ │ │ internal/service/user │
|
||||
│ channel/channel.go │ │ user/ │ │ │
|
||||
│ │ │ login_weixin.go │ │ - BindInviter() │
|
||||
│ - Create() │ │ login_douyin.go │ │ (定时任务调用) │
|
||||
│ - Modify() │ │ │ │ │
|
||||
│ - Delete() │ │ - LoginWeixin() │ │ │
|
||||
│ - List() │ │ - LoginDouyin() │ │ │
|
||||
│ - GetStats() │ │ 查渠道并绑定用户 │ │ │
|
||||
│ - GetByID() │ │ │ │ │
|
||||
└─────────────────────────┘ └───────────────────┘ └───────────────────────┘
|
||||
│ │ │
|
||||
└─────────────────────┼─────────────────────┘
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ 数据访问层 (DAO) │
|
||||
│ internal/repository/mysql/dao/ │
|
||||
│ │
|
||||
│ - channels.gen.go 渠道表操作 │
|
||||
│ - users.gen.go 用户表操作 │
|
||||
│ - user_invites.gen.go 邀请关系表操作 │
|
||||
│ - livestream_activities.gen.go 直播间活动表(含渠道字段) │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ MySQL 数据库 │
|
||||
│ - channels 表 │
|
||||
│ - users 表 (channel_id 字段) │
|
||||
│ - user_invites 表 │
|
||||
│ - livestream_activities 表 (channel_id, channel_code 字段) │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、详细调用链
|
||||
|
||||
### 3.1 渠道管理(管理端)
|
||||
|
||||
#### 创建渠道
|
||||
|
||||
```
|
||||
HTTP POST /api/admin/channels
|
||||
│
|
||||
├── 参数: { name, code, type, remarks }
|
||||
│
|
||||
▼
|
||||
internal/api/admin/channels.go:26 CreateChannel()
|
||||
│
|
||||
├── 验证参数
|
||||
│
|
||||
▼
|
||||
internal/service/channel/channel.go:79 Create()
|
||||
│
|
||||
├── 创建 Channels 模型
|
||||
│
|
||||
▼
|
||||
dao.Channels.Create(m)
|
||||
│
|
||||
▼
|
||||
MySQL INSERT INTO channels
|
||||
```
|
||||
|
||||
#### 查询渠道列表(含用户数统计)
|
||||
|
||||
```
|
||||
HTTP GET /api/admin/channels
|
||||
│
|
||||
▼
|
||||
internal/api/admin/channels.go:135 ListChannels()
|
||||
│
|
||||
▼
|
||||
internal/service/channel/channel.go:111 List()
|
||||
│
|
||||
├── 1. 查询渠道列表
|
||||
│ SELECT * FROM channels WHERE name LIKE ? ORDER BY id DESC
|
||||
│
|
||||
├── 2. 统计每个渠道的用户数
|
||||
│ SELECT channel_id, count(*) as count
|
||||
│ FROM users
|
||||
│ WHERE channel_id IN (?)
|
||||
│ GROUP BY channel_id
|
||||
│
|
||||
▼
|
||||
返回渠道列表(含 user_count 字段)
|
||||
```
|
||||
|
||||
#### 渠道数据分析
|
||||
|
||||
```
|
||||
HTTP GET /api/admin/channels/:channel_id/stats
|
||||
│
|
||||
▼
|
||||
internal/api/admin/channels.go:53 ChannelStats()
|
||||
│
|
||||
▼
|
||||
internal/service/channel/channel.go:169 GetStats()
|
||||
│
|
||||
├── 1. 统计渠道用户总数
|
||||
│ SELECT count(*) FROM users WHERE channel_id = ?
|
||||
│
|
||||
├── 2. 统计渠道订单数和GMV
|
||||
│ SELECT count(*) as count, sum(actual_amount) as gmv
|
||||
│ FROM orders o
|
||||
│ JOIN users u ON u.id = o.user_id
|
||||
│ WHERE u.channel_id = ? AND o.status = 2
|
||||
│
|
||||
├── 3. 月度用户增长统计
|
||||
│ SELECT DATE_FORMAT(created_at, '%Y-%m') as date, count(*) as count
|
||||
│ FROM users
|
||||
│ WHERE channel_id = ? AND created_at >= ?
|
||||
│ GROUP BY date
|
||||
│
|
||||
├── 4. 月度订单统计
|
||||
│ SELECT DATE_FORMAT(created_at, '%Y-%m') as date, count(*), sum(actual_amount)
|
||||
│ FROM orders o
|
||||
│ JOIN users u ON u.id = o.user_id
|
||||
│ WHERE u.channel_id = ? AND o.status = 2 AND o.created_at >= ?
|
||||
│ GROUP BY date
|
||||
│
|
||||
▼
|
||||
返回渠道统计数据
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.2 用户注册绑定渠道
|
||||
|
||||
#### 微信登录(绑定渠道)
|
||||
|
||||
```
|
||||
HTTP POST /api/app/users/weixin/login
|
||||
│
|
||||
├── 参数: { code, invite_code, douyin_id, channel_code }
|
||||
│
|
||||
▼
|
||||
internal/api/user/login_app.go:47 WeixinLogin()
|
||||
│
|
||||
├── 1. 微信 code2session 获取 openid/unionid
|
||||
│
|
||||
▼
|
||||
internal/service/user/login_weixin.go:42 LoginWeixin()
|
||||
│
|
||||
├── 2. 查询渠道(如果传入 channel_code)
|
||||
│ ch, _ := s.readDB.Channels.Where(Channels.Code.Eq(in.ChannelCode)).First()
|
||||
│ channelID = ch.ID
|
||||
│ 【文件位置: login_weixin.go:86-92】
|
||||
│
|
||||
├── 3. 查找或创建用户
|
||||
│ ├── 查找: WHERE openid = ? OR unionid = ?
|
||||
│ │
|
||||
│ └── 创建新用户:
|
||||
│ u = &model.Users{
|
||||
│ Nickname: nickname,
|
||||
│ Openid: in.OpenID,
|
||||
│ ChannelID: channelID, // 绑定渠道
|
||||
│ ...
|
||||
│ }
|
||||
│ 【文件位置: login_weixin.go:113-124】
|
||||
│
|
||||
├── 4. 更新已有用户(如果传入 channel_code)
|
||||
│ if channelID > 0 {
|
||||
│ UPDATE users SET channel_id = ? WHERE id = ?
|
||||
│ }
|
||||
│ 【文件位置: login_weixin.go:141-143】
|
||||
│
|
||||
├── 5. 处理邀请关系(如果传入 invite_code 且是新用户)
|
||||
│ 【详见 3.3 节】
|
||||
│
|
||||
▼
|
||||
返回用户信息和 Token
|
||||
```
|
||||
|
||||
**关键代码片段**(`login_weixin.go:86-92`):
|
||||
|
||||
```go
|
||||
// 查找渠道ID
|
||||
var channelID int64
|
||||
if in.ChannelCode != "" {
|
||||
ch, _ := s.readDB.Channels.WithContext(ctx).Where(s.readDB.Channels.Code.Eq(in.ChannelCode)).First()
|
||||
if ch != nil {
|
||||
channelID = ch.ID
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.3 抖音登录(绑定渠道)
|
||||
|
||||
```
|
||||
HTTP POST /api/app/users/douyin/login
|
||||
│
|
||||
├── 参数: { code, anonymous_code, invite_code, channel_code }
|
||||
│
|
||||
▼
|
||||
internal/api/user/login_douyin_app.go:44 DouyinLogin()
|
||||
│
|
||||
├── 参数校验
|
||||
│
|
||||
▼
|
||||
internal/service/user/login_douyin.go:39 LoginDouyin()
|
||||
│
|
||||
├── 1. 抖音 code2session 获取 openid
|
||||
│
|
||||
├── 2. 查询渠道(如果传入 channel_code)
|
||||
│ ch, _ := s.readDB.Channels.Where(Channels.Code.Eq(in.ChannelCode)).First()
|
||||
│ channelID = ch.ID
|
||||
│ 【文件位置: login_douyin.go:91-97】
|
||||
│
|
||||
├── 3. 查找或创建用户
|
||||
│ ├── 查找: WHERE douyin_id = ? OR unionid = ?
|
||||
│ │
|
||||
│ └── 创建新用户:
|
||||
│ u = &model.Users{
|
||||
│ Nickname: nickname,
|
||||
│ DouyinID: openID,
|
||||
│ ChannelID: channelID, // 绑定渠道
|
||||
│ ...
|
||||
│ }
|
||||
│ 【文件位置: login_douyin.go:119-127】
|
||||
│
|
||||
├── 4. 更新已有用户(如果传入 channel_code 且未绑定)
|
||||
│ if channelID > 0 && u.ChannelID == 0 {
|
||||
│ UPDATE users SET channel_id = ?
|
||||
│ }
|
||||
│ 【文件位置: login_douyin.go:143-144】
|
||||
│
|
||||
└── 5. 处理邀请关系(如果传入 invite_code 且是新用户)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.4 邀请关系绑定
|
||||
|
||||
用户绑定邀请人有两种方式:
|
||||
|
||||
#### 方式一:登录时自动绑定(推荐)
|
||||
|
||||
```
|
||||
用户登录时传入 invite_code 参数
|
||||
│
|
||||
▼
|
||||
LoginWeixin() / LoginDouyin() 内部处理
|
||||
│
|
||||
├── 检查是否新用户
|
||||
│
|
||||
├── 查找邀请人(通过 invite_code)
|
||||
│
|
||||
├── 创建 user_invites 记录
|
||||
│
|
||||
├── 更新 users.inviter_id
|
||||
│
|
||||
▼
|
||||
触发任务中心奖励: task.OnInviteSuccess()
|
||||
```
|
||||
|
||||
#### 方式二:用户主动绑定
|
||||
|
||||
```
|
||||
HTTP POST /api/app/users/inviter/bind
|
||||
│
|
||||
├── 参数: { invite_code }
|
||||
│
|
||||
▼
|
||||
internal/api/user/bind_inviter_app.go:34 BindInviter()
|
||||
│
|
||||
▼
|
||||
internal/service/user/bind_inviter.go:33 BindInviter()
|
||||
│
|
||||
├── 1. 加锁获取当前用户
|
||||
│
|
||||
├── 2. 检查是否已绑定(inviter_id != 0 则拒绝)
|
||||
│
|
||||
├── 3. 查找邀请人
|
||||
│
|
||||
├── 4. 创建 user_invites 记录
|
||||
│
|
||||
├── 5. 更新 users.inviter_id
|
||||
│
|
||||
▼
|
||||
触发任务中心奖励: task.OnInviteSuccess()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.5 定时任务自动绑定渠道(直播间奖品发放)
|
||||
|
||||
**重要场景:直播间用户通过渠道绑定主播邀请人**
|
||||
|
||||
```
|
||||
定时任务 (每5分钟)
|
||||
│
|
||||
▼
|
||||
internal/service/douyin/scheduler.go:24 StartDouyinOrderSync()
|
||||
│
|
||||
├── ticker5min.C 触发
|
||||
│
|
||||
▼
|
||||
internal/service/douyin/scheduler.go:155 GrantLivestreamPrizes()
|
||||
│
|
||||
├── 1. 查找未发放的直播抽奖记录
|
||||
│ SELECT * FROM livestream_draw_logs WHERE is_granted = 0
|
||||
│
|
||||
├── 2. 解析活动关联的渠道/主播邀请码
|
||||
│ resolveActivityAnchorCodes()
|
||||
│ 【文件位置: scheduler.go:418-489】
|
||||
│
|
||||
│ ├── 查询直播间活动的渠道信息
|
||||
│ │ SELECT id, channel_id, channel_code
|
||||
│ │ FROM livestream_activities
|
||||
│ │ WHERE id IN (?)
|
||||
│ │ 【文件位置: scheduler.go:451-458】
|
||||
│ │
|
||||
│ └── 补充缺失的渠道 code
|
||||
│ fetchChannelCodes()
|
||||
│ SELECT id, code FROM channels WHERE id IN (?)
|
||||
│ 【文件位置: scheduler.go:491-513】
|
||||
│
|
||||
├── 3. 自动绑定主播邀请人(如果用户未绑定)
|
||||
│ bindAnchorInviterIfNeeded(ctx, userID, anchorCode)
|
||||
│ 【文件位置: scheduler.go:515-546】
|
||||
│
|
||||
│ ├── 查询用户是否已有邀请人
|
||||
│ │ SELECT inviter_id FROM users WHERE id = ?
|
||||
│ │
|
||||
│ └── 如果 inviter_id == 0,调用绑定服务
|
||||
│ s.userSvc.BindInviter(ctx, userID, BindInviterInput{InviteCode: anchorCode})
|
||||
│ 【文件位置: scheduler.go:534】
|
||||
│
|
||||
└── 4. 发放奖品并更新状态
|
||||
```
|
||||
|
||||
**关键代码:自动绑定主播邀请人**
|
||||
|
||||
```go
|
||||
// scheduler.go:515-546
|
||||
func (s *service) bindAnchorInviterIfNeeded(ctx context.Context, userID int64, anchorCode string) {
|
||||
// 1. 检查用户是否已有邀请人
|
||||
userRecord, err := s.readDB.Users.WithContext(ctx).
|
||||
Select(s.readDB.Users.InviterID).
|
||||
Where(s.readDB.Users.ID.Eq(userID)).
|
||||
First()
|
||||
if userRecord.InviterID != 0 {
|
||||
return // 已绑定,跳过
|
||||
}
|
||||
|
||||
// 2. 自动绑定主播邀请人
|
||||
s.userSvc.BindInviter(ctx, userID, user.BindInviterInput{InviteCode: anchorCode})
|
||||
}
|
||||
```
|
||||
|
||||
**数据流:**
|
||||
|
||||
```
|
||||
┌───────────────────────────────────────────────────────────────────────┐
|
||||
│ 直播间活动配置 │
|
||||
│ livestream_activities │
|
||||
│ ├── channel_id (关联渠道ID) │
|
||||
│ └── channel_code (主播邀请码) │
|
||||
└───────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌───────────────────────────────────────────────────────────────────────┐
|
||||
│ 直播抽奖记录 │
|
||||
│ livestream_draw_logs │
|
||||
│ ├── activity_id (关联活动) │
|
||||
│ ├── local_user_id (本地用户ID) │
|
||||
│ └── is_granted (发放状态) │
|
||||
└───────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌───────────────────────────────────────────────────────────────────────┐
|
||||
│ 定时任务处理 │
|
||||
│ GrantLivestreamPrizes() │
|
||||
│ │
|
||||
│ 1. 查 activity → 获取 channel_code │
|
||||
│ 2. 查 channels → 补充缺失的 code │
|
||||
│ 3. 查 users.inviter_id → 检查是否已绑定 │
|
||||
│ 4. 未绑定 → 调用 BindInviter() 绑定主播 │
|
||||
└───────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌───────────────────────────────────────────────────────────────────────┐
|
||||
│ 用户邀请关系 │
|
||||
│ users.inviter_id = 主播用户ID │
|
||||
│ user_invites 表新增记录 │
|
||||
└───────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、核心文件索引
|
||||
|
||||
| 文件路径 | 说明 | 关键函数 |
|
||||
|----------|------|----------|
|
||||
| `internal/api/admin/channels.go` | 渠道管理 API | CreateChannel, ListChannels, ChannelStats |
|
||||
| `internal/service/channel/channel.go` | 渠道业务逻辑 | Create, List, GetStats |
|
||||
| `internal/api/user/login_app.go` | 用户登录 API (微信) | WeixinLogin |
|
||||
| `internal/service/user/login_weixin.go` | 微信登录逻辑 | LoginWeixin(渠道绑定核心) |
|
||||
| `internal/api/user/login_douyin_app.go` | 用户登录 API (抖音) | DouyinLogin |
|
||||
| `internal/service/user/login_douyin.go` | 抖音登录逻辑 | LoginDouyin(渠道绑定核心) |
|
||||
| `internal/api/user/bind_inviter_app.go` | 绑定邀请人 API | BindInviter |
|
||||
| `internal/service/user/bind_inviter.go` | 绑定邀请人逻辑 | BindInviter |
|
||||
| **`internal/service/douyin/scheduler.go`** | **抖音定时任务** | **GrantLivestreamPrizes, bindAnchorInviterIfNeeded** |
|
||||
| `internal/repository/mysql/model/channels.gen.go` | 渠道模型 | Channels struct |
|
||||
| `internal/repository/mysql/model/users.gen.go` | 用户模型 | Users struct(含 channel_id) |
|
||||
| `internal/repository/mysql/model/user_invites.gen.go` | 邀请关系模型 | UserInvites struct |
|
||||
| `internal/repository/mysql/model/livestream_activities.gen.go` | 直播间活动模型 | LivestreamActivities(含 channel_id, channel_code) |
|
||||
| `internal/router/router.go` | 路由配置 | 渠道路由: 215-219 行 |
|
||||
|
||||
---
|
||||
|
||||
## 五、数据流图
|
||||
|
||||
### 5.1 用户注册绑定渠道流程
|
||||
|
||||
```
|
||||
┌──────────┐ ┌──────────────┐ ┌────────────────┐
|
||||
│ 前端 │────▶│ 微信登录 API │────▶│ 用户 Service │
|
||||
│ │ │ │ │ │
|
||||
│ channel_ │ │ code │ │ 1. code2session│
|
||||
│ code │ │ invite_code │ │ 2. 查渠道 │
|
||||
└──────────┘ └──────────────┘ │ 3. 创建/更新用户│
|
||||
│ 4. 绑定邀请人 │
|
||||
└───────┬────────┘
|
||||
│
|
||||
┌─────────────────────────┼─────────────────────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
||||
│ channels 表 │ │ users 表 │ │user_invites表│
|
||||
│ │ │ │ │ │
|
||||
│ code → ID │ │ channel_id │ │ inviter_id │
|
||||
│ │ │ inviter_id │ │ invitee_id │
|
||||
└──────────────┘ └──────────────┘ └──────────────┘
|
||||
```
|
||||
|
||||
### 5.2 渠道统计查询流程
|
||||
|
||||
```
|
||||
┌──────────┐ ┌──────────────┐ ┌────────────────┐
|
||||
│ 管理后台 │────▶│ 渠道统计 API │────▶│ 渠道 Service │
|
||||
│ │ │ │ │ │
|
||||
│ 选择渠道 │ │ channel_id │ │ 1. 统计用户数 │
|
||||
│ 时间范围 │ │ days │ │ 2. 统计订单数 │
|
||||
└──────────┘ │ start_date │ │ 3. 统计GMV │
|
||||
│ end_date │ │ 4. 月度趋势 │
|
||||
└──────────────┘ └───────┬────────┘
|
||||
│
|
||||
┌─────────────────────────┴────────────────┐
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────────┐ ┌──────────────┐
|
||||
│ users 表 │ │ orders 表 │
|
||||
│ │ │ │
|
||||
│ WHERE │ │ JOIN users │
|
||||
│ channel_id=? │ │ WHERE │
|
||||
│ │ │ channel_id=? │
|
||||
└──────────────┘ └──────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、业务规则
|
||||
|
||||
### 6.1 渠道绑定规则
|
||||
|
||||
| 场景 | 触发条件 | 说明 |
|
||||
|------|----------|------|
|
||||
| 微信登录绑定 | 传入 `channel_code` | 查询 channels 表,绑定 channel_id 到用户 |
|
||||
| 抖音登录绑定 | 传入 `channel_code` | 查询 channels 表,仅当用户未绑定时才更新 |
|
||||
| 定时任务绑定 | 直播间活动配置了 `channel_code` | 自动绑定主播邀请人(邀请关系,非渠道) |
|
||||
|
||||
### 6.2 渠道与邀请人的区别
|
||||
|
||||
| 概念 | 字段 | 说明 |
|
||||
|------|------|------|
|
||||
| **渠道 (Channel)** | `users.channel_id` | 用户来源渠道,用于统计分析 |
|
||||
| **邀请人 (Inviter)** | `users.inviter_id` | 邀请该用户注册的人,用于奖励计算 |
|
||||
|
||||
**定时任务场景:**
|
||||
- 直播间活动的 `channel_code` 用作**主播邀请码**
|
||||
- 定时任务调用 `BindInviter()` 绑定的是**邀请关系**,而非渠道
|
||||
- 主播邀请码 = 某个用户的 `invite_code`(通常是主播账号)
|
||||
|
||||
### 6.3 邀请绑定规则
|
||||
|
||||
1. **仅限一次**: 用户绑定邀请人后不可更改
|
||||
2. **不能自邀**: 用户不能邀请自己
|
||||
3. **奖励触发**: 绑定成功后触发任务中心奖励逻辑
|
||||
4. **定时任务补绑**: 直播间用户未绑定邀请人时,自动绑定主播
|
||||
|
||||
### 6.4 权限控制
|
||||
|
||||
渠道管理接口需要以下权限:
|
||||
|
||||
| 操作 | 权限标识 |
|
||||
|------|----------|
|
||||
| 创建渠道 | `channel:create` |
|
||||
| 修改渠道 | `channel:modify` |
|
||||
| 删除渠道 | `channel:delete` |
|
||||
| 查看渠道 | `channel:view` |
|
||||
|
||||
---
|
||||
|
||||
## 七、相关迁移文件
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `migrations/20260223_add_channel_fields_to_livestream_activities.sql` | 直播间活动添加渠道字段 |
|
||||
|
||||
---
|
||||
|
||||
## 八、注意事项
|
||||
|
||||
1. **渠道 Code 唯一性**: 渠道的 `code` 字段必须唯一,用于用户登录时匹配
|
||||
2. **统计性能**: 渠道统计涉及多表 JOIN,大数据量时需注意性能优化
|
||||
3. **事务处理**: 用户创建和渠道绑定在同一事务中,保证数据一致性
|
||||
4. **软删除**: 渠道删除为软删除,不影响已绑定用户
|
||||
|
||||
---
|
||||
|
||||
*文档生成时间: 2026-02-27*
|
||||
*项目: bindbox_game*
|
||||
142
.claude/plan/task-center-claim-bug.md
Normal file
142
.claude/plan/task-center-claim-bug.md
Normal file
@ -0,0 +1,142 @@
|
||||
# 任务中心领取 Bug 修复计划
|
||||
|
||||
## 问题描述
|
||||
小程序(bindbox-mini)任务中心,活动前端领取不了但是可以看到。
|
||||
|
||||
## 根因分析
|
||||
|
||||
### 核心问题:前后端进度数据源不一致
|
||||
|
||||
**后端 `ClaimTier`**(service.go:738)使用 `TierProgressMap`(基于窗口化、受任务时间约束的进度)来校验是否达标:
|
||||
```go
|
||||
if tp, ok := progress.TierProgressMap[tierID]; ok {
|
||||
currentOrderCount = tp.OrderCount // 窗口化进度,受任务 StartTime 约束
|
||||
}
|
||||
```
|
||||
|
||||
**前端 `isTierClaimable`**(index.vue:387-437)使用的是 `subProgress`(活动级别汇总)或全局进度(`orderCount` / `orderAmount`),**完全没有使用 `tier_progress_map`**。
|
||||
|
||||
### 触发条件
|
||||
|
||||
最近提交 `e0db875` 修改了 `computeTimeWindow`:
|
||||
- **修改前**:`lifetime` / `since_registration` / 空窗口 → `return nil, nil`(不限时间)
|
||||
- **修改后**:`lifetime` / 默认窗口 → `return taskStart, taskEnd`(受任务时间约束)
|
||||
|
||||
这导致 `TierProgressMap` 中的进度值被任务时间限制,但 API 返回的 `order_count` / `sub_progress` 全局进度仍然不受时间限制(service.go:618-622 用 `nil, nil` 查询)。
|
||||
|
||||
### 不一致的结果
|
||||
|
||||
| 数据源 | 时间约束 | 进度值 | 使用方 |
|
||||
|--------|---------|--------|--------|
|
||||
| `TierProgressMap` | 受任务 StartTime/EndTime 约束 | 较小 | 后端 ClaimTier |
|
||||
| `SubProgress` / 全局进度 | 无时间约束 | 较大 | 前端 isTierClaimable |
|
||||
| API 返回的 `order_count` | 无时间约束(或活动级别) | 较大 | 前端 isTierClaimable |
|
||||
|
||||
**场景举例**:
|
||||
- 用户在任务创建前有 5 笔历史订单,任务创建后有 2 笔新订单
|
||||
- 任务档位要求 `order_count >= 3`
|
||||
- 前端看到全局 `orderCount = 7`(不限时间) → 显示"领取"按钮
|
||||
- 后端 `TierProgressMap.OrderCount = 2`(只统计任务开始后) → 返回"任务条件未达成"
|
||||
|
||||
**或者反过来**:
|
||||
- 前端也使用 `subProgress` 做判断,但 `subProgress` 的统计可能不包含某些场景的数据
|
||||
- 导致前端 `isTierClaimable` 返回 `false`,按钮不出现
|
||||
- 用户看到任务但无法领取
|
||||
|
||||
## 任务类型
|
||||
- [x] 后端 (→ 后端逻辑修复)
|
||||
- [x] 前端 (→ 前端判断修复)
|
||||
|
||||
## 技术方案
|
||||
|
||||
**方案 A(推荐):后端 API 返回 `tier_progress_map`,前端使用**
|
||||
|
||||
让前后端使用同一份进度数据源(`TierProgressMap`),确保判断一致。
|
||||
|
||||
### 实施步骤
|
||||
|
||||
#### Step 1:后端 - API Response 增加 `tier_progress_map` 字段
|
||||
|
||||
**文件**: `internal/api/task_center/tasks_app.go`
|
||||
|
||||
1. 在 `taskProgressResponse` 结构体中添加 `TierProgressMap` 字段:
|
||||
```go
|
||||
type tierProgressItem struct {
|
||||
TierID int64 `json:"tier_id"`
|
||||
OrderCount int64 `json:"order_count"`
|
||||
OrderAmount int64 `json:"order_amount"`
|
||||
InviteCount int64 `json:"invite_count"`
|
||||
FirstOrder bool `json:"first_order"`
|
||||
}
|
||||
|
||||
type taskProgressResponse struct {
|
||||
// ... existing fields ...
|
||||
TierProgress []tierProgressItem `json:"tier_progress"` // 新增
|
||||
}
|
||||
```
|
||||
|
||||
2. 在 `GetTaskProgressForApp` handler 中填充该字段。
|
||||
|
||||
#### Step 2:前端 - `isTierClaimable` 优先使用 `tier_progress`
|
||||
|
||||
**文件**: `bindbox-mini/pages-user/tasks/index.vue`
|
||||
|
||||
1. 在 `fetchData` 中解析并存储 `tier_progress` 到 `taskProgress[taskId]`
|
||||
2. 修改 `isTierClaimable` 函数,优先从 `tierProgress` 中查找对应 tier 的进度
|
||||
3. 修改 `getTierProgressText` 和 `getTierProgressPercent`,同步使用新数据源
|
||||
|
||||
```js
|
||||
function isTierClaimable(task, tier) {
|
||||
const progress = taskProgress[task.id] || {}
|
||||
|
||||
// 优先使用 tier 级别窗口化进度(与后端 ClaimTier 保持一致)
|
||||
if (progress.tierProgress) {
|
||||
const tp = progress.tierProgress.find(t => t.tier_id === tier.id)
|
||||
if (tp) {
|
||||
const metric = tier.metric || ''
|
||||
const threshold = tier.threshold || 0
|
||||
const operator = tier.operator || '>='
|
||||
|
||||
let current = 0
|
||||
if (metric === 'first_order') return tp.first_order || false
|
||||
else if (metric === 'order_count') current = tp.order_count || 0
|
||||
else if (metric === 'order_amount') current = tp.order_amount || 0
|
||||
else if (metric === 'invite_count') current = tp.invite_count || 0
|
||||
|
||||
if (operator === '>=') return current >= threshold
|
||||
if (operator === '==') return current === threshold
|
||||
if (operator === '>') return current > threshold
|
||||
return current >= threshold
|
||||
}
|
||||
}
|
||||
|
||||
// fallback: 原有逻辑
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
#### Step 3:同步修改进度显示
|
||||
|
||||
修改 `getTierProgressText` 和 `getTierProgressPercent` 也优先使用 `tierProgress` 数据,确保用户看到的进度和可领取状态一致。
|
||||
|
||||
### 关键文件
|
||||
|
||||
| 文件 | 操作 | 说明 |
|
||||
|------|------|------|
|
||||
| `internal/api/task_center/tasks_app.go:106-170` | 修改 | 添加 tier_progress 到响应体 |
|
||||
| `bindbox-mini/pages-user/tasks/index.vue:387-437` | 修改 | isTierClaimable 使用 tier_progress |
|
||||
| `bindbox-mini/pages-user/tasks/index.vue:440-478` | 修改 | getTierProgressText 使用 tier_progress |
|
||||
| `bindbox-mini/pages-user/tasks/index.vue:590-630` | 修改 | getTierProgressPercent 使用 tier_progress |
|
||||
| `bindbox-mini/pages-user/tasks/index.vue:551-576` | 修改 | fetchData 解析 tier_progress |
|
||||
|
||||
### 风险与缓解
|
||||
|
||||
| 风险 | 缓解措施 |
|
||||
|------|----------|
|
||||
| 前端旧版本未使用 `tier_progress` 字段 | 保持原有 `order_count`、`sub_progress` 字段不变,`tier_progress` 为新增字段,向后兼容 |
|
||||
| `tier_progress_map` 为空(数据库无 tiers 配置) | 前端 fallback 到原有 `subProgress` / 全局进度逻辑 |
|
||||
| 已部署但未刷新前端的用户 | `tier_progress` 是附加字段,不影响旧逻辑 |
|
||||
|
||||
### SESSION_ID(供 /ccg:execute 使用)
|
||||
- CODEX_SESSION: N/A(未使用外部模型)
|
||||
- GEMINI_SESSION: N/A(未使用外部模型)
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -27,7 +27,6 @@ go.work.sum
|
||||
resources/*
|
||||
build/resources/admin/
|
||||
logs/
|
||||
web/*
|
||||
|
||||
# 敏感配置文件
|
||||
configs/*.toml
|
||||
|
||||
@ -0,0 +1 @@
|
||||
./inflections.go
|
||||
@ -0,0 +1 @@
|
||||
v1 0027d7b9e48cc563856ebbe018ca392c0380191db7057bd5c6ad5590058e19af f6460e601786f316ed501e85d3f6d89d20e99f1cfe32edc0fd4c1f38fdceceef 479 1772341459770076000
|
||||
@ -0,0 +1 @@
|
||||
v1 004248d4af5c24fab846e4f0d35d33273304315843bd4bb956274801c010da24 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 1772341463855836000
|
||||
@ -0,0 +1,14 @@
|
||||
./assertI2I.go
|
||||
./base64_compat.go
|
||||
./fastconv.go
|
||||
./fastmem.go
|
||||
./fastvalue.go
|
||||
./gcwb.go
|
||||
./growslice.go
|
||||
./int48.go
|
||||
./map_siwss_go124.go
|
||||
./pool.go
|
||||
./stubs.go
|
||||
./table.go
|
||||
./types.go
|
||||
./asm_compat.s
|
||||
@ -0,0 +1 @@
|
||||
v1 004b0af6c36d9d8eed8c2a29a6afed52c277aa05814050dadb080d5eb41f579b dc7445418ed2fd747b88073bcada36fe086a7aa634c2740b36bfcbbacae1385d 394824 1772341466498068000
|
||||
Binary file not shown.
@ -0,0 +1 @@
|
||||
v1 005ae50987ddaed418137eefeeeab4c9746917594fd3496111f2d1a0f0840c59 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 1772341462985082000
|
||||
@ -0,0 +1 @@
|
||||
v1 005e1f097c25fbb93dc6bffcc2336e31c331cb4add48da218bd708d97b5be992 09730b6a56d68e766986704d7aedb5e784b7523e18ebe04f8e8c850456e9c903 98 1772341460330220000
|
||||
@ -0,0 +1 @@
|
||||
v1 0060b867c320ba7a126368965bc597d818357f24552232f6374e7ffbbb4407e0 ddac1224448d22823a11fbf9f38bfa78d458ec7ec898b1ca8eab41746f442487 266 1772341463202328000
|
||||
Binary file not shown.
@ -0,0 +1 @@
|
||||
v1 0078588be74d4a9dc77e5add59427c71e6714a1a1398323ead99bd74a45b0138 1b5f10e5da3d839eda22be2bf43bdaf80a5a731bca3d99f3a1e5e0488bd7f1c3 646252 1772341462868690000
|
||||
@ -0,0 +1 @@
|
||||
v1 00798196f22bc20f9a85fabf7ced346cb9b41c1f3136e0e787b6830c21e6c874 879e941c6cff8537553a4bd74a1249ca03d45c9b709e3096b54edb62ddd5a584 293 1772341464123573000
|
||||
@ -0,0 +1 @@
|
||||
v1 0080a2d3c0dcbc6ab481304bd643e8dc47b59b93f95f71045591987cd48631dc e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 1772341464615696000
|
||||
@ -0,0 +1 @@
|
||||
v1 008347c54f7aad0f3d604bcf61e24928140c90fc3ae9fe64a606c8db4bffec00 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 1772341464681487000
|
||||
@ -0,0 +1 @@
|
||||
v1 008683d681b3adf0ad5b3cc15a3c02c64451d040a01702537482312d36745c9b 67b875e647c12268b021f5bdbbcbaeaaa961fbccad48dd5bf479eb164f5eedfa 1921554 1772341462767691000
|
||||
@ -0,0 +1 @@
|
||||
v1 00bb624d0a7863689f2d2cc0b82b036fbfcb11a6e1c60d29f7e4cefd7042bd4b e65d2f3cd83f084b7a3ef4f33d45d8c56f513e2f3b9fbf5719b3b14c30ae6cbd 715 1772341459969002000
|
||||
@ -0,0 +1 @@
|
||||
v1 00c45c3fa85a39f067f0624fdb8dc9609afd078f7f0b6b6e14343b503cafca50 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 1772341462018876000
|
||||
@ -0,0 +1 @@
|
||||
v1 00d2eb94e953b271596fd0528682f0ece656ac8d73b0445e62ac9914ba31563b cfc12b3c4d18b5887b2c07c787b4dbf5483bc7562617f716e857f596253e5f3a 276778 1772341464207407000
|
||||
@ -0,0 +1 @@
|
||||
./godebug.go
|
||||
Binary file not shown.
@ -0,0 +1,6 @@
|
||||
./ast.go
|
||||
./builder.go
|
||||
./doc.go
|
||||
./kind.go
|
||||
./parser.go
|
||||
./scanner.go
|
||||
Binary file not shown.
@ -0,0 +1 @@
|
||||
v1 011ebf6bc843f99ab28d15146048dfeee2c62fd1067e75a378bb9d1e62662dfa f9cc50b4663366c39cac8d6ed0b9fb1474a55b99912b0ed32a15697684b7805e 1200866 1772341460375940000
|
||||
@ -0,0 +1 @@
|
||||
v1 011f87c0ad369852441f4ddda1e1a2b415388f6abd647296bdcba6b5405ee367 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 1772341463600224000
|
||||
@ -0,0 +1 @@
|
||||
v1 0125f1c08af9d4c3cc94f391170106327590885a29ea1a84f038f17001dbaac2 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 1772341464094428000
|
||||
@ -0,0 +1 @@
|
||||
v1 01615a2ad2cbba91aa9ffe6986e743f305273c4989f856992c98b0c89d1ea1d8 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 1772341464358733000
|
||||
@ -0,0 +1 @@
|
||||
v1 016ea07c95ecc6b071a34aafae05bb2b38313eb5802f329eb623211d7ef195e5 34a2943b8794a30f40e7dd9296858be67407669b4f83cab2897fac32b9cb9bfc 1868056 1772341464879061000
|
||||
@ -0,0 +1 @@
|
||||
./ignorepc.go
|
||||
@ -0,0 +1 @@
|
||||
v1 0194c6901fd8dec2ff9b3ef65cefc33e4fca04a0089c762705fa5f92bb95a850 b5699f899979d2d681bcec8b297aa093255ab952f4729f169ec17e904ebadb56 288080 1772341460310223000
|
||||
@ -0,0 +1 @@
|
||||
v1 01a754f07601ceca17362c314b58dd9d0e16644f2ebccc2e5309443d076b5b1c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 1772341462939766000
|
||||
@ -0,0 +1 @@
|
||||
v1 01d1f64adfaa4373ca9c6dee7f74fa361e9a79c913540b3e7c679e52e98578f9 b1d1850a47832d0d06a1adde71436cad248080d16292f5a2776575c09f29f384 697 1772341459850231000
|
||||
Binary file not shown.
@ -0,0 +1 @@
|
||||
v1 01fd13c8028899be8f5581c7e09513b7246f08c3d8ebf334a508768099e6c7dc e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 1772341462198596000
|
||||
Binary file not shown.
@ -0,0 +1 @@
|
||||
v1 024c0d5b779fbfa407d58b0596f7534977efb91f52c1a48d5b1d862bea568acb 1214475e07c7435a359c50c4c9f5aa43e1e75f70eea995a1b4f1669e3d0a0af2 162372 1772341462019400000
|
||||
@ -0,0 +1 @@
|
||||
v1 025195ca10e32a45a9142a7c922b0a194a65dfa0bb6a8514c56a1c8bfd88d21a c498fe8624736d651ee1e65f55e866ad53b0a55a641079dd1d1029bd0fcb08b6 77206 1772341463065444000
|
||||
Binary file not shown.
@ -0,0 +1,10 @@
|
||||
./format.go
|
||||
./format_rfc3339.go
|
||||
./sleep.go
|
||||
./sys_unix.go
|
||||
./tick.go
|
||||
./time.go
|
||||
./zoneinfo.go
|
||||
./zoneinfo_goroot.go
|
||||
./zoneinfo_read.go
|
||||
./zoneinfo_unix.go
|
||||
@ -0,0 +1 @@
|
||||
v1 0276b3e6ca37051cb11b5854546c02b27bb187c4a63ecaf2e115effc3237d670 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 1772341462858338000
|
||||
@ -0,0 +1 @@
|
||||
v1 02807f1cf2431b48f57bd53071c04d1bb9bd26278a12672e15855de0aa3a1bc8 3a5cdc824bca6444dc87e8e57d4792ddcdfb38c62ea6008263424384d4f4fe48 73 1772341462621509000
|
||||
@ -0,0 +1,2 @@
|
||||
./cors.go
|
||||
./utils.go
|
||||
@ -0,0 +1 @@
|
||||
v1 02a69eeee669f96f852761f3a545dc13819721097192ebe1f7ee53411b83b571 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 1772341462067569000
|
||||
@ -0,0 +1 @@
|
||||
v1 02a99851c63f34f71da6f1e2751b6662ade7ee472a6ef4cfa3b0f6c580908b4a ae5c5d1d4af6c8a4be3159a450324ac93e5e4393b0145ca7176c795dea50c2d3 684364 1772341463993922000
|
||||
Binary file not shown.
@ -0,0 +1 @@
|
||||
v1 02d705fe277d278e149b941bbe5242af8c38be0b93ac6a4398ad9851dcb56455 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 1772341462118153000
|
||||
@ -0,0 +1 @@
|
||||
v1 02d71ca3fd2c793ee989faf0b7dbe2ca85e4560ed0abaf2964791f5b02c82eec ea8cb45d584d9384b541ad180ed362aa88f4a2634e904750dfc5db513a43a117 348 1772341459858832000
|
||||
Binary file not shown.
@ -0,0 +1,42 @@
|
||||
./adapter.go
|
||||
./any.go
|
||||
./any_array.go
|
||||
./any_bool.go
|
||||
./any_float.go
|
||||
./any_int32.go
|
||||
./any_int64.go
|
||||
./any_invalid.go
|
||||
./any_nil.go
|
||||
./any_number.go
|
||||
./any_object.go
|
||||
./any_str.go
|
||||
./any_uint32.go
|
||||
./any_uint64.go
|
||||
./config.go
|
||||
./iter.go
|
||||
./iter_array.go
|
||||
./iter_float.go
|
||||
./iter_int.go
|
||||
./iter_object.go
|
||||
./iter_skip.go
|
||||
./iter_skip_strict.go
|
||||
./iter_str.go
|
||||
./jsoniter.go
|
||||
./pool.go
|
||||
./reflect.go
|
||||
./reflect_array.go
|
||||
./reflect_dynamic.go
|
||||
./reflect_extension.go
|
||||
./reflect_json_number.go
|
||||
./reflect_json_raw_message.go
|
||||
./reflect_map.go
|
||||
./reflect_marshaler.go
|
||||
./reflect_native.go
|
||||
./reflect_optional.go
|
||||
./reflect_slice.go
|
||||
./reflect_struct_decoder.go
|
||||
./reflect_struct_encoder.go
|
||||
./stream.go
|
||||
./stream_float.go
|
||||
./stream_int.go
|
||||
./stream_str.go
|
||||
@ -0,0 +1 @@
|
||||
v1 02fc2b1b830b7ade9d77044adcad7a9a4a57cf1b0cf61eddb3741225043eb398 7716152837087e14b9bcd0e8f8d94bc6b573b7357ca1c497420f384461410349 133144 1772341463086740000
|
||||
@ -0,0 +1 @@
|
||||
v1 030b4ba59b5b43bfce4b42d558f0b803a73a28a6e8a9e9dc0a9d47a64e3a86a0 c3123b2c3800ce5313dac4be625b9f86c476e2a8c2f118527338aec1bb54ae5f 3694 1772341459982335000
|
||||
Binary file not shown.
Binary file not shown.
@ -0,0 +1 @@
|
||||
v1 034e1dc78deebe692796527c71d18c45651cc6ca896e9d0a73571c4009ef290c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 1772341462145386000
|
||||
@ -0,0 +1 @@
|
||||
v1 035e9e05508c281231207c45087246c370a21b1413bde994f8807242452ec035 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 1772341463007122000
|
||||
@ -0,0 +1 @@
|
||||
v1 03618d69e403c6a792cd0358b14a7eae5fe10476465c4d1925e2c35e36f79636 6d16aed1afc018b12829a094871b7e2bd30d654ca5cdf244da83dd983acc51fd 9 1772341465718120000
|
||||
@ -0,0 +1 @@
|
||||
v1 03673185c07dbd6aa5c58edea0ebd836d10bfcbcc880ca9d3b08f4ae2d62dd7d e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 1772341460253738000
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1 @@
|
||||
v1 039b8f45a6e9c3167c7c2d224cfc333464a10acf4fd3c69aec98187269b43591 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 1772341463010982000
|
||||
@ -0,0 +1 @@
|
||||
v1 03a0f2517ee71e2152a17a8e20df068b6799a30acbca4ab7563f0db9d88a60fe 1d647cfb2a3507b6201a30bb975bdf620015e9fdbae85128a598765722483bba 618738 1772341463077954000
|
||||
@ -0,0 +1 @@
|
||||
v1 03ad22550bf62742af275db9cf8ecaca8d22dd3de471fcecb4aeacd6802d30b5 28705ba6a6c4d9fa91033f0d45b624a414786fc37b2046774eee672e5ddfef75 1110 1772341459772488000
|
||||
Binary file not shown.
@ -0,0 +1 @@
|
||||
v1 03cd960e6780f47a1bbe2de71bfcfd9f77bcfd387a1db10f634287a7d767d4b2 3b78e01ec9aeb35516dad96f495cfddd5ceed02f9483ca0c018570cbeaf1ed56 2536 1772341460056872000
|
||||
@ -0,0 +1 @@
|
||||
v1 03d54565ae2ef9fdbb6b310f1c1b435c713b7b605b91203c6c22ab3b49908cd2 6060af58c3467f320159a76b5e59f64738ce7264d6483b229d14f6b9a3dc1aaf 259738 1772341463973434000
|
||||
Binary file not shown.
@ -0,0 +1 @@
|
||||
v1 03dab1b1cc7ad33783573189a6a902adfc7e092be9e3e33588aa69b274b7965b 47a532157373b8eb3154f788683cb2875e9561016af49dccd66c6c6c96a98d10 3181 1772341459968904000
|
||||
@ -0,0 +1 @@
|
||||
v1 04082d8c4ed8aa82b26d6fbc789426eed6ec934ae9a728bc8ec087cac278b2bc 2a6491b22e434d3ec3c7a0d23c093a0f100f092af19cd8592ee392d185344d1c 784 1772341462997469000
|
||||
@ -0,0 +1 @@
|
||||
v1 042075ee4b23c849788b99bf5418546d81747010488758b8e93ab4099bbf093c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 1772341460247772000
|
||||
Binary file not shown.
@ -0,0 +1 @@
|
||||
v1 042d0543a4162599bb79b6daeb070c61d24406ae9ecd8abdd6c47af7b297d4e2 341ef2f104587e3ca031596f710653b08ee23b870a761adfd896b3aed56175fe 163870 1772341463486813000
|
||||
Binary file not shown.
@ -0,0 +1 @@
|
||||
v1 044a459d3867e21a5cdd80185e95689596d4154a4ed6901f6a2ec0f959d46264 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 1772341460301399000
|
||||
@ -0,0 +1 @@
|
||||
v1 0462f97d5bc45b14a1bf6263658ef502c80a7d5aba156d95e783060e89584913 5c009450a074dea7546b45e3dff2f1c67c36fa5ba0ee94ddd010e18270d23423 209178 1772341461479908000
|
||||
@ -0,0 +1 @@
|
||||
v1 046397a7f62b5b725c637268d57ab265ee043ed99b0d1c62aa4d2b67ad73248b ee3fb04d9eabdfa66ab70e6f68d2a6ebff1c0dd7c4c7cdcf219199e453b18580 313 1772341464785664000
|
||||
@ -0,0 +1 @@
|
||||
v1 04686bf4eaa5bbb303a97ed0b49b30fbe3c8e71e16e5d9cd5120d777ce4a005c 5c53921b8a3050cc63c1af65b9902dab3b892c04675999708493a13cffe83861 12 1772341461335009000
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1 @@
|
||||
v1 0478ea5fa20518367a10ace01bedd747306e5cc79608d182be89c287267df714 aeeb6267b3b9d1cdbab34441c764783c6fd7aacfb3910c8d76fd808e5a909240 1780 1772341459949180000
|
||||
@ -0,0 +1 @@
|
||||
v1 04a6bb0675a90957df92862917fa8aca1cb39aa6f8b113c7b4b78b80ca740c86 d8797fb25aba7f81441243d068447e9986730e1031c3a3dc717933178ae3160b 1271 1772341459730826000
|
||||
@ -0,0 +1,7 @@
|
||||
./deflate.go
|
||||
./deflatefast.go
|
||||
./dict_decoder.go
|
||||
./huffman_bit_writer.go
|
||||
./huffman_code.go
|
||||
./inflate.go
|
||||
./token.go
|
||||
@ -0,0 +1 @@
|
||||
./client.go
|
||||
@ -0,0 +1 @@
|
||||
./print.go
|
||||
@ -0,0 +1 @@
|
||||
v1 04cb9ecb7c3ca5c91dcbdba1f04fa3dc0276ec0dad8b1a59b6004c3985856abc 724247c1b27e0a6793d09b0ee129c7ad57394b8f76eade795e640826f5022c42 177 1772341464520401000
|
||||
@ -0,0 +1 @@
|
||||
v1 04dc9076a2cf0e3810eff56f0ae0305aadb52a0c916e0ab787c2494afd745006 97c3a025e69ed8397da209537a7f1304d0ad5780906f35b8f090a280c9e1d2ba 20 1772341463899650000
|
||||
@ -0,0 +1 @@
|
||||
v1 04e64e9ce71f18fc189faa265da25c7e7fc95b2ff8a504b5c4a3736e72e46967 438f8c8f272341e5d276cb57659f6bc4fd26b8cd22584fefd856aa15d56a6416 11 1772341462001585000
|
||||
@ -0,0 +1 @@
|
||||
v1 04eb248ebd3d4f1ac66b6f5bf0a68e4fd26c013e64a3918663a4cf6b47ebe7cb fbddce55cf44c4648333c5c7f10bb32cd327faa38e2277e94046d1689047c513 15980 1772341459884399000
|
||||
@ -0,0 +1 @@
|
||||
v1 04f46b8be0801a268a33507c5ac5f473a4a47a025465b9f6858f907771d48f66 7abd9cca9490c55a43566fefced85476fe8408d82a1a8f66dbd8070eff08a460 11 1772341461779209000
|
||||
@ -0,0 +1 @@
|
||||
v1 04f7bf62cc23339a9177baadb0ceeb52d6b427ae0672d315f138decaa5aca109 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 1772341460382955000
|
||||
@ -0,0 +1 @@
|
||||
v1 05082962caa84921f53d9890147de2378b66ad8ab60da33b13c14701707e97d5 175546acd7a6627235043ab183765c58f0e9a92116a707e8f59fe67b0c2869d3 93 1772341465269576000
|
||||
@ -0,0 +1 @@
|
||||
v1 0522ac625674fbf716e334eee59cd0c7453f2728e04586fadcc790cb6adcbeaf b5cf5b64adf71736f5bf0f2aa47875409a8d3a8b32ae153ae58cd67d30ebfb72 50526 1772341461441123000
|
||||
@ -0,0 +1 @@
|
||||
v1 05321930316695a9ae203f0ed3acae7bafbb0abcde0b5c715cb54643aa1106fd 12d04cc806a555c6c49b74bd41bda7b03e6ec9d5ca08a829985fd8d1f0812d9a 30 1772341465329856000
|
||||
Binary file not shown.
@ -0,0 +1,9 @@
|
||||
./callbacks.go
|
||||
./callbacks_traceback.go
|
||||
./handle.go
|
||||
./iscgo.go
|
||||
./setenv.go
|
||||
_cgo_gotypes.go
|
||||
cgo.cgo1.go
|
||||
_cgo_import.go
|
||||
./asm_arm64.s
|
||||
@ -0,0 +1 @@
|
||||
v1 05455f7cda26dace86bf8a1748e9a34b76a03fa9f5a8a743c8110b683281a8a3 596e7bf6e6f265ea570f1b4230842a56ada9debe0b8bcbaba7d050e8db44e32d 124 1772341462507616000
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user