diff --git a/.gitignore b/.gitignore
index 5ac55ea..36ebdcb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,7 +6,6 @@
**/.DS_Store
#deploy data
-
data/*
!data/.gitkeep
@@ -19,6 +18,14 @@ data/*
/tmp/
+# 打包出来的可执行文件
/app/api
+/app/main/api/main
+/app/main/api/debug
+/app/main/api/test
+
+# 文档目录
+documents/*
+!documents/.gitkeep
deploy/script/js
diff --git a/API调用失败退款时的代理处理确认.md b/API调用失败退款时的代理处理确认.md
deleted file mode 100644
index f9822f8..0000000
--- a/API调用失败退款时的代理处理确认.md
+++ /dev/null
@@ -1,131 +0,0 @@
-# API调用失败退款时的代理处理确认
-
-## 执行流程分析
-
-### 关键执行顺序
-
-```
-1. 支付成功 → 创建代理订单(ProcessStatus = 0,未处理)
- ↓
-2. 支付回调 → 发送查询任务
- ↓
-3. 查询任务执行:
- ├─ 创建查询记录(query表,状态为 "pending") ✅ 第77-87行
- ├─ 生成授权书 ✅ 第108-142行
- ├─ 调用API(第164行)⚠️ 可能失败
- │ ├─ 如果成功 → 继续执行
- │ └─ 如果失败 → return handleError(第166行)⚠️ 直接返回
- │
- ├─ [API成功才会执行到这里]
- ├─ 保存响应数据 ✅ 第177-182行
- ├─ 更新查询状态为 "success" ✅ 第184-189行
- └─ 发送代理处理任务 ✅ 第192行(关键!只有到这里才会发送)
-```
-
-### 关键代码位置
-
-**第164-167行**:
-```go
-combinedResponse, err := l.svcCtx.ApiRequestService.ProcessRequests(decryptData, product.Id)
-if err != nil {
- return l.handleError(ctx, err, order, query) // ← 直接返回,不会继续执行
-}
-```
-
-**第192行(发送代理处理任务)**:
-```go
-// 报告生成成功后,发送代理处理异步任务(不阻塞报告流程)
-if asyncErr := l.svcCtx.AsynqService.SendAgentProcessTask(order.Id); asyncErr != nil {
- logx.Errorf("发送代理处理任务失败,订单ID: %d, 错误: %v", order.Id, asyncErr)
-}
-```
-
----
-
-## 确认结果
-
-### ✅ API调用失败时的情况(第166-167行)
-
-**执行流程**:
-1. 支付时创建代理订单(`ProcessStatus = 0`)
-2. API调用失败(第164行)
-3. **直接返回** `handleError`(第166行)
-4. 进入退款流程
-5. **第192行不会执行**(因为已经return了)
-
-**代理状态**:
-- ✅ 代理订单 `ProcessStatus = 0`(未处理)
-- ✅ 代理**没有收到**佣金
-- ✅ 代理**没有收到**返佣
-- ✅ 代理处理任务**没有发送**
-
----
-
-## 业务逻辑分析
-
-### ✅ 符合业务逻辑
-
-**理由**:
-1. **服务未完成交付**
- - API调用失败,意味着无法生成查询报告
- - 用户支付了订单,但服务没有成功交付
-
-2. **用户权益保护**
- - 平台退款给用户是合理的
- - 用户没有获得服务,不应该承担费用
-
-3. **代理收益逻辑**
- - 代理的收益应该基于**服务成功交付**
- - 而不是仅仅因为用户支付了订单
- - 如果服务未成功交付,代理不应该获得收益
-
-4. **当前实现正确**
- - 只有在查询成功后(第184行更新状态为 "success")
- - 才会发送代理处理任务(第192行)
- - API调用失败时,查询未成功,代理任务不会发送
-
----
-
-## 结论
-
-### ✅ 当前处理完全正确
-
-**对于 API 调用失败退款的情况**:
-- ✅ 代理订单未处理(`ProcessStatus = 0`)
-- ✅ 代理没有收到佣金和返佣
-- ✅ 代理处理任务没有发送
-- ✅ **完全符合业务逻辑**
-
-**业务逻辑合理性**:
-- ✅ 代理收益应该在服务成功交付后才发放
-- ✅ API调用失败意味着服务未交付,不应发放收益
-- ✅ 退款给用户是合理的,保护用户权益
-
----
-
-## 不需要修改
-
-当前逻辑已经正确处理了这种情况,**无需修改**。
-
-**关键保护点**:
-1. 代理处理任务只在查询成功后发送(第192行)
-2. API调用失败会直接返回,不会执行到第192行
-3. 代理订单状态保持为0,代理没有收益
-
----
-
-## 需要注意的其他场景
-
-虽然当前场景处理正确,但需要注意以下场景:
-
-### ⚠️ 场景:代理已处理但订单被退款
-
-如果:
-1. API调用成功
-2. 查询状态更新为 "success"
-3. 发送代理处理任务
-4. 代理处理任务执行,发放佣金和返佣(`ProcessStatus = 1`)
-5. **之后**订单被退款(比如管理员手动退款)
-
-这种情况下需要撤销代理收益(需要另外处理,不是当前场景)。
-
diff --git a/app/main/api/internal/logic/agent/applyforagentlogic.go b/app/main/api/internal/logic/agent/applyforagentlogic.go
index 12f46d7..a466f21 100644
--- a/app/main/api/internal/logic/agent/applyforagentlogic.go
+++ b/app/main/api/internal/logic/agent/applyforagentlogic.go
@@ -1,22 +1,22 @@
package agent
import (
- "context"
- "database/sql"
- "fmt"
- "os"
- "time"
- "ycc-server/app/main/model"
- "ycc-server/common/ctxdata"
- "ycc-server/common/globalkey"
- "ycc-server/common/xerr"
- "ycc-server/pkg/lzkit/crypto"
- "strconv"
+ "context"
+ "database/sql"
+ "fmt"
+ "os"
+ "strconv"
+ "time"
+ "ycc-server/app/main/model"
+ "ycc-server/common/ctxdata"
+ "ycc-server/common/globalkey"
+ "ycc-server/common/xerr"
+ "ycc-server/pkg/lzkit/crypto"
- "github.com/google/uuid"
- "github.com/pkg/errors"
- "github.com/zeromicro/go-zero/core/stores/redis"
- "github.com/zeromicro/go-zero/core/stores/sqlx"
+ "github.com/google/uuid"
+ "github.com/pkg/errors"
+ "github.com/zeromicro/go-zero/core/stores/redis"
+ "github.com/zeromicro/go-zero/core/stores/sqlx"
"ycc-server/app/main/api/internal/svc"
"ycc-server/app/main/api/internal/types"
@@ -50,9 +50,9 @@ func (l *ApplyForAgentLogic) ApplyForAgent(req *types.AgentApplyReq) (resp *type
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "加密手机号失败: %v", err)
}
- if req.Referrer == "" {
- return nil, errors.Wrapf(xerr.NewErrMsg("请填写邀请信息"), "")
- }
+ if req.Referrer == "" {
+ return nil, errors.Wrapf(xerr.NewErrMsg("请填写邀请信息"), "")
+ }
// 2. 校验验证码(开发环境下跳过验证码校验)
if os.Getenv("ENV") != "development" {
@@ -115,49 +115,49 @@ func (l *ApplyForAgentLogic) ApplyForAgent(req *types.AgentApplyReq) (resp *type
return errors.Wrapf(xerr.NewErrMsg("您已经是代理"), "")
}
- var inviteCodeModel *model.AgentInviteCode
- var parentAgentId string
- var targetLevel int64
+ var inviteCodeModel *model.AgentInviteCode
+ var parentAgentId string
+ var targetLevel int64
- inviteCodeModel, err = l.svcCtx.AgentInviteCodeModel.FindOneByCode(transCtx, req.Referrer)
- if err != nil && !errors.Is(err, model.ErrNotFound) {
- return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询邀请码失败, %v", err)
- }
- if inviteCodeModel != nil {
- if inviteCodeModel.Status != 0 {
- if inviteCodeModel.Status == 1 {
- return errors.Wrapf(xerr.NewErrMsg("邀请码已使用"), "")
- }
- return errors.Wrapf(xerr.NewErrMsg("邀请码已失效"), "")
- }
- if inviteCodeModel.ExpireTime.Valid && inviteCodeModel.ExpireTime.Time.Before(time.Now()) {
- return errors.Wrapf(xerr.NewErrMsg("邀请码已过期"), "")
- }
- targetLevel = inviteCodeModel.TargetLevel
- if inviteCodeModel.AgentId.Valid {
- parentAgentId = inviteCodeModel.AgentId.String
- }
- } else {
- if codeVal, parseErr := strconv.ParseInt(req.Referrer, 10, 64); parseErr == nil && codeVal > 0 {
- parentAgent, err := l.findAgentByCode(transCtx, codeVal)
- if err != nil {
- return err
- }
- parentAgentId = parentAgent.Id
- targetLevel = 1
- } else {
- encRefMobile, _ := crypto.EncryptMobile(req.Referrer, l.svcCtx.Config.Encrypt.SecretKey)
- agents, findErr := l.svcCtx.AgentModel.FindAll(transCtx, l.svcCtx.AgentModel.SelectBuilder().Where("mobile = ? AND del_state = ?", encRefMobile, globalkey.DelStateNo).Limit(1), "")
- if findErr != nil {
- return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询上级代理失败, %v", findErr)
- }
- if len(agents) == 0 {
- return errors.Wrapf(xerr.NewErrMsg("邀请信息无效"), "")
- }
- parentAgentId = agents[0].Id
- targetLevel = 1
- }
- }
+ inviteCodeModel, err = l.svcCtx.AgentInviteCodeModel.FindOneByCode(transCtx, req.Referrer)
+ if err != nil && !errors.Is(err, model.ErrNotFound) {
+ return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询邀请码失败, %v", err)
+ }
+ if inviteCodeModel != nil {
+ if inviteCodeModel.Status != 0 {
+ if inviteCodeModel.Status == 1 {
+ return errors.Wrapf(xerr.NewErrMsg("邀请码已使用"), "")
+ }
+ return errors.Wrapf(xerr.NewErrMsg("邀请码已失效"), "")
+ }
+ if inviteCodeModel.ExpireTime.Valid && inviteCodeModel.ExpireTime.Time.Before(time.Now()) {
+ return errors.Wrapf(xerr.NewErrMsg("邀请码已过期"), "")
+ }
+ targetLevel = inviteCodeModel.TargetLevel
+ if inviteCodeModel.AgentId.Valid {
+ parentAgentId = inviteCodeModel.AgentId.String
+ }
+ } else {
+ if codeVal, parseErr := strconv.ParseInt(req.Referrer, 10, 64); parseErr == nil && codeVal > 0 {
+ parentAgent, err := l.findAgentByCode(transCtx, codeVal)
+ if err != nil {
+ return err
+ }
+ parentAgentId = parentAgent.Id
+ targetLevel = 1
+ } else {
+ encRefMobile, _ := crypto.EncryptMobile(req.Referrer, l.svcCtx.Config.Encrypt.SecretKey)
+ agents, findErr := l.svcCtx.AgentModel.FindAll(transCtx, l.svcCtx.AgentModel.SelectBuilder().Where("mobile = ? AND del_state = ?", encRefMobile, globalkey.DelStateNo).Limit(1), "")
+ if findErr != nil {
+ return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询上级代理失败, %v", findErr)
+ }
+ if len(agents) == 0 {
+ return errors.Wrapf(xerr.NewErrMsg("邀请信息无效"), "")
+ }
+ parentAgentId = agents[0].Id
+ targetLevel = 1
+ }
+ }
// 4.5 创建代理记录
newAgent := &model.Agent{
@@ -279,7 +279,7 @@ func (l *ApplyForAgentLogic) ApplyForAgent(req *types.AgentApplyReq) (resp *type
}
// 6. 生成并返回token
- token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal)
+ token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成token失败: %v", err)
}
diff --git a/app/main/api/internal/logic/agent/registerbyinvitecodelogic.go b/app/main/api/internal/logic/agent/registerbyinvitecodelogic.go
index be625cb..a1dc0eb 100644
--- a/app/main/api/internal/logic/agent/registerbyinvitecodelogic.go
+++ b/app/main/api/internal/logic/agent/registerbyinvitecodelogic.go
@@ -105,10 +105,16 @@ func (l *RegisterByInviteCodeLogic) RegisterByInviteCode(req *types.RegisterByIn
return errors.Wrapf(xerr.NewErrMsg("您已经是代理"), "")
}
- // 如果是临时用户(微信环境下),检查手机号是否已绑定其他微信号,并绑定临时用户到正式用户
- // 注意:非微信环境下 claims 为 nil,此逻辑不会执行,直接使用已存在的 user.Id
+ // 检查用户是否有mobile绑定(没有mobile则不能成为代理)
+ // 如果是临时用户(微信环境下),需要先绑定手机号
claims, err := ctxdata.GetClaimsFromCtx(l.ctx)
- if err == nil && claims != nil && claims.UserType == model.UserTypeTemp {
+ if err == nil && claims != nil {
+ // 获取用户的mobile信息
+ if !user.Mobile.Valid || user.Mobile.String == "" {
+ // 临时用户(无mobile)不能直接成为代理,需要先绑定mobile
+ return errors.Wrapf(xerr.NewErrMsg("请先绑定手机号后再申请成为代理"), "")
+ }
+ // 检查是否已绑定手机号认证(用于确保后续可通过手机号登录)
userAuth, err := l.svcCtx.UserAuthModel.FindOneByUserIdAuthType(l.ctx, user.Id, claims.AuthType)
if err != nil && !errors.Is(err, model.ErrNotFound) {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询用户认证失败, %v", err)
@@ -116,11 +122,6 @@ func (l *RegisterByInviteCodeLogic) RegisterByInviteCode(req *types.RegisterByIn
if userAuth != nil && userAuth.AuthKey != claims.AuthKey {
return errors.Wrapf(xerr.NewErrMsg("该手机号已绑定其他微信号"), "")
}
- // 绑定临时用户到正式用户
- err = l.svcCtx.UserService.TempUserBindUser(l.ctx, session, user.Id)
- if err != nil {
- return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "绑定用户失败: %v", err)
- }
}
userID = user.Id
@@ -263,7 +264,7 @@ func (l *RegisterByInviteCodeLogic) RegisterByInviteCode(req *types.RegisterByIn
}
// 5. 生成并返回token
- token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal)
+ token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成token失败: %v", err)
}
diff --git a/app/main/api/internal/logic/query/queryservicelogic.go b/app/main/api/internal/logic/query/queryservicelogic.go
index eb986f5..4830454 100644
--- a/app/main/api/internal/logic/query/queryservicelogic.go
+++ b/app/main/api/internal/logic/query/queryservicelogic.go
@@ -8,7 +8,6 @@ import (
"os"
"time"
"ycc-server/app/main/api/internal/service"
- "ycc-server/app/main/model"
"ycc-server/common/ctxdata"
"ycc-server/common/xerr"
"ycc-server/pkg/lzkit/crypto"
@@ -60,60 +59,60 @@ var productProcessors = map[string]func(*QueryServiceLogic, *types.QueryServiceR
func (l *QueryServiceLogic) PreprocessLogic(req *types.QueryServiceReq, product string) (*types.QueryServiceResp, error) {
if processor, exists := productProcessors[product]; exists {
- return processor(l, req) // 调用对应的处理函数
+ return processor(l, req) // 璋冪敤瀵瑰簲鐨勫鐞嗗嚱鏁?
}
- return nil, errors.New("未找到相应的处理程序")
+ return nil, errors.New("鏈壘鍒扮浉搴旂殑澶勭悊绋嬪簭")
}
func (l *QueryServiceLogic) ProcessMarriageLogic(req *types.QueryServiceReq) (*types.QueryServiceResp, error) {
- // AES解密
+ // AES瑙e瘑
decryptData, DecryptDataErr := l.DecryptData(req.Data)
if DecryptDataErr != nil {
return nil, DecryptDataErr
}
- // 校验参数
+ // 鏍¢獙鍙傛暟
var data types.MarriageReq
if unmarshalErr := json.Unmarshal(decryptData, &data); unmarshalErr != nil {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %+v", unmarshalErr)
+ return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "鏌ヨ鏈嶅姟, 瑙e瘑鍚庣殑鏁版嵁鏍煎紡涓嶆纭? %+v", unmarshalErr)
}
if validatorErr := validator.Validate(data); validatorErr != nil {
- return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "查询服务, 参数不正确: %+v", validatorErr)
+ return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "鏌ヨ鏈嶅姟, 鍙傛暟涓嶆纭? %+v", validatorErr)
}
- // 校验验证码
+ // 鏍¢獙楠岃瘉鐮?
verifyCodeErr := l.VerifyCode(data.Mobile, data.Code)
if verifyCodeErr != nil {
return nil, verifyCodeErr
}
- // 校验三要素
+ // 鏍¢獙涓夎绱?
verifyErr := l.Verify(data.Name, data.IDCard, data.Mobile)
if verifyErr != nil {
return nil, verifyErr
}
- // 缓存
+ // 缂撳瓨
params := map[string]interface{}{
"name": data.Name,
"id_card": data.IDCard,
"mobile": data.Mobile,
}
- userID, userType, err := l.GetOrCreateUser()
- if err != nil {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 处理用户失败: %v", err)
- }
+ userID, err := l.GetOrCreateUser()
+ if err != nil {
+ return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "鏌ヨ鏈嶅姟, 澶勭悊鐢ㄦ埛澶辫触: %v", err)
+ }
cacheNo, cacheDataErr := l.CacheData(params, "marriage", userID)
if cacheDataErr != nil {
return nil, cacheDataErr
}
- token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, userType)
+ token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
if err != nil {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %v", err)
+ return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "鏌ヨ鏈嶅姟, 鐢熸垚token澶辫触 : %v", err)
}
- // 获取当前时间戳
+ // 鑾峰彇褰撳墠鏃堕棿鎴?
now := time.Now().Unix()
return &types.QueryServiceResp{
Id: cacheNo,
@@ -123,58 +122,58 @@ func (l *QueryServiceLogic) ProcessMarriageLogic(req *types.QueryServiceReq) (*t
}, nil
}
-// 处理家政服务相关逻辑
+// 澶勭悊瀹舵斂鏈嶅姟鐩稿叧閫昏緫
func (l *QueryServiceLogic) ProcessHomeServiceLogic(req *types.QueryServiceReq) (*types.QueryServiceResp, error) {
- // AES解密
+ // AES瑙e瘑
decryptData, DecryptDataErr := l.DecryptData(req.Data)
if DecryptDataErr != nil {
return nil, DecryptDataErr
}
- // 校验参数
+ // 鏍¢獙鍙傛暟
var data types.HomeServiceReq
if unmarshalErr := json.Unmarshal(decryptData, &data); unmarshalErr != nil {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %+v", unmarshalErr)
+ return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "鏌ヨ鏈嶅姟, 瑙e瘑鍚庣殑鏁版嵁鏍煎紡涓嶆纭? %+v", unmarshalErr)
}
if validatorErr := validator.Validate(data); validatorErr != nil {
- return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "查询服务, 参数不正确: %+v", validatorErr)
+ return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "鏌ヨ鏈嶅姟, 鍙傛暟涓嶆纭? %+v", validatorErr)
}
- // 校验验证码
+ // 鏍¢獙楠岃瘉鐮?
verifyCodeErr := l.VerifyCode(data.Mobile, data.Code)
if verifyCodeErr != nil {
return nil, verifyCodeErr
}
- // 校验三要素
+ // 鏍¢獙涓夎绱?
verifyErr := l.Verify(data.Name, data.IDCard, data.Mobile)
if verifyErr != nil {
return nil, verifyErr
}
- // 缓存
+ // 缂撳瓨
params := map[string]interface{}{
"name": data.Name,
"id_card": data.IDCard,
"mobile": data.Mobile,
}
- userID, userType, err := l.GetOrCreateUser()
- if err != nil {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 处理用户失败: %v", err)
- }
+ userID, err := l.GetOrCreateUser()
+ if err != nil {
+ return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "鏌ヨ鏈嶅姟, 澶勭悊鐢ㄦ埛澶辫触: %v", err)
+ }
cacheNo, cacheDataErr := l.CacheData(params, "homeservice", userID)
if cacheDataErr != nil {
return nil, cacheDataErr
}
- token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, userType)
+ token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
if err != nil {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
+ return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "鏌ヨ鏈嶅姟, 鐢熸垚token澶辫触 : %d", userID)
}
- // 获取当前时间戳
+ // 鑾峰彇褰撳墠鏃堕棿鎴?
now := time.Now().Unix()
return &types.QueryServiceResp{
Id: cacheNo,
@@ -184,58 +183,58 @@ func (l *QueryServiceLogic) ProcessHomeServiceLogic(req *types.QueryServiceReq)
}, nil
}
-// 处理风险评估相关逻辑
+// 澶勭悊椋庨櫓璇勪及鐩稿叧閫昏緫
func (l *QueryServiceLogic) ProcessRiskAssessmentLogic(req *types.QueryServiceReq) (*types.QueryServiceResp, error) {
- // AES解密
+ // AES瑙e瘑
decryptData, DecryptDataErr := l.DecryptData(req.Data)
if DecryptDataErr != nil {
return nil, DecryptDataErr
}
- // 校验参数
+ // 鏍¢獙鍙傛暟
var data types.RiskAssessmentReq
if unmarshalErr := json.Unmarshal(decryptData, &data); unmarshalErr != nil {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %+v", unmarshalErr)
+ return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "鏌ヨ鏈嶅姟, 瑙e瘑鍚庣殑鏁版嵁鏍煎紡涓嶆纭? %+v", unmarshalErr)
}
if validatorErr := validator.Validate(data); validatorErr != nil {
- return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "查询服务, 参数不正确: %+v", validatorErr)
+ return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "鏌ヨ鏈嶅姟, 鍙傛暟涓嶆纭? %+v", validatorErr)
}
- // 校验验证码
+ // 鏍¢獙楠岃瘉鐮?
verifyCodeErr := l.VerifyCode(data.Mobile, data.Code)
if verifyCodeErr != nil {
return nil, verifyCodeErr
}
- // 校验三要素
+ // 鏍¢獙涓夎绱?
verifyErr := l.Verify(data.Name, data.IDCard, data.Mobile)
if verifyErr != nil {
return nil, verifyErr
}
- // 缓存
+ // 缂撳瓨
params := map[string]interface{}{
"name": data.Name,
"id_card": data.IDCard,
"mobile": data.Mobile,
}
- userID, userType, err := l.GetOrCreateUser()
- if err != nil {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 处理用户失败: %v", err)
- }
+ userID, err := l.GetOrCreateUser()
+ if err != nil {
+ return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "鏌ヨ鏈嶅姟, 澶勭悊鐢ㄦ埛澶辫触: %v", err)
+ }
cacheNo, cacheDataErr := l.CacheData(params, "riskassessment", userID)
if cacheDataErr != nil {
return nil, cacheDataErr
}
- token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, userType)
+ token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
if err != nil {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
+ return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "鏌ヨ鏈嶅姟, 鐢熸垚token澶辫触 : %d", userID)
}
- // 获取当前时间戳
+ // 鑾峰彇褰撳墠鏃堕棿鎴?
now := time.Now().Unix()
return &types.QueryServiceResp{
Id: cacheNo,
@@ -245,57 +244,57 @@ func (l *QueryServiceLogic) ProcessRiskAssessmentLogic(req *types.QueryServiceRe
}, nil
}
-// 处理公司信息查询相关逻辑
+// 澶勭悊鍏徃淇℃伅鏌ヨ鐩稿叧閫昏緫
func (l *QueryServiceLogic) ProcessCompanyInfoLogic(req *types.QueryServiceReq) (*types.QueryServiceResp, error) {
- // AES解密
+ // AES瑙e瘑
decryptData, DecryptDataErr := l.DecryptData(req.Data)
if DecryptDataErr != nil {
return nil, DecryptDataErr
}
- // 校验参数
+ // 鏍¢獙鍙傛暟
var data types.CompanyInfoReq
if unmarshalErr := json.Unmarshal(decryptData, &data); unmarshalErr != nil {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %+v", unmarshalErr)
+ return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "鏌ヨ鏈嶅姟, 瑙e瘑鍚庣殑鏁版嵁鏍煎紡涓嶆纭? %+v", unmarshalErr)
}
if validatorErr := validator.Validate(data); validatorErr != nil {
- return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "查询服务, 参数不正确: %+v", validatorErr)
+ return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "鏌ヨ鏈嶅姟, 鍙傛暟涓嶆纭? %+v", validatorErr)
}
- // 校验验证码
+ // 鏍¢獙楠岃瘉鐮?
verifyCodeErr := l.VerifyCode(data.Mobile, data.Code)
if verifyCodeErr != nil {
return nil, verifyCodeErr
}
- // 校验三要素
+ // 鏍¢獙涓夎绱?
verifyErr := l.Verify(data.Name, data.IDCard, data.Mobile)
if verifyErr != nil {
return nil, verifyErr
}
- // 缓存
+ // 缂撳瓨
params := map[string]interface{}{
"name": data.Name,
"id_card": data.IDCard,
"mobile": data.Mobile,
}
- userID, userType, err := l.GetOrCreateUser()
- if err != nil {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 处理用户失败: %v", err)
- }
+ userID, err := l.GetOrCreateUser()
+ if err != nil {
+ return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "鏌ヨ鏈嶅姟, 澶勭悊鐢ㄦ埛澶辫触: %v", err)
+ }
cacheNo, cacheDataErr := l.CacheData(params, "companyinfo", userID)
if cacheDataErr != nil {
return nil, cacheDataErr
}
- token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, userType)
+ token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
if err != nil {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
+ return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "鏌ヨ鏈嶅姟, 鐢熸垚token澶辫触 : %d", userID)
}
- // 获取当前时间戳
+ // 鑾峰彇褰撳墠鏃堕棿鎴?
now := time.Now().Unix()
return &types.QueryServiceResp{
Id: cacheNo,
@@ -305,58 +304,58 @@ func (l *QueryServiceLogic) ProcessCompanyInfoLogic(req *types.QueryServiceReq)
}, nil
}
-// 处理租赁信息查询相关逻辑
+// 澶勭悊绉熻祦淇℃伅鏌ヨ鐩稿叧閫昏緫
func (l *QueryServiceLogic) ProcessRentalInfoLogic(req *types.QueryServiceReq) (*types.QueryServiceResp, error) {
- // AES解密
+ // AES瑙e瘑
decryptData, DecryptDataErr := l.DecryptData(req.Data)
if DecryptDataErr != nil {
return nil, DecryptDataErr
}
- // 校验参数
+ // 鏍¢獙鍙傛暟
var data types.RentalInfoReq
if unmarshalErr := json.Unmarshal(decryptData, &data); unmarshalErr != nil {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %+v", unmarshalErr)
+ return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "鏌ヨ鏈嶅姟, 瑙e瘑鍚庣殑鏁版嵁鏍煎紡涓嶆纭? %+v", unmarshalErr)
}
if validatorErr := validator.Validate(data); validatorErr != nil {
- return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "查询服务, 参数不正确: %+v", validatorErr)
+ return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "鏌ヨ鏈嶅姟, 鍙傛暟涓嶆纭? %+v", validatorErr)
}
- // 校验验证码
+ // 鏍¢獙楠岃瘉鐮?
verifyCodeErr := l.VerifyCode(data.Mobile, data.Code)
if verifyCodeErr != nil {
return nil, verifyCodeErr
}
- // 校验三要素
+ // 鏍¢獙涓夎绱?
verifyErr := l.Verify(data.Name, data.IDCard, data.Mobile)
if verifyErr != nil {
return nil, verifyErr
}
- // 缓存
+ // 缂撳瓨
params := map[string]interface{}{
"name": data.Name,
"id_card": data.IDCard,
"mobile": data.Mobile,
}
- userID, userType, err := l.GetOrCreateUser()
- if err != nil {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 处理用户失败: %v", err)
- }
+ userID, err := l.GetOrCreateUser()
+ if err != nil {
+ return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "鏌ヨ鏈嶅姟, 澶勭悊鐢ㄦ埛澶辫触: %v", err)
+ }
cacheNo, cacheDataErr := l.CacheData(params, "rentalinfo", userID)
if cacheDataErr != nil {
return nil, cacheDataErr
}
- token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, userType)
+ token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
if err != nil {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
+ return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "鏌ヨ鏈嶅姟, 鐢熸垚token澶辫触 : %d", userID)
}
- // 获取当前时间戳
+ // 鑾峰彇褰撳墠鏃堕棿鎴?
now := time.Now().Unix()
return &types.QueryServiceResp{
Id: cacheNo,
@@ -366,58 +365,58 @@ func (l *QueryServiceLogic) ProcessRentalInfoLogic(req *types.QueryServiceReq) (
}, nil
}
-// 处理贷前背景检查相关逻辑
+// 澶勭悊璐峰墠鑳屾櫙妫€鏌ョ浉鍏抽€昏緫
func (l *QueryServiceLogic) ProcessPreLoanBackgroundCheckLogic(req *types.QueryServiceReq) (*types.QueryServiceResp, error) {
- // AES解密
+ // AES瑙e瘑
decryptData, DecryptDataErr := l.DecryptData(req.Data)
if DecryptDataErr != nil {
return nil, DecryptDataErr
}
- // 校验参数
+ // 鏍¢獙鍙傛暟
var data types.PreLoanBackgroundCheckReq
if unmarshalErr := json.Unmarshal(decryptData, &data); unmarshalErr != nil {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %+v", unmarshalErr)
+ return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "鏌ヨ鏈嶅姟, 瑙e瘑鍚庣殑鏁版嵁鏍煎紡涓嶆纭? %+v", unmarshalErr)
}
if validatorErr := validator.Validate(data); validatorErr != nil {
- return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "查询服务, 参数不正确: %+v", validatorErr)
+ return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "鏌ヨ鏈嶅姟, 鍙傛暟涓嶆纭? %+v", validatorErr)
}
- // 校验验证码
+ // 鏍¢獙楠岃瘉鐮?
verifyCodeErr := l.VerifyCode(data.Mobile, data.Code)
if verifyCodeErr != nil {
return nil, verifyCodeErr
}
- // 校验三要素
+ // 鏍¢獙涓夎绱?
verifyErr := l.Verify(data.Name, data.IDCard, data.Mobile)
if verifyErr != nil {
return nil, verifyErr
}
- // 缓存
+ // 缂撳瓨
params := map[string]interface{}{
"name": data.Name,
"id_card": data.IDCard,
"mobile": data.Mobile,
}
- userID, userType, err := l.GetOrCreateUser()
- if err != nil {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 处理用户失败: %v", err)
- }
+ userID, err := l.GetOrCreateUser()
+ if err != nil {
+ return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "鏌ヨ鏈嶅姟, 澶勭悊鐢ㄦ埛澶辫触: %v", err)
+ }
cacheNo, cacheDataErr := l.CacheData(params, "preloanbackgroundcheck", userID)
if cacheDataErr != nil {
return nil, cacheDataErr
}
- token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, userType)
+ token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
if err != nil {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
+ return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "鏌ヨ鏈嶅姟, 鐢熸垚token澶辫触 : %d", userID)
}
- // 获取当前时间戳
+ // 鑾峰彇褰撳墠鏃堕棿鎴?
now := time.Now().Unix()
return &types.QueryServiceResp{
Id: cacheNo,
@@ -427,57 +426,57 @@ func (l *QueryServiceLogic) ProcessPreLoanBackgroundCheckLogic(req *types.QueryS
}, nil
}
-// 处理人事背调相关逻辑
+// 澶勭悊浜轰簨鑳岃皟鐩稿叧閫昏緫
func (l *QueryServiceLogic) ProcessBackgroundCheckLogic(req *types.QueryServiceReq) (*types.QueryServiceResp, error) {
- // AES解密
+ // AES瑙e瘑
decryptData, DecryptDataErr := l.DecryptData(req.Data)
if DecryptDataErr != nil {
return nil, DecryptDataErr
}
- // 校验参数
+ // 鏍¢獙鍙傛暟
var data types.BackgroundCheckReq
if unmarshalErr := json.Unmarshal(decryptData, &data); unmarshalErr != nil {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %+v", unmarshalErr)
+ return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "鏌ヨ鏈嶅姟, 瑙e瘑鍚庣殑鏁版嵁鏍煎紡涓嶆纭? %+v", unmarshalErr)
}
if validatorErr := validator.Validate(data); validatorErr != nil {
- return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "查询服务, 参数不正确: %+v", validatorErr)
+ return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "鏌ヨ鏈嶅姟, 鍙傛暟涓嶆纭? %+v", validatorErr)
}
- // 校验验证码
+ // 鏍¢獙楠岃瘉鐮?
verifyCodeErr := l.VerifyCode(data.Mobile, data.Code)
if verifyCodeErr != nil {
return nil, verifyCodeErr
}
- // 校验三要素
+ // 鏍¢獙涓夎绱?
verifyErr := l.Verify(data.Name, data.IDCard, data.Mobile)
if verifyErr != nil {
return nil, verifyErr
}
- // 缓存
+ // 缂撳瓨
params := map[string]interface{}{
"name": data.Name,
"id_card": data.IDCard,
"mobile": data.Mobile,
}
- userID, userType, err := l.GetOrCreateUser()
- if err != nil {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 处理用户失败: %v", err)
- }
+ userID, err := l.GetOrCreateUser()
+ if err != nil {
+ return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "鏌ヨ鏈嶅姟, 澶勭悊鐢ㄦ埛澶辫触: %v", err)
+ }
cacheNo, cacheDataErr := l.CacheData(params, "backgroundcheck", userID)
if cacheDataErr != nil {
return nil, cacheDataErr
}
- token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, userType)
+ token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
if err != nil {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
+ return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "鏌ヨ鏈嶅姟, 鐢熸垚token澶辫触 : %d", userID)
}
- // 获取当前时间戳
+ // 鑾峰彇褰撳墠鏃堕棿鎴?
now := time.Now().Unix()
return &types.QueryServiceResp{
Id: cacheNo,
@@ -487,55 +486,55 @@ func (l *QueryServiceLogic) ProcessBackgroundCheckLogic(req *types.QueryServiceR
}, nil
}
func (l *QueryServiceLogic) ProcessPersonalDataLogic(req *types.QueryServiceReq) (*types.QueryServiceResp, error) {
- // AES解密
+ // AES瑙e瘑
decryptData, DecryptDataErr := l.DecryptData(req.Data)
if DecryptDataErr != nil {
return nil, DecryptDataErr
}
- // 校验参数
+ // 鏍¢獙鍙傛暟
var data types.PersonalDataReq
if unmarshalErr := json.Unmarshal(decryptData, &data); unmarshalErr != nil {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %+v", unmarshalErr)
+ return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "鏌ヨ鏈嶅姟, 瑙e瘑鍚庣殑鏁版嵁鏍煎紡涓嶆纭? %+v", unmarshalErr)
}
if validatorErr := validator.Validate(data); validatorErr != nil {
- return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "查询服务, 参数不正确: %+v", validatorErr)
+ return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "鏌ヨ鏈嶅姟, 鍙傛暟涓嶆纭? %+v", validatorErr)
}
- // 校验验证码
+ // 鏍¢獙楠岃瘉鐮?
verifyCodeErr := l.VerifyCode(data.Mobile, data.Code)
if verifyCodeErr != nil {
return nil, verifyCodeErr
}
- // 校验三要素
+ // 鏍¢獙涓夎绱?
verifyErr := l.Verify(data.Name, data.IDCard, data.Mobile)
if verifyErr != nil {
return nil, verifyErr
}
- // 缓存
+ // 缂撳瓨
params := map[string]interface{}{
"name": data.Name,
"id_card": data.IDCard,
"mobile": data.Mobile,
}
- userID, userType, err := l.GetOrCreateUser()
- if err != nil {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 处理用户失败: %v", err)
- }
+ userID, err := l.GetOrCreateUser()
+ if err != nil {
+ return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "鏌ヨ鏈嶅姟, 澶勭悊鐢ㄦ埛澶辫触: %v", err)
+ }
cacheNo, cacheDataErr := l.CacheData(params, "personalData", userID)
if cacheDataErr != nil {
return nil, cacheDataErr
}
- token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, userType)
+ token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
if err != nil {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
+ return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "鏌ヨ鏈嶅姟, 鐢熸垚token澶辫触 : %d", userID)
}
- // 获取当前时间戳
+ // 鑾峰彇褰撳墠鏃堕棿鎴?
now := time.Now().Unix()
return &types.QueryServiceResp{
Id: cacheNo,
@@ -545,55 +544,55 @@ func (l *QueryServiceLogic) ProcessPersonalDataLogic(req *types.QueryServiceReq)
}, nil
}
func (l *QueryServiceLogic) ProcessConsumerFinanceReportLogic(req *types.QueryServiceReq) (*types.QueryServiceResp, error) {
- // AES解密
+ // AES瑙e瘑
decryptData, DecryptDataErr := l.DecryptData(req.Data)
if DecryptDataErr != nil {
return nil, DecryptDataErr
}
- // 校验参数
+ // 鏍¢獙鍙傛暟
var data types.ConsumerFinanceReportReq
if unmarshalErr := json.Unmarshal(decryptData, &data); unmarshalErr != nil {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %+v", unmarshalErr)
+ return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "鏌ヨ鏈嶅姟, 瑙e瘑鍚庣殑鏁版嵁鏍煎紡涓嶆纭? %+v", unmarshalErr)
}
if validatorErr := validator.Validate(data); validatorErr != nil {
- return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "查询服务, 参数不正确: %+v", validatorErr)
+ return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "鏌ヨ鏈嶅姟, 鍙傛暟涓嶆纭? %+v", validatorErr)
}
- // 校验验证码
+ // 鏍¢獙楠岃瘉鐮?
verifyCodeErr := l.VerifyCode(data.Mobile, data.Code)
if verifyCodeErr != nil {
return nil, verifyCodeErr
}
- // 校验三要素
+ // 鏍¢獙涓夎绱?
verifyErr := l.Verify(data.Name, data.IDCard, data.Mobile)
if verifyErr != nil {
return nil, verifyErr
}
- // 缓存
+ // 缂撳瓨
params := map[string]interface{}{
"name": data.Name,
"id_card": data.IDCard,
"mobile": data.Mobile,
}
- userID, userType, err := l.GetOrCreateUser()
- if err != nil {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 处理用户失败: %v", err)
- }
+ userID, err := l.GetOrCreateUser()
+ if err != nil {
+ return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "鏌ヨ鏈嶅姟, 澶勭悊鐢ㄦ埛澶辫触: %v", err)
+ }
cacheNo, cacheDataErr := l.CacheData(params, "consumerFinanceReport", userID)
if cacheDataErr != nil {
return nil, cacheDataErr
}
- token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, userType)
+ token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
if err != nil {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 生成token失败 : %d", userID)
+ return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "鏌ヨ鏈嶅姟, 鐢熸垚token澶辫触 : %d", userID)
}
- // 获取当前时间戳
+ // 鑾峰彇褰撳墠鏃堕棿鎴?
now := time.Now().Unix()
return &types.QueryServiceResp{
Id: cacheNo,
@@ -606,43 +605,43 @@ func (l *QueryServiceLogic) DecryptData(data string) ([]byte, error) {
secretKey := l.svcCtx.Config.Encrypt.SecretKey
key, decodeErr := hex.DecodeString(secretKey)
if decodeErr != nil {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "密钥获取失败: %+v", decodeErr)
+ return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "瀵嗛挜鑾峰彇澶辫触: %+v", decodeErr)
}
decryptData, aesDecryptErr := crypto.AesDecrypt(data, key)
if aesDecryptErr != nil || len(decryptData) == 0 {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "解密失败: %+v", aesDecryptErr)
+ return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "瑙e瘑澶辫触: %+v", aesDecryptErr)
}
return decryptData, nil
}
-// 校验验证码
+// 鏍¢獙楠岃瘉鐮?
func (l *QueryServiceLogic) VerifyCode(mobile string, code string) error {
- // 开发环境下跳过验证码校验
+ // 寮€鍙戠幆澧冧笅璺宠繃楠岃瘉鐮佹牎楠?
if os.Getenv("ENV") == "development" {
return nil
}
secretKey := l.svcCtx.Config.Encrypt.SecretKey
encryptedMobile, err := crypto.EncryptMobile(mobile, secretKey)
if err != nil {
- return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "加密手机号失败: %+v", err)
+ return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "鍔犲瘑鎵嬫満鍙峰け璐? %+v", err)
}
codeRedisKey := fmt.Sprintf("%s:%s", "query", encryptedMobile)
cacheCode, err := l.svcCtx.Redis.Get(codeRedisKey)
if err != nil {
if errors.Is(err, redis.Nil) {
- return errors.Wrapf(xerr.NewErrMsg("验证码已过期"), "验证码过期: %s", mobile)
+ return errors.Wrapf(xerr.NewErrMsg("楠岃瘉鐮佸凡杩囨湡"), "楠岃瘉鐮佽繃鏈? %s", mobile)
}
- return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "读取验证码redis缓存失败, mobile: %s, err: %+v", mobile, err)
+ return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "璇诲彇楠岃瘉鐮乺edis缂撳瓨澶辫触, mobile: %s, err: %+v", mobile, err)
}
if cacheCode != code {
- return errors.Wrapf(xerr.NewErrMsg("验证码不正确"), "验证码不正确: %s", mobile)
+ return errors.Wrapf(xerr.NewErrMsg("楠岃瘉鐮佷笉姝g‘"), "楠岃瘉鐮佷笉姝g‘: %s", mobile)
}
return nil
}
-// 二、三要素验证
+// 浜屻€佷笁瑕佺礌楠岃瘉
func (l *QueryServiceLogic) Verify(Name string, IDCard string, Mobile string) error {
- // 开发环境下跳过二/三要素验证
+ // 寮€鍙戠幆澧冧笅璺宠繃浜?涓夎绱犻獙璇?
if os.Getenv("ENV") == "development" {
return nil
}
@@ -653,13 +652,13 @@ func (l *QueryServiceLogic) Verify(Name string, IDCard string, Mobile string) er
}
verification, err := l.svcCtx.VerificationService.TwoFactorVerification(twoVerification)
if err != nil {
- return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "二要素验证失败: %v", err)
+ return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "浜岃绱犻獙璇佸け璐? %v", err)
}
if !verification.Passed {
- return errors.Wrapf(xerr.NewErrCodeMsg(xerr.SERVER_COMMON_ERROR, verification.Err.Error()), "二要素验证不通过: %v", err)
+ return errors.Wrapf(xerr.NewErrCodeMsg(xerr.SERVER_COMMON_ERROR, verification.Err.Error()), "浜岃绱犻獙璇佷笉閫氳繃: %v", err)
}
} else {
- // 三要素验证
+ // 涓夎绱犻獙璇?
threeVerification := service.ThreeFactorVerificationRequest{
Name: Name,
IDCard: IDCard,
@@ -667,30 +666,30 @@ func (l *QueryServiceLogic) Verify(Name string, IDCard string, Mobile string) er
}
verification, err := l.svcCtx.VerificationService.ThreeFactorVerification(threeVerification)
if err != nil {
- return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "三要素验证失败: %v", err)
+ return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "涓夎绱犻獙璇佸け璐? %v", err)
}
if !verification.Passed {
- return errors.Wrapf(xerr.NewErrCodeMsg(xerr.SERVER_COMMON_ERROR, verification.Err.Error()), "三要素验证不通过: %v", err)
+ return errors.Wrapf(xerr.NewErrCodeMsg(xerr.SERVER_COMMON_ERROR, verification.Err.Error()), "涓夎绱犻獙璇佷笉閫氳繃: %v", err)
}
}
return nil
}
-// 缓存
+// 缂撳瓨
func (l *QueryServiceLogic) CacheData(params map[string]interface{}, Product string, userID string) (string, error) {
agentIdentifier, _ := l.ctx.Value("agentIdentifier").(string)
secretKey := l.svcCtx.Config.Encrypt.SecretKey
key, decodeErr := hex.DecodeString(secretKey)
if decodeErr != nil {
- return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 获取AES密钥失败: %+v", decodeErr)
+ return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "鏌ヨ鏈嶅姟, 鑾峰彇AES瀵嗛挜澶辫触: %+v", decodeErr)
}
paramsMarshal, marshalErr := json.Marshal(params)
if marshalErr != nil {
- return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 序列化参数失败: %+v", marshalErr)
+ return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "鏌ヨ鏈嶅姟, 搴忓垪鍖栧弬鏁板け璐? %+v", marshalErr)
}
encryptParams, aesEncryptErr := crypto.AesEncrypt(paramsMarshal, key)
if aesEncryptErr != nil {
- return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 加密参数失败: %+v", aesEncryptErr)
+ return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "鏌ヨ鏈嶅姟, 鍔犲瘑鍙傛暟澶辫触: %+v", aesEncryptErr)
}
queryCache := types.QueryCacheLoad{
Params: encryptParams,
@@ -699,7 +698,7 @@ func (l *QueryServiceLogic) CacheData(params map[string]interface{}, Product str
}
jsonData, marshalErr := json.Marshal(queryCache)
if marshalErr != nil {
- return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 序列化参数失败: %+v", marshalErr)
+ return "", errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "鏌ヨ鏈嶅姟, 搴忓垪鍖栧弬鏁板け璐? %+v", marshalErr)
}
outTradeNo := "Q_" + l.svcCtx.AlipayService.GenerateOutTradeNo()
redisKey := fmt.Sprintf(types.QueryCacheKey, userID, outTradeNo)
@@ -710,18 +709,18 @@ func (l *QueryServiceLogic) CacheData(params map[string]interface{}, Product str
return outTradeNo, nil
}
-// GetOrCreateUser 获取或创建用户
-// 1. 如果上下文中已有用户ID,直接返回
-// 2. 如果是代理查询或APP请求,创建新用户
-// 3. 其他情况返回未登录错误
-func (l *QueryServiceLogic) GetOrCreateUser() (string, int64, error) {
- claims, err := ctxdata.GetClaimsFromCtx(l.ctx)
- if err == nil && claims != nil {
- return claims.UserId, claims.UserType, nil
- }
- userID, regErr := l.svcCtx.UserService.RegisterUUIDUser(l.ctx)
- if regErr != nil {
- return "", 0, regErr
- }
- return userID, model.UserTypeTemp, nil
+// GetOrCreateUser 鑾峰彇鎴栧垱寤虹敤鎴?
+// 1. 濡傛灉涓婁笅鏂囦腑宸叉湁鐢ㄦ埛ID锛岀洿鎺ヨ繑鍥?
+// 2. 濡傛灉鏄唬鐞嗘煡璇㈡垨APP璇锋眰锛屽垱寤烘柊鐢ㄦ埛
+// 3. 鍏朵粬鎯呭喌杩斿洖鏈櫥褰曢敊璇?
+func (l *QueryServiceLogic) GetOrCreateUser() (string, error) {
+ claims, err := ctxdata.GetClaimsFromCtx(l.ctx)
+ if err == nil && claims != nil {
+ return claims.UserId, nil
+ }
+ userID, regErr := l.svcCtx.UserService.RegisterUUIDUser(l.ctx)
+ if regErr != nil {
+ return "", regErr
+ }
+ return userID, nil
}
diff --git a/app/main/api/internal/logic/user/authlogic.go b/app/main/api/internal/logic/user/authlogic.go
index d67113e..02753ff 100644
--- a/app/main/api/internal/logic/user/authlogic.go
+++ b/app/main/api/internal/logic/user/authlogic.go
@@ -23,7 +23,7 @@ func NewAuthLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AuthLogic {
}
func (l *AuthLogic) Auth(req *types.AuthReq) (*types.AuthResp, error) {
- var userID string
+ var userID string
var userType int64
var authType string
var authKey string
@@ -88,7 +88,7 @@ func (l *AuthLogic) Auth(req *types.AuthReq) (*types.AuthResp, error) {
return nil, errors.Wrapf(xerr.NewErrMsg("不支持的平台类型"), "platform=%s", req.Platform)
}
- token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, userType)
+ token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成Token失败: %v", err)
}
diff --git a/app/main/api/internal/logic/user/bindmobilelogic.go b/app/main/api/internal/logic/user/bindmobilelogic.go
index 9d34f41..5fdb0b7 100644
--- a/app/main/api/internal/logic/user/bindmobilelogic.go
+++ b/app/main/api/internal/logic/user/bindmobilelogic.go
@@ -90,8 +90,8 @@ func (l *BindMobileLogic) BindMobile(req *types.BindMobileReq) (resp *types.Bind
if _, err := l.svcCtx.UserAuthModel.Insert(l.ctx, nil, &model.UserAuth{Id: uuid.NewString(), UserId: finalUserID, AuthType: model.UserAuthTypeMobile, AuthKey: encryptedMobile}); err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建手机号认证失败: %v", err)
}
- // 发放正式用户token
- token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, finalUserID, model.UserTypeNormal)
+ // 发放token(userType会根据mobile字段动态计算)
+ token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, finalUserID)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成Token失败: %v", err)
}
@@ -118,7 +118,7 @@ func (l *BindMobileLogic) BindMobile(req *types.BindMobileReq) (resp *types.Bind
}
// 如果当前认证已属于目标手机号用户,直接发放token(无需合并)
if existingAuth != nil && existingAuth.UserId == finalUserID {
- token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, finalUserID, model.UserTypeNormal)
+ token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, finalUserID)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成Token失败: %v", err)
}
@@ -232,8 +232,8 @@ func (l *BindMobileLogic) BindMobile(req *types.BindMobileReq) (resp *types.Bind
return nil, err
}
- // 合并完成后生成并返回正式用户token
- token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, finalUserID, model.UserTypeNormal)
+ // 合并完成后生成token(userType会根据mobile字段动态计算)
+ token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, finalUserID)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成Token失败: %v", err)
}
diff --git a/app/main/api/internal/logic/user/detaillogic.go b/app/main/api/internal/logic/user/detaillogic.go
index c3d71b3..bbcab09 100644
--- a/app/main/api/internal/logic/user/detaillogic.go
+++ b/app/main/api/internal/logic/user/detaillogic.go
@@ -36,17 +36,8 @@ func (l *DetailLogic) Detail() (resp *types.UserInfoResp, err error) {
}
userID := claims.UserId
- userType := claims.UserType
- if userType != model.UserTypeNormal {
- return &types.UserInfoResp{
- UserInfo: types.User{
- Id: userID,
- UserType: userType,
- Mobile: "",
- NickName: "",
- },
- }, nil
- }
+
+ // 无论是临时用户还是正常用户,都需要从数据库中查询用户信息
user, err := l.svcCtx.UserModel.FindOne(l.ctx, userID)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
@@ -54,6 +45,7 @@ func (l *DetailLogic) Detail() (resp *types.UserInfoResp, err error) {
}
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "用户信息, 数据库查询用户信息失败, %v", err)
}
+
var userInfo types.User
err = copier.Copy(&userInfo, user)
if err != nil {
diff --git a/app/main/api/internal/logic/user/gettokenlogic.go b/app/main/api/internal/logic/user/gettokenlogic.go
index 95146a8..be9bdb9 100644
--- a/app/main/api/internal/logic/user/gettokenlogic.go
+++ b/app/main/api/internal/logic/user/gettokenlogic.go
@@ -2,9 +2,9 @@ package user
import (
"context"
+ "time"
"ycc-server/common/ctxdata"
"ycc-server/common/xerr"
- "time"
"github.com/pkg/errors"
@@ -33,7 +33,7 @@ func (l *GetTokenLogic) GetToken() (resp *types.MobileCodeLoginResp, err error)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "用户信息, %v", err)
}
- token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, claims.UserId, claims.UserType)
+ token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, claims.UserId)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "用户信息, %v", err)
}
diff --git a/app/main/api/internal/logic/user/mobilecodeloginlogic.go b/app/main/api/internal/logic/user/mobilecodeloginlogic.go
index 4701dff..7b78b0b 100644
--- a/app/main/api/internal/logic/user/mobilecodeloginlogic.go
+++ b/app/main/api/internal/logic/user/mobilecodeloginlogic.go
@@ -62,7 +62,7 @@ func (l *MobileCodeLoginLogic) MobileCodeLogin(req *types.MobileCodeLoginReq) (r
return nil, errors.Wrapf(xerr.NewErrMsg("用户不存在"), "手机登录, 用户不存在: %s", encryptedMobile)
}
userID = user.Id
- token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal)
+ token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "手机登录, 生成token失败 : %s", userID)
}
diff --git a/app/main/api/internal/logic/user/wxh5authlogic.go b/app/main/api/internal/logic/user/wxh5authlogic.go
index 417bc1e..66da8f0 100644
--- a/app/main/api/internal/logic/user/wxh5authlogic.go
+++ b/app/main/api/internal/logic/user/wxh5authlogic.go
@@ -1,22 +1,22 @@
package user
import (
- "context"
- "ycc-server/app/main/model"
- "ycc-server/common/xerr"
- "encoding/json"
- "fmt"
- "io"
- "net/http"
- "time"
+ "context"
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+ "time"
+ "ycc-server/app/main/model"
+ "ycc-server/common/xerr"
- "github.com/google/uuid"
- "github.com/pkg/errors"
+ "github.com/google/uuid"
+ "github.com/pkg/errors"
- "ycc-server/app/main/api/internal/svc"
- "ycc-server/app/main/api/internal/types"
+ "ycc-server/app/main/api/internal/svc"
+ "ycc-server/app/main/api/internal/types"
- "github.com/zeromicro/go-zero/core/logx"
+ "github.com/zeromicro/go-zero/core/logx"
)
type WxH5AuthLogic struct {
@@ -47,29 +47,28 @@ func (l *WxH5AuthLogic) WxH5Auth(req *types.WXH5AuthReq) (resp *types.WXH5AuthRe
}
// Step 3: 处理用户信息
- var userID string
- var userType int64
- if userAuth != nil {
- // 已存在用户,直接登录
- userID = userAuth.UserId
- userType = model.UserTypeNormal
- } else {
- user := &model.User{Id: uuid.NewString()}
- _, err := l.svcCtx.UserModel.Insert(l.ctx, nil, user)
- if err != nil {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建用户失败: %v", err)
- }
- ua := &model.UserAuth{Id: uuid.NewString(), UserId: user.Id, AuthType: model.UserAuthTypeWxh5OpenID, AuthKey: accessTokenResp.Openid}
- _, err = l.svcCtx.UserAuthModel.Insert(l.ctx, nil, ua)
- if err != nil {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建用户授权失败: %v", err)
- }
- userID = user.Id
- userType = model.UserTypeTemp
- }
+ var userID string
+ if userAuth != nil {
+ // 已存在用户,直接登录
+ userID = userAuth.UserId
+ } else {
+ // 新用户创建为临时用户(没有mobile)
+ user := &model.User{Id: uuid.NewString()}
+ _, err := l.svcCtx.UserModel.Insert(l.ctx, nil, user)
+ if err != nil {
+ return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建用户失败: %v", err)
+ }
+ ua := &model.UserAuth{Id: uuid.NewString(), UserId: user.Id, AuthType: model.UserAuthTypeWxh5OpenID, AuthKey: accessTokenResp.Openid}
+ _, err = l.svcCtx.UserAuthModel.Insert(l.ctx, nil, ua)
+ if err != nil {
+ return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建用户授权失败: %v", err)
+ }
+ userID = user.Id
+ l.Infof("Created new weixin user: userID=%s, openid=%s", userID, accessTokenResp.Openid)
+ }
- // Step 4: 生成JWT Token
- token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, userType)
+ // Step 4: 生成JWT Token(动态计算userType)
+ token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成JWT token失败: %v", err)
}
diff --git a/app/main/api/internal/logic/user/wxminiauthlogic.go b/app/main/api/internal/logic/user/wxminiauthlogic.go
index d94842a..89f68d5 100644
--- a/app/main/api/internal/logic/user/wxminiauthlogic.go
+++ b/app/main/api/internal/logic/user/wxminiauthlogic.go
@@ -1,21 +1,21 @@
package user
import (
- "context"
- "encoding/json"
- "fmt"
- "io"
- "net/http"
- "time"
+ "context"
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+ "time"
- "ycc-server/app/main/api/internal/svc"
- "ycc-server/app/main/api/internal/types"
- "ycc-server/app/main/model"
- "ycc-server/common/xerr"
+ "ycc-server/app/main/api/internal/svc"
+ "ycc-server/app/main/api/internal/types"
+ "ycc-server/app/main/model"
+ "ycc-server/common/xerr"
- "github.com/google/uuid"
- "github.com/pkg/errors"
- "github.com/zeromicro/go-zero/core/logx"
+ "github.com/google/uuid"
+ "github.com/pkg/errors"
+ "github.com/zeromicro/go-zero/core/logx"
)
type WxMiniAuthLogic struct {
@@ -46,29 +46,27 @@ func (l *WxMiniAuthLogic) WxMiniAuth(req *types.WXMiniAuthReq) (resp *types.WXMi
}
// 3. 处理用户信息
- var userID string
- var userType int64
- if userAuth != nil {
- // 已存在用户,直接登录
- userID = userAuth.UserId
- userType = model.UserTypeNormal
- } else {
- user := &model.User{Id: uuid.NewString()}
- _, err := l.svcCtx.UserModel.Insert(l.ctx, nil, user)
- if err != nil {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建用户失败: %v", err)
- }
- ua := &model.UserAuth{Id: uuid.NewString(), UserId: user.Id, AuthType: model.UserAuthTypeWxMiniOpenID, AuthKey: sessionKeyResp.Openid}
- _, err = l.svcCtx.UserAuthModel.Insert(l.ctx, nil, ua)
- if err != nil {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建用户授权失败: %v", err)
- }
- userID = user.Id
- userType = model.UserTypeTemp
- }
+ var userID string
+ if userAuth != nil {
+ // 已存在用户,直接登录
+ userID = userAuth.UserId
+ } else {
+ // 新用户创建为临时用户(没有mobile)
+ user := &model.User{Id: uuid.NewString()}
+ _, err := l.svcCtx.UserModel.Insert(l.ctx, nil, user)
+ if err != nil {
+ return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建用户失败: %v", err)
+ }
+ ua := &model.UserAuth{Id: uuid.NewString(), UserId: user.Id, AuthType: model.UserAuthTypeWxMiniOpenID, AuthKey: sessionKeyResp.Openid}
+ _, err = l.svcCtx.UserAuthModel.Insert(l.ctx, nil, ua)
+ if err != nil {
+ return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建用户授权失败: %v", err)
+ }
+ userID = user.Id
+ }
- // 4. 生成JWT Token
- token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, userType)
+ // 4. 生成JWT Token(动态计算userType)
+ token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成JWT Token失败: %v", err)
}
diff --git a/app/main/api/internal/middleware/userauthinterceptormiddleware.go b/app/main/api/internal/middleware/userauthinterceptormiddleware.go
index 0f05d1c..730f1ab 100644
--- a/app/main/api/internal/middleware/userauthinterceptormiddleware.go
+++ b/app/main/api/internal/middleware/userauthinterceptormiddleware.go
@@ -1,10 +1,10 @@
package middleware
import (
+ "net/http"
"ycc-server/app/main/model"
"ycc-server/common/ctxdata"
"ycc-server/common/xerr"
- "net/http"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/rest/httpx"
@@ -24,8 +24,10 @@ func (m *UserAuthInterceptorMiddleware) Handle(next http.HandlerFunc) http.Handl
httpx.Error(w, errors.Wrapf(xerr.NewErrCode(ErrCodeUnauthorized), "token解析失败: %v", err))
return
}
+ // 检查用户是否绑定了mobile(没有mobile表示是临时用户,不允许访问需要认证的接口)
+ // 注:临时用户现在基于 mobile 字段判断,而不是 UserType
if claims.UserType == model.UserTypeTemp {
- httpx.Error(w, errors.Wrapf(xerr.NewErrCode(xerr.USER_NEED_BIND_MOBILE), "token解析失败: %v", err))
+ httpx.Error(w, errors.Wrapf(xerr.NewErrCode(xerr.USER_NEED_BIND_MOBILE), "请先绑定手机号: %v", err))
return
}
next(w, r)
diff --git a/app/main/api/internal/service/userService.go b/app/main/api/internal/service/userService.go
index fb37bbd..8003657 100644
--- a/app/main/api/internal/service/userService.go
+++ b/app/main/api/internal/service/userService.go
@@ -63,17 +63,38 @@ func (s *UserService) RegisterUUIDUser(ctx context.Context) (string, error) {
return userId, nil
}
-// GeneralUserToken 生成用户token
-func (s *UserService) GeneralUserToken(ctx context.Context, userID string, userType int64) (string, error) {
+// GetUserType 根据user.Mobile字段动态计算用户类型
+// 如果有mobile,则为正式用户(UserTypeNormal),否则为临时用户(UserTypeTemp)
+func (s *UserService) GetUserType(ctx context.Context, userID string) (int64, error) {
+ user, err := s.userModel.FindOne(ctx, userID)
+ if err != nil {
+ return 0, err
+ }
+ if user.Mobile.Valid && user.Mobile.String != "" {
+ return model.UserTypeNormal, nil
+ }
+ return model.UserTypeTemp, nil
+}
+
+// GeneralUserToken 生成用户token(动态计算userType)
+func (s *UserService) GeneralUserToken(ctx context.Context, userID string) (string, error) {
platform, err := ctxdata.GetPlatformFromCtx(ctx)
if err != nil {
return "", err
}
+ // 动态计算userType
+ userType, err := s.GetUserType(ctx, userID)
+ if err != nil {
+ return "", errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取用户信息失败: %v", err)
+ }
+
var isAgent int64
var agentID string
var authType string
var authKey string
+
+ // 只有正式用户(有mobile)才可能是代理
if userType == model.UserTypeNormal {
agent, err := s.agentModel.FindOneByUserId(ctx, userID)
if err != nil && !errors.Is(err, model.ErrNotFound) {
@@ -89,6 +110,7 @@ func (s *UserService) GeneralUserToken(ctx context.Context, userID string, userT
authKey = userAuth.AuthKey
}
} else {
+ // 临时用户获取其他平台的auth信息
platAuthType := s.getAuthTypeByPlatform(platform)
ua, err := s.userAuthModel.FindOneByUserIdAuthType(ctx, userID, platAuthType)
if err == nil && ua != nil {
@@ -159,12 +181,16 @@ func (s *UserService) RegisterUser(ctx context.Context, mobile string) (string,
return userId, nil
}
- // 双重判断是否已经注册
- if claims.UserType == model.UserTypeNormal {
+ // 双重判断是否已经注册(根据mobile判断,而不是userType)
+ currentUser, err := s.userModel.FindOne(ctx, claims.UserId)
+ if err != nil && !errors.Is(err, model.ErrNotFound) {
+ return "", err
+ }
+ if currentUser != nil && currentUser.Mobile.Valid && currentUser.Mobile.String != "" {
return "", errors.New("用户已注册")
}
var userId string
- // 临时转正式注册
+ // 临时用户绑定mobile转正式注册
err = s.userModel.Trans(ctx, func(ctx context.Context, session sqlx.Session) error {
user := &model.User{Id: uuid.NewString(), Mobile: sql.NullString{String: mobile, Valid: true}}
if _, userInsertErr := s.userModel.Insert(ctx, session, user); userInsertErr != nil {
@@ -187,17 +213,26 @@ func (s *UserService) RegisterUser(ctx context.Context, mobile string) (string,
return userId, nil
}
-// TempUserBindUser 临时用户绑定用户
+// TempUserBindUser 临时用户绑定用户(添加mobile使其变为正式用户)
func (s *UserService) TempUserBindUser(ctx context.Context, session sqlx.Session, normalUserID string) error {
claims, err := ctxdata.GetClaimsFromCtx(ctx)
if err != nil && !errors.Is(err, ctxdata.ErrNoInCtx) {
return err
}
- if claims == nil || claims.UserType != model.UserTypeTemp {
+ if claims == nil {
return errors.New("无临时用户")
}
+ // 检查当前用户是否已经绑定了mobile(根据mobile判断,而不是userType)
+ tempUser, err := s.userModel.FindOne(ctx, claims.UserId)
+ if err != nil && !errors.Is(err, model.ErrNotFound) {
+ return err
+ }
+ if tempUser != nil && tempUser.Mobile.Valid && tempUser.Mobile.String != "" {
+ return errors.New("临时用户已注册")
+ }
+
existingAuth, err := s.userAuthModel.FindOneByAuthTypeAuthKey(ctx, claims.AuthType, claims.AuthKey)
if err != nil && !errors.Is(err, model.ErrNotFound) {
return err
diff --git a/app/main/api/main.go b/app/main/api/main.go
index 3d993ec..4c17ca1 100644
--- a/app/main/api/main.go
+++ b/app/main/api/main.go
@@ -61,7 +61,7 @@ func main() {
defer server.Stop()
handler.RegisterHandlers(server, svcContext)
-
+
// 自动注册API到数据库
apiRegistry := service.NewApiRegistryService(svcContext.AdminApiModel)
routes := server.Routes()
@@ -70,7 +70,7 @@ func main() {
} else {
logx.Infof("API注册成功,共注册 %d 个路由", len(routes))
}
-
+
fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
server.Start()
}
diff --git a/distributeNormalAgentBonus配置项说明.md b/distributeNormalAgentBonus配置项说明.md
deleted file mode 100644
index c7beaa0..0000000
--- a/distributeNormalAgentBonus配置项说明.md
+++ /dev/null
@@ -1,101 +0,0 @@
-# distributeNormalAgentBonus 函数使用的配置项说明
-
-## 函数位置
-`ycc-proxy-server/app/main/api/internal/service/agentService.go:254-478`
-
-## 使用的配置项列表
-
-### 1. `direct_parent_amount_normal`
-- **配置键**:`direct_parent_amount_normal`
-- **类型**:浮点数(float64)
-- **默认值**:2.0元
-- **使用位置**:第376行
-- **用途**:普通代理给直接上级普通代理的返佣金额
-- **使用场景**:
- - 当直接上级是普通代理时使用
- - 无论后续层级有多少普通代理,这部分金额只给推广人的直接上级
-- **示例**:等级加成6元,给直接上级普通代理2元
-
-### 2. `direct_parent_amount_gold`
-- **配置键**:`direct_parent_amount_gold`
-- **类型**:浮点数(float64)
-- **默认值**:3.0元
-- **使用位置**:第326行
-- **用途**:普通代理给直接上级黄金代理的返佣金额
-- **使用场景**:
- - 当直接上级是黄金代理时使用
- - 这部分金额给直接上级黄金代理,剩余部分继续向上分配给钻石上级
-- **示例**:等级加成6元,给直接上级黄金代理3元,剩余3元给钻石上级
-
-### 3. `max_gold_rebate_amount`
-- **配置键**:`max_gold_rebate_amount`
-- **类型**:浮点数(float64)
-- **默认值**:3.0元
-- **使用位置**:第492行
-- **用途**:普通代理给黄金上级的最大返佣金额
-- **使用场景**:
- - 当直接上级是普通代理,且只有黄金上级(没有钻石上级)时使用
- - 即使剩余金额超过这个值,也只能给黄金上级最多这个金额,超出部分归平台
-- **示例**:剩余4元,最大限额3元,则给黄金3元,剩余1元归平台
-
-### 4. `level_2_bonus`
-- **配置键**:`level_2_bonus`
-- **类型**:整数(int64),从配置读取时转换为浮点数计算
-- **默认值**:3元
-- **使用位置**:第438行(通过 `getLevelBonus(ctx, 2)` 调用)
-- **用途**:黄金代理的等级加成
-- **使用场景**:
- - 用于计算等级加成的差(普通等级加成 - 黄金等级加成)
- - 当直接上级是普通代理,且上级链中有黄金和钻石时,用于计算给黄金的返佣金额
-- **计算逻辑**:
- - 等级加成差 = 普通等级加成(6元)- 黄金等级加成(3元)= 3元
- - 给黄金的金额 = 等级加成差(3元)- 已给普通的部分(2元)= 1元
-
-### 5. `level_1_bonus`
-- **配置键**:`level_1_bonus`
-- **类型**:整数(int64)
-- **默认值**:6元
-- **使用位置**:通过 `amount` 参数传入(在 `AgentProcess` 中通过 `getLevelBonus(ctx, 1)` 获取)
-- **用途**:普通代理的等级加成
-- **说明**:
- - 不在 `distributeNormalAgentBonus` 函数中直接读取
- - 作为 `amount` 参数传入函数
- - 这是需要分配的总额
-
-## 配置项使用总结表
-
-| 配置键 | 默认值 | 使用场景 | 代码行号 |
-|--------|--------|---------|---------|
-| `direct_parent_amount_normal` | 2.0元 | 直接上级是普通代理时,给直接上级的返佣金额 | 376 |
-| `direct_parent_amount_gold` | 3.0元 | 直接上级是黄金代理时,给直接上级的返佣金额 | 326 |
-| `max_gold_rebate_amount` | 3.0元 | 只有黄金上级(无钻石)时,给黄金的最大返佣金额 | 492 |
-| `level_2_bonus` | 3元 | 黄金代理等级加成,用于计算等级加成差 | 438 |
-| `level_1_bonus` | 6元 | 普通代理等级加成,通过参数传入 | - |
-
-## 配置项在代码中的调用关系
-
-```
-AgentProcess (处理订单)
- ↓
- 调用 getLevelBonus(ctx, agent.Level) 获取普通代理等级加成
- ↓
- 传入 distributeNormalAgentBonus(amount=6元, ...)
- ↓
- 在函数内使用:
- 1. getRebateConfigFloat("direct_parent_amount_normal", 2.0) # 第376行
- 2. getRebateConfigFloat("direct_parent_amount_gold", 3.0) # 第326行
- 3. getRebateConfigFloat("max_gold_rebate_amount", 3.0) # 第492行
- 4. getLevelBonus(ctx, 2) -> 读取 level_2_bonus # 第438行
-```
-
-## 注意事项
-
-1. **所有配置项都支持动态配置**:如果配置不存在,会使用默认值
-2. **配置读取失败会记录日志**:确保配置问题时可以追踪
-3. **配置值的类型转换**:
- - 返佣配置直接使用浮点数(支持小数)
- - 等级加成从配置读取时为整数,计算时转换为浮点数
-4. **配置项命名规范**:
- - 返佣配置使用描述性名称:`direct_parent_amount_normal`、`direct_parent_amount_gold`、`max_gold_rebate_amount`
- - 等级加成配置使用数字后缀:`level_1_bonus`、`level_2_bonus`
-
diff --git a/代理系统测试用例清单.md b/代理系统测试用例清单.md
deleted file mode 100644
index fb0890c..0000000
--- a/代理系统测试用例清单.md
+++ /dev/null
@@ -1,448 +0,0 @@
-# 代理系统测试用例清单
-
-## 一、邀请下级代理测试
-
-### 1.1 钻石代理邀请下级
-- [ ] **钻石邀请普通代理**
- - 预期:建立上下级关系,普通代理的 team_leader_id 指向钻石代理
- - 验证:关系类型为直接关系,团队首领正确
-
-- [ ] **钻石邀请黄金代理**
- - 预期:建立上下级关系,黄金代理的 team_leader_id 指向钻石代理
- - 验证:关系类型为直接关系,团队首领正确
-
-- [ ] **钻石邀请钻石代理(不允许)**
- - 预期:拒绝,提示"代理等级不能高于上级代理"
- - 验证:邀请失败,无关系建立
-
-### 1.2 黄金代理邀请下级
-- [ ] **黄金邀请普通代理**
- - 预期:建立上下级关系,普通代理的 team_leader_id 指向上级钻石代理
- - 验证:关系类型为直接关系,团队首领正确(向上查找钻石)
-
-- [ ] **黄金邀请黄金代理(不允许)**
- - 预期:拒绝,提示"代理等级不能高于上级代理"
- - 验证:邀请失败,无关系建立
-
-- [ ] **黄金邀请钻石代理(不允许)**
- - 预期:拒绝,提示"代理等级不能高于上级代理"
- - 验证:邀请失败,无关系建立
-
-### 1.3 普通代理邀请下级
-- [ ] **普通邀请普通代理**
- - 预期:建立上下级关系,普通代理的 team_leader_id 向上查找钻石/黄金
- - 验证:关系类型为直接关系,团队首领正确
-
-- [ ] **普通邀请黄金代理(不允许)**
- - 预期:拒绝,提示"代理等级不能高于上级代理"
- - 验证:邀请失败,无关系建立
-
-- [ ] **普通邀请钻石代理(不允许)**
- - 预期:拒绝,提示"代理等级不能高于上级代理"
- - 验证:邀请失败,无关系建立
-
-### 1.4 多层级邀请测试
-- [ ] **钻石 → 黄金 → 普通(3层)**
- - 预期:3层关系链,所有代理的 team_leader_id 指向钻石
- - 验证:关系链正确,团队首领一致
-
-- [ ] **钻石 → 普通 → 普通 → 普通(4层)**
- - 预期:4层关系链,所有代理的 team_leader_id 指向钻石
- - 验证:关系链正确,团队首领一致
-
-- [ ] **钻石 → 普通 → 普通 → 普通 → 普通(5层)**
- - 预期:5层关系链,所有代理的 team_leader_id 指向钻石
- - 验证:关系链正确,团队首领一致
-
----
-
-## 二、推广报告收益测试(等级加成返佣)
-
-### 2.1 钻石代理推广报告
-- [ ] **钻石代理自己推广的报告**
- - 等级加成:0元
- - 预期:无等级加成返佣,全部收益归自己
- - 验证:agent_rebate 表无记录,代理收益 = 设定价格 - 基础底价 - 提价成本
-
-### 2.2 黄金代理推广报告
-- [ ] **黄金代理(上级是钻石)推广报告**
- - 等级加成:3元
- - 预期:3元全部返佣给钻石上级
- - 验证:agent_rebate 表记录正确,钻石上级钱包增加3元
-
-- [ ] **黄金代理(无上级/上级不是钻石)推广报告**
- - 等级加成:3元
- - 预期:返佣归平台(异常情况)
- - 验证:agent_rebate 表无记录
-
-### 2.3 普通代理推广报告(等级加成6元)
-
-#### 2.3.1 直接上级是钻石
-- [ ] **普通代理(上级是钻石)推广报告**
- - 等级加成:6元
- - 预期:6元全部返佣给钻石上级
- - 验证:agent_rebate 表记录正确,钻石上级钱包增加6元
-
-#### 2.3.2 直接上级是黄金
-- [ ] **普通代理(上级是黄金,黄金上级是钻石)推广报告**
- - 等级加成:6元
- - 预期:
- - 3元给黄金上级(配置:normal_to_gold_rebate)
- - 3元给钻石上级(上上级)
- - 验证:agent_rebate 表有2条记录,金额分配正确
-
-- [ ] **普通代理(上级是黄金,无钻石上级)推广报告**
- - 等级加成:6元
- - 预期:3元给黄金上级,剩余3元归平台
- - 验证:agent_rebate 表只有1条记录(3元给黄金),剩余归平台
-
-#### 2.3.3 直接上级是普通(多层普通代理)
-- [ ] **普通 → 普通 → 钻石(3层)**
- - 等级加成:6元
- - 预期:
- - 2元给直接上级普通(配置:normal_to_normal_rebate)
- - 4元给钻石上级(跳过中间普通,直接给钻石)
- - 验证:agent_rebate 表有2条记录,金额分配正确
-
-- [ ] **普通 → 普通 → 普通 → 钻石(4层)**
- - 等级加成:6元
- - 预期:
- - 2元给直接上级普通
- - 4元给钻石上级(跳过中间所有普通代理)
- - 验证:agent_rebate 表有2条记录,金额分配正确
-
-- [ ] **普通 → 普通 → 黄金(无钻石,3层)**
- - 等级加成:6元
- - 预期:
- - 2元给直接上级普通
- - 3元给黄金上级(配置:normal_to_gold_rebate_max)
- - 1元归平台(超出部分)
- - 验证:agent_rebate 表有2条记录,金额分配正确,剩余归平台
-
-- [ ] **普通 → 普通 → 普通(全部是普通,无钻石/黄金)**
- - 等级加成:6元
- - 预期:
- - 2元给直接上级普通
- - 4元归平台(无钻石/黄金上级)
- - 验证:agent_rebate 表只有1条记录(2元),剩余归平台
-
----
-
-## 三、升级代理收益测试
-
-### 3.1 自主付费升级
-
-#### 3.1.1 普通 → 黄金(199元)
-- [ ] **普通代理升级为黄金(上级是钻石)**
- - 升级费用:199元
- - 返佣:139元给原直接上级
- - 预期:
- - 原直接上级(钻石)钱包增加139元
- - 升级后不脱离关系(钻石 > 黄金)
- - 仍属于原团队
- - 验证:升级成功,返佣记录在 agent_upgrade 表,钱包余额正确
-
-- [ ] **普通代理升级为黄金(上级是黄金)**
- - 升级费用:199元
- - 返佣:139元给原直接上级(黄金)
- - 预期:
- - 原直接上级(黄金)钱包增加139元
- - 升级后脱离关系(同级不能作为上下级)
- - 保留团队关系(向上查找钻石)
- - 验证:升级成功,关系脱离,团队首领正确
-
-- [ ] **普通代理升级为黄金(上级是普通)**
- - 升级费用:199元
- - 返佣:139元给原直接上级(普通)
- - 预期:
- - 原直接上级(普通)钱包增加139元
- - 升级后脱离关系(下级等级高于上级)
- - 保留团队关系(向上查找钻石/黄金)
- - 验证:升级成功,关系脱离,团队首领正确
-
-#### 3.1.2 普通 → 钻石(980元)
-- [ ] **普通代理升级为钻石(上级是钻石)**
- - 升级费用:980元
- - 返佣:680元给原直接上级(钻石)
- - 预期:
- - 原直接上级(钻石)钱包增加680元
- - 升级后脱离关系(同级不能作为上下级)
- - 独立成为新团队,team_leader_id = 自己
- - 所有下级跟随到新团队
- - 验证:升级成功,独立成团队,下级团队首领更新
-
-- [ ] **普通代理升级为钻石(上级是黄金)**
- - 升级费用:980元
- - 返佣:680元给原直接上级(黄金)
- - 预期:
- - 原直接上级(黄金)钱包增加680元
- - 升级后脱离关系(下级等级高于上级)
- - 独立成为新团队,team_leader_id = 自己
- - 所有下级跟随到新团队
- - 验证:升级成功,独立成团队,下级团队首领更新
-
-- [ ] **普通代理升级为钻石(上级是普通)**
- - 升级费用:980元
- - 返佣:680元给原直接上级(普通)
- - 预期:
- - 原直接上级(普通)钱包增加680元
- - 升级后脱离关系(下级等级高于上级)
- - 独立成为新团队,team_leader_id = 自己
- - 所有下级跟随到新团队
- - 验证:升级成功,独立成团队,下级团队首领更新
-
-#### 3.1.3 黄金 → 钻石(980元)
-- [ ] **黄金代理升级为钻石(上级是钻石)**
- - 升级费用:980元
- - 返佣:680元给原直接上级(钻石)
- - 预期:
- - 原直接上级(钻石)钱包增加680元
- - 升级后脱离关系(同级不能作为上下级)
- - 独立成为新团队,team_leader_id = 自己
- - 所有下级跟随到新团队
- - 验证:升级成功,独立成团队,下级团队首领更新
-
-- [ ] **黄金代理升级为钻石(无上级)**
- - 升级费用:980元
- - 返佣:无
- - 预期:
- - 独立成为新团队,team_leader_id = 自己
- - 所有下级跟随到新团队
- - 验证:升级成功,独立成团队,下级团队首领更新
-
-### 3.2 钻石升级下级(免费)
-- [ ] **钻石升级下级(普通 → 黄金)**
- - 升级费用:免费
- - 返佣:无
- - 预期:
- - 被升级代理无需付费
- - 升级后根据原上级等级决定是否脱离关系
- - 保留团队关系
- - 验证:升级成功,费用为0,关系处理正确
-
----
-
-## 四、升级后团队转移测试
-
-### 4.1 普通 → 黄金升级(保留团队)
-
-#### 4.1.1 上级是钻石(不脱离)
-- [ ] **普通 → 黄金(上级是钻石),有下级**
- - 预期:
- - 不脱离关系
- - 保留原团队(team_leader_id 不变)
- - 所有下级(直接+间接)的 team_leader_id 不变
- - 验证:关系保留,所有下级团队首领不变
-
-#### 4.1.2 上级是黄金(脱离关系)
-- [ ] **普通 → 黄金(上级是黄金),有下级**
- - 预期:
- - 脱离直接上下级关系
- - 保留团队关系(向上查找钻石)
- - 所有下级(直接+间接)的 team_leader_id 不变
- - 验证:关系脱离(RelationType=2),所有下级团队首领不变
-
-#### 4.1.3 上级是普通(脱离关系)
-- [ ] **普通 → 黄金(上级是普通),有下级**
- - 预期:
- - 脱离直接上下级关系
- - 保留团队关系(向上查找钻石/黄金)
- - 所有下级(直接+间接)的 team_leader_id 不变
- - 验证:关系脱离,所有下级团队首领不变
-
-### 4.2 升级为钻石(独立成新团队)
-
-#### 4.2.1 普通 → 钻石
-- [ ] **普通 → 钻石(上级是钻石),有下级**
- - 预期:
- - 脱离关系
- - 独立成新团队(team_leader_id = 自己)
- - 所有直接下级的 team_leader_id 更新为自己
- - 所有间接下级的 team_leader_id 更新为自己(递归)
- - 验证:
- - 升级代理的 team_leader_id = 自己
- - 所有下级(直接+间接)的 team_leader_id = 升级代理ID
- - 下级数量统计正确
-
-- [ ] **普通 → 钻石(上级是黄金),有下级(2层)**
- - 预期:
- - 脱离关系
- - 独立成新团队
- - 直接下级跟随
- - 间接下级跟随
- - 验证:所有下级团队首领更新为新钻石
-
-- [ ] **普通 → 钻石(上级是普通),有下级(3层以上)**
- - 预期:
- - 脱离关系
- - 独立成新团队
- - 所有层级的下级都跟随
- - 验证:所有下级团队首领更新为新钻石
-
-#### 4.2.2 黄金 → 钻石
-- [ ] **黄金 → 钻石(上级是钻石),有下级**
- - 预期:
- - 脱离关系
- - 独立成新团队
- - 所有下级跟随
- - 验证:所有下级团队首领更新为新钻石
-
-### 4.3 复杂团队转移场景
-
-#### 4.3.1 多层级团队
-- [ ] **钻石A → 黄金B → 普通C → 普通D,B升级为钻石**
- - 预期:
- - B独立成新团队
- - C和D的 team_leader_id 更新为B
- - A的团队:只剩自己
- - B的团队:B、C、D
- - 验证:团队划分正确,关系链正确
-
-#### 4.3.2 跨团队转移
-- [ ] **钻石A → 普通B → 普通C,C升级为钻石**
- - 预期:
- - C独立成新团队
- - C无下级,团队只有C自己
- - A的团队:A、B
- - 验证:团队划分正确
-
-#### 4.3.3 深度层级转移
-- [ ] **钻石A → 普通B → 普通C → 普通D → 普通E,C升级为钻石**
- - 预期:
- - C独立成新团队
- - D和E的 team_leader_id 更新为C
- - A的团队:A、B
- - C的团队:C、D、E
- - 验证:团队划分正确,所有层级更新正确
-
----
-
-## 五、综合测试场景
-
-### 5.1 完整业务流程
-- [ ] **创建团队 → 邀请下级 → 推广报告 → 收益分配 → 升级 → 团队转移**
- - 步骤:
- 1. 创建钻石代理A
- 2. A邀请黄金代理B
- 3. B邀请普通代理C
- 4. C邀请普通代理D
- 5. D推广报告,验证收益分配
- 6. C升级为黄金,验证关系变化
- 7. B升级为钻石,验证团队转移
- - 验证:每个步骤的数据正确
-
-### 5.2 收益统计测试
-- [ ] **查询代理收益统计(包含佣金和返佣)**
- - 验证:agent_wallet 表的 Balance 和 TotalEarnings 正确
-
-- [ ] **查询下级列表和统计**
- - 验证:下级数量、团队规模统计正确
-
-### 5.3 边界情况测试
-- [ ] **钻石代理无下级时升级(边界情况)**
- - 验证:独立成团队,team_leader_id = 自己
-
-- [ ] **普通代理无上级时升级**
- - 验证:独立成团队,无返佣
-
-- [ ] **多层普通代理链,无钻石/黄金上级**
- - 验证:收益分配正确(部分归平台)
-
----
-
-## 六、数据验证检查点
-
-### 6.1 关系表验证
-- [ ] agent_relation 表的关系类型正确(1=直接关系,2=已脱离)
-- [ ] 脱离关系时,DetachReason 和 DetachTime 正确记录
-
-### 6.2 钱包验证
-- [ ] agent_wallet 表的 Balance(可用余额)正确
-- [ ] agent_wallet 表的 FrozenBalance(冻结余额)正确(如有)
-- [ ] agent_wallet 表的 TotalEarnings(累计收益)正确
-
-### 6.3 返佣记录验证
-- [ ] agent_rebate 表的记录完整(推广报告返佣)
-- [ ] agent_upgrade 表的返佣记录正确(升级返佣)
-- [ ] 返佣金额计算正确
-
-### 6.4 团队验证
-- [ ] agent 表的 team_leader_id 正确指向钻石代理
-- [ ] 升级后所有下级 team_leader_id 更新正确
-
-### 6.5 订单和佣金验证
-- [ ] agent_order 表记录完整
-- [ ] agent_commission 表记录完整
-- [ ] 佣金金额计算正确
-
----
-
-## 七、测试数据准备建议
-
-### 7.1 创建测试代理账号
-建议准备以下测试账号(可用不同手机号):
-
-1. **钻石代理**:
- - 钻石A(团队首领,无上级)
- - 钻石B(团队首领,无上级)
-
-2. **黄金代理**:
- - 黄金A(上级:钻石A)
- - 黄金B(上级:钻石A)
- - 黄金C(上级:钻石B)
-
-3. **普通代理**:
- - 普通A(上级:钻石A)
- - 普通B(上级:黄金A)
- - 普通C(上级:普通B)
- - 普通D(上级:普通C)
- - 普通E(上级:普通D)
-
-### 7.2 测试产品配置
-- [ ] 确保有测试产品配置(agent_product_config 表)
-- [ ] 配置基础底价、提价阈值、提价手续费比例
-
-### 7.3 测试返佣配置
-- [ ] normal_to_normal_rebate(默认2元)
-- [ ] normal_to_gold_rebate(默认3元)
-- [ ] normal_to_gold_rebate_max(默认3元)
-
----
-
-## 八、测试执行顺序建议
-
-1. **第一阶段**:基础功能测试
- - 邀请下级(各种组合)
- - 验证关系建立
- - 验证团队首领
-
-2. **第二阶段**:收益分配测试
- - 推广报告
- - 收益计算
- - 返佣分配
-
-3. **第三阶段**:升级功能测试
- - 自主付费升级
- - 钻石升级下级
- - 升级返佣
-
-4. **第四阶段**:团队转移测试
- - 普通→黄金升级(保留团队)
- - 升级为钻石(独立成团队)
- - 复杂场景测试
-
-5. **第五阶段**:综合测试
- - 完整业务流程
- - 边界情况
- - 数据一致性验证
-
----
-
-## 注意事项
-
-1. **开发环境**:测试时确保使用开发环境(ENV=development),可跳过验证码校验
-2. **数据清理**:每次测试后建议清理测试数据,避免相互影响
-3. **事务验证**:注意验证事务的一致性,确保要么全部成功,要么全部回滚
-4. **并发测试**:如有需要,可进行并发场景测试(多代理同时升级等)
-5. **日志记录**:测试过程中查看日志,确保业务流程正确
-
diff --git a/代理配置表分析和优化建议.md b/代理配置表分析和优化建议.md
deleted file mode 100644
index 168ba80..0000000
--- a/代理配置表分析和优化建议.md
+++ /dev/null
@@ -1,774 +0,0 @@
-# 代理配置表分析和优化建议
-
-## 一、当前配置表结构分析
-
-### 1.1 数据库表结构
-
-**表名**: `agent_config`
-
-```sql
-CREATE TABLE `agent_config` (
- `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
- `config_key` varchar(100) NOT NULL COMMENT '配置键(唯一)',
- `config_value` varchar(500) NOT NULL COMMENT '配置值',
- `config_type` varchar(50) NOT NULL COMMENT '配置类型:price=价格,bonus=等级加成,upgrade=升级费用,rebate=返佣,tax=税费',
- `description` varchar(500) DEFAULT NULL COMMENT '配置描述',
- `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
- `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- `delete_time` datetime DEFAULT NULL,
- `del_state` tinyint NOT NULL DEFAULT 0,
- `version` bigint NOT NULL DEFAULT 0,
- PRIMARY KEY (`id`),
- UNIQUE KEY `uk_config_key` (`config_key`),
- KEY `idx_config_type` (`config_type`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-```
-
-### 1.2 配置表设计评估
-
-**优点**:
-✅ **键值对存储灵活**:使用 `config_key` 和 `config_value` 的键值对模式,易于扩展新配置项
-✅ **类型分类清晰**:`config_type` 字段将配置分为 price、bonus、upgrade、rebate、tax 等类型,便于管理
-✅ **唯一索引合理**:`config_key` 的唯一索引确保配置键不重复
-✅ **版本控制**:包含 `version` 字段支持乐观锁,适合配置更新场景
-✅ **软删除支持**:支持软删除,保留历史配置记录
-
-**缺点/问题**:
-❌ **配置值类型限制**:`config_value` 为 `varchar(500)`,所有值都存储为字符串,需要在使用时转换
-❌ **缺少验证机制**:数据库层面无法验证配置值的格式和范围
-❌ **配置值长度限制**:对于复杂配置(如JSON对象),500字符可能不够
-
----
-
-## 二、当前配置项清单
-
-### 2.1 SQL初始化脚本中的配置项
-
-根据 `agent_system_migration.sql` 文件,当前初始化的配置项:
-
-| 配置键 | 配置值 | 类型 | 说明 |
-| ------------------------------- | ------- | ------- | ------------------------ |
-| `base_price` | 0.00 | price | 系统基础底价 |
-| `system_max_price` | 9999.99 | price | 系统价格上限 |
-| `price_threshold` | 0.00 | price | 提价标准阈值 |
-| `price_fee_rate` | 0.0000 | price | 提价手续费比例 |
-| `level_bonus_normal` | 6.00 | bonus | 普通代理等级加成 |
-| `level_bonus_gold` | 3.00 | bonus | 黄金代理等级加成 |
-| `level_bonus_diamond` | 0.00 | bonus | 钻石代理等级加成 |
-| `upgrade_fee_normal_to_gold` | 199.00 | upgrade | 普通→黄金升级费用 |
-| `upgrade_fee_to_diamond` | 980.00 | upgrade | 升级为钻石费用 |
-| `upgrade_rebate_normal_to_gold` | 139.00 | upgrade | 普通→黄金返佣金额 |
-| `upgrade_rebate_to_diamond` | 680.00 | upgrade | 升级为钻石返佣金额 |
-| `direct_parent_amount_diamond` | 6.00 | rebate | 直接上级是钻石的返佣金额 |
-| `direct_parent_amount_gold` | 3.00 | rebate | 直接上级是黄金的返佣金额 |
-| `direct_parent_amount_normal` | 2.00 | rebate | 直接上级是普通的返佣金额 |
-| `max_gold_rebate_amount` | 3.00 | rebate | 黄金代理最大返佣金额 |
-| `tax_rate` | 0.0600 | tax | 提现税率(6%) |
-
-**共15个配置项**
-
-### 2.2 代码中使用的配置键
-
-#### 2.2.1 AgentService 中使用的配置键
-
-- `base_price` - 基础底价 ✅
-- `price_threshold` - 提价标准阈值 ✅
-- `price_fee_rate` - 提价手续费比例 ✅
-- `level_bonus` - **硬编码,未从配置表读取** ❌
-
-#### 2.2.2 AdminGetAgentConfigLogic 中使用的配置键
-
-- `level_1_bonus` - 普通代理等级加成 ❌ **配置键不匹配**
-- `level_2_bonus` - 黄金代理等级加成 ❌ **配置键不匹配**
-- `level_3_bonus` - 钻石代理等级加成 ❌ **配置键不匹配**
-- `upgrade_to_gold_fee` - 升级为黄金费用 ❌ **配置键不匹配**
-- `upgrade_to_diamond_fee` - 升级为钻石费用 ❌ **配置键不匹配**
-- `upgrade_to_gold_rebate` - 升级为黄金返佣 ❌ **配置键不匹配**
-- `upgrade_to_diamond_rebate` - 升级为钻石返佣 ❌ **配置键不匹配**
-- `tax_rate` - 税率 ✅
-- `tax_exemption_amount` - 免税额度 ❌ **配置项缺失**
-
----
-
-## 三、发现的问题
-
-### 3.1 🔴 严重问题:价格配置不应在系统配置表中
-
-**问题描述**:
-价格相关配置(底价、上限、提价阈值、手续费比例)应该按产品配置,而不是全局系统配置。当前这些配置同时存在于 `agent_config` 和 `agent_product_config` 表中,但代码只从系统配置表读取,没有实现产品级别的配置覆盖。
-
-**问题影响**:
-- ❌ 不同产品无法设置不同的底价和价格策略
-- ❌ 代码中只读取系统配置,忽略了产品配置(`AgentService.AgentProcess`、`PaymentLogic`)
-- ❌ 产品配置表虽然存在,但在订单处理逻辑中未被使用
-
-**应该移除的系统配置项**:
-- `base_price` - 应该只在产品配置表中
-- `system_max_price` - 应该只在产品配置表中
-- `price_threshold` - 应该只在产品配置表中(可选)
-- `price_fee_rate` - 应该只在产品配置表中(可选)
-
-**正确的设计**:
-- ✅ 所有价格相关配置都应该在 `agent_product_config` 表中按产品配置
-- ✅ 系统配置表只保留全局配置(等级加成、升级费用、税费等)
-- ✅ 代码应该优先从产品配置读取,如果产品未配置,则使用默认值
-
-### 3.2 🔴 严重问题:配置键命名不一致
-
-**问题描述**:
-SQL初始化脚本和代码逻辑中使用的配置键命名不一致,导致配置无法正确读取。
-
-**具体情况**:
-
-| 配置项 | SQL初始化脚本 | 代码逻辑期望 | 状态 |
-| ---------------- | ------------------------------- | --------------------------- | -------- |
-| 普通代理等级加成 | `level_bonus_normal` | `level_1_bonus` | ❌ 不匹配 |
-| 黄金代理等级加成 | `level_bonus_gold` | `level_2_bonus` | ❌ 不匹配 |
-| 钻石代理等级加成 | `level_bonus_diamond` | `level_3_bonus` | ❌ 不匹配 |
-| 升级为黄金费用 | `upgrade_fee_normal_to_gold` | `upgrade_to_gold_fee` | ❌ 不匹配 |
-| 升级为钻石费用 | `upgrade_fee_to_diamond` | `upgrade_to_diamond_fee` | ❌ 不匹配 |
-| 升级为黄金返佣 | `upgrade_rebate_normal_to_gold` | `upgrade_to_gold_rebate` | ❌ 不匹配 |
-| 升级为钻石返佣 | `upgrade_rebate_to_diamond` | `upgrade_to_diamond_rebate` | ❌ 不匹配 |
-
-**影响**:
-- 后台管理系统无法正确读取和显示配置值
-- 配置更新可能无法正常工作
-
-### 3.3 🔴 严重问题:代码中硬编码等级加成
-
-**问题位置**:
-```go
-// agentService.go:144-155
-func (s *AgentService) getLevelBonus(level int64) int64 {
- switch level {
- case 1: // 普通
- return 6 // ❌ 硬编码,应该从配置表读取
- case 2: // 黄金
- return 3 // ❌ 硬编码,应该从配置表读取
- case 3: // 钻石
- return 0 // ❌ 硬编码,应该从配置表读取
- default:
- return 0
- }
-}
-```
-
-**问题影响**:
-- 等级加成值无法通过后台配置动态调整
-- 必须修改代码才能更改等级加成
-- 违反了配置化的设计原则
-
-### 3.4 🟡 中等问题:缺少配置项
-
-**缺失的配置项**:
-1. `tax_exemption_amount` - 免税额度(前端接口需要,但SQL初始化脚本中未包含)
-2. `upgrade_fee_gold_to_diamond` - 黄金→钻石升级费用(如果独立配置)
-
-### 3.5 🟡 中等问题:配置键命名规范不统一
-
-**当前问题**:
-- 部分使用下划线分隔:`level_bonus_normal`
-- 部分使用数字后缀:`level_1_bonus`
-- 部分使用驼峰式:`upgrade_fee_normal_to_gold`
-
-**建议**:统一命名规范
-
-### 3.6 🟢 轻微问题:配置值存储方式
-
-**当前方式**:所有配置值以字符串形式存储,需要在使用时转换
-- `strconv.ParseFloat()` 转换为浮点数
-- `strconv.ParseInt()` 转换为整数
-
-**影响**:每次读取都需要类型转换,性能影响较小,但容易出错
-
----
-
-## 四、配置使用情况分析
-
-### 4.1 实际使用的配置项
-
-**在业务逻辑中使用的配置**:
-1. ✅ `base_price` - 订单处理时使用
-2. ✅ `price_threshold` - 计算提价成本时使用
-3. ✅ `price_fee_rate` - 计算提价成本时使用
-4. ❌ `level_bonus_*` - **未使用,代码中硬编码**
-
-**在后台管理中使用的配置**:
-1. ❌ `level_1_bonus` - 读取配置(但键名不匹配)
-2. ❌ `level_2_bonus` - 读取配置(但键名不匹配)
-3. ❌ `level_3_bonus` - 读取配置(但键名不匹配)
-4. ❌ `upgrade_to_gold_fee` - 读取配置(但键名不匹配)
-5. ❌ `upgrade_to_diamond_fee` - 读取配置(但键名不匹配)
-6. ❌ `upgrade_to_gold_rebate` - 读取配置(但键名不匹配)
-7. ❌ `upgrade_to_diamond_rebate` - 读取配置(但键名不匹配)
-8. ✅ `tax_rate` - 读取配置
-9. ❌ `tax_exemption_amount` - 读取配置(但配置项缺失)
-
-### 4.2 未使用的配置项
-
-以下配置项在SQL中初始化了,但在代码中**似乎未使用**:
-- `direct_parent_amount_diamond` - 直接上级是钻石的返佣金额
-- `direct_parent_amount_gold` - 直接上级是黄金的返佣金额
-- `direct_parent_amount_normal` - 直接上级是普通的返佣金额
-- `max_gold_rebate_amount` - 黄金代理最大返佣金额
-
-**说明**:这些配置项可能被硬编码在 `distributeNormalAgentBonus` 等函数中,需要确认是否需要配置化。
-
----
-
-## 五、优化建议
-
-### 5.1 🔴 立即修复:移除系统配置表中的价格配置
-
-**问题**:
-价格相关配置(`base_price`、`system_max_price`、`price_threshold`、`price_fee_rate`)应该完全由产品配置表管理,系统配置表中不应该存在这些配置。
-
-**修复步骤**:
-
-1. **从系统配置表中删除价格相关配置**:
-
-```sql
--- 删除系统配置表中的价格相关配置
-DELETE FROM `agent_config` WHERE `config_key` IN (
- 'base_price',
- 'system_max_price',
- 'price_threshold',
- 'price_fee_rate'
-);
-```
-
-2. **修改订单处理逻辑,从产品配置表读取**:
-
-需要修改的文件:
-- `app/main/api/internal/service/agentService.go` - `AgentProcess` 方法
-- `app/main/api/internal/logic/pay/paymentlogic.go` - 创建订单时的价格计算
-
-**示例代码修改**(`agentService.go`):
-
-```go
-// AgentProcess 处理代理订单(新系统)
-func (s *AgentService) AgentProcess(ctx context.Context, order *model.Order) error {
- // ... 前面的代码不变 ...
-
- // 4. 获取产品配置(优先使用产品配置,如果不存在则使用默认值)
- productConfig, err := s.AgentProductConfigModel.FindOneByProductId(ctx, order.ProductId)
- if err != nil && !errors.Is(err, model.ErrNotFound) {
- return errors.Wrapf(err, "查询产品配置失败, productId: %d", order.ProductId)
- }
-
- // 使用产品配置的底价,如果产品未配置则使用默认值0
- basePrice := 0.0
- if productConfig != nil {
- basePrice = productConfig.BasePrice
- }
-
- // ... 使用basePrice计算实际底价 ...
-
- // 6.2 计算提价成本(使用产品配置)
- priceThreshold := 0.0
- priceFeeRate := 0.0
- if productConfig != nil {
- if productConfig.PriceThreshold.Valid {
- priceThreshold = productConfig.PriceThreshold.Float64
- }
- if productConfig.PriceFeeRate.Valid {
- priceFeeRate = productConfig.PriceFeeRate.Float64
- }
- }
- priceCost := s.calculatePriceCost(agentOrder.SetPrice, priceThreshold, priceFeeRate)
-
- // ... 后续代码 ...
-}
-```
-
-3. **更新后台管理系统**:
-
-- 移除系统配置页面中的价格相关配置项
-- 确保产品配置页面可以正确配置这些价格参数
-
-### 5.2 🔴 立即修复:统一配置键命名
-
-**方案一:修改SQL初始化脚本,使用代码期望的键名**
-
-```sql
--- 修改等级加成配置键
-UPDATE `agent_config` SET `config_key` = 'level_1_bonus' WHERE `config_key` = 'level_bonus_normal';
-UPDATE `agent_config` SET `config_key` = 'level_2_bonus' WHERE `config_key` = 'level_bonus_gold';
-UPDATE `agent_config` SET `config_key` = 'level_3_bonus' WHERE `config_key` = 'level_bonus_diamond';
-
--- 修改升级费用配置键
-UPDATE `agent_config` SET `config_key` = 'upgrade_to_gold_fee' WHERE `config_key` = 'upgrade_fee_normal_to_gold';
-UPDATE `agent_config` SET `config_key` = 'upgrade_to_diamond_fee' WHERE `config_key` = 'upgrade_fee_to_diamond';
-
--- 修改升级返佣配置键
-UPDATE `agent_config` SET `config_key` = 'upgrade_to_gold_rebate' WHERE `config_key` = 'upgrade_rebate_normal_to_gold';
-UPDATE `agent_config` SET `config_key` = 'upgrade_to_diamond_rebate' WHERE `config_key` = 'upgrade_rebate_to_diamond';
-```
-
-**方案二:修改代码逻辑,使用SQL中的键名**
-
-需要修改的文件:
-- `app/main/api/internal/logic/admin_agent/admingetagentconfiglogic.go`
-- `app/main/api/internal/logic/admin_agent/adminupdateagentconfiglogic.go`
-
-**推荐方案一**,因为:
-- 代码中的命名更清晰(`level_1_bonus` 比 `level_bonus_normal` 更直观)
-- 数字后缀与等级数字(1/2/3)对应,易于理解
-
-### 5.3 🔴 立即修复:从配置表读取等级加成
-
-**修改 `agentService.go` 中的 `getLevelBonus` 函数**:
-
-```go
-// getLevelBonus 获取等级加成(从配置表读取)
-func (s *AgentService) getLevelBonus(ctx context.Context, level int64) (int64, error) {
- var configKey string
- switch level {
- case 1:
- configKey = "level_1_bonus"
- case 2:
- configKey = "level_2_bonus"
- case 3:
- configKey = "level_3_bonus"
- default:
- return 0, nil
- }
-
- bonus, err := s.getConfigFloat(ctx, configKey)
- if err != nil {
- // 配置不存在时返回默认值
- switch level {
- case 1:
- return 6, nil
- case 2:
- return 3, nil
- case 3:
- return 0, nil
- }
- return 0, nil
- }
- return int64(bonus), nil
-}
-```
-
-**修改调用处**:
-```go
-// agentService.go:108
-levelBonus, err := s.getLevelBonus(ctx, agent.Level)
-if err != nil {
- return errors.Wrapf(err, "获取等级加成配置失败")
-}
-actualBasePrice := basePrice + float64(levelBonus)
-```
-
-### 5.4 🟡 补充缺失的配置项
-
-**添加 `tax_exemption_amount` 配置**:
-
-```sql
-INSERT INTO `agent_config` (`config_key`, `config_value`, `config_type`, `description`)
-VALUES ('tax_exemption_amount', '0.00', 'tax', '提现免税额度(元,默认0)');
-```
-
-### 5.5 🟡 规范化配置键命名
-
-**建议统一使用以下命名规范**:
-
-```
-{类型}_{编号/级别}_{属性}
-```
-
-**示例**:
-- ✅ `level_1_bonus` - 等级1的加成
-- ✅ `level_2_bonus` - 等级2的加成
-- ✅ `level_3_bonus` - 等级3的加成
-- ✅ `upgrade_to_gold_fee` - 升级到黄金的费用
-- ✅ `upgrade_to_diamond_fee` - 升级到钻石的费用
-- ✅ `upgrade_to_gold_rebate` - 升级到黄金的返佣
-- ✅ `upgrade_to_diamond_rebate` - 升级到钻石的返佣
-
-### 5.6 🟢 优化建议:添加配置验证
-
-**在更新配置时添加验证逻辑**:
-
-```go
-// 验证配置值的合理性
-func validateConfigValue(key string, value float64) error {
- switch key {
- case "tax_rate":
- if value < 0 || value > 1 {
- return errors.New("税率必须在0-1之间")
- }
- case "price_fee_rate":
- if value < 0 || value > 1 {
- return errors.New("提价费率必须在0-1之间")
- }
- case "base_price", "system_max_price":
- if value < 0 {
- return errors.New("价格配置不能为负数")
- }
- }
- return nil
-}
-```
-
----
-
-## 六、配置项完整清单(修正后)
-
-### 6.1 价格相关配置(应移除,改为产品配置)
-
-**⚠️ 重要说明**:以下配置项应该从系统配置表(`agent_config`)中移除,改为在产品配置表(`agent_product_config`)中按产品配置。
-
-| 配置键 | 说明 | 配置位置 |
-| ---------------------- | ----------------------------- | ----------------------------- |
-| ~~`base_price`~~ | ~~系统基础底价~~ | ✅ 应在 `agent_product_config` |
-| ~~`system_max_price`~~ | ~~系统价格上限~~ | ✅ 应在 `agent_product_config` |
-| ~~`price_threshold`~~ | ~~提价标准阈值~~ | ✅ 应在 `agent_product_config` |
-| ~~`price_fee_rate`~~ | ~~提价手续费比例(0-1之间)~~ | ✅ 应在 `agent_product_config` |
-
-**产品配置表结构**(`agent_product_config`):
-- `base_price` - 产品基础底价(必填)
-- `system_max_price` - 产品价格上限(必填)
-- `price_threshold` - 提价标准阈值(可选,NULL时表示不设阈值)
-- `price_fee_rate` - 提价手续费比例(可选,NULL时表示不收费)
-
-### 6.2 等级加成配置(bonus类型)
-
-| 配置键 | 默认值 | 说明 |
-| --------------- | ------ | ---------------------- |
-| `level_1_bonus` | 6.00 | 普通代理等级加成(元) |
-| `level_2_bonus` | 3.00 | 黄金代理等级加成(元) |
-| `level_3_bonus` | 0.00 | 钻石代理等级加成(元) |
-
-### 6.3 升级费用配置(upgrade类型)
-
-| 配置键 | 默认值 | 说明 |
-| --------------------------- | ------ | ------------------------ |
-| `upgrade_to_gold_fee` | 199.00 | 普通→黄金升级费用(元) |
-| `upgrade_to_diamond_fee` | 980.00 | 升级为钻石费用(元) |
-| `upgrade_to_gold_rebate` | 139.00 | 普通→黄金返佣金额(元) |
-| `upgrade_to_diamond_rebate` | 680.00 | 升级为钻石返佣金额(元) |
-
-**注意**:`gold_to_diamond` 的费用和返佣可以通过计算得出:
-- 费用:`upgrade_to_diamond_fee - upgrade_to_gold_fee = 980 - 199 = 781元`
-- 返佣:可以通过前端或后端计算,不单独存储
-
-### 6.4 返佣规则配置(rebate类型)
-
-| 配置键 | 默认值 | 说明 |
-| ------------------------------ | ------ | ------------------------------ |
-| `direct_parent_amount_diamond` | 6.00 | 直接上级是钻石的返佣金额(元) |
-| `direct_parent_amount_gold` | 3.00 | 直接上级是黄金的返佣金额(元) |
-| `direct_parent_amount_normal` | 2.00 | 直接上级是普通的返佣金额(元) |
-| `max_gold_rebate_amount` | 3.00 | 黄金代理最大返佣金额(元) |
-
-**建议**:这些配置项目前在代码中硬编码,建议配置化。
-
-### 6.5 税费配置(tax类型)
-
-| 配置键 | 默认值 | 说明 |
-| ---------------------- | ------ | ---------------------- |
-| `tax_rate` | 0.0600 | 提现税率(6%,即0.06) |
-| `tax_exemption_amount` | 0.00 | 免税额度(元,默认0) |
-
----
-
-## 七、配置表的合理性评估
-
-### 7.1 表结构设计
-
-| 评估项 | 评分 | 说明 |
-| ------------ | ----- | ---------------------------- |
-| **灵活性** | ⭐⭐⭐⭐⭐ | 键值对设计非常灵活,易于扩展 |
-| **可维护性** | ⭐⭐⭐⭐ | 类型分类清晰,便于管理 |
-| **性能** | ⭐⭐⭐⭐ | 唯一索引优化查询,缓存支持 |
-| **类型安全** | ⭐⭐⭐ | 所有值都是字符串,需要转换 |
-| **验证机制** | ⭐⭐ | 缺少数据库层面的验证 |
-
-### 7.2 总体评价
-
-**设计优点**:
-✅ 采用键值对存储,扩展性强
-✅ 类型分类清晰,便于管理
-✅ 支持版本控制和软删除
-
-**存在的问题**:
-❌ **价格配置应该在产品配置表,而不是系统配置表**(严重设计问题)
-❌ 配置键命名不一致(严重)
-❌ 部分配置硬编码(严重)
-❌ 缺少部分配置项(中等)
-❌ 缺少配置验证机制(中等)
-
-**总体评分**:⭐⭐⭐(3/5)
-
-**结论**:配置表的设计思路基本合理,但存在**价格配置位置设计错误**的严重问题。价格相关配置应该完全由产品配置表管理,系统配置表只应该包含全局配置(等级加成、升级费用、税费等)。修复这些问题后,配置表设计将更加完善和符合项目规范。
-
----
-
-## 八、修复后的完整配置SQL
-
-```sql
--- ============================================
--- 代理系统配置初始化(修正版)
--- ============================================
-
--- 删除价格相关配置(应该在产品配置表中)
-DELETE FROM `agent_config` WHERE `config_key` IN (
- 'base_price',
- 'system_max_price',
- 'price_threshold',
- 'price_fee_rate'
-);
-
--- 删除旧的不一致配置(如果存在)
-DELETE FROM `agent_config` WHERE `config_key` IN (
- 'level_bonus_normal',
- 'level_bonus_gold',
- 'level_bonus_diamond',
- 'upgrade_fee_normal_to_gold',
- 'upgrade_fee_to_diamond',
- 'upgrade_rebate_normal_to_gold',
- 'upgrade_rebate_to_diamond'
-);
-
--- 插入修正后的配置项
-INSERT INTO `agent_config` (`config_key`, `config_value`, `config_type`, `description`) VALUES
--- 注意:价格相关配置(base_price, system_max_price, price_threshold, price_fee_rate)
--- 已从系统配置表中移除,改为在产品配置表(agent_product_config)中按产品配置
-
--- 等级加成配置(修正键名)
-('level_1_bonus', '6.00', 'bonus', '普通代理等级加成(6元)'),
-('level_2_bonus', '3.00', 'bonus', '黄金代理等级加成(3元)'),
-('level_3_bonus', '0.00', 'bonus', '钻石代理等级加成(0元)'),
-
--- 升级费用配置(修正键名)
-('upgrade_to_gold_fee', '199.00', 'upgrade', '普通→黄金升级费用(199元)'),
-('upgrade_to_diamond_fee', '980.00', 'upgrade', '升级为钻石费用(980元)'),
-
--- 升级返佣配置(修正键名)
-('upgrade_to_gold_rebate', '139.00', 'upgrade', '普通→黄金返佣金额(139元)'),
-('upgrade_to_diamond_rebate', '680.00', 'upgrade', '升级为钻石返佣金额(680元)'),
-
--- 返佣规则配置
-('direct_parent_amount_diamond', '6.00', 'rebate', '直接上级是钻石的返佣金额(6元)'),
-('direct_parent_amount_gold', '3.00', 'rebate', '直接上级是黄金的返佣金额(3元)'),
-('direct_parent_amount_normal', '2.00', 'rebate', '直接上级是普通的返佣金额(2元)'),
-('max_gold_rebate_amount', '3.00', 'rebate', '黄金代理最大返佣金额(3元)'),
-
--- 税费配置
-('tax_rate', '0.0600', 'tax', '提现税率(6%,即0.06)'),
-('tax_exemption_amount', '0.00', 'tax', '提现免税额度(元,默认0)')
-ON DUPLICATE KEY UPDATE
- `config_value` = VALUES(`config_value`),
- `update_time` = NOW();
-```
-
----
-
-## 九、建议的改进方案
-
-### 方案A:保持当前键值对设计(推荐)
-
-**优点**:
-- 灵活性强,易于扩展
-- 不需要修改表结构
-- 符合项目当前架构
-
-**需要做的修改**:
-1. 统一配置键命名
-2. 修复代码硬编码问题
-3. 补充缺失的配置项
-4. 添加配置验证逻辑
-
-### 方案B:改为结构化JSON配置(可选)
-
-**如果配置项继续增长,可以考虑**:
-- 将相关配置组合成JSON对象存储在 `config_value` 中
-- 例如:`level_bonus_config` 存储 `{"1": 6, "2": 3, "3": 0}`
-
-**缺点**:
-- 需要修改所有读取配置的代码
-- 不利于单个配置项的独立更新
-
-**建议**:当前配置项数量不多(约15个),保持键值对设计更合适。
-
----
-
-## 十、总结
-
-### 当前状态
-- ✅ 配置表设计思路合理
-- ❌ **价格配置设计错误**(应该在产品配置表,不在系统配置表)
-- ❌ 配置键命名不一致(需要立即修复)
-- ❌ 部分配置硬编码(需要立即修复)
-- ⚠️ 缺少部分配置项(需要补充)
-
-### 修复优先级
-1. **P0(紧急)**:移除系统配置表中的价格配置,改为完全由产品配置表管理
-2. **P0(紧急)**:修改订单处理逻辑,从产品配置表读取价格参数
-3. **P0(紧急)**:统一配置键命名,修复不一致问题
-4. **P0(紧急)**:修改代码,从配置表读取等级加成,移除硬编码
-5. **P1(重要)**:补充缺失的配置项(`tax_exemption_amount`)
-6. **P2(建议)**:添加配置验证逻辑
-7. **P2(建议)**:将返佣规则配置化(当前硬编码)
-
-### 是否符合项目
-
-**配置表设计**:✅ 基本符合,但价格配置的位置设计错误
-
-**存在的问题**:
-1. ❌ **价格配置应该在产品配置表中,而不是系统配置表**(严重设计问题)
-2. ❌ 配置键命名不一致
-3. ❌ 部分配置硬编码
-4. ⚠️ 缺少部分配置项
-
-**结论**:需要修复上述问题后才能完全符合项目的配置化设计理念。**最重要的是将价格配置从系统配置表移除,改为完全由产品配置表管理。**
-
----
-
-## 十一、产品配置表使用规范
-
-### 11.1 产品配置表结构
-
-**表名**: `agent_product_config`
-
-每个产品都应该有对应的配置记录,包含以下字段:
-
-| 字段 | 类型 | 必填 | 说明 |
-| ------------------ | ------------- | ---- | ----------------------------------------- |
-| `product_id` | bigint | ✅ 是 | 产品ID(唯一) |
-| `product_name` | varchar(100) | ✅ 是 | 产品名称 |
-| `base_price` | decimal(10,2) | ✅ 是 | 产品基础底价 |
-| `system_max_price` | decimal(10,2) | ✅ 是 | 产品价格上限 |
-| `price_threshold` | decimal(10,2) | ❌ 否 | 提价标准阈值(NULL表示不设阈值) |
-| `price_fee_rate` | decimal(5,4) | ❌ 否 | 提价手续费比例(NULL表示不收费,0-1之间) |
-
-### 11.2 配置优先级和默认值
-
-**规则**:
-1. 所有价格参数必须从产品配置表读取
-2. 如果产品配置记录不存在,应该报错或使用合理的默认值(建议报错,强制每个产品必须配置)
-3. 可选字段(`price_threshold`、`price_fee_rate`)如果为NULL,表示不启用该功能
- - `price_threshold = NULL` → 不设提价阈值
- - `price_fee_rate = NULL` → 不收取提价手续费
-
-### 11.3 代码修改示例
-
-#### 修改 AgentService.AgentProcess 方法
-
-```go
-// AgentProcess 处理代理订单(新系统)
-func (s *AgentService) AgentProcess(ctx context.Context, order *model.Order) error {
- // 1-3. 前面的代码不变...
-
- // 4. 获取产品配置(必须存在)
- productConfig, err := s.AgentProductConfigModel.FindOneByProductId(ctx, order.ProductId)
- if err != nil {
- if errors.Is(err, model.ErrNotFound) {
- return errors.Wrapf(err, "产品配置不存在, productId: %d,请先在后台配置产品价格参数", order.ProductId)
- }
- return errors.Wrapf(err, "查询产品配置失败, productId: %d", order.ProductId)
- }
-
- // 使用产品配置的底价
- basePrice := productConfig.BasePrice
-
- // 6. 使用事务处理订单
- return s.AgentWalletModel.Trans(ctx, func(transCtx context.Context, session sqlx.Session) error {
- // 6.1 计算实际底价和代理收益
- levelBonus, err := s.getLevelBonus(ctx, agent.Level)
- if err != nil {
- return errors.Wrapf(err, "获取等级加成配置失败")
- }
- actualBasePrice := basePrice + float64(levelBonus)
-
- // 6.2 计算提价成本(使用产品配置)
- priceThreshold := 0.0
- priceFeeRate := 0.0
- if productConfig.PriceThreshold.Valid {
- priceThreshold = productConfig.PriceThreshold.Float64
- }
- if productConfig.PriceFeeRate.Valid {
- priceFeeRate = productConfig.PriceFeeRate.Float64
- }
- priceCost := s.calculatePriceCost(agentOrder.SetPrice, priceThreshold, priceFeeRate)
-
- // 6.3 计算代理收益
- agentProfit := agentOrder.SetPrice - actualBasePrice - priceCost
-
- // ... 后续代码不变 ...
- })
-}
-```
-
-#### 修改 PaymentLogic 创建订单时的价格计算
-
-```go
-// 如果是代理推广订单,创建完整的代理订单记录
-if data.AgentIdentifier != "" && agentLinkModel != nil {
- // ... 获取代理信息 ...
-
- // 获取产品配置(必须存在)
- productConfig, err := l.svcCtx.AgentProductConfigModel.FindOneByProductId(l.ctx, product.Id)
- if err != nil {
- if errors.Is(err, model.ErrNotFound) {
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
- "生成订单失败,产品配置不存在, productId: %d,请先在后台配置产品价格参数", product.Id)
- }
- return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR),
- "生成订单, 查询产品配置失败: %+v", err)
- }
-
- // 使用产品配置的底价
- basePrice := productConfig.BasePrice
-
- // 计算实际底价(基础底价+等级加成)
- levelBonus, _ := l.getLevelBonus(agent.Level)
- actualBasePrice := basePrice + float64(levelBonus)
-
- // 计算提价成本(使用产品配置)
- priceThreshold := 0.0
- priceFeeRate := 0.0
- if productConfig.PriceThreshold.Valid {
- priceThreshold = productConfig.PriceThreshold.Float64
- }
- if productConfig.PriceFeeRate.Valid {
- priceFeeRate = productConfig.PriceFeeRate.Float64
- }
-
- priceCost := 0.0
- if agentLinkModel.SetPrice > priceThreshold {
- priceCost = (agentLinkModel.SetPrice - priceThreshold) * priceFeeRate
- }
-
- // 计算代理收益
- agentProfit := agentLinkModel.SetPrice - actualBasePrice - priceCost
-
- // ... 创建代理订单记录 ...
-}
-```
-
-### 11.4 需要修改的文件清单
-
-需要修改以支持产品配置的代码文件:
-
-1. **`app/main/api/internal/service/agentService.go`**
- - `AgentProcess()` 方法:从产品配置表读取价格参数
-
-2. **`app/main/api/internal/logic/pay/paymentlogic.go`**
- - 创建订单时的价格计算:从产品配置表读取价格参数
-
-3. **`app/main/api/internal/logic/agent/getagentproductconfiglogic.go`**
- - 移除从系统配置表读取价格参数的逻辑(只保留从产品配置表读取)
-
-4. **`app/main/api/internal/logic/admin_agent/admingetagentconfiglogic.go`**
- - 移除价格相关配置项的返回
-
-5. **`app/main/api/internal/logic/admin_agent/adminupdateagentconfiglogic.go`**
- - 移除价格相关配置项的更新逻辑
-
-6. **后台管理系统前端**
- - 系统配置页面:移除价格相关配置项
- - 产品配置页面:确保可以正确配置所有价格参数
-
diff --git a/佣金冻结功能完成总结.md b/佣金冻结功能完成总结.md
deleted file mode 100644
index d184eaa..0000000
--- a/佣金冻结功能完成总结.md
+++ /dev/null
@@ -1,136 +0,0 @@
-# 佣金冻结功能实现完成总结
-
-## ✅ 已完成的工作
-
-### 1. 数据库表
-- ✅ 创建了 `agent_freeze_task` 表(`deploy/sql/agent_freeze_task_migration.sql`)
-- ✅ 表结构包含:代理ID、订单ID、佣金ID、冻结金额、订单单价、冻结比例、状态、冻结时间、解冻时间等
-
-### 2. 核心业务逻辑
-- ✅ 修改了 `giveAgentCommission` 函数(`agentService.go`)
- - 增加订单单价参数
- - 实现冻结逻辑:订单单价 >= 100元时,冻结订单单价的10%(可配置)
- - 创建冻结任务记录
- - 更新钱包余额和冻结余额
-
-### 3. 异步任务系统
-- ✅ 创建了解冻任务处理器(`queue/unfreezeCommission.go`)
-- ✅ 添加了 `SendUnfreezeTask` 方法到 `AsynqService`
-- ✅ 在 `agentProcess.go` 中,代理处理成功后自动发送解冻任务
-- ✅ 注册了解冻任务路由
-
-### 4. 配置支持
-- ✅ 冻结比例从配置表 `agent_config` 读取,配置键为 `commission_freeze_ratio`
-
-### 5. ServiceContext 更新
-- ✅ 添加了 `AgentFreezeTaskModel` 字段
-- ✅ 初始化了 `agentFreezeTaskModel`
-- ✅ 传递给 `AgentService`
-- ✅ 添加到返回的 `ServiceContext`
-
-## 📋 功能说明
-
-### 触发条件
-- 订单单价 >= 配置阈值(默认100元,可通过 `commission_freeze_threshold` 配置)
-
-### 冻结规则
-- 冻结阈值:从配置表读取(配置键:`commission_freeze_threshold`,默认100元)
-- 冻结比例:从配置表读取(配置键:`commission_freeze_ratio`,默认10%)
-- 冻结金额 = 订单单价 × 冻结比例
-- 冻结金额不能超过佣金金额
-- 例如:订单单价140元,佣金134元,冻结14元(140 × 10%),实际到账120元(134 - 14)
-
-### 冻结时长
-- 从配置表读取(配置键:`commission_freeze_days`,默认30天)
-- 注意:配置只在创建任务时读取,已创建的任务不受后续配置修改影响
-
-### 解冻机制
-- 通过异步任务延迟执行
-- 1个月后自动解冻
-- 解冻时:`FrozenBalance -= 冻结金额`,`Balance += 冻结金额`
-
-### 数据持久化
-- 冻结任务记录在 `agent_freeze_task` 表中
-- 保证任务的一致性和持久化
-- 支持任务重试和状态追踪
-
-## 🔧 还需要完成的工作
-
-### 1. 添加配置项
-在 `agent_config` 表中添加配置:
-```sql
--- 冻结阈值(订单单价达到此金额才触发冻结,默认100元)
-INSERT INTO `agent_config` (`config_key`, `config_value`, `config_type`, `description`)
-VALUES ('commission_freeze_threshold', '100', 'rebate', '佣金冻结阈值(订单单价达到此金额才触发冻结,单位:元)');
-
--- 冻结比例(默认10%)
-INSERT INTO `agent_config` (`config_key`, `config_value`, `config_type`, `description`)
-VALUES ('commission_freeze_ratio', '0.1', 'rebate', '佣金冻结比例(例如:0.1表示10%)');
-
--- 解冻天数(默认30天,即1个月)
-INSERT INTO `agent_config` (`config_key`, `config_value`, `config_type`, `description`)
-VALUES ('commission_freeze_days', '30', 'rebate', '佣金冻结解冻天数(单位:天,例如:30表示30天后解冻)');
-```
-
-### 2. 运行SQL创建表(如果还没运行)
-```bash
-mysql -u用户名 -p数据库名 < deploy/sql/agent_freeze_task_migration.sql
-```
-
-## 📝 代码文件清单
-
-### 新增文件
-1. `deploy/sql/agent_freeze_task_migration.sql` - 冻结任务表SQL
-2. `app/main/api/internal/queue/unfreezeCommission.go` - 解冻任务处理器
-3. `佣金冻结功能实现说明.md` - 实现说明文档
-
-### 修改文件
-1. `app/main/api/internal/service/agentService.go` - 修改佣金发放逻辑
-2. `app/main/api/internal/service/asynqService.go` - 添加发送解冻任务方法
-3. `app/main/api/internal/queue/agentProcess.go` - 添加发送解冻任务逻辑
-4. `app/main/api/internal/queue/routes.go` - 注册解冻任务路由
-5. `app/main/api/internal/types/taskname.go` - 添加解冻任务类型
-6. `app/main/api/internal/types/payload.go` - 添加解冻任务负载类型
-7. `app/main/api/internal/svc/servicecontext.go` - 添加 AgentFreezeTaskModel
-
-## 🎯 功能流程
-
-```
-订单支付成功
- ↓
-代理处理(AgentProcess)
- ↓
-发放佣金(giveAgentCommission)
- ↓
-判断:订单单价 >= 100元?
- ├─ 是 → 计算冻结金额(订单单价 × 10%)
- │ 创建冻结任务记录
- │ 更新钱包:Balance += (佣金-冻结金额), FrozenBalance += 冻结金额
- │ 发送解冻异步任务(延迟配置天数)
- │
- └─ 否 → 正常发放佣金,不冻结
- ↓
-配置天数后
- ↓
-解冻任务执行(UnfreezeCommissionHandler)
- ↓
-更新冻结任务状态为已解冻
- ↓
-更新钱包:FrozenBalance -= 冻结金额, Balance += 冻结金额
-```
-
-## ✅ 代码检查
-- ✅ 所有文件已通过编译检查
-- ✅ 没有 lint 错误
-- ✅ ServiceContext 已正确更新
-- ✅ 异步任务路由已注册
-
-## 🚀 下一步
-1. 运行SQL创建表(如果还没运行)
-2. 添加配置项到 `agent_config` 表
-3. 测试功能:
- - 测试订单单价 < 100元:不应冻结
- - 测试订单单价 >= 100元:应冻结订单单价的10%
- - 测试冻结金额超过佣金:应冻结全部佣金
- - 测试解冻任务:在配置的天数后应自动解冻
-
diff --git a/佣金冻结功能实现说明.md b/佣金冻结功能实现说明.md
deleted file mode 100644
index d9ec35c..0000000
--- a/佣金冻结功能实现说明.md
+++ /dev/null
@@ -1,106 +0,0 @@
-# 佣金冻结功能实现说明
-
-## 功能需求
-当订单单价 >= 配置阈值(默认100元)时,抽取订单单价的配置比例(默认10%)放到冻结余额,冻结配置天数(默认30天)后自动解冻。所有配置项都可在配置表中修改,但配置只在创建任务时读取,已创建的任务不受后续配置修改影响。
-
-## 实现步骤
-
-### 1. 运行SQL创建冻结任务表
-```bash
-# 执行SQL文件
-mysql -u用户名 -p数据库名 < deploy/sql/agent_freeze_task_migration.sql
-```
-
-### 2. 生成Model
-运行代码生成工具生成 `AgentFreezeTaskModel`:
-```bash
-# 在 ycc-proxy-server 目录下运行
-goctl model mysql datasource -url="数据库连接字符串" -table="agent_freeze_task" -dir="./app/main/model" -cache
-```
-
-### 3. 更新ServiceContext
-在 `app/main/api/internal/svc/servicecontext.go` 中:
-
-#### 3.1 添加字段
-```go
-AgentFreezeTaskModel model.AgentFreezeTaskModel
-```
-
-#### 3.2 初始化Model
-在 `NewServiceContext` 函数中添加:
-```go
-agentFreezeTaskModel := model.NewAgentFreezeTaskModel(db, cacheConf)
-```
-
-#### 3.3 传递给AgentService
-修改 `NewAgentService` 调用,添加参数:
-```go
-agentService := service.NewAgentService(c, orderModel, agentModel, agentWalletModel,
- agentRelationModel, agentLinkModel, agentOrderModel, agentCommissionModel, agentRebateModel,
- agentUpgradeModel, agentWithdrawalModel, agentConfigModel, agentProductConfigModel,
- agentRealNameModel, agentWithdrawalTaxModel, agentFreezeTaskModel) // 添加这个参数
-```
-
-#### 3.4 添加到ServiceContext返回
-在返回的 `ServiceContext` 结构体中添加:
-```go
-AgentFreezeTaskModel: agentFreezeTaskModel,
-```
-
-### 4. 添加配置项
-在 `agent_config` 表中添加配置:
-```sql
--- 冻结阈值(订单单价达到此金额才触发冻结,默认100元)
-INSERT INTO `agent_config` (`config_key`, `config_value`, `config_type`, `description`)
-VALUES ('commission_freeze_threshold', '100', 'rebate', '佣金冻结阈值(订单单价达到此金额才触发冻结,单位:元)');
-
--- 冻结比例(默认10%)
-INSERT INTO `agent_config` (`config_key`, `config_value`, `config_type`, `description`)
-VALUES ('commission_freeze_ratio', '0.1', 'rebate', '佣金冻结比例(例如:0.1表示10%)');
-
--- 解冻天数(默认30天,即1个月)
-INSERT INTO `agent_config` (`config_key`, `config_value`, `config_type`, `description`)
-VALUES ('commission_freeze_days', '30', 'rebate', '佣金冻结解冻天数(单位:天,例如:30表示30天后解冻)');
-```
-
-### 5. 代码已完成的修改
-
-#### 5.1 已修改的文件
-- ✅ `deploy/sql/agent_freeze_task_migration.sql` - 创建冻结任务表
-- ✅ `app/main/api/internal/service/agentService.go` - 修改 `giveAgentCommission` 函数,增加冻结逻辑
-- ✅ `app/main/api/internal/service/asynqService.go` - 添加 `SendUnfreezeTask` 方法
-- ✅ `app/main/api/internal/queue/unfreezeCommission.go` - 创建解冻任务处理器
-- ✅ `app/main/api/internal/queue/routes.go` - 注册解冻任务路由
-- ✅ `app/main/api/internal/queue/agentProcess.go` - 在代理处理成功后发送解冻任务
-- ✅ `app/main/api/internal/types/taskname.go` - 添加解冻任务类型
-- ✅ `app/main/api/internal/types/payload.go` - 添加解冻任务负载类型
-
-#### 5.2 核心逻辑说明
-
-**冻结逻辑**(在 `giveAgentCommission` 中):
-1. 判断订单单价是否 >= 100元
-2. 如果是,从配置表读取冻结比例(默认10%)
-3. 计算冻结金额 = 订单单价 × 冻结比例(不超过佣金金额)
-4. 创建冻结任务记录(状态=待解冻,解冻时间=从配置读取的天数后)
-5. 更新钱包:`Balance += (佣金金额 - 冻结金额)`,`FrozenBalance += 冻结金额`
-
-**解冻逻辑**(在 `unfreezeCommission.go` 中):
-1. 查询冻结任务
-2. 检查解冻时间是否已到
-3. 更新冻结任务状态为已解冻
-4. 更新钱包:`FrozenBalance -= 冻结金额`,`Balance += 冻结金额`
-
-### 6. 注意事项
-
-1. **Model生成**:必须先运行SQL并生成Model,否则代码无法编译
-2. **配置项**:需要在 `agent_config` 表中添加 `commission_freeze_ratio` 配置项
-3. **异步任务**:解冻任务通过 asynq 延迟执行,确保在配置的天数后自动解冻
-4. **数据一致性**:所有操作都在事务中执行,保证数据一致性
-
-### 7. 测试建议
-
-1. 测试订单单价 < 100元:不应冻结
-2. 测试订单单价 >= 100元:应冻结订单单价的10%
-3. 测试冻结金额超过佣金:应冻结全部佣金
-4. 测试解冻任务:在配置的天数后应自动解冻
-
diff --git a/删除推广订单功能计划.md b/删除推广订单功能计划.md
deleted file mode 100644
index b9e05a2..0000000
--- a/删除推广订单功能计划.md
+++ /dev/null
@@ -1,213 +0,0 @@
-# 删除推广订单功能计划
-
-## 目标
-删除后台以及后端中promotion(推广订单)相关功能,但**保留推广数据分析和推广链接管理**功能。
-
-## 功能区分
-
-### ✅ 需要保留的功能
-1. **推广链接管理**
- - 创建推广链接
- - 更新推广链接
- - 删除推广链接
- - 获取推广链接列表
- - 获取推广链接详情
- - 记录链接点击
-
-2. **推广数据分析**
- - 获取推广总统计
- - 获取推广历史记录
-
-### ❌ 需要删除的功能
-1. **推广订单功能**
- - AdminPromotionOrder 模型及相关代码
- - 订单管理中的 IsPromotion 字段
- - 订单创建/更新/删除中的推广订单逻辑
- - 前端订单管理中的推广订单字段
-
----
-
-## 详细删除计划
-
-### 阶段一:后端代码清理
-
-#### 1.1 删除推广订单模型文件
-- [ ] 删除 `ycc-proxy-server/app/main/model/adminPromotionOrderModel.go`
-- [ ] 删除 `ycc-proxy-server/app/main/model/adminPromotionOrderModel_gen.go`
-- [ ] 确认 `ycc-proxy-server/deploy/script/gen_models.ps1` 中 `admin_promotion_order` 已被注释(第57行,已确认已注释)
-
-#### 1.2 从 ServiceContext 中移除推广订单模型
-- [ ] 从 `ycc-proxy-server/app/main/api/internal/svc/servicecontext.go` 中删除:
- - `AdminPromotionOrderModel` 字段声明
- - `AdminPromotionOrderModel` 初始化代码
-
-#### 1.3 修改订单管理相关逻辑
-
-**文件:`ycc-proxy-server/app/main/api/internal/logic/admin_order/admincreateorderlogic.go`**
-- [ ] 删除 `IsPromotion` 字段处理逻辑(第75-87行)
-- [ ] 删除 `AdminPromotionOrderModel` 的引用
-
-**文件:`ycc-proxy-server/app/main/api/internal/logic/admin_order/adminupdateorderlogic.go`**
-- [ ] 删除推广订单状态处理逻辑(第79-101行)
-- [ ] 删除 `AdminPromotionOrderModel` 的引用
-
-**文件:`ycc-proxy-server/app/main/api/internal/logic/admin_order/admindeleteorderlogic.go`**
-- [ ] 删除删除推广订单记录的逻辑(第44-51行)
-- [ ] 删除 `AdminPromotionOrderModel` 的引用
-
-**文件:`ycc-proxy-server/app/main/api/internal/logic/admin_order/admingetorderlistlogic.go`**
-- [ ] 删除 `IsPromotion` 查询条件(第57-59行)
-- [ ] 删除判断是否为推广订单的逻辑(第277-280行)
-- [ ] 删除返回结果中的 `IsPromotion` 字段赋值
-
-**文件:`ycc-proxy-server/app/main/api/internal/logic/admin_order/admingetorderdetaillogic.go`**
-- [ ] 删除判断是否为推广订单的逻辑(第43-47行)
-- [ ] 删除返回结果中的 `IsPromotion` 字段赋值(第121行)
-
-#### 1.4 修改 API 定义文件
-
-**文件:`ycc-proxy-server/app/main/api/desc/admin/order.api`**
-- [ ] 从 `AdminGetOrderListReq` 中删除 `IsPromotion` 字段(第56行)
-- [ ] 从 `AdminGetOrderListResp` 的 `OrderItem` 中删除 `IsPromotion` 字段(第83行)
-- [ ] 从 `AdminGetOrderDetailResp` 中删除 `IsPromotion` 字段(第105行)
-- [ ] 从 `AdminCreateOrderReq` 中删除 `IsPromotion` 字段(第119行)
-- [ ] 从 `AdminUpdateOrderReq` 中删除 `IsPromotion` 字段(第137行)
-
-#### 1.5 修改 Types 定义
-
-**文件:`ycc-proxy-server/app/main/api/internal/types/types.go`**
-- [ ] 从 `AdminGetOrderListReq` 中删除 `IsPromotion` 字段
-- [ ] 从 `AdminGetOrderListResp` 的 `OrderItem` 中删除 `IsPromotion` 字段
-- [ ] 从 `AdminGetOrderDetailResp` 中删除 `IsPromotion` 字段
-- [ ] 从 `AdminCreateOrderReq` 中删除 `IsPromotion` 字段
-- [ ] 从 `AdminUpdateOrderResp` 中删除 `IsPromotion` 字段
-
-#### 1.6 重新生成代码
-- [ ] 运行 `goctl api go -api desc/admin/order.api -dir . -style gozero`
-- [ ] 检查生成的代码是否正确
-
----
-
-### 阶段二:前端代码清理
-
-#### 2.1 修改订单管理页面
-
-**文件:`ycc-proxy-admin/apps/web-antd/src/views/order/order/data.ts`**
-- [ ] 删除推广订单列定义(第110-121行)
-- [ ] 删除表单中的推广订单字段(第224-225行)
-
-**文件:`ycc-proxy-admin/apps/web-antd/src/api/order/order.ts`**
-- [ ] 从接口类型定义中删除 `is_promotion` 字段(第19行)
-
-#### 2.2 检查其他前端文件
-- [ ] 搜索前端代码中是否还有其他地方引用了 `is_promotion` 或 `IsPromotion`
-- [ ] 删除所有相关引用
-
----
-
-### 阶段三:数据库清理(可选)
-
-#### 3.1 数据库表处理
-- [ ] 确认是否删除 `admin_promotion_order` 表
- - 如果保留历史数据,可以保留表但不使用
- - 如果需要完全清理,可以执行删除表的SQL
-
-#### 3.2 数据库迁移脚本(如需要)
-- [ ] 创建迁移脚本,删除 `admin_promotion_order` 表(如果决定删除)
-
----
-
-### 阶段四:验证和测试
-
-#### 4.1 功能验证
-- [ ] 验证推广链接管理功能正常
-- [ ] 验证推广数据分析功能正常
-- [ ] 验证订单创建功能正常(不再有推广订单选项)
-- [ ] 验证订单更新功能正常(不再有推广订单选项)
-- [ ] 验证订单列表查询功能正常(不再显示推广订单字段)
-- [ ] 验证订单详情查看功能正常(不再显示推广订单字段)
-
-#### 4.2 代码检查
-- [ ] 运行 `go build` 确保后端代码编译通过
-- [ ] 运行前端构建确保前端代码正常
-- [ ] 检查是否有编译错误或警告
-- [ ] 检查是否有未使用的导入
-
----
-
-## 注意事项
-
-1. **保留的功能不受影响**
- - 推广链接管理(`AdminPromotionLink`)完全保留
- - 推广数据分析(`AdminPromotionLinkStatsTotal`, `AdminPromotionLinkStatsHistory`)完全保留
- - 记录链接点击功能完全保留
-
-2. **数据库表处理**
- - `admin_promotion_order` 表可以保留(用于历史数据),但不再使用
- - 或者完全删除该表(需要确认是否有历史数据需要保留)
-
-3. **代码生成**
- - 修改 `.api` 文件后需要重新生成代码
- - 确保生成的代码与手动修改的代码一致
-
-4. **向后兼容**
- - 如果前端已经部署,需要确保前端和后端同时更新
- - 或者先更新后端,保持向后兼容一段时间
-
----
-
-## 执行顺序建议
-
-1. **第一步**:修改后端 API 定义文件(`.api` 文件)
-2. **第二步**:重新生成后端代码
-3. **第三步**:手动修改后端逻辑代码(删除推广订单相关逻辑)
-4. **第四步**:修改前端代码
-5. **第五步**:测试验证
-6. **第六步**:清理数据库(如需要)
-
----
-
-## 文件清单
-
-### 需要修改的后端文件
-1. `ycc-proxy-server/app/main/api/desc/admin/order.api`
-2. `ycc-proxy-server/app/main/api/internal/types/types.go`
-3. `ycc-proxy-server/app/main/api/internal/logic/admin_order/admincreateorderlogic.go`
-4. `ycc-proxy-server/app/main/api/internal/logic/admin_order/adminupdateorderlogic.go`
-5. `ycc-proxy-server/app/main/api/internal/logic/admin_order/admindeleteorderlogic.go`
-6. `ycc-proxy-server/app/main/api/internal/logic/admin_order/admingetorderlistlogic.go`
-7. `ycc-proxy-server/app/main/api/internal/logic/admin_order/admingetorderdetaillogic.go`
-8. `ycc-proxy-server/app/main/api/internal/svc/servicecontext.go`
-9. `ycc-proxy-server/app/main/model/vars.go`(如需要)
-
-### 需要删除的后端文件
-1. `ycc-proxy-server/app/main/model/adminPromotionOrderModel.go`
-2. `ycc-proxy-server/app/main/model/adminPromotionOrderModel_gen.go`
-
-### 需要修改的前端文件
-1. `ycc-proxy-admin/apps/web-antd/src/views/order/order/data.ts`
-2. `ycc-proxy-admin/apps/web-antd/src/api/order/order.ts`
-
-### 需要保留的文件(推广链接和数据分析)
-1. `ycc-proxy-server/app/main/api/desc/admin/promotion.api` ✅
-2. `ycc-proxy-server/app/main/api/internal/logic/admin_promotion/*` ✅
-3. `ycc-proxy-server/app/main/api/internal/handler/admin_promotion/*` ✅
-4. `ycc-proxy-server/app/main/model/adminPromotionLinkModel*` ✅
-5. `ycc-proxy-server/app/main/model/adminPromotionLinkStats*` ✅
-6. `ycc-proxy-server/app/main/api/internal/service/adminPromotionLinkStatsService.go` ✅
-7. `ycc-proxy-admin/apps/web-antd/src/views/promotion/*` ✅
-8. `ycc-proxy-admin/apps/web-antd/src/api/promotion/*` ✅
-
----
-
-## 完成标志
-
-- [ ] 所有后端代码修改完成
-- [ ] 所有前端代码修改完成
-- [ ] 代码编译通过
-- [ ] 功能测试通过
-- [ ] 推广链接管理功能正常
-- [ ] 推广数据分析功能正常
-- [ ] 订单管理功能正常(无推广订单相关功能)
-- [ ] 无编译错误和警告
-
diff --git a/定时清理报告系统问题分析.md b/定时清理报告系统问题分析.md
deleted file mode 100644
index 0f2961d..0000000
--- a/定时清理报告系统问题分析.md
+++ /dev/null
@@ -1,111 +0,0 @@
-# 定时清理报告系统问题分析
-
-## 🔴 严重问题
-
-### 1. **重复插入清理日志记录(第129-137行)**
-**问题**:每批次都插入新的清理日志记录,导致同一个清理任务产生多条日志记录。
-
-**影响**:
-- 数据冗余,同一个清理任务会有多条日志
-- 无法准确统计单次清理的总影响行数
-- 日志记录混乱
-
-**当前代码**:
-```go
-// 4. 保存清理日志(每批次都记录)
-cleanupLogInsertResult, err := l.svcCtx.QueryCleanupLogModel.Insert(ctx, session, cleanupLog)
-```
-
-**应该**:先创建一条日志记录,然后只更新 `AffectedRows` 字段,或者只在最后插入一次。
-
-### 2. **大事务问题(第96行)**
-**问题**:整个清理过程在一个大事务中,如果数据量很大,会导致:
-- 事务持续时间过长(可能几小时)
-- 锁等待时间过长
-- 可能导致事务超时
-- 数据库连接占用时间过长
-- 如果中途失败,所有操作回滚,已处理的数据无法恢复
-
-**影响**:
-- 数据库性能下降
-- 可能导致其他操作阻塞
-- 数据量大时可能失败
-
-**建议**:每个批次使用独立事务,或者使用小事务分批提交。
-
-### 3. **缺少超时控制**
-**问题**:没有为整个清理任务设置超时,如果数据量很大,可能会运行很长时间。
-
-**影响**:
-- 任务可能无限期运行
-- 无法及时响应系统关闭信号
-
-**建议**:添加超时控制,例如最多运行1小时。
-
-### 4. **查询条件缺少排序(第100-103行)**
-**问题**:查询条件没有排序,可能导致每次查询的结果不一致。
-
-**影响**:
-- 可能导致某些数据被重复处理或遗漏
-- 无法保证处理顺序
-
-**建议**:添加 `OrderBy("id ASC")` 或 `OrderBy("create_time ASC")`。
-
-## 🟡 中等问题
-
-### 5. **缺少进度监控**
-**问题**:没有记录处理进度,如果任务中断,无法知道处理到哪里了。
-
-**影响**:
-- 任务中断后无法恢复
-- 无法监控处理进度
-
-**建议**:记录已处理的批次数和最后处理的ID。
-
-### 6. **错误处理不够完善**
-**问题**:如果某个批次失败,整个事务回滚,但已经处理的数据无法恢复。
-
-**影响**:
-- 部分数据可能已经处理,但失败后全部回滚
-- 无法知道哪些数据已经处理过
-
-**建议**:每个批次使用独立事务,失败时只回滚当前批次。
-
-### 7. **缺少优雅关闭支持**
-**问题**:如果服务关闭,正在处理的任务可能被中断。
-
-**影响**:
-- 数据可能处于不一致状态
-- 无法记录已处理的进度
-
-**建议**:检查 context 是否被取消,优雅退出。
-
-## 🟢 小问题
-
-### 8. **日志信息不够详细**
-**问题**:日志信息可以更详细,便于排查问题。
-
-**建议**:记录批次处理情况、处理时间等。
-
-### 9. **配置验证不足**
-**问题**:没有验证配置值的合理性(如批次大小、保留天数)。
-
-**建议**:添加配置验证,确保配置值在合理范围内。
-
-## 修复建议优先级
-
-### 🔴 高优先级(必须修复)
-1. **修复重复插入日志问题** - 影响数据准确性
-2. **修复大事务问题** - 影响性能和可靠性
-3. **添加查询排序** - 影响数据一致性
-
-### 🟡 中优先级(建议修复)
-4. **添加超时控制** - 提高可靠性
-5. **改进错误处理** - 提高容错性
-6. **添加进度监控** - 便于排查问题
-
-### 🟢 低优先级(可选)
-7. **优雅关闭支持** - 如果服务很少重启可以不做
-8. **更详细的日志** - 便于排查问题
-9. **配置验证** - 提高健壮性
-
diff --git a/报告查询链路和代理分配逻辑检查报告.md b/报告查询链路和代理分配逻辑检查报告.md
deleted file mode 100644
index 051f13a..0000000
--- a/报告查询链路和代理分配逻辑检查报告.md
+++ /dev/null
@@ -1,221 +0,0 @@
-# 报告查询链路和代理分配逻辑检查报告
-
-## 一、报告查询链路检查
-
-### 1.1 整体流程
-
-```
-用户支付订单
- ↓
-支付回调(支付宝/微信)
- ↓
-更新订单状态为 "paid"
- ↓
-发送异步任务 SendQueryTask(order.Id)
- ↓
-异步任务处理 PaySuccessNotifyUserHandler.ProcessTask
- ├─ 创建报告记录(query表)
- ├─ 生成授权书
- ├─ 调用API请求服务获取报告数据
- ├─ 更新报告状态为 "success"
- ├─ 调用代理处理 AgentService.AgentProcess
- └─ 删除Redis缓存
-```
-
-### 1.2 链路检查结果
-
-✅ **链路基本通顺**,但存在以下问题:
-
-#### ✅ 问题1:代理处理失败时的处理逻辑 - 已修复
-
-**原问题**:
-- 代理处理在报告生成成功后同步执行
-- 如果代理处理失败,会触发 `handleError`,但报告已经生成成功
-
-**修复方案**:
-1. ✅ 创建了独立的代理处理异步任务 Handler(`app/main/api/internal/queue/agentProcess.go`)
-2. ✅ 修改 `paySuccessNotify.go` 改为发送异步任务,不再阻塞报告流程
-3. ✅ 代理处理失败时只记录日志,不影响报告使用
-4. ✅ 保留了手动重试接口供管理员使用
-
-**修复后的代码**:
-```go
-// 报告生成成功后,发送代理处理异步任务(不阻塞报告流程)
-if asyncErr := l.svcCtx.AsynqService.SendAgentProcessTask(order.Id); asyncErr != nil {
- // 代理处理任务发送失败,只记录日志,不影响报告流程
- logx.Errorf("发送代理处理任务失败,订单ID: %d, 错误: %v", order.Id, asyncErr)
-}
-```
-
-### 1.3 支付回调链路
-
-✅ **支付回调链路正常**:
-- 支付宝回调:`AlipayCallbackLogic.handleQueryOrderPayment`
-- 微信回调:`WechatPayCallbackLogic.handleQueryOrderPayment`
-- 支付成功后正确发送异步任务
-
----
-
-## 二、代理分配逻辑检查
-
-### 2.1 代理收益计算
-
-✅ **代理收益计算逻辑正确**
-
-**计算公式**:
-```go
-实际底价 = 基础底价 + 等级加成
-提价成本 = (设定价格 - 提价阈值) × 提价手续费比例(当设定价格 > 提价阈值时)
-代理收益 = 设定价格 - 实际底价 - 提价成本
-```
-
-**位置**:`app/main/api/internal/service/agentService.go:131-132`
-
-**验证**:
-- ✅ 使用产品配置的底价(从 `agent_product_config` 表读取)
-- ✅ 等级加成从配置表读取(支持动态配置)
-- ✅ 提价成本计算逻辑正确
-
-### 2.2 等级加成返佣分配
-
-#### 2.2.1 黄金代理(等级加成3元)
-
-✅ **实现正确**
-- 全部给钻石上级
-- 找不到钻石上级时,返佣归平台
-
-**位置**:`app/main/api/internal/service/agentService.go:233-244`
-
-#### 2.2.2 普通代理(等级加成6元)
-
-✅ **已按照新规则修复**
-
-**新规则**(已确认并实现):
-1. **直接上级是钻石**:等级加成全部给钻石上级
-2. **直接上级是黄金**:一部分给黄金上级(配置:`normal_to_gold_rebate`,默认3元),剩余给钻石上级
-3. **直接上级是普通**:
- - 一部分给直接上级普通(配置:`normal_to_normal_rebate`,默认2元)
- - 剩余金额:
- - 有钻石上级:剩余全部给钻石上级
- - 只有黄金上级:最多给黄金上级(配置:`normal_to_gold_rebate_max`,默认3元),超出归平台
- - 都没有:全部归平台
-
-**代码实现**(`app/main/api/internal/service/agentService.go:254-368`):
-- ✅ 按照新规则完全重写
-- ✅ 支持从配置表读取返佣金额(如果配置不存在使用默认值)
-- ✅ 跳过多层普通代理,直接查找钻石/黄金上级
-
-**配置项**:
-- `normal_to_normal_rebate`:普通代理给直接上级普通的金额(默认2元)
-- `normal_to_gold_rebate`:普通代理给直接上级黄金的金额(默认3元)
-- `normal_to_gold_rebate_max`:普通代理给黄金上级的最大金额(默认3元)
-
-### 2.3 代理收益分配流程
-
-✅ **分配流程正确**
-
-**流程**:
-1. 检查是否是代理订单
-2. 检查订单是否已处理(防重复)
-3. 获取代理信息和产品配置
-4. 使用事务处理(保证原子性):
- - 计算代理收益
- - 更新代理订单状态
- - 发放代理佣金到代理钱包
- - 分配等级加成返佣给上级链
-
-**位置**:`app/main/api/internal/service/agentService.go:76-156`
-
-### 2.4 事务处理和错误处理
-
-✅ **事务处理正确**
-
-**验证**:
-- ✅ 使用 `AgentWalletModel.Trans` 事务处理
-- ✅ 所有数据库操作在同一事务中
-- ✅ 任何步骤失败都会回滚
-
-**位置**:`app/main/api/internal/service/agentService.go:109`
-
-### 2.5 防重复处理
-
-✅ **防重复处理正确**
-
-**验证**:
-- ✅ 检查 `agent_order.ProcessStatus == 1` 判断是否已处理
-- ✅ 已处理的订单直接返回,不重复处理
-
-**位置**:`app/main/api/internal/service/agentService.go:87-91`
-
----
-
-## 三、其他发现
-
-### 3.1 重试机制
-
-✅ **已有手动重试接口**
-
-**位置**:
-- 接口:`POST /api/v1/admin/order/retry-agent-process/:id`
-- Logic:`app/main/api/internal/logic/admin_order/adminretryagentprocesslogic.go`
-
-**功能**:
-- 管理员可以手动触发代理处理重试
-- 可以处理代理处理失败的情况
-
-### 3.2 代理处理失败时的错误处理
-
-**当前行为**:
-- 代理处理失败会触发 `handleError`
-- `handleError` 会尝试退款(如果报告状态为 pending)
-- 但报告已经生成成功,可能不会触发退款
-
-**建议**:
-- 代理处理失败不应该触发退款(因为报告已经成功生成)
-- 应该记录错误日志,供管理员手动重试
-
----
-
-## 四、总结和建议
-
-### 4.1 需要确认的问题
-
-1. **普通代理等级加成返佣分配规则**
- - 文档和代码实现不一致
- - 需要确认正确的业务规则
-
-### 4.2 建议改进
-
-1. **代理处理失败的处理**
- - 建议将代理处理改为独立的异步任务,或至少确保失败不影响报告使用
- - 失败时只记录日志,不触发退款
-
-2. **文档更新**
- - 根据最终确认的业务规则,更新文档或代码
-
-3. **日志增强**
- - 在代理处理失败时记录更详细的日志,方便排查问题
-
-### 4.3 整体评价
-
-- ✅ 报告查询链路基本通顺
-- ✅ 代理收益计算逻辑正确
-- ✅ 事务处理和防重复处理正确
-- ⚠️ 普通代理等级加成返佣分配规则需要确认
-- ⚠️ 代理处理失败时的处理逻辑可以优化
-
----
-
-## 五、代码位置索引
-
-### 报告查询链路
-- 支付回调:`app/main/api/internal/logic/pay/alipaycallbacklogic.go:56-106`
-- 异步任务:`app/main/api/internal/queue/paySuccessNotify.go:35-178`
-- 报告查询:`app/main/api/internal/logic/query/querylistlogic.go`
-
-### 代理分配逻辑
-- 代理处理入口:`app/main/api/internal/service/agentService.go:76-156`
-- 收益计算:`app/main/api/internal/service/agentService.go:131-132`
-- 等级加成返佣:`app/main/api/internal/service/agentService.go:226-328`
-- 普通代理返佣分配:`app/main/api/internal/service/agentService.go:254-328`
-
diff --git a/新代理系统完整文档.md b/新代理系统完整文档.md
deleted file mode 100644
index 457468c..0000000
--- a/新代理系统完整文档.md
+++ /dev/null
@@ -1,1833 +0,0 @@
-# 新代理系统完整文档
-
-## 文档说明
-
-本文档详细记录了新代理系统的完整链路、业务逻辑、数据表结构、API接口和配置说明,用于后续系统调整和维护。
-
-**文档版本**: v1.0
-**最后更新**: 2024年
-**维护人员**: 开发团队
-
----
-
-## 目录
-
-1. [系统概述](#一系统概述)
-2. [数据表结构](#二数据表结构)
-3. [核心业务规则](#三核心业务规则)
-4. [完整链路流程](#四完整链路流程)
-5. [关键代码逻辑](#五关键代码逻辑)
-6. [API接口列表](#六api接口列表)
-7. [系统配置说明](#七系统配置说明)
-8. [数据流转图](#八数据流转图)
-
----
-
-## 一、系统概述
-
-### 1.1 系统定位
-
-新代理系统是一个三级代理分销系统,**用户只能通过邀请码成为代理**,支持邀请码管理、推广、收益分配、升级和提现等完整业务流程。
-
-### 1.2 核心特性
-
-- **三级代理体系**:普通(Level 1) → 黄金(Level 2) → 钻石(Level 3)
-- **团队结构管理**:钻石代理作为团队首领,管理整个团队
-- **邀请码机制**:**用户不能自主成为代理,只能通过邀请码成为代理**
- - 平台发放钻石邀请码:管理员生成,用户使用后成为钻石代理(**只能使用一次,使用后立即失效**)
- - 代理发放邀请码:代理生成,用户使用后成为该代理的下级(**普通邀请码可以无限使用**)
- - 邀请链接/二维码:代理生成,用户通过链接注册成为该代理的下级
-- **灵活的价格体系**:代理可自定义推广价格,系统自动计算收益
-- **智能收益分配**:自动计算代理收益和上级返佣
-- **升级机制**:支持自主付费升级和钻石代理升级下级
-- **税费管理**:月度累计提现,自动计算税费
-- **实名认证**:提现前必须完成实名认证(三要素核验,无需审核)
-
-### 1.3 技术架构
-
-- **框架**: Go-Zero
-- **数据库**: MySQL
-- **缓存**: Redis
-- **支付**: 支付宝、微信支付
-- **加密**: AES加密(手机号、身份证号)
-
----
-
-## 二、数据表结构
-
-### 2.1 核心数据表
-
-#### 2.1.1 agent(代理基本信息表)
-
-| 字段 | 类型 | 说明 |
-| -------------- | ------------ | -------------------------------- |
-| id | bigint | 主键ID |
-| user_id | bigint | 用户ID(唯一) |
-| level | tinyint | 代理等级:1=普通,2=黄金,3=钻石 |
-| region | varchar(50) | 区域(可选) |
-| mobile | varchar(50) | 手机号(加密) |
-| wechat_id | varchar(100) | 微信号(可选) |
-| team_leader_id | bigint | 团队首领ID(钻石代理的ID) |
-| create_time | datetime | 创建时间 |
-| update_time | datetime | 更新时间 |
-| delete_time | datetime | 删除时间 |
-| del_state | tinyint | 删除状态:0=未删除,1=已删除 |
-| version | bigint | 版本号(乐观锁) |
-
-**索引**:
-- PRIMARY KEY (`id`)
-- UNIQUE KEY `uk_user_id` (`user_id`)
-- KEY `idx_mobile` (`mobile`)
-- KEY `idx_level` (`level`)
-- KEY `idx_team_leader_id` (`team_leader_id`)
-
-#### 2.1.2 agent_wallet(代理钱包表)
-
-| 字段 | 类型 | 说明 |
-| ---------------- | ------------- | -------------- |
-| id | bigint | 主键ID |
-| agent_id | bigint | 代理ID(唯一) |
-| balance | decimal(10,2) | 可用余额 |
-| frozen_balance | decimal(10,2) | 冻结余额 |
-| total_earnings | decimal(10,2) | 累计收益 |
-| withdrawn_amount | decimal(10,2) | 已提现金额 |
-| create_time | datetime | 创建时间 |
-| update_time | datetime | 更新时间 |
-| delete_time | datetime | 删除时间 |
-| del_state | tinyint | 删除状态 |
-| version | bigint | 版本号 |
-
-#### 2.1.4 agent_relation(代理关系表)
-
-| 字段 | 类型 | 说明 |
-| ------------- | ------------ | ------------------------------ |
-| id | bigint | 主键ID |
-| parent_id | bigint | 上级代理ID |
-| child_id | bigint | 下级代理ID |
-| relation_type | tinyint | 关系类型:1=直接关系,2=已脱离 |
-| detach_reason | varchar(200) | 脱离原因 |
-| detach_time | datetime | 脱离时间 |
-| create_time | datetime | 创建时间 |
-| update_time | datetime | 更新时间 |
-| delete_time | datetime | 删除时间 |
-| del_state | tinyint | 删除状态 |
-| version | bigint | 版本号 |
-
-**关系类型说明**:
-- `relation_type = 1`: 直接关系(正常上下级)
-- `relation_type = 2`: 已脱离(升级后脱离直接关系)
-
-**唯一约束**:
-- UNIQUE KEY `uk_parent_child_type` (`parent_id`, `child_id`, `relation_type`)
-
-#### 2.1.5 agent_link(推广链接表)
-
-| 字段 | 类型 | 说明 |
-| ----------------- | ------------- | ----------------------------- |
-| id | bigint | 主键ID |
-| agent_id | bigint | 代理ID |
-| user_id | bigint | 用户ID |
-| product_id | bigint | 产品ID |
-| link_identifier | varchar(200) | 链接标识(加密) |
-| set_price | decimal(10,2) | 设定价格 |
-| actual_base_price | decimal(10,2) | 实际底价(基础底价+等级加成) |
-| create_time | datetime | 创建时间 |
-| update_time | datetime | 更新时间 |
-| delete_time | datetime | 删除时间 |
-| del_state | tinyint | 删除状态 |
-| version | bigint | 版本号 |
-
-**唯一约束**:
-- UNIQUE KEY `uk_agent_product_price` (`agent_id`, `product_id`, `set_price`, `del_state`)
-
-#### 2.1.6 agent_order(代理订单表)
-
-| 字段 | 类型 | 说明 |
-| ----------------- | ------------- | ------------------------------------------ |
-| id | bigint | 主键ID |
-| agent_id | bigint | 代理ID |
-| order_id | bigint | 订单ID |
-| product_id | bigint | 产品ID |
-| order_amount | decimal(10,2) | 订单金额(SetPrice) |
-| set_price | decimal(10,2) | 设定价格 |
-| actual_base_price | decimal(10,2) | 实际底价 |
-| price_cost | decimal(10,2) | 提价成本 |
-| agent_profit | decimal(10,2) | 代理收益 |
-| process_status | tinyint | 处理状态:0=待处理,1=处理成功,2=处理失败 |
-| process_time | datetime | 处理时间 |
-| process_remark | varchar(500) | 处理备注 |
-| create_time | datetime | 创建时间 |
-| update_time | datetime | 更新时间 |
-| delete_time | datetime | 删除时间 |
-| del_state | tinyint | 删除状态 |
-| version | bigint | 版本号 |
-
-**处理状态说明**:
-- `process_status = 0`: 待处理(订单创建后)
-- `process_status = 1`: 处理成功(收益已分配)
-- `process_status = 2`: 处理失败
-
-#### 2.1.7 agent_commission(代理佣金表)
-
-| 字段 | 类型 | 说明 |
-| ----------- | ------------- | -------------------- |
-| id | bigint | 主键ID |
-| agent_id | bigint | 代理ID |
-| order_id | bigint | 订单ID |
-| product_id | bigint | 产品ID |
-| amount | decimal(10,2) | 佣金金额(代理收益) |
-| status | tinyint | 状态:1=已发放 |
-| create_time | datetime | 创建时间 |
-| update_time | datetime | 更新时间 |
-| delete_time | datetime | 删除时间 |
-| del_state | tinyint | 删除状态 |
-| version | bigint | 版本号 |
-
-#### 2.1.8 agent_rebate(代理返佣表)
-
-| 字段 | 类型 | 说明 |
-| --------------- | ------------- | -------------------------------------------------------- |
-| id | bigint | 主键ID |
-| agent_id | bigint | 获得返佣的代理ID |
-| source_agent_id | bigint | 来源代理ID(产生订单的代理) |
-| order_id | bigint | 订单ID |
-| product_id | bigint | 产品ID |
-| rebate_type | tinyint | 返佣类型:1=直接上级返佣,2=钻石上级返佣,3=黄金上级返佣 |
-| level_bonus | decimal(10,2) | 等级加成金额 |
-| rebate_amount | decimal(10,2) | 返佣金额 |
-| status | tinyint | 状态:1=已发放 |
-| create_time | datetime | 创建时间 |
-| update_time | datetime | 更新时间 |
-| delete_time | datetime | 删除时间 |
-| del_state | tinyint | 删除状态 |
-| version | bigint | 版本号 |
-
-**返佣类型说明**:
-- `rebate_type = 1`: 直接上级返佣(普通代理的等级加成给直接上级)
-- `rebate_type = 2`: 钻石上级返佣(给钻石上级的返佣)
-- `rebate_type = 3`: 黄金上级返佣(给黄金上级的返佣)
-
-#### 2.1.9 agent_upgrade(代理升级表)
-
-| 字段 | 类型 | 说明 |
-| ----------------- | ------------- | ------------------------------------ |
-| id | bigint | 主键ID |
-| agent_id | bigint | 代理ID |
-| from_level | tinyint | 原等级 |
-| to_level | tinyint | 目标等级 |
-| upgrade_type | tinyint | 升级类型:1=自主付费,2=钻石升级下级 |
-| upgrade_fee | decimal(10,2) | 升级费用 |
-| rebate_amount | decimal(10,2) | 返佣金额(给原直接上级) |
-| rebate_agent_id | bigint | 返佣代理ID(原直接上级) |
-| operator_agent_id | bigint | 操作代理ID(钻石升级下级时使用) |
-| order_no | varchar(100) | 支付订单号 |
-| status | tinyint | 状态:1=待处理,2=已完成,3=已失败 |
-| remark | varchar(500) | 备注 |
-| create_time | datetime | 创建时间 |
-| update_time | datetime | 更新时间 |
-| delete_time | datetime | 删除时间 |
-| del_state | tinyint | 删除状态 |
-| version | bigint | 版本号 |
-
-**升级类型说明**:
-- `upgrade_type = 1`: 自主付费升级(代理自己付费)
-- `upgrade_type = 2`: 钻石升级下级(钻石代理操作,免费)
-
-#### 2.1.10 agent_withdrawal(代理提现表)
-
-| 字段 | 类型 | 说明 |
-| ------------- | ------------- | ------------------------------------------------------------------------ |
-| id | bigint | 主键ID |
-| agent_id | bigint | 代理ID |
-| withdraw_no | varchar(100) | 提现单号(唯一) |
-| payee_account | varchar(100) | 收款账户(支付宝账号) |
-| payee_name | varchar(100) | 收款人姓名 |
-| amount | decimal(10,2) | 提现金额 |
-| tax_amount | decimal(10,2) | 税费金额 |
-| actual_amount | decimal(10,2) | 实际到账金额 |
-| status | tinyint | 状态:1=待审核,2=审核通过,3=审核拒绝,4=提现中,5=提现成功,6=提现失败 |
-| remark | varchar(500) | 备注 |
-| create_time | datetime | 创建时间 |
-| update_time | datetime | 更新时间 |
-| delete_time | datetime | 删除时间 |
-| del_state | tinyint | 删除状态 |
-| version | bigint | 版本号 |
-
-**状态说明**:
-- `status = 1`: 待审核(代理申请后)
-- `status = 2`: 审核通过(管理员审核通过,准备转账)
-- `status = 3`: 审核拒绝(管理员拒绝)
-- `status = 4`: 提现中(已调用支付宝转账接口,处理中)
-- `status = 5`: 提现成功(转账成功)
-- `status = 6`: 提现失败(转账失败)
-
-#### 2.1.11 agent_withdrawal_tax(代理提现扣税表)
-
-| 字段 | 类型 | 说明 |
-| ----------------- | ------------- | ------------------------------ |
-| id | bigint | 主键ID |
-| agent_id | bigint | 代理ID |
-| withdrawal_id | bigint | 提现记录ID |
-| year_month | bigint | 年月(格式:YYYYMM,如202401) |
-| withdrawal_amount | decimal(10,2) | 提现金额 |
-| taxable_amount | decimal(10,2) | 应税金额 |
-| tax_rate | decimal(5,4) | 税率 |
-| tax_amount | decimal(10,2) | 税费金额 |
-| actual_amount | decimal(10,2) | 实际到账金额 |
-| tax_status | tinyint | 扣税状态:1=待扣税,2=已扣税 |
-| tax_time | datetime | 扣税时间 |
-| remark | varchar(500) | 备注 |
-| create_time | datetime | 创建时间 |
-| update_time | datetime | 更新时间 |
-| delete_time | datetime | 删除时间 |
-| del_state | tinyint | 删除状态 |
-| version | bigint | 版本号 |
-
-**扣税状态说明**:
-- `tax_status = 1`: 待扣税(提现申请后)
-- `tax_status = 2`: 已扣税(提现成功后)
-
-#### 2.1.12 agent_config(代理系统配置表)
-
-| 字段 | 类型 | 说明 |
-| ------------ | ------------ | ----------------------------------------------------------------------------- |
-| id | bigint | 主键ID |
-| config_key | varchar(100) | 配置键(唯一) |
-| config_value | varchar(500) | 配置值 |
-| config_type | varchar(50) | 配置类型:price=价格,bonus=等级加成,upgrade=升级费用,rebate=返佣,tax=税费 |
-| description | varchar(500) | 配置描述 |
-| create_time | datetime | 创建时间 |
-| update_time | datetime | 更新时间 |
-| delete_time | datetime | 删除时间 |
-| del_state | tinyint | 删除状态 |
-| version | bigint | 版本号 |
-
-**配置键列表**:
-- `base_price`: 基础底价
-- `system_max_price`: 系统价格上限
-- `price_threshold`: 提价标准阈值
-- `price_fee_rate`: 提价手续费比例
-- `level_1_bonus`: 普通代理等级加成(6元)
-- `level_2_bonus`: 黄金代理等级加成(3元)
-- `level_3_bonus`: 钻石代理等级加成(0元)
-- `upgrade_to_gold_fee`: 升级为黄金费用(199元)
-- `upgrade_to_diamond_fee`: 升级为钻石费用(980元)
-- `upgrade_to_gold_rebate`: 升级为黄金返佣(139元)
-- `upgrade_to_diamond_rebate`: 升级为钻石返佣(680元)
-- `tax_rate`: 税率(默认0.06,即6%)
-- `tax_exemption_amount`: 免税额度(默认0)
-
-#### 2.1.13 agent_product_config(代理产品配置表)
-
-| 字段 | 类型 | 说明 |
-| ---------------- | ------------- | ------------------------------------ |
-| id | bigint | 主键ID |
-| product_id | bigint | 产品ID(唯一) |
-| product_name | varchar(100) | 产品名称 |
-| base_price | decimal(10,2) | 基础底价(可覆盖系统配置) |
-| system_max_price | decimal(10,2) | 系统价格上限(可覆盖系统配置) |
-| price_threshold | decimal(10,2) | 提价标准阈值(可选,覆盖系统配置) |
-| price_fee_rate | decimal(5,4) | 提价手续费比例(可选,覆盖系统配置) |
-| create_time | datetime | 创建时间 |
-| update_time | datetime | 更新时间 |
-| delete_time | datetime | 删除时间 |
-| del_state | tinyint | 删除状态 |
-| version | bigint | 版本号 |
-
-#### 2.1.14 agent_real_name(代理实名认证表)
-
-| 字段 | 类型 | 说明 |
-| ----------- | ----------- | ---------------------------------------------- |
-| id | bigint | 主键ID |
-| agent_id | bigint | 代理ID(唯一) |
-| name | varchar(50) | 真实姓名 |
-| id_card | varchar(50) | 身份证号(加密) |
-| mobile | varchar(50) | 手机号(加密) |
-| verify_time | datetime | 验证时间(三要素验证通过时间,NULL表示未验证) |
-| create_time | datetime | 创建时间 |
-| update_time | datetime | 更新时间 |
-| delete_time | datetime | 删除时间 |
-| del_state | tinyint | 删除状态 |
-| version | bigint | 版本号 |
-
-**验证说明**:
-- `verify_time IS NULL`: 未验证(未通过三要素核验或未提交)
-- `verify_time IS NOT NULL`: 已通过(三要素核验通过,可以提现)
-
-**重要**:实名认证改为三要素核验,无需人工审核。提交实名认证信息后,系统自动进行三要素核验(姓名、身份证号、手机号),核验通过后自动设置 `verify_time`,无需管理员审核。
-
-### 2.2 数据表关系图
-
-```
-agent (代理表)
-├── agent_wallet (钱包表) 1:1
-├── agent_link (推广链接表) 1:N
-├── agent_order (代理订单表) 1:N
-├── agent_relation (关系表) 1:N (作为parent_id)
-│ └── agent_relation (关系表) 1:N (作为child_id)
-├── agent_commission (佣金表) 1:N
-├── agent_rebate (返佣表) 1:N (作为agent_id)
-│ └── agent_rebate (返佣表) 1:N (作为source_agent_id)
-├── agent_upgrade (升级表) 1:N
-├── agent_withdrawal (提现表) 1:N
-│ └── agent_withdrawal_tax (扣税表) 1:N
-└── agent_real_name (实名认证表) 1:1
-```
-
----
-
-## 三、核心业务规则
-
-### 3.1 代理等级体系
-
-#### 3.1.1 等级定义
-
-| 等级 | 数值 | 名称 | 说明 |
-| ------- | ---- | -------- | ---------------------------------- |
-| Level 1 | 1 | 普通代理 | 初始等级,所有新代理默认等级 |
-| Level 2 | 2 | 黄金代理 | 中级等级,可通过付费或钻石升级获得 |
-| Level 3 | 3 | 钻石代理 | 最高等级,团队首领,可通过付费获得 |
-
-#### 3.1.2 等级加成规则
-
-| 等级 | 等级加成 | 说明 |
-| -------- | -------- | ------------------------- |
-| 普通代理 | +6元 | 实际底价 = 基础底价 + 6元 |
-| 黄金代理 | +3元 | 实际底价 = 基础底价 + 3元 |
-| 钻石代理 | +0元 | 实际底价 = 基础底价 |
-
-**计算公式**:
-```
-实际底价 = 基础底价 + 等级加成
-```
-
-### 3.2 团队结构规则
-
-#### 3.2.1 团队定义
-
-- **团队**: 由一个钻石代理作为首领,及其所有下级代理组成的层级关系链
-- **团队首领**: 必须是钻石代理,每个团队有且仅有一个首领
-- **团队关系**: 通过 `team_leader_id` 字段关联,所有团队成员指向同一个钻石代理
-
-#### 3.2.2 上下级关系约束
-
-**核心原则**:
-1. **下级不能比上级等级高**: 下级等级必须 ≤ 上级等级
-2. **同级不能作为上下级**(除了普通代理): 黄金和钻石不能作为同级上下级
-
-**允许的关系**:
-- 普通 → 普通 ✓(同级普通允许)
-- 黄金 → 普通 ✓(上级等级高于下级)
-- 钻石 → 普通 ✓(上级等级高于下级)
-- 钻石 → 黄金 ✓(上级等级高于下级,允许建立关系)
-
-**禁止的关系**:
-- 普通 → 黄金 ✗(下级等级高于上级)
-- 普通 → 钻石 ✗(下级等级高于上级)
-- 黄金 → 黄金 ✗(同级不能作为上下级)
-- 钻石 → 钻石 ✗(同级不能作为上下级)
-- 黄金 → 钻石 ✗(下级等级高于上级)
-
-**升级后脱离关系的规则**:
-- 下级等级 > 上级等级 → 脱离关系
-- 同级(黄金或钻石)→ 脱离关系
-- 普通代理同级 → 不脱离关系(允许保持)
-
-#### 3.2.3 升级规则
-
-**核心规则**: 代理升级后,其所有下级(直接+间接)会跟随该代理。
-
-**升级场景**:
-
-1. **普通 → 黄金**:
- - 升级后根据上级等级决定是否脱离关系:
- * 如果上级是普通代理:必须脱离(下级等级高于上级)
- * 如果上级是黄金代理:必须脱离(同级不能作为上下级)
- * 如果上级是钻石代理:不脱离(钻石等级高于黄金,允许保持关系)
- - 保留团队关系(通过团队首领钻石代理)
- - 仍属于原团队
- - 所有下级(直接+间接)继续跟随该代理
-
-2. **黄金 → 钻石**:
- - 独立成为新团队
- - 成为新团队的首领(`team_leader_id = 自己`)
- - 所有下级(直接+间接)跟随该代理到新团队
-
-3. **普通 → 钻石**:
- - 独立成为新团队
- - 成为新团队的首领
- - 所有下级(直接+间接)跟随该代理到新团队
-
-#### 3.2.4 升级方法和费用规则
-
-**升级方式**:
-
-1. **钻石代理升级下级**:
- - 钻石代理可以将下级的普通代理升级为黄金代理
- - 升级方式: 钻石代理操作,无需被升级代理付费
- - 限制: 只能升级普通代理为黄金代理
-
-2. **代理自主付费升级**:
- - 普通代理可以付费升级为黄金代理(199元)
- - 普通代理可以付费升级为钻石代理(980元)
- - 黄金代理可以付费升级为钻石代理(980元)
-
-**升级费用和返佣规则**:
-
-| 升级类型 | 升级费用 | 直接上级返佣 | 说明 |
-| ------------------------- | -------- | ------------ | -------------------------------- |
-| 普通→黄金 | 199元 | 139元 | 付费后立即返佣给直接上级 |
-| 普通→钻石 | 980元 | 680元 | 付费后立即返佣给直接上级 |
-| 黄金→钻石 | 980元 | 680元 | 付费后立即返佣给直接上级 |
-| 钻石升级下级(普通→黄金) | 免费 | 无 | 钻石代理操作,被升级代理无需付费 |
-
-**重要规则**:
-- ✅ **返佣给原直接上级**: 即使升级后脱离直接上下级关系,返佣仍然给原直接上级
-- ✅ **返佣时机**: 付费成功后立即返佣,然后执行升级操作
-- ✅ **升级流程**: 付费 → 返佣给直接上级 → 升级 → 根据情况脱离关系
-
-### 3.3 价格体系规则
-
-#### 3.3.1 价格计算公式
-
-```
-实际底价 = 基础底价 + 等级加成
-代理收益 = 设定价格 - 实际底价 - 提价成本
-提价成本 = (设定价格 - 提价阈值) × 提价手续费比例(当设定价格 > 提价阈值时)
-```
-
-#### 3.3.2 价格约束
-
-- **最低价格**: 实际底价(基础底价 + 等级加成)
-- **最高价格**: 系统价格上限(或产品配置的价格上限)
-- **价格范围**: `实际底价 ≤ 设定价格 ≤ 系统价格上限`
-
-#### 3.3.3 提价成本计算
-
-- **当设定价格 ≤ 提价阈值**: 提价成本 = 0
-- **当设定价格 > 提价阈值**: 提价成本 = (设定价格 - 提价阈值) × 提价手续费比例
-
-### 3.4 收益分配规则
-
-#### 3.4.1 代理收益
-
-代理收益 = 设定价格 - 实际底价 - 提价成本
-
-**分配流程**:
-1. 订单支付成功后,触发 `AgentService.AgentProcess`
-2. 计算代理收益并发放到代理钱包
-3. 创建 `agent_commission` 记录
-4. 更新 `agent_wallet` 余额和累计收益
-
-#### 3.4.2 等级加成返佣分配
-
-**核心原则**:
-- **基础底价(BasePrice)**:无论哪个等级,全部归平台(固定收入)
-- **等级加成部分**:作为返佣分配给上级链
-
-**分配规则总结表**:
-
-| 代理等级 | 等级加成 | 直接上级 | 等级加成返佣分配规则 |
-| -------- | -------- | -------- | ------------------------------------------------------------------------------------------------------------------ |
-| 钻石 | 0元 | 无 | 无返佣分配(加成为0) |
-| 黄金 | 3元 | 钻石 | 3元全部给钻石上级 |
-| 普通 | 6元 | 钻石 | 6元全部给钻石上级 |
-| 普通 | 6元 | 黄金 | 3元给黄金上级,3元给钻石上级(上上级) |
-| 普通 | 6元 | 普通 | 2元给直接上级普通,剩余4元:
- 有钻石:4元全部给钻石
- 只有黄金:3元给黄金,1元归平台
- 都没有:4元归平台 |
-
-**详细规则说明**:
-
-1. **普通代理(等级加成,例如6元)**:
- - **直接上级是钻石**:等级加成全部给钻石上级
- - **直接上级是黄金**:一部分给黄金上级(配置:`normal_to_gold_rebate`,默认3元),剩余给钻石上级(上上级)
- - **直接上级是普通**:
- - 一部分给直接上级普通(配置:`normal_to_normal_rebate`,默认2元)
- - 剩余金额:
- - 有钻石上级:剩余全部给钻石上级
- - 只有黄金上级:最多给黄金上级(配置:`normal_to_gold_rebate_max`,默认3元),超出部分归平台
- - 都没有:全部归平台
- - **注意**:如果在团队上下级链路中这中间有很多层普通代理,给直接上级的金额只给推广人的直接上级,对于中间的会直接跳过,然后到黄金/钻石代理
-
-2. **黄金代理(等级加成,例如3元)**:
- - 全部给钻石上级(如有)
- - 如果找不到钻石上级,归平台
-
-3. **钻石代理(等级加成0元)**:
- - 无返佣分配
-
-**配置项说明**:
-- 返佣金额的具体取值从配置表读取,支持动态配置
-- 配置键:
- - `normal_to_normal_rebate`:普通代理给直接上级普通的金额(默认2元)
- - `normal_to_gold_rebate`:普通代理给直接上级黄金的金额(默认3元)
- - `normal_to_gold_rebate_max`:普通代理给黄金上级的最大金额(默认3元)
-- 如果配置不存在,使用默认值
-
-#### 3.4.3 返佣记录
-
-所有返佣都会记录到 `agent_rebate` 表,包含:
-- 获得返佣的代理ID
-- 来源代理ID(产生订单的代理)
-- 返佣类型(1=直接上级,2=钻石上级,3=黄金上级)
-- 返佣金额
-- 等级加成金额
-
-### 3.5 提现规则
-
-#### 3.5.1 提现条件
-
-1. **实名认证**: 必须完成实名认证且三要素核验已通过(`verify_time` 不为空)
-2. **余额充足**: 钱包余额 >= 提现金额
-3. **账户信息**: 必须提供支付宝账号和收款人姓名
-
-#### 3.5.2 税费计算规则
-
-**计算公式**:
-```
-本月累计提现金额 = 查询本月所有已提现金额之和
-剩余免税额度 = 免税额度 - 本月累计提现金额
-
-如果 本次提现金额 <= 剩余免税额度:
- 应税金额 = 0
- 税费 = 0
-否则:
- 应税金额 = 本次提现金额 - 剩余免税额度
- 税费 = 应税金额 × 税率
-
-实际到账金额 = 提现金额 - 税费
-```
-
-**示例**:
-- 免税额度: 1000元
-- 税率: 6%
-- 本月已提现: 800元
-- 本次提现: 500元
-
-计算:
-- 剩余免税额度 = 1000 - 800 = 200元
-- 本次提现500元 > 剩余免税额度200元
-- 应税金额 = 500 - 200 = 300元
-- 税费 = 300 × 6% = 18元
-- 实际到账 = 500 - 18 = 482元
-
-#### 3.5.3 提现流程
-
-1. **代理申请提现**:
- - 验证实名认证状态
- - 验证余额
- - 计算税费
- - 冻结余额
- - 创建提现记录和扣税记录
-
-2. **管理员审核**:
- - 审核通过: 调用支付宝转账接口
- - 审核拒绝: 解冻余额,返回余额
-
-3. **转账处理**:
- - 转账成功: 解冻并扣除余额,更新扣税状态
- - 转账失败: 解冻余额,返回余额
- - 处理中: 保持提现中状态,后续轮询更新
-
----
-
-## 四、完整链路流程
-
-### 4.1 通过邀请码成为代理链路
-
-**重要规则**:用户不能自主成为代理,只能通过邀请码成为代理。成为代理的唯一途径包括:
-1. 平台发放钻石邀请码
-2. 代理发放邀请码
-3. 代理邀请链接/二维码
-
-```
-┌─────────────────────────────────────────────────────────────┐
-│ 1. 用户通过邀请码申请成为代理 │
-│ API: POST /api/v1/agent/apply │
-│ Logic: ApplyForAgentLogic │
-│ - 必须提供邀请码(必填) │
-│ - 如果没有邀请码,直接拒绝 │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 2. 验证手机验证码 │
-│ - 从Redis读取验证码 │
-│ - 验证码校验 │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 3. 用户注册/绑定(如需要) │
-│ - 检查用户是否存在 │
-│ - 不存在则注册新用户 │
-│ - 临时用户则绑定为正式用户 │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 4. 检查是否已是代理 │
-│ - 检查是否已是代理(agent表) │
-│ - 如果已是代理,直接拒绝 │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 5. 验证邀请码 │
-│ - 查询邀请码是否存在 │
-│ - 验证邀请码状态(未使用、未过期) │
-│ - 获取邀请码信息(目标等级、发放代理ID) │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 6. 创建代理记录 │
-│ - 根据邀请码的target_level设置代理等级 │
-│ - 如果是代理发放的邀请码,建立上下级关系 │
-│ - 如果是平台发放的钻石邀请码,独立成团队 │
-│ - 初始化钱包 │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 7. 更新邀请码状态 │
-│ - **钻石邀请码**:状态更新为"已使用"(status=1),使用后立即失效│
-│ - **普通邀请码**:不更新状态,保持未使用(status=0),可继续使用│
-│ - 记录使用用户ID和代理ID(用于统计) │
-│ - 记录使用时间 │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 8. 生成并返回Token │
-│ - 生成JWT Token │
-│ - 返回给前端 │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 8. 完成(无需审核,直接成为代理) │
-│ - 代理记录已创建 │
-│ - 钱包已初始化 │
-│ - 关系已建立(如有上级) │
-│ - 团队首领已设置 │
-│ - 微信号和区域为可选字段 │
-└─────────────────────────────────────────────────────────────┘
-```
-
-### 4.2 推广链接生成链路
-
-```
-┌─────────────────────────────────────────────────────────────┐
-│ 1. 代理生成推广链接 │
-│ API: POST /api/v1/agent/link/generate │
-│ Logic: GeneratingLinkLogic │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 2. 获取代理信息 │
-│ - 从Token获取用户ID │
-│ - 查询 agent 表 │
-│ - 验证代理身份 │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 3. 获取系统配置 │
-│ - base_price(基础底价) │
-│ - system_max_price(系统价格上限) │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 4. 计算实际底价 │
-│ 实际底价 = 基础底价 + 等级加成 │
-│ - 普通代理: +6元 │
-│ - 黄金代理: +3元 │
-│ - 钻石代理: +0元 │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 5. 验证设定价格范围 │
-│ - 实际底价 ≤ 设定价格 ≤ 系统价格上限 │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 6. 检查是否已存在相同链接 │
-│ - 查询 agent_link 表 │
-│ - 条件: agent_id + product_id + set_price + del_state=0 │
-│ - 如果存在,直接返回已有链接 │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 7. 生成推广链接标识 │
-│ - 构建 AgentIdentifier 结构 │
-│ { AgentID, ProductID, SetPrice } │
-│ - JSON序列化 │
-│ - AES加密生成 LinkIdentifier │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 8. 保存推广链接 │
-│ - 插入 agent_link 表 │
-│ - 记录 agent_id, product_id, link_identifier │
-│ - 记录 set_price, actual_base_price │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 9. 返回加密后的 LinkIdentifier │
-│ - 前端使用此标识生成推广链接 │
-└─────────────────────────────────────────────────────────────┘
-```
-
-### 4.3 订单处理与收益分配链路
-
-```
-┌─────────────────────────────────────────────────────────────┐
-│ 1. 用户通过推广链接下单 │
-│ API: POST /api/v1/pay/payment │
-│ Logic: PaymentLogic │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 2. 解析推广链接标识 │
-│ - 解密 LinkIdentifier │
-│ - 解析 AgentIdentifier │
-│ - 获取 AgentID, ProductID, SetPrice │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 3. 查询推广链接信息 │
-│ - 查询 agent_link 表 │
-│ - 验证链接有效性 │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 4. 创建订单记录 │
-│ - 插入 order 表 │
-│ - 记录订单金额(SetPrice) │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 5. 创建代理订单记录 │
-│ - 插入 agent_order 表 │
-│ - OrderAmount = SetPrice │
-│ - SetPrice = 设定价格 │
-│ - ActualBasePrice = 实际底价(基础底价+等级加成) │
-│ - PriceCost = 提价成本 │
-│ - AgentProfit = 代理收益(SetPrice - ActualBasePrice - PriceCost)│
-│ - ProcessStatus = 0(待处理) │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 6. 用户支付订单 │
-│ - 调用支付宝/微信支付接口 │
-│ - 生成支付订单 │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 7. 支付成功回调 │
-│ - 支付宝/微信支付回调 │
-│ - 验证签名 │
-│ - 更新订单状态 │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 8. 触发代理订单处理 │
-│ Service: AgentService.AgentProcess │
-│ - 检查是否是代理订单 │
-│ - 检查订单是否已处理(防重复) │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 9. 获取代理信息和系统配置 │
-│ - 查询 agent 表 │
-│ - 获取代理等级 │
-│ - 获取系统配置(base_price等) │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 10. 使用事务处理订单(开始事务) │
-│ - 计算实际底价和代理收益 │
-│ - 计算提价成本 │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 11. 更新代理订单状态 │
-│ - ProcessStatus = 1(处理成功) │
-│ - ProcessTime = 当前时间 │
-│ - ProcessRemark = "处理成功" │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 12. 发放代理佣金 │
-│ - 创建 agent_commission 记录 │
-│ - 更新 agent_wallet │
-│ Balance += AgentProfit │
-│ TotalEarnings += AgentProfit │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 13. 分配等级加成返佣 │
-│ - 根据代理等级分配返佣 │
-│ - 普通代理(6元): 给直接上级3元,剩余给钻石/黄金上级 │
-│ - 黄金代理(3元): 全部给钻石上级 │
-│ - 钻石代理(0元): 无返佣 │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 14. 记录返佣 │
-│ - 创建 agent_rebate 记录 │
-│ - 更新上级钱包余额 │
-│ - 记录返佣类型和金额 │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 15. 提交事务(完成) │
-└─────────────────────────────────────────────────────────────┘
-```
-
-### 4.4 代理升级链路
-
-#### 4.4.1 自主付费升级链路
-
-```
-┌─────────────────────────────────────────────────────────────┐
-│ 1. 代理申请升级 │
-│ API: POST /api/v1/agent/upgrade/apply │
-│ Logic: ApplyUpgradeLogic │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 2. 验证升级条件 │
-│ - 验证当前等级和目标等级 │
-│ - 普通→黄金: ✓ │
-│ - 普通→钻石: ✓ │
-│ - 黄金→钻石: ✓ │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 3. 计算升级费用和返佣 │
-│ - 普通→黄金: 199元,返佣139元 │
-│ - 普通→钻石: 980元,返佣680元 │
-│ - 黄金→钻石: 980元,返佣680元 │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 4. 查找原直接上级 │
-│ - 查询 agent_relation 表 │
-│ - RelationType = 1(直接关系) │
-│ - 用于返佣 │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 5. 创建升级记录 │
-│ - 插入 agent_upgrade 表 │
-│ - UpgradeType = 1(自主付费) │
-│ - Status = 1(待处理) │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 6. 返回升级ID和订单号 │
-│ - 用于支付 │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 7. 用户支付升级费用 │
-│ - 调用支付接口 │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 8. 支付成功回调 │
-│ - 触发升级处理 │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 9. 执行升级操作 │
-│ Service: AgentService.ProcessUpgrade │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 10. 返佣给原直接上级(开始事务) │
-│ - 更新上级钱包余额 │
-│ - Balance += RebateAmount │
-│ - TotalEarnings += RebateAmount │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 11. 更新代理等级 │
-│ - agent.Level = toLevel │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 12. 检查是否需要脱离直接上级关系 │
-│ - 检查升级后等级是否高于上级 │
-│ - 检查是否同级(黄金/钻石,普通代理同级除外) │
-└─────────────────────────────────────────────────────────────┘
- ↓
- ┌───────────────────────────────┐
- │ 需要脱离关系 │
- └───────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 13. 脱离直接上级关系 │
-│ - 更新 agent_relation 表 │
-│ - RelationType = 2(已脱离) │
-│ - DetachReason = "upgrade" │
-│ - DetachTime = 当前时间 │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 14. 更新团队首领 │
-│ - 如果升级为钻石: team_leader_id = 自己 │
-│ - 如果升级为黄金: 查找上级链中的钻石代理 │
-│ - 更新所有下级的 team_leader_id │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 15. 更新升级记录状态 │
-│ - Status = 2(已完成) │
-│ - Remark = "升级成功" │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 16. 提交事务(完成) │
-└─────────────────────────────────────────────────────────────┘
-```
-
-#### 4.4.2 钻石升级下级链路
-
-```
-┌─────────────────────────────────────────────────────────────┐
-│ 1. 钻石代理升级下级 │
-│ API: POST /api/v1/agent/upgrade/subordinate │
-│ Logic: UpgradeSubordinateLogic │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 2. 验证权限 │
-│ - 必须是钻石代理(Level = 3) │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 3. 验证下级等级 │
-│ - 只能是普通代理(Level = 1) │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 4. 验证关系 │
-│ - 必须是直接下级 │
-│ - 查询 agent_relation 表 │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 5. 验证目标等级 │
-│ - 只能升级为黄金(toLevel = 2) │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 6. 创建升级记录 │
-│ - 插入 agent_upgrade 表 │
-│ - UpgradeType = 2(钻石升级下级) │
-│ - UpgradeFee = 0(免费) │
-│ - RebateAmount = 0(无返佣) │
-│ - OperatorAgentId = 操作者代理ID │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 7. 执行升级操作 │
-│ Service: AgentService.ProcessUpgrade │
-│ - 更新下级等级为黄金 │
-│ - 脱离直接上级关系(如需要) │
-│ - 更新团队首领(保持原团队首领) │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 8. 更新升级记录状态 │
-│ - Status = 2(已完成) │
-│ - Remark = "钻石代理升级下级成功" │
-└─────────────────────────────────────────────────────────────┘
-```
-
-### 4.5 提现链路
-
-```
-┌─────────────────────────────────────────────────────────────┐
-│ 1. 代理申请提现 │
-│ API: POST /api/v1/agent/withdrawal/apply │
-│ Logic: ApplyWithdrawalLogic │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 2. 验证实名认证 │
-│ - 查询 agent_real_name 表 │
-│ - verify_time 必须不为空(三要素核验已通过) │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 3. 验证提现金额 │
-│ - Amount > 0 │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 4. 验证钱包余额 │
-│ - Balance >= Amount │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 5. 计算税费 │
-│ - 查询本月累计提现金额 │
-│ - 计算剩余免税额度 │
-│ - 计算应税金额 │
-│ - 计算税费 = 应税金额 × 税率 │
-│ - 计算实际到账金额 = 提现金额 - 税费 │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 6. 生成提现单号 │
-│ - 格式: WD + timestamp + agentId │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 7. 使用事务处理提现申请(开始事务) │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 8. 冻结余额 │
-│ - FrozenBalance += Amount │
-│ - Balance -= Amount │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 9. 创建提现记录 │
-│ - 插入 agent_withdrawal 表 │
-│ - Status = 1(待审核) │
-│ - 记录 PayeeAccount, PayeeName │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 10. 创建扣税记录 │
-│ - 插入 agent_withdrawal_tax 表 │
-│ - TaxStatus = 1(待扣税) │
-│ - 记录 YearMonth, TaxableAmount, TaxAmount │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 11. 提交事务(完成) │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 12. 管理员审核提现 │
-│ API: POST /api/v1/admin/agent/withdrawal/audit │
-│ Logic: AdminAuditWithdrawalLogic │
-└─────────────────────────────────────────────────────────────┘
- ↓
- ┌───────────────────────────────┐
- │ 审核通过(Status=2) │
- └───────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 13. 更新提现状态为提现中 │
-│ - Status = 4(提现中) │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 14. 调用支付宝转账接口 │
-│ - AliTransfer(account, name, amount, remark, outBizNo) │
-└─────────────────────────────────────────────────────────────┘
- ↓
- ┌───────────────┬───────────────┬───────────────┐
- │ 转账成功 │ 转账失败 │ 处理中 │
- │ (SUCCESS) │ (FAIL) │ (DEALING) │
- └───────────────┴───────────────┴───────────────┘
- ↓ ↓ ↓
- ┌───────────────┐ ┌───────────────┐ ┌───────────────┐
- │ 15. 更新状态 │ │ 15. 更新状态 │ │ 15. 保持状态 │
- │ Status = 5 │ │ Status = 6 │ │ Status = 4 │
- │ (提现成功) │ │ (提现失败) │ │ (提现中) │
- └───────────────┘ └───────────────┘ └───────────────┘
- ↓ ↓
- ┌───────────────┐ ┌───────────────┐
- │ 16. 解冻并扣 │ │ 16. 解冻余额 │
- │ 除余额 │ │ 返回余额 │
- │ - FrozenBalance│ │ - FrozenBalance│
- │ -= Amount │ │ -= Amount │
- │ - WithdrawnAmount│ │ - Balance │
- │ += Amount │ │ += Amount │
- └───────────────┘ └───────────────┘
- ↓
- ┌───────────────┐
- │ 17. 更新扣税 │
- │ 状态 │
- │ TaxStatus = 2 │
- │ (已扣税) │
- └───────────────┘
-```
-
-### 4.6 实名认证链路
-
-```
-┌─────────────────────────────────────────────────────────────┐
-│ 1. 代理提交实名认证信息 │
-│ API: POST /api/v1/agent/real_name │
-│ Logic: RealNameAuthLogic │
-│ - 姓名、身份证号、手机号 │
-│ - 验证码(手机号验证码) │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 2. 验证代理身份 │
-│ - 从Token获取用户ID │
-│ - 查询 agent 表 │
-│ - 验证手机号是否匹配 │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 3. 验证手机验证码 │
-│ - 从Redis读取验证码 │
-│ - 验证码校验 │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 4. 三要素核验(自动) │
-│ - 调用 VerificationService.ThreeFactorVerification │
-│ - 核验姓名、身份证号、手机号是否一致 │
-│ - 核验通过:继续流程 │
-│ - 核验失败:返回错误 │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 5. 加密敏感信息 │
-│ - 加密身份证号 │
-│ - 加密手机号 │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 6. 保存实名认证记录(开始事务) │
-│ - 检查是否已有记录 │
-│ - 如有记录:更新姓名、身份证号、手机号、verify_time │
-│ - 如无记录:创建新记录,设置 verify_time = 当前时间 │
-│ - verify_time 不为空表示已通过三要素核验 │
-└─────────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────────┐
-│ 7. 提交事务(完成) │
-│ - 返回成功,实名认证完成 │
-│ - 可以申请提现 │
-└─────────────────────────────────────────────────────────────┘
-```
-
-**重要说明**:
-- 实名认证改为三要素核验,无需人工审核
-- 核验通过后自动设置 `verify_time`,无需管理员操作
-- `verify_time` 不为空即表示已通过认证,可以提现
-
----
-
-## 五、关键代码逻辑
-
-### 5.1 AgentService 核心方法
-
-#### 5.1.1 AgentProcess(订单处理)
-
-**文件**: `app/main/api/internal/service/agentService.go`
-
-**功能**: 处理代理订单,分配收益和返佣
-
-**核心逻辑**:
-```go
-func (s *AgentService) AgentProcess(ctx context.Context, order *model.Order) error {
- // 1. 检查是否是代理订单
- agentOrder, err := s.AgentOrderModel.FindOneByOrderId(ctx, order.Id)
- if err != nil {
- if errors.Is(err, model.ErrNotFound) {
- return nil // 不是代理订单
- }
- return err
- }
-
- // 2. 检查订单是否已处理(防重复)
- if agentOrder.ProcessStatus == 1 {
- return nil
- }
-
- // 3. 获取代理信息和系统配置
- agent, err := s.AgentModel.FindOne(ctx, agentOrder.AgentId)
- basePrice, err := s.getConfigFloat(ctx, "base_price")
-
- // 4. 使用事务处理
- return s.AgentWalletModel.Trans(ctx, func(transCtx context.Context, session sqlx.Session) error {
- // 4.1 计算实际底价和代理收益
- levelBonus := s.getLevelBonus(agent.Level)
- actualBasePrice := basePrice + float64(levelBonus)
- priceCost := s.calculatePriceCost(agentOrder.SetPrice, priceThreshold, priceFeeRate)
- agentProfit := agentOrder.SetPrice - actualBasePrice - priceCost
-
- // 4.2 更新代理订单状态
- agentOrder.ProcessStatus = 1
- s.AgentOrderModel.UpdateWithVersion(transCtx, session, agentOrder)
-
- // 4.3 发放代理佣金
- s.giveAgentCommission(transCtx, session, agentOrder.AgentId, order.Id, order.ProductId, agentProfit)
-
- // 4.4 分配等级加成返佣
- if levelBonus > 0 {
- s.distributeLevelBonus(transCtx, session, agent, order.Id, order.ProductId, float64(levelBonus), levelBonus)
- }
-
- return nil
- })
-}
-```
-
-#### 5.1.2 distributeLevelBonus(等级加成返佣分配)
-
-**功能**: 根据代理等级分配等级加成返佣给上级链
-
-**核心逻辑**:
-```go
-func (s *AgentService) distributeLevelBonus(ctx context.Context, session sqlx.Session, agent *model.Agent, orderId, productId int64, levelBonus float64, levelBonusInt int64) error {
- // 钻石代理:等级加成为0,无返佣分配
- if agent.Level == 3 {
- return nil
- }
-
- // 黄金代理:等级加成3元,全部给钻石上级
- if agent.Level == 2 {
- diamondParent, err := s.findDiamondParent(ctx, agent.Id)
- if diamondParent != nil {
-100002 return s.giveRebate(ctx, session, diamondParent.Id, agent.Id, orderId, productId, levelBonus, levelBonusInt, 2)
- }
- return nil
- }
-
- // 普通代理:等级加成6元,按规则分配给上级链
- if agent.Level == 1 {
- return s.distributeNormalAgentBonus(ctx, session, agent, orderId, productId, levelBonus, levelBonusInt)
- }
-
- return nil
-}
-```
-
-#### 5.1.3 ProcessUpgrade(代理升级处理)
-
-**功能**: 处理代理升级,包括返佣、关系脱离、团队首领更新
-
-**核心逻辑**:
-```go
-func (s *AgentService) ProcessUpgrade(ctx context.Context, agentId, toLevel int64, upgradeType int64, upgradeFee, rebateAmount float64, orderNo string, operatorAgentId int64) error {
- return s.AgentWalletModel.Trans(ctx, func(transCtx context.Context, session sqlx.Session) error {
- // 1. 获取代理信息
- agent, err := s.AgentModel.FindOne(transCtx, agentId)
-
- // 2. 如果是自主付费升级,处理返佣
- if upgradeType == 1 {
- parent, err := s.findDirectParent(transCtx, agentId)
- if parent != nil && rebateAmount > 0 {
- s.giveRebateForUpgrade(transCtx, session, parent.Id, agentId, rebateAmount)
- }
- }
-
- // 3. 更新代理等级
- agent.Level = toLevel
-
- // 4. 检查是否需要脱离直接上级关系
- needDetach, err := s.needDetachFromParent(transCtx, agent, toLevel)
- if needDetach {
- s.detachFromParent(transCtx, session, agentId)
- }
-
- // 5. 如果升级为钻石,独立成新团队
- if toLevel == 3 {
- agent.TeamLeaderId = sql.NullInt64{Int64: agentId, Valid: true}
- s.updateChildrenTeamLeader(transCtx, session, agentId, agentId)
- } else {
- // 更新团队首领(查找上级链中的钻石代理)
- teamLeaderId, _ := s.findTeamLeaderId(transCtx, agentId)
- if teamLeaderId > 0 {
- agent.TeamLeaderId = sql.NullInt64{Int64: teamLeaderId, Valid: true}
- }
- }
-
- // 6. 更新代理记录
- s.AgentModel.UpdateWithVersion(transCtx, session, agent)
-
- return nil
- })
-}
-```
-
-#### 5.1.4 calculateTax(税费计算)
-
-**功能**: 计算提现税费,基于月度累计和免税额度
-
-**核心逻辑**:
-```go
-func calculateTax(ctx context.Context, agentId int64, amount float64, yearMonth int64) (*TaxInfo, error) {
- // 1. 获取税率配置(默认6%)
- taxRate := 0.06
- config, _ := AgentConfigModel.FindOneByConfigKey(ctx, "tax_rate")
- if config != nil {
- taxRate = parseFloat(config.ConfigValue)
- }
-
- // 2. 查询本月已提现金额
- taxRecords, _ := AgentWithdrawalTaxModel.FindAll(...)
- monthlyTotal := sum(taxRecords.WithdrawalAmount)
-
- // 3. 获取免税额度配置(默认0)
- exemptionAmount := 0.0
- exemptionConfig, _ := AgentConfigModel.FindOneByConfigKey(ctx, "tax_exemption_amount")
- if exemptionConfig != nil {
- exemptionAmount = parseFloat(exemptionConfig.ConfigValue)
- }
-
- // 4. 计算应税金额
- remainingExemption := exemptionAmount - monthlyTotal
- taxableAmount := amount
- if remainingExemption > 0 {
- if amount <= remainingExemption {
- taxableAmount = 0 // 完全免税
- } else {
- taxableAmount = amount - remainingExemption // 部分免税
- }
- }
-
- // 5. 计算税费和实际到账金额
- taxAmount := taxableAmount * taxRate
- actualAmount := amount - taxAmount
-
- return &TaxInfo{
- TaxableAmount: taxableAmount,
- TaxRate: taxRate,
- TaxAmount: taxAmount,
- ActualAmount: actualAmount,
- }, nil
-}
-```
-
----
-
-## 六、API接口列表
-
-### 6.1 前端接口(agent.api)
-
-#### 6.1.1 公开接口(无需登录)
-
-| 接口 | 方法 | 路径 | 说明 |
-| ---------------------- | ---- | --------------------- | -------------------------------------------- |
-| 获取推广链接数据 | GET | `/api/v1/agent/link` | 根据LinkIdentifier获取推广链接信息 |
-| 通过邀请码申请成为代理 | POST | `/api/v1/agent/apply` | 用户通过邀请码申请成为代理(必须提供邀请码) |
-
-#### 6.1.2 需要登录的接口
-
-| 接口 | 方法 | 路径 | 说明 |
-| -------------------- | ------- | ----------------------------------- | ------------------------------------------------------ |
-| ~~查询代理申请状态~~ | ~~GET~~ | ~~`/api/v1/agent/audit/status`~~ | ~~查询当前用户的代理申请审核状态(已废弃:无需审核)~~ |
-| 查看代理信息 | GET | `/api/v1/agent/info` | 获取当前代理的详细信息 |
-| 生成推广链接 | POST | `/api/v1/agent/generating_link` | 生成产品推广链接 |
-| 获取产品配置 | GET | `/api/v1/agent/product_config` | 获取代理可配置的产品价格信息 |
-| 获取团队统计 | GET | `/api/v1/agent/team/statistics` | 获取团队统计数据 |
-| 获取下级列表 | GET | `/api/v1/agent/subordinate/list` | 分页查询直接下级列表 |
-| 获取收益信息 | GET | `/api/v1/agent/revenue` | 获取钱包余额和收益信息 |
-| 获取佣金记录 | GET | `/api/v1/agent/commission/list` | 分页查询佣金记录 |
-| 获取返佣记录 | GET | `/api/v1/agent/rebate/list` | 分页查询返佣记录 |
-| 获取升级记录 | GET | `/api/v1/agent/upgrade/list` | 分页查询升级记录 |
-| 申请升级 | POST | `/api/v1/agent/upgrade/apply` | 自主付费升级 |
-| 钻石升级下级 | POST | `/api/v1/agent/upgrade/subordinate` | 钻石代理升级下级 |
-| 获取提现列表 | GET | `/api/v1/agent/withdrawal/list` | 分页查询提现记录 |
-| 申请提现 | POST | `/api/v1/agent/withdrawal/apply` | 申请提现 |
-| 实名认证 | POST | `/api/v1/agent/real_name` | 提交实名认证信息(三要素核验,自动通过) |
-
-### 6.2 后台管理接口(admin_agent.api)
-
-| 接口 | 方法 | 路径 | 说明 |
-| -------------------- | -------- | ------------------------------------------- | ------------------------------------------------------------ |
-| 代理分页查询 | GET | `/api/v1/admin/agent/list` | 分页查询代理列表 |
-| ~~代理审核~~ | ~~POST~~ | ~~`/api/v1/admin/agent/audit`~~ | ~~审核代理申请(已废弃:通过邀请码直接成为代理,无需审核)~~ |
-| 推广链接分页查询 | GET | `/api/v1/admin/agent/link/list` | 分页查询推广链接 |
-| 代理订单分页查询 | GET | `/api/v1/admin/agent/order/list` | 分页查询代理订单 |
-| 代理佣金分页查询 | GET | `/api/v1/admin/agent/commission/list` | 分页查询佣金记录 |
-| 代理返佣分页查询 | GET | `/api/v1/admin/agent/rebate/list` | 分页查询返佣记录 |
-| 代理升级记录分页查询 | GET | `/api/v1/admin/agent/upgrade/list` | 分页查询升级记录 |
-| 代理提现分页查询 | GET | `/api/v1/admin/agent/withdrawal/list` | 分页查询提现记录 |
-| 代理提现审核 | POST | `/api/v1/admin/agent/withdrawal/audit` | 审核提现申请 |
-| 代理实名认证分页查询 | GET | `/api/v1/admin/agent/real_name/list` | 分页查询实名认证记录 |
-| ~~代理实名认证审核~~ | ~~POST~~ | ~~`/api/v1/admin/agent/real_name/audit`~~ | ~~审核实名认证(已废弃:改为三要素核验,无需审核)~~ |
-| 系统配置查询 | GET | `/api/v1/admin/agent/config` | 查询系统配置 |
-| 系统配置更新 | POST | `/api/v1/admin/agent/config/update` | 更新系统配置 |
-| 产品配置分页查询 | GET | `/api/v1/admin/agent/product_config/list` | 分页查询产品配置 |
-| 产品配置更新 | POST | `/api/v1/admin/agent/product_config/update` | 更新产品配置 |
-
----
-
-## 七、系统配置说明
-
-### 7.1 系统配置项(agent_config表)
-
-| 配置键 | 配置类型 | 默认值 | 说明 |
-| --------------------------- | -------- | ------ | ------------------------------- |
-| `base_price` | price | - | 基础底价(系统默认) |
-| `system_max_price` | price | - | 系统价格上限 |
-| `price_threshold` | price | - | 提价标准阈值 |
-| `price_fee_rate` | price | - | 提价手续费比例(0-1之间的小数) |
-| `level_1_bonus` | bonus | 6 | 普通代理等级加成(元) |
-| `level_2_bonus` | bonus | 3 | 黄金代理等级加成(元) |
-| `level_3_bonus` | bonus | 0 | 钻石代理等级加成(元) |
-| `upgrade_to_gold_fee` | upgrade | 199 | 升级为黄金费用(元) |
-| `upgrade_to_diamond_fee` | upgrade | 980 | 升级为钻石费用(元) |
-| `upgrade_to_gold_rebate` | rebate | 139 | 升级为黄金返佣(元) |
-| `upgrade_to_diamond_rebate` | rebate | 680 | 升级为钻石返佣(元) |
-| `tax_rate` | tax | 0.06 | 税率(默认6%,即0.06) |
-| `tax_exemption_amount` | tax | 0 | 免税额度(元,默认0) |
-
-### 7.2 产品配置项(agent_product_config表)
-
-每个产品可以单独配置以下参数,覆盖系统默认配置:
-
-| 字段 | 说明 |
-| ------------------ | ---------------------------------------- |
-| `base_price` | 产品基础底价(覆盖系统配置) |
-| `system_max_price` | 产品价格上限(覆盖系统配置) |
-| `price_threshold` | 产品提价标准阈值(可选,覆盖系统配置) |
-| `price_fee_rate` | 产品提价手续费比例(可选,覆盖系统配置) |
-
-### 7.3 配置优先级
-
-1. **产品配置** > **系统配置**
- - 如果产品配置了 `base_price`,使用产品配置
- - 如果产品未配置,使用系统配置
-
-2. **系统配置** > **代码默认值**
- - 如果系统配置存在,使用系统配置
- - 如果系统配置不存在,使用代码中的默认值
-
----
-
-## 八、数据流转图
-
-### 8.1 订单处理数据流
-
-```
-用户下单
- ↓
-创建 order 记录
- ↓
-创建 agent_order 记录(ProcessStatus=0)
- ↓
-用户支付
- ↓
-支付成功回调
- ↓
-调用 AgentService.AgentProcess
- ↓
-┌─────────────────────────────────────┐
-│ 事务开始 │
-│ 1. 计算实际底价 = 基础底价 + 等级加成 │
-│ 2. 计算提价成本 │
-│ 3. 计算代理收益 │
-│ 4. 更新 agent_order (ProcessStatus=1) │
-│ 5. 创建 agent_commission 记录 │
-│ 6. 更新 agent_wallet (Balance, TotalEarnings) │
-│ 7. 分配等级加成返佣 │
-│ - 创建 agent_rebate 记录 │
-│ - 更新上级 agent_wallet │
-│ 事务提交 │
-└─────────────────────────────────────┘
-```
-
-### 8.2 升级处理数据流
-
-```
-代理申请升级
- ↓
-创建 agent_upgrade 记录(Status=1)
- ↓
-用户支付升级费用
- ↓
-支付成功回调
- ↓
-调用 AgentService.ProcessUpgrade
- ↓
-┌─────────────────────────────────────┐
-│ 事务开始 │
-│ 1. 返佣给原直接上级(如需要) │
-│ - 更新上级 agent_wallet │
-│ 2. 更新 agent.Level │
-│ 3. 检查是否需要脱离关系 │
-│ - 更新 agent_relation (RelationType=2) │
-│ 4. 更新团队首领 │
-│ - 更新 agent.TeamLeaderId │
-│ - 更新所有下级的 TeamLeaderId │
-│ 5. 更新 agent_upgrade (Status=2) │
-│ 事务提交 │
-└─────────────────────────────────────┘
-```
-
-### 8.3 提现处理数据流
-
-```
-代理申请提现
- ↓
-验证实名认证和余额
- ↓
-计算税费
- ↓
-┌─────────────────────────────────────┐
-│ 事务开始 │
-│ 1. 冻结余额 │
-│ - agent_wallet.FrozenBalance += Amount │
-│ - agent_wallet.Balance -= Amount │
-│ 2. 创建 agent_withdrawal 记录 │
-│ (Status=1, 待审核) │
-│ 3. 创建 agent_withdrawal_tax 记录 │
-│ (TaxStatus=1, 待扣税) │
-│ 事务提交 │
-└─────────────────────────────────────┘
- ↓
-管理员审核
- ↓
-审核通过(Status=2)
- ↓
-调用支付宝转账接口
- ↓
-转账成功/失败/处理中
- ↓
-┌─────────────────────────────────────┐
-│ 事务开始 │
-│ 1. 更新 agent_withdrawal.Status │
-│ 2. 解冻并扣除余额(成功) │
-│ - agent_wallet.FrozenBalance -= Amount │
-│ - agent_wallet.WithdrawnAmount += Amount │
-│ 3. 更新 agent_withdrawal_tax.TaxStatus │
-│ 事务提交 │
-└─────────────────────────────────────┘
-```
-
----
-
-## 九、关键算法说明
-
-### 9.1 团队统计递归算法
-
-```go
-func getTeamMembers(agentId int64) []int64 {
- teamMembers := []int64{agentId} // 包括自己
-
- var collectChildren func(int64)
- collectChildren = func(parentId int64) {
- // 查找直接下级(relation_type=1)
- relations := findDirectChildren(parentId)
- for _, relation := range relations {
- teamMembers = append(teamMembers, relation.ChildId)
- collectChildren(relation.ChildId) // 递归
- }
- }
-
- collectChildren(agentId)
- return teamMembers
-}
-```
-
-### 9.2 查找上级链算法
-
-```go
-// 查找直接上级
-func findDirectParent(agentId int64) *Agent {
- relation := findRelation(childId=agentId, relationType=1)
- return findAgent(relation.ParentId)
-}
-
-// 查找钻石上级(向上递归)
-func findDiamondParent(agentId int64) *Agent {
- currentId := agentId
- maxDepth := 100
- depth := 0
-
- for depth < maxDepth {
- parent := findDirectParent(currentId)
- if parent == nil {
- return nil
- }
- if parent.Level == 3 { // 钻石
- return parent
- }
- currentId = parent.Id
- depth++
- }
- return nil
-}
-```
-
-### 9.3 价格计算算法
-
-```go
-// 计算实际底价
-func calculateActualBasePrice(agentLevel int64, basePrice float64) float64 {
- levelBonus := getLevelBonus(agentLevel) // 6, 3, 0
- return basePrice + float64(levelBonus)
-}
-
-// 计算提价成本
-func calculatePriceCost(setPrice, priceThreshold, priceFeeRate float64) float64 {
- if setPrice <= priceThreshold {
- return 0
- }
- return (setPrice - priceThreshold) * priceFeeRate
-}
-
-// 计算代理收益
-func calculateAgentProfit(setPrice, actualBasePrice, priceCost float64) float64 {
- return setPrice - actualBasePrice - priceCost
-}
-```
-
----
-
-## 十、注意事项和最佳实践
-
-### 10.1 数据一致性
-
-1. **事务使用**: 所有涉及多表更新的操作必须使用事务
- - 订单处理(agent_order, agent_commission, agent_wallet, agent_rebate)
- - 升级处理(agent, agent_relation, agent_wallet, agent_upgrade)
- - 提现处理(agent_withdrawal, agent_withdrawal_tax, agent_wallet)
-
-2. **乐观锁**: 所有更新操作使用 `version` 字段进行乐观锁控制
- - 使用 `UpdateWithVersion` 方法
- - 更新失败时重试或提示用户
-
-3. **防重复处理**:
- - 订单处理前检查 `agent_order.ProcessStatus`
- - 升级处理前检查 `agent_upgrade.Status`
-
-### 10.2 性能优化
-
-1. **批量查询**: 避免N+1查询问题
- - 列表查询时批量获取产品名称、代理信息等
-
-2. **索引使用**:
- - `agent_relation`: `idx_parent_relation`, `idx_child_relation`
- - `agent_rebate`: `idx_order_rebate_type`
- - `agent_order`: `idx_process_status`
-
-3. **递归深度限制**:
- - 查找上级链时设置最大深度(如100层)
- - 防止无限循环
-
-### 10.3 错误处理
-
-1. **配置缺失**: 配置项不存在时使用默认值
- - 税率默认6%
- - 等级加成使用代码中的固定值
-
-2. **关系异常**:
- - 找不到上级时,返佣归平台
- - 升级时找不到直接上级,跳过返佣
-
-3. **余额不足**:
- - 提现前验证余额
- - 使用事务确保余额扣减的原子性
-
-### 10.4 安全考虑
-
-1. **数据加密**:
- - 手机号、身份证号使用AES加密存储
- - 推广链接标识加密传输
-
-2. **权限控制**:
- - 代理只能查看自己的数据
- - 管理员需要认证和授权
-
-3. **金额精度**:
- - 使用 `decimal(10,2)` 类型存储金额
- - 计算时注意浮点数精度问题
-
----
-
-## 十一、常见问题解答
-
-### 11.1 升级后关系脱离规则
-
-**Q**: 普通代理升级为黄金后什么情况下会脱离直接上级关系?
-
-**A**: 根据关系约束规则:
-- **下级不能比上级等级高**:如果直接上级是普通代理,升级后黄金等级高于普通,必须脱离
-- **同级不能作为上下级(除了普通代理)**:如果直接上级是黄金代理,升级后同级,必须脱离
-- **钻石可以拥有黄金下级**:如果直接上级是钻石代理,升级后钻石等级高于黄金,不脱离关系,保持直接下级关系
-
-### 11.2 返佣分配规则
-
-**Q**: 普通代理的6元等级加成如何分配?
-
-**A**: 按优先级分配:
-1. 给直接上级(根据上级等级:钻石6元,黄金3元,普通2元)
-2. 剩余金额给钻石上级(如有)
-3. 如果无钻石上级,给黄金上级(最多3元)
-4. 都没有,剩余归平台
-
-### 11.3 团队归属
-
-**Q**: 升级后团队归属如何变化?
-
-**A**:
-- 普通→黄金:仍属于原团队(通过team_leader_id指向原钻石代理)
-- 黄金→钻石:独立成新团队(team_leader_id指向自己)
-- 普通→钻石:独立成新团队(team_leader_id指向自己)
-
-### 11.4 税费计算
-
-**Q**: 税费如何计算?
-
-**A**:
-1. 查询本月累计提现金额
-2. 计算剩余免税额度 = 免税额度 - 本月累计
-3. 如果本次提现 <= 剩余免税额度,免税
-4. 否则,应税金额 = 本次提现 - 剩余免税额度
-5. 税费 = 应税金额 × 税率
-
----
-
-## 十二、后续优化建议
-
-### 12.1 功能扩展
-
-1. **多级返佣**: 支持更多层级的返佣分配
-2. **业绩统计**: 增加团队业绩统计和排行榜
-3. **消息通知**: 收益到账、升级成功等消息推送
-
-### 12.2 性能优化
-
-1. **缓存策略**:
- - 系统配置缓存
- - 团队统计数据缓存
- - 代理信息缓存
-
-2. **异步处理**:
- - 订单处理异步化
- - 团队统计异步计算
-
-### 12.3 监控和日志
-
-1. **关键操作日志**:
- - 订单处理日志
- - 升级操作日志
- - 提现操作日志
-
-2. **性能监控**:
- - 订单处理耗时
- - 数据库查询耗时
- - 接口响应时间
-
----
-
-## 附录
-
-### A. 数据表索引说明
-
-详见 `deploy/sql/agent_system_migration.sql` 文件中的索引定义。
-
-### B. API接口详细定义
-
-详见 `app/main/api/desc/front/agent.api` 和 `app/main/api/desc/admin/admin_agent.api` 文件。
-
-### C. 代码文件清单
-
-- **Service层**: `app/main/api/internal/service/agentService.go`
-- **Logic层**: `app/main/api/internal/logic/agent/*.go`
-- **Model层**: `app/main/model/agent*.go`
-- **API定义**: `app/main/api/desc/front/agent.api`, `app/main/api/desc/admin/admin_agent.api`
-
----
-
-**文档结束**
\ No newline at end of file
diff --git a/新代理系统检查清单.md b/新代理系统检查清单.md
deleted file mode 100644
index 449f137..0000000
--- a/新代理系统检查清单.md
+++ /dev/null
@@ -1,506 +0,0 @@
-# 新代理系统完整检查清单
-
-## 检查说明
-
-本文档提供新代理系统的完整检查方案,按照业务流程顺序组织,确保系统逻辑正确性和完整性。
-
-**检查原则**:
-1. 按照业务流程顺序检查(从注册到提现)
-2. 先检查核心链路,再检查辅助功能
-3. 每个检查点包含:功能点、关键逻辑、预期结果
-4. 重点关注数据一致性、事务完整性、边界条件
-
----
-
-## 一、基础数据检查
-
-### 1.1 数据表结构
-- [ ] 确认所有新表已创建(参考 `deploy/sql/agent_system_migration.sql`)
-- [ ] 检查表结构是否与文档一致
-- [ ] 验证索引是否正确创建
-- [ ] 确认所有表都有 `id`, `create_time`, `update_time`, `delete_time`, `del_state`, `version` 字段
-
-**关键表**:
-- `agent` - 代理基本信息
-- `agent_wallet` - 代理钱包
-- `agent_relation` - 代理关系
-- `agent_link` - 推广链接
-- `agent_order` - 代理订单
-- `agent_commission` - 代理佣金
-- `agent_rebate` - 代理返佣
-- `agent_upgrade` - 代理升级
-- `agent_withdrawal` - 代理提现
-- `agent_withdrawal_tax` - 提现扣税
-- `agent_config` - 系统配置
-- `agent_product_config` - 产品配置
-- `agent_real_name` - 实名认证
-- `agent_invite_code` - 邀请码
-
-### 1.2 系统配置初始化
-- [ ] 检查 `agent_config` 表是否有基础配置数据
-- [ ] 验证关键配置项是否存在:
- - `base_price` - 基础底价
- - `system_max_price` - 系统价格上限
- - `level_1_bonus` - 普通代理等级加成(6元)
- - `level_2_bonus` - 黄金代理等级加成(3元)
- - `level_3_bonus` - 钻石代理等级加成(0元)
- - `upgrade_to_gold_fee` - 升级为黄金费用(199元)
- - `upgrade_to_diamond_fee` - 升级为钻石费用(980元)
- - `tax_rate` - 税率(0.06)
- - `tax_exemption_amount` - 免税额度
-
----
-
-## 二、核心业务流程检查
-
-### 2.1 通过邀请码成为代理链路
-
-**入口**:`POST /api/v1/agent/apply` 或 `POST /api/v1/agent/register/invite`
-
-**检查点**:
-
-#### 2.1.1 邀请码验证
-- [ ] **文件**:`app/main/api/internal/logic/agent/applyforagentlogic.go`
-- [ ] 验证邀请码必填(没有邀请码直接拒绝)
-- [ ] 验证邀请码存在性(`agent_invite_code` 表)
-- [ ] 验证邀请码状态(`status=0` 未使用)
-- [ ] 验证邀请码是否过期(`expire_time`)
-- [ ] 验证邀请码使用后状态更新为已使用(`status=1`)
-
-#### 2.1.2 用户注册/绑定
-- [ ] 检查用户是否存在(通过手机号查询)
-- [ ] 不存在则注册新用户
-- [ ] 临时用户则绑定为正式用户
-- [ ] 验证手机号加密存储
-
-#### 2.1.3 代理记录创建
-- [ ] 检查是否已是代理(防止重复)
-- [ ] 根据邀请码的 `target_level` 设置代理等级
-- [ ] 创建 `agent` 记录
-- [ ] 初始化 `agent_wallet`(余额为0)
-- [ ] 可选字段处理:`region`, `wechat_id` 可以为空
-
-#### 2.1.4 关系建立
-- [ ] **代理发放的邀请码**:
- - [ ] 验证上级代理存在
- - [ ] 验证关系是否允许(下级等级不能高于上级)
- - [ ] 创建 `agent_relation` 记录(`relation_type=1` 直接关系)
- - [ ] 设置 `team_leader_id`(查找上级链中的钻石代理)
-- [ ] **平台发放的钻石邀请码**:
- - [ ] 独立成团队(`team_leader_id = 自己`)
-
-#### 2.1.5 邀请码状态更新
-- [ ] 更新邀请码状态为已使用(`status=1`)
-- [ ] 记录使用用户ID和代理ID
-- [ ] 记录使用时间
-
-#### 2.1.6 Token生成
-- [ ] 生成JWT Token
-- [ ] 返回给前端
-
-**测试场景**:
-1. 正常流程:使用有效邀请码注册
-2. 边界条件:邀请码不存在、已使用、已过期
-3. 重复注册:已是代理的用户再次申请
-4. 关系验证:下级等级高于上级的情况
-
----
-
-### 2.2 推广链接生成链路
-
-**入口**:`POST /api/v1/agent/generating_link`
-
-**检查点**:
-
-#### 2.2.1 代理身份验证
-- [ ] **文件**:`app/main/api/internal/logic/agent/generatinglinklogic.go`
-- [ ] 从Token获取用户ID
-- [ ] 查询 `agent` 表验证代理身份
-- [ ] 非代理用户拒绝
-
-#### 2.2.2 价格计算
-- [ ] 获取系统配置(`base_price`, `system_max_price`)
-- [ ] 计算实际底价 = 基础底价 + 等级加成
- - 普通代理:+6元
- - 黄金代理:+3元
- - 钻石代理:+0元
-- [ ] 验证设定价格范围:`实际底价 ≤ 设定价格 ≤ 系统价格上限`
-
-#### 2.2.3 链接生成
-- [ ] 检查是否已存在相同链接(`agent_id + product_id + set_price`)
-- [ ] 构建 `AgentIdentifier` 结构(`AgentID`, `ProductID`, `SetPrice`)
-- [ ] JSON序列化并AES加密生成 `LinkIdentifier`
-- [ ] 保存到 `agent_link` 表
-- [ ] 记录 `set_price` 和 `actual_base_price`
-
-**测试场景**:
-1. 正常生成:有效代理生成推广链接
-2. 价格验证:设定价格低于实际底价或高于系统上限
-3. 重复链接:相同代理、产品、价格的链接复用
-
----
-
-### 2.3 订单处理与收益分配链路
-
-**入口**:`POST /api/v1/pay/payment` → 支付回调 → `AgentService.AgentProcess`
-
-**检查点**:
-
-#### 2.3.1 订单创建
-- [ ] **文件**:`app/main/api/internal/logic/pay/paymentlogic.go`
-- [ ] 解析推广链接标识(解密 `LinkIdentifier`)
-- [ ] 查询 `agent_link` 表验证链接有效性
-- [ ] 创建 `order` 记录(订单金额 = SetPrice)
-- [ ] 创建 `agent_order` 记录:
- - `order_amount` = SetPrice
- - `set_price` = 设定价格
- - `actual_base_price` = 实际底价(基础底价+等级加成)
- - `price_cost` = 提价成本
- - `agent_profit` = 代理收益(SetPrice - ActualBasePrice - PriceCost)
- - `process_status` = 0(待处理)
-
-#### 2.3.2 支付成功处理
-- [ ] 支付回调验证签名
-- [ ] 更新订单状态
-- [ ] 触发 `AgentService.AgentProcess`
-
-#### 2.3.3 代理订单处理
-- [ ] **文件**:`app/main/api/internal/service/agentService.go` - `AgentProcess`
-- [ ] 检查是否是代理订单(查询 `agent_order` 表)
-- [ ] 检查订单是否已处理(`process_status=1` 防重复)
-- [ ] 获取代理信息和系统配置
-- [ ] 使用事务处理:
- - 更新 `agent_order` 状态为处理成功
- - 创建 `agent_commission` 记录
- - 更新 `agent_wallet`(`balance += agent_profit`, `total_earnings += agent_profit`)
-
-#### 2.3.4 等级加成返佣分配
-- [ ] **文件**:`app/main/api/internal/service/agentService.go` - `distributeLevelBonus`
-- [ ] **普通代理(6元)**:
- - [ ] 给直接上级(最多3元)
- - [ ] 剩余给钻石上级(如有)
- - [ ] 如果无钻石上级,给黄金上级(最多3元)
-- [ ] **黄金代理(3元)**:
- - [ ] 全部给钻石上级(如有)
-- [ ] **钻石代理(0元)**:
- - [ ] 无返佣
-- [ ] 创建 `agent_rebate` 记录
-- [ ] 更新上级钱包余额
-
-**测试场景**:
-1. 正常订单:普通代理订单,验证收益和返佣分配
-2. 防重复:已处理订单再次处理
-3. 返佣分配:不同等级代理的返佣分配逻辑
-4. 无上级:没有上级代理时的返佣处理
-
----
-
-### 2.4 代理升级链路
-
-#### 2.4.1 自主付费升级
-
-**入口**:`POST /api/v1/agent/upgrade/apply`
-
-**检查点**:
-- [ ] **文件**:`app/main/api/internal/logic/agent/applyupgradelogic.go`
-- [ ] 验证升级条件(普通→黄金、普通→钻石、黄金→钻石)
-- [ ] 计算升级费用和返佣
-- [ ] 查找原直接上级(用于返佣)
-- [ ] 创建 `agent_upgrade` 记录(`status=1` 待处理)
-- [ ] 支付成功后调用 `AgentService.ProcessUpgrade`
-- [ ] **升级处理**:
- - [ ] 返佣给原直接上级(如需要)
- - [ ] 更新代理等级
- - [ ] 检查是否需要脱离直接上级关系
- - [ ] 更新团队首领(升级为钻石时独立成团队)
- - [ ] 更新所有下级的 `team_leader_id`
-
-#### 2.4.2 钻石升级下级
-
-**入口**:`POST /api/v1/agent/upgrade/subordinate`
-
-**检查点**:
-- [ ] **文件**:`app/main/api/internal/logic/agent/upgradesubordinatelogic.go`
-- [ ] 验证权限(必须是钻石代理)
-- [ ] 验证下级等级(只能是普通代理)
-- [ ] 验证关系(必须是直接下级)
-- [ ] 验证目标等级(只能升级为黄金)
-- [ ] 创建升级记录(`upgrade_type=2`,`upgrade_fee=0`)
-- [ ] 执行升级操作
-
-**测试场景**:
-1. 正常升级:普通→黄金,验证返佣和关系脱离
-2. 升级为钻石:验证团队独立
-3. 钻石升级下级:验证权限和限制
-4. 关系脱离:升级后等级高于上级的情况
-
----
-
-### 2.5 实名认证链路
-
-**入口**:`POST /api/v1/agent/real_name`
-
-**检查点**:
-- [ ] **文件**:`app/main/api/internal/logic/agent/realnameauthlogic.go`
-- [ ] 验证代理身份
-- [ ] 验证手机号是否匹配
-- [ ] 验证手机验证码
-- [ ] 三要素核验(姓名、身份证号、手机号)
-- [ ] 加密身份证号和手机号
-- [ ] 保存实名认证记录(`verify_time` 不为空表示已通过)
-- [ ] 无需人工审核,核验通过即生效
-
-**测试场景**:
-1. 正常认证:三要素核验通过
-2. 核验失败:三要素不匹配
-3. 手机号不匹配:与代理注册手机号不一致
-
----
-
-### 2.6 提现链路
-
-**入口**:`POST /api/v1/agent/withdrawal/apply`
-
-**检查点**:
-
-#### 2.6.1 提现申请
-- [ ] **文件**:`app/main/api/internal/logic/agent/applywithdrawallogic.go`
-- [ ] 验证实名认证(`verify_time` 不为空)
-- [ ] 验证提现金额(> 0)
-- [ ] 验证钱包余额(`balance >= amount`)
-- [ ] 计算税费:
- - 查询本月累计提现金额
- - 计算剩余免税额度
- - 计算应税金额和税费
-- [ ] 冻结余额(`frozen_balance += amount`, `balance -= amount`)
-- [ ] 创建 `agent_withdrawal` 记录(`status=1` 待审核)
-- [ ] 创建 `agent_withdrawal_tax` 记录(`tax_status=1` 待扣税)
-
-#### 2.6.2 提现审核
-- [ ] **文件**:`app/main/api/internal/logic/admin_agent/adminauditwithdrawallogic.go`
-- [ ] 审核通过:调用支付宝转账接口
-- [ ] 审核拒绝:解冻余额
-- [ ] 转账成功:解冻并扣除余额,更新扣税状态
-- [ ] 转账失败:解冻余额
-
-**测试场景**:
-1. 正常提现:实名认证通过,余额充足
-2. 税费计算:验证月度累计和免税额度
-3. 余额不足:提现金额大于余额
-4. 未实名:未完成实名认证
-
----
-
-## 三、查询接口检查
-
-### 3.1 代理信息查询
-- [ ] **文件**:`app/main/api/internal/logic/agent/getagentinfologic.go`
-- [ ] 查询代理基本信息
-- [ ] 查询实名认证状态(`verify_time` 不为空)
-- [ ] 处理可选字段(`region`, `wechat_id`)
-
-### 3.2 收益信息查询
-- [ ] **文件**:`app/main/api/internal/logic/agent/getrevenueinfologic.go`
-- [ ] 查询钱包余额和累计收益
-- [ ] 返回 `balance`, `frozen_balance`, `total_earnings`, `withdrawn_amount`
-
-### 3.3 佣金记录查询
-- [ ] **文件**:`app/main/api/internal/logic/agent/getcommissionlistlogic.go`
-- [ ] 分页查询佣金记录
-- [ ] 关联查询产品名称
-
-### 3.4 返佣记录查询
-- [ ] **文件**:`app/main/api/internal/logic/agent/getrebatelistlogic.go`
-- [ ] 分页查询返佣记录
-- [ ] 显示返佣类型和金额
-
-### 3.5 团队统计查询
-- [ ] **文件**:`app/main/api/internal/logic/agent/getteamstatisticslogic.go`
-- [ ] 递归查询所有下级(直接+间接)
-- [ ] 统计总人数、按等级统计
-- [ ] 权限检查(只能查看自己团队)
-
-### 3.6 下级列表查询
-- [ ] **文件**:`app/main/api/internal/logic/agent/getsubordinatelistlogic.go`
-- [ ] 分页查询直接下级
-- [ ] 显示下级等级和信息
-
----
-
-## 四、邀请码管理检查
-
-### 4.1 生成邀请码
-- [ ] **文件**:`app/main/api/internal/logic/agent/generateinvitecodelogic.go`
-- [ ] 验证代理身份
-- [ ] 生成唯一邀请码
-- [ ] 设置目标等级(默认1=普通)
-- [ ] 保存到 `agent_invite_code` 表
-
-### 4.2 邀请码列表
-- [ ] **文件**:`app/main/api/internal/logic/agent/getinvitecodelistlogic.go`
-- [ ] 查询代理生成的邀请码
-- [ ] 显示状态(未使用、已使用、已失效)
-
-### 4.3 邀请链接
-- [ ] **文件**:`app/main/api/internal/logic/agent/getinvitelinklogic.go`
-- [ ] 生成邀请链接和二维码
-- [ ] 链接包含邀请码信息
-
-### 4.4 后台生成钻石邀请码
-- [ ] **文件**:`app/main/api/internal/logic/admin_agent/admingeneratediamondinvitecodelogic.go`
-- [ ] 管理员生成钻石邀请码
-- [ ] 目标等级为3(钻石)
-- [ ] 无发放代理ID(平台发放)
-
----
-
-## 五、后台管理接口检查
-
-### 5.1 代理列表查询
-- [ ] **文件**:`app/main/api/internal/logic/admin_agent/admingetagentlistlogic.go`
-- [ ] 分页查询代理列表
-- [ ] 显示代理等级、实名状态
-- [ ] 处理可选字段(`region`, `wechat_id`)
-
-### 5.2 代理订单查询
-- [ ] **文件**:`app/main/api/internal/logic/admin_agent/admingetagentorderlistlogic.go`
-- [ ] 分页查询代理订单
-- [ ] 显示订单金额、代理收益、处理状态
-
-### 5.3 系统配置管理
-- [ ] **文件**:`app/main/api/internal/logic/admin_agent/admingetagentconfiglogic.go`
-- [ ] 查询系统配置
-- [ ] **文件**:`app/main/api/internal/logic/admin_agent/adminupdateagentconfiglogic.go`
-- [ ] 更新系统配置
-
-### 5.4 产品配置管理
-- [ ] **文件**:`app/main/api/internal/logic/admin_agent/admingetagentproductconfiglistlogic.go`
-- [ ] 查询产品配置列表
-- [ ] **文件**:`app/main/api/internal/logic/admin_agent/adminupdateagentproductconfiglogic.go`
-- [ ] 更新产品配置
-
----
-
-## 六、关键业务逻辑验证
-
-### 6.1 关系约束验证
-- [ ] 下级等级不能高于上级
-- [ ] 同级不能作为上下级(除了普通代理)
-- [ ] 钻石可以拥有黄金下级(钻石等级高于黄金)
-- [ ] 升级后关系脱离逻辑(根据上级等级判断)
-
-### 6.2 团队首领逻辑
-- [ ] 钻石代理独立成团队(`team_leader_id = 自己`)
-- [ ] 普通/黄金代理指向上级链中的钻石代理
-- [ ] 升级为钻石时,所有下级跟随到新团队
-
-### 6.3 价格计算逻辑
-- [ ] 实际底价 = 基础底价 + 等级加成
-- [ ] 提价成本计算(当设定价格 > 提价阈值时)
-- [ ] 代理收益 = 设定价格 - 实际底价 - 提价成本
-
-### 6.4 返佣分配逻辑
-- [ ] 普通代理6元:直接上级3元,剩余给钻石/黄金上级
-- [ ] 黄金代理3元:全部给钻石上级
-- [ ] 钻石代理0元:无返佣
-
-### 6.5 税费计算逻辑
-- [ ] 月度累计提现金额查询
-- [ ] 剩余免税额度计算
-- [ ] 应税金额和税费计算
-
----
-
-## 七、数据一致性检查
-
-### 7.1 事务完整性
-- [ ] 代理注册:用户创建、代理创建、钱包初始化、关系建立、邀请码更新在同一事务
-- [ ] 订单处理:订单状态更新、佣金发放、返佣分配在同一事务
-- [ ] 升级处理:等级更新、关系脱离、团队首领更新在同一事务
-- [ ] 提现申请:余额冻结、提现记录、扣税记录在同一事务
-
-### 7.2 乐观锁
-- [ ] 所有更新操作使用 `version` 字段
-- [ ] 使用 `UpdateWithVersion` 方法
-- [ ] 更新失败时正确处理
-
-### 7.3 防重复处理
-- [ ] 订单处理前检查 `process_status`
-- [ ] 升级处理前检查 `status`
-- [ ] 邀请码使用后立即更新状态
-
----
-
-## 八、边界条件和异常处理
-
-### 8.1 边界条件
-- [ ] 邀请码不存在、已使用、已过期
-- [ ] 已是代理的用户再次申请
-- [ ] 下级等级高于上级的情况
-- [ ] 设定价格低于实际底价或高于系统上限
-- [ ] 余额不足的提现申请
-- [ ] 未完成实名认证的提现申请
-
-### 8.2 异常处理
-- [ ] 数据库操作失败的回滚
-- [ ] 三要素核验失败的处理
-- [ ] 支付回调失败的处理
-- [ ] 转账失败的处理
-
----
-
-## 九、性能和安全检查
-
-### 9.1 性能优化
-- [ ] 批量查询避免N+1问题
-- [ ] 索引使用(`agent_relation`, `agent_rebate`, `agent_order`)
-- [ ] 递归深度限制(查找上级链时)
-
-### 9.2 安全考虑
-- [ ] 手机号、身份证号加密存储
-- [ ] 推广链接标识加密传输
-- [ ] 权限控制(代理只能查看自己的数据)
-- [ ] 金额精度(使用 `decimal` 类型)
-
----
-
-## 十、测试建议
-
-### 10.1 单元测试
-- [ ] 价格计算函数
-- [ ] 税费计算函数
-- [ ] 返佣分配函数
-- [ ] 关系验证函数
-
-### 10.2 集成测试
-- [ ] 完整注册流程
-- [ ] 完整订单处理流程
-- [ ] 完整升级流程
-- [ ] 完整提现流程
-
-### 10.3 压力测试
-- [ ] 并发订单处理
-- [ ] 并发提现申请
-- [ ] 大量下级查询
-
----
-
-## 检查顺序建议
-
-1. **第一阶段**:基础数据检查(1.1, 1.2)
-2. **第二阶段**:核心业务流程(2.1, 2.2, 2.3)
-3. **第三阶段**:升级和实名认证(2.4, 2.5)
-4. **第四阶段**:提现流程(2.6)
-5. **第五阶段**:查询接口(三、四)
-6. **第六阶段**:后台管理(五)
-7. **第七阶段**:业务逻辑验证(六、七、八)
-8. **第八阶段**:性能和安全(九)
-
----
-
-**检查完成后,建议**:
-1. 记录发现的问题
-2. 修复问题后重新检查
-3. 编写测试用例覆盖关键流程
-4. 进行端到端测试验证
-
diff --git a/短链系统实现说明.md b/短链系统实现说明.md
deleted file mode 100644
index 5101bd7..0000000
--- a/短链系统实现说明.md
+++ /dev/null
@@ -1,135 +0,0 @@
-# 短链系统实现说明
-
-## 一、功能说明
-
-短链系统用于生成推广链接和邀请链接的短链,格式为:`https://推广域名/s/{shortCode}`
-
-### 支持两种类型
-1. **推广报告(promotion)**:类型值为1
- - 用于推广产品查询服务
- - 前端传入目标地址,如:`/agent/promotionInquire/{linkIdentifier}`
-
-2. **邀请好友(invite)**:类型值为2
- - 用于邀请用户成为代理
- - 前端传入目标地址,如:`/register?invite_code=XXXXX`
-
-### 工作流程
-1. 前端调用接口时传入 `target_path`(目标地址)
-2. 后端生成6位随机短链标识
-3. 短链存储在 `agent_short_link` 表中,包含类型和目标地址
-4. 返回短链URL:`https://推广域名/s/{shortCode}`
-5. 用户访问短链时,根据 `target_path` 重定向到对应页面
-
-## 二、执行步骤
-
-### 1. 执行SQL创建表
-
-```bash
-# 执行SQL文件创建短链表
-mysql -u root -p your_database < deploy/sql/add_agent_short_link_table.sql
-```
-
-或者直接在数据库客户端执行 `deploy/sql/add_agent_short_link_table.sql` 文件。
-
-### 2. 生成Model
-
-使用goctl工具生成Model代码:
-
-```bash
-# Windows PowerShell
-cd ycc-proxy-server
-goctl model mysql datasource -url="ycc:5vg67b3UNHu8@tcp(127.0.0.1:21001)/ycc" -table="agent_short_link" -dir="./app/main/model" --home="./deploy/template" -cache=true --style=goZero
-
-# Linux/Mac
-cd ycc-proxy-server
-goctl model mysql datasource -url="ycc:5vg67b3UNHu8@tcp(127.0.0.1:21001)/ycc" -table="agent_short_link" -dir="./app/main/model" --home="./deploy/template" -cache=true --style=goZero
-```
-
-### 3. 代码已实现
-
-代码已经实现完成,生成Model后即可使用。
-
-### 4. 配置推广域名
-
-在配置文件中设置推广域名:
-
-```yaml
-# main.yaml 或 main.dev.yaml
-Promotion:
- PromotionDomain: "https://promo.example.com" # 推广域名
-```
-
-## 三、接口说明
-
-### 短链重定向接口
-
-- **路径**: `/s/{shortCode}`
-- **方法**: `GET`
-- **说明**: 不需要 `/api/v1` 前缀,直接放在根路径
-- **功能**: 根据短链标识查询对应的推广链接,重定向到推广页面
-
-### 生成推广链接接口
-
-- **路径**: `/api/v1/agent/generating_link`
-- **方法**: `POST`
-- **请求参数**:
- ```json
- {
- "product_id": 1,
- "set_price": 10.00,
- "target_path": "/agent/promotionInquire/{linkIdentifier}" // 前端传入目标地址
- }
- ```
-- **返回**:
- ```json
- {
- "link_identifier": "加密的链接标识",
- "full_link": "https://推广域名/s/xxxxxx"
- }
- ```
-
-### 生成邀请链接接口
-
-- **路径**: `/api/v1/agent/invite_link`
-- **方法**: `GET`
-- **请求参数**:
- - `invite_code`: 邀请码
- - `target_path`: 目标地址(可选,默认:`/register?invite_code=xxx`)
-- **返回**:
- ```json
- {
- "invite_link": "https://推广域名/s/xxxxxx"
- }
- ```
-
-## 四、数据库表结构
-
-### agent_short_link 表
-
-| 字段 | 类型 | 说明 |
-|------|------|------|
-| id | bigint | 主键ID |
-| type | tinyint | 类型:1=推广报告,2=邀请好友 |
-| link_id | bigint | 推广链接ID(关联agent_link表,仅推广报告使用) |
-| invite_code_id | bigint | 邀请码ID(关联agent_invite_code表,仅邀请好友使用) |
-| link_identifier | varchar(200) | 推广链接标识(加密,仅推广报告使用) |
-| invite_code | varchar(50) | 邀请码(仅邀请好友使用) |
-| short_code | varchar(20) | 短链标识(6位随机字符串) |
-| target_path | varchar(500) | 目标地址(前端传入,如:/agent/promotionInquire/xxx) |
-| promotion_domain | varchar(200) | 推广域名 |
-| create_time | datetime | 创建时间 |
-| update_time | datetime | 更新时间 |
-| delete_time | datetime | 删除时间 |
-| del_state | tinyint | 删除状态:0=未删除,1=已删除 |
-| version | bigint | 版本号(乐观锁) |
-
-## 五、注意事项
-
-1. 短链标识是6位随机字符串(大小写字母+数字)
-2. 同一推广链接或邀请码同一类型只会生成一个短链(通过唯一索引保证)
-3. 短链重定向使用 `target_path`(相对路径),域名切换由服务器配置处理
-4. 如果推广域名未配置,生成短链时会返回空字符串
-5. **前端必须传入 `target_path` 参数**,后端只负责确定推广域名并生成短链
-6. 推广报告类型:前端传入 `/agent/promotionInquire/{linkIdentifier}`
-7. 邀请好友类型:前端传入 `/register?invite_code=XXXXX`(如果不传则使用默认值)
-
diff --git a/解冻任务优化建议.md b/解冻任务优化建议.md
deleted file mode 100644
index bad524c..0000000
--- a/解冻任务优化建议.md
+++ /dev/null
@@ -1,177 +0,0 @@
-# 解冻任务优化建议
-
-## 当前实现分析
-
-### 优点
-✅ 使用信号量控制并发,避免数据库压力过大
-✅ 使用事务和乐观锁保证数据一致性
-✅ 双重检查防止重复处理
-✅ 错误处理不中断整体流程
-
-### 可改进点
-
-## 1. ⚠️ 超时控制(重要)
-
-**问题**:如果某个任务处理时间过长(比如数据库慢查询),会阻塞整个扫描流程。
-
-**建议**:为每个任务添加超时控制
-
-```go
-// 为每个任务设置30秒超时
-taskCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
-defer cancel()
-
-err := l.svcCtx.AgentFreezeTaskModel.Trans(taskCtx, func(transCtx context.Context, session sqlx.Session) error {
- // ... 处理逻辑
-})
-```
-
-## 2. 📊 批次大小限制(可选)
-
-**问题**:如果任务量非常大(比如几千个),一次性查询所有会占用大量内存。
-
-**建议**:如果任务量超过一定数量,可以分批处理
-
-```go
-const maxBatchSize = 1000 // 每次最多查询1000个
-if len(freezeTasks) > maxBatchSize {
- logx.Warnf("任务数量过多(%d),本次只处理前%d个,剩余将在下次扫描处理", len(freezeTasks), maxBatchSize)
- freezeTasks = freezeTasks[:maxBatchSize]
-}
-```
-
-## 3. 🔄 错误分类处理
-
-**问题**:所有错误都统一处理,没有区分临时错误和永久错误。
-
-**建议**:区分错误类型,临时错误可以重试,永久错误跳过
-
-```go
-if err != nil {
- // 判断错误类型
- if isTemporaryError(err) {
- // 临时错误,记录但继续处理其他任务
- logx.Errorf("解冻任务临时失败,将在下次扫描重试: freezeTaskId=%d, err=%v", task.Id, err)
- } else {
- // 永久错误(如数据异常),记录详细日志
- logx.Errorf("解冻任务永久失败: freezeTaskId=%d, err=%v", task.Id, err)
- }
-}
-```
-
-## 4. ⏱️ 处理时间监控
-
-**问题**:缺少处理时间统计,无法评估性能。
-
-**建议**:记录处理时间,便于监控和优化
-
-```go
-startTime := time.Now()
-// ... 处理逻辑
-duration := time.Since(startTime)
-logx.Infof("解冻任务处理耗时: freezeTaskId=%d, duration=%v", task.Id, duration)
-```
-
-## 5. 🛡️ 优雅关闭支持
-
-**问题**:如果服务关闭,正在处理的任务可能被中断。
-
-**建议**:检查 context 是否被取消
-
-```go
-select {
-case <-ctx.Done():
- logx.Infof("扫描任务被取消,已处理: 成功=%d, 失败=%d", successCount, failCount)
- return ctx.Err()
-default:
- // 继续处理
-}
-```
-
-## 6. 📈 延迟统计
-
-**问题**:无法知道任务延迟了多久才被处理。
-
-**建议**:记录延迟时间,便于监控
-
-```go
-delay := time.Since(currentTask.UnfreezeTime)
-if delay > 1*time.Hour {
- logx.Warnf("解冻任务延迟处理: freezeTaskId=%d, 延迟=%v", task.Id, delay)
-}
-```
-
-## 7. 🔍 数据库连接池监控
-
-**问题**:并发处理时可能耗尽数据库连接池。
-
-**建议**:监控连接池使用情况,如果连接数不足,降低并发数
-
-```go
-// 可以根据数据库连接池情况动态调整并发数
-maxConcurrency := 2
-if dbConnPoolAvailable < 5 {
- maxConcurrency = 1 // 连接池紧张时降低并发
-}
-```
-
-## 8. 🎯 幂等性增强
-
-**问题**:虽然有乐观锁,但可以进一步优化。
-
-**建议**:添加更多的幂等性检查
-
-```go
-// 检查是否已经解冻过(通过 actual_unfreeze_time)
-if currentTask.ActualUnfreezeTime.Valid {
- logx.Infof("任务已解冻,跳过: freezeTaskId=%d", task.Id)
- return nil
-}
-```
-
-## 9. 📝 更详细的日志
-
-**问题**:日志信息可以更详细,便于排查问题。
-
-**建议**:添加更多上下文信息
-
-```go
-logx.Infof("解冻任务详情: freezeTaskId=%d, agentId=%d, amount=%.2f, orderPrice=%.2f, freezeTime=%v, unfreezeTime=%v, delay=%v",
- task.Id, currentTask.AgentId, currentTask.FreezeAmount, currentTask.OrderPrice,
- currentTask.FreezeTime, currentTask.UnfreezeTime, delay)
-```
-
-## 10. 🔧 配置化并发数
-
-**问题**:并发数硬编码为2,不够灵活。
-
-**建议**:从配置表读取并发数
-
-```go
-// 从配置表读取并发数,默认2
-maxConcurrency, err := l.svcCtx.AgentConfigModel.FindOneByConfigKey(ctx, "unfreeze_max_concurrency")
-if err != nil || maxConcurrency == nil {
- maxConcurrency = 2 // 默认值
-}
-```
-
-## 优先级建议
-
-### 🔴 高优先级(建议立即实现)
-1. **超时控制** - 防止任务卡死
-2. **错误分类处理** - 提高可靠性
-
-### 🟡 中优先级(建议后续优化)
-3. **处理时间监控** - 便于性能优化
-4. **延迟统计** - 便于监控
-5. **更详细的日志** - 便于排查问题
-
-### 🟢 低优先级(可选)
-6. **批次大小限制** - 如果任务量不大可以不做
-7. **优雅关闭** - 如果服务很少重启可以不做
-8. **配置化并发数** - 如果并发数不需要调整可以不做
-
-## 实施建议
-
-建议先实现**超时控制**和**错误分类处理**,这两个对可靠性影响最大。其他优化可以根据实际运行情况逐步添加。
-
diff --git a/解冻任务实现方案说明.md b/解冻任务实现方案说明.md
deleted file mode 100644
index 0beb0d3..0000000
--- a/解冻任务实现方案说明.md
+++ /dev/null
@@ -1,132 +0,0 @@
-# 解冻任务实现方案说明
-
-## 方案对比
-
-### 方案1:Asynq 延迟任务(已实现但未使用)
-**优点:**
-- ✅ 精确到秒级执行
-- ✅ 自动重试机制
-- ✅ 无需额外调度器
-
-**缺点:**
-- ❌ 依赖 Redis 持久化,Redis 数据丢失会导致任务丢失
-- ❌ 系统长时间停机可能导致延迟任务过期
-- ❌ 需要补偿机制
-
-### 方案2:定时任务扫描(✅ 已采用)
-**优点:**
-- ✅ **数据持久化在数据库,更可靠**(核心优势)
-- ✅ **系统停机后重启,定时任务会继续扫描并处理**(核心优势)
-- ✅ 可以批量处理,效率高
-- ✅ 已有定时任务基础设施
-- ✅ 不依赖 Redis 持久化
-
-**缺点:**
-- ⚠️ 执行时间不够精确(取决于扫描频率,如每5分钟扫描一次)
-- ⚠️ 需要处理并发扫描(已通过乐观锁解决)
-
-## 最终选择:定时任务扫描方案
-
-### 选择理由
-1. **金融场景,可靠性优先**:涉及资金解冻,必须保证任务不丢失
-2. **解冻时间允许延迟**:解冻时间可以有一定的延迟(比如几分钟内都可以接受)
-3. **已有基础设施**:项目中已有定时任务实现(`cleanQueryData.go`)
-4. **数据库表已设计好**:`status` 和 `unfreeze_time` 字段支持扫描查询
-
-## 实现细节
-
-### 1. 定时任务配置
-- **执行频率**:每2小时执行一次(`0 */2 * * *`)- 节省性能
-- **任务类型**:`MsgUnfreezeCommissionScan`
-- **处理器**:`UnfreezeCommissionScanHandler`
-- **批次大小**:每次最多处理2个任务,避免并发太多
-
-### 2. 扫描逻辑
-```go
-// 查询条件:
-// - status = 1(待解冻)
-// - unfreeze_time <= 当前时间
-// - del_state = 0(未删除)
-// - 按 unfreeze_time 升序排序
-```
-
-### 3. 并发安全
-- 使用**乐观锁**(`version` 字段)确保并发安全
-- 每个任务在事务中处理,确保原子性
-- 双重检查:查询后再次检查状态,防止并发处理
-
-### 4. 错误处理
-- 单个任务失败不影响其他任务
-- 记录详细的错误日志
-- 失败的任务会在下次扫描时重试
-
-### 5. 性能优化
-- 使用数据库索引优化查询(`idx_status` 和 `idx_unfreeze_time`)
-- **扫描频率**:每2小时扫描一次,减少数据库查询压力
-- **查询所有任务**:每次扫描找到所有需要解冻的任务(不限制数量)
-- **并发控制**:使用信号量(Semaphore)限制最多同时处理2个任务
-- **批量处理**:所有任务都会处理,但通过并发控制避免同时处理太多,节省性能
-
-## 代码文件
-
-### 新增文件
-- `app/main/api/internal/queue/unfreezeCommissionScan.go` - 定时扫描处理器
-
-### 修改文件
-- `app/main/api/internal/queue/routes.go` - 注册定时任务
-- `app/main/api/internal/queue/agentProcess.go` - 移除发送延迟任务的逻辑
-- `app/main/api/internal/types/taskname.go` - 添加任务类型常量
-
-### 保留文件(备用)
-- `app/main/api/internal/queue/unfreezeCommission.go` - 延迟任务处理器(保留作为备用)
-- `app/main/api/internal/service/asynqService.go` - `SendUnfreezeTask` 方法(保留作为备用)
-
-## 执行流程
-
-```
-定时任务启动(每2小时)
- ↓
-扫描数据库:status=1 AND unfreeze_time <= 当前时间
- ↓
-查询所有需要解冻的任务(不限制数量)
- ↓
-并发处理(使用信号量限制最多同时2个)
- ├─ 任务1(goroutine 1)
- ├─ 任务2(goroutine 2)
- ├─ 任务3(等待,直到前2个完成)
- ├─ 任务4(等待,直到前2个完成)
- └─ ...(以此类推,两个两个处理)
- ↓
-每个任务使用事务 + 乐观锁处理
- ↓
-更新任务状态:status = 2(已解冻)
- ↓
-更新钱包:FrozenBalance -= 冻结金额, Balance += 冻结金额
- ↓
-等待所有任务完成
- ↓
-记录日志
-```
-
-## 监控建议
-
-1. **监控扫描任务执行情况**
- - 检查定时任务是否正常执行
- - 监控每次扫描找到的任务数量
- - 监控成功/失败数量
-
-2. **监控解冻延迟**
- - 记录 `actual_unfreeze_time - unfreeze_time` 的差值
- - 如果延迟超过10分钟,需要检查定时任务是否正常
-
-3. **监控异常情况**
- - 冻结余额不足的情况(数据异常)
- - 任务状态异常的情况
-
-## 后续优化建议
-
-1. **可配置扫描频率**:将扫描频率(当前5分钟)配置到配置表
-2. **批次大小限制**:如果任务量很大,可以限制每次处理的数量
-3. **告警机制**:如果连续多次扫描都失败,发送告警
-4. **补偿机制**:提供手动触发扫描的接口,用于紧急情况
-
diff --git a/退款时代理处理逻辑分析.md b/退款时代理处理逻辑分析.md
deleted file mode 100644
index c1be031..0000000
--- a/退款时代理处理逻辑分析.md
+++ /dev/null
@@ -1,232 +0,0 @@
-# 退款时代理处理逻辑分析
-
-## 当前流程分析
-
-### 1. 订单支付成功后的流程
-
-```
-支付成功
- ↓
-支付回调(支付宝/微信)
- ↓
-更新订单状态为 "paid"
- ↓
-发送异步任务 SendQueryTask(order.Id)
- ↓
-PaySuccessNotifyUserHandler.ProcessTask
- ├─ 创建查询记录(query表,状态为 "pending")
- ├─ 生成授权书
- ├─ 调用API请求服务(第164行)← 可能失败
- ├─ 如果API成功:
- │ ├─ 更新查询状态为 "success"
- │ └─ 发送代理处理任务 SendAgentProcessTask(第192行)
- └─ 如果API失败:
- └─ handleError → 退款 → 更新订单状态为 "refunded"
-```
-
-### 2. 代理订单创建时机
-
-**在支付时创建**(`paymentlogic.go:316-327`):
-- 代理订单在用户支付时创建
-- `ProcessStatus = 0`(待处理)
-- 此时还没有发放佣金和返佣
-
-### 3. 代理处理任务执行时机
-
-**只在查询成功后发送**(`paySuccessNotify.go:192`):
-```go
-// 报告生成成功后,发送代理处理异步任务(不阻塞报告流程)
-if asyncErr := l.svcCtx.AsynqService.SendAgentProcessTask(order.Id); asyncErr != nil {
- // 代理处理任务发送失败,只记录日志,不影响报告流程
- logx.Errorf("发送代理处理任务失败,订单ID: %d, 错误: %v", order.Id, asyncErr)
-}
-```
-
-### 4. API调用失败时的处理(第164-167行)
-
-```go
-combinedResponse, err := l.svcCtx.ApiRequestService.ProcessRequests(decryptData, product.Id)
-if err != nil {
- return l.handleError(ctx, err, order, query)
-}
-```
-
-**handleError 的处理**(第206-257行):
-1. 删除Redis缓存
-2. 更新查询状态为 `failed`
-3. **退款**
-4. 更新订单状态为 `refunded`
-
-**关键点**:此时代理处理任务**还没有发送**(因为查询还没成功)
-
----
-
-## 问题分析
-
-### ✅ 情况1:API调用失败,退款(正常情况)
-
-**流程**:
-1. 支付成功,创建代理订单(`ProcessStatus = 0`)
-2. 调用API失败(第164-167行)
-3. 进入 `handleError`,退款
-4. 更新订单状态为 `refunded`
-5. **代理处理任务还没发送**(查询未成功)
-
-**代理状态**:
-- ✅ 代理订单 `ProcessStatus = 0`(未处理)
-- ✅ 代理**没有收到**佣金
-- ✅ 代理**没有收到**返佣
-- ✅ **处理正确**
-
-### ⚠️ 情况2:查询成功但订单被退款(边界情况)
-
-**可能的场景**:
-1. 支付成功,创建代理订单(`ProcessStatus = 0`)
-2. 调用API成功,查询状态更新为 `success`
-3. 发送代理处理任务(第192行)
-4. **但在代理处理任务执行前**,订单被退款(比如管理员手动退款)
-
-**代理处理任务的保护机制**(`agentProcess.go:43-46`):
-```go
-// 检查订单状态
-if order.Status != "paid" {
- logx.Infof("代理处理任务跳过,订单未支付: orderID=%d, status=%s", payload.OrderID, order.Status)
- return nil // 订单未支付,不处理,不重试
-}
-```
-
-**代理状态**:
-- ✅ 如果订单状态是 `refunded`,代理处理任务会跳过
-- ✅ 代理订单 `ProcessStatus` 仍然是 0
-- ✅ 代理**没有收到**佣金和返佣
-- ✅ **处理正确**
-
-### ⚠️ 情况3:代理已处理但订单被退款(需要处理)
-
-**可能的场景**:
-1. 支付成功,创建代理订单(`ProcessStatus = 0`)
-2. 调用API成功,查询状态更新为 `success`
-3. 发送代理处理任务
-4. 代理处理任务执行,发放佣金和返佣(`ProcessStatus = 1`)
-5. **之后**订单被退款(比如管理员手动退款)
-
-**当前问题**:
-- ❌ **代理已经收到佣金和返佣**
-- ❌ **没有撤销代理收益的逻辑**
-- ❌ **退款回调中也没有处理代理订单的逻辑**
-
----
-
-## 发现的问题
-
-### 问题1:退款回调中缺少代理订单处理
-
-**当前退款回调逻辑**(`wechatpayrefundcallbacklogic.go` 和 `alipayrefundcallbacklogic.go`):
-- ✅ 只更新订单状态和退款记录
-- ❌ **没有检查代理订单**
-- ❌ **没有撤销代理收益**
-
-### 问题2:管理员手动退款时缺少代理订单处理
-
-**当前管理员退款逻辑**(`adminrefundorderlogic.go`):
-- ✅ 创建退款记录,更新订单状态
-- ❌ **没有检查代理订单**
-- ❌ **没有撤销代理收益**
-
----
-
-## 建议的解决方案
-
-### 方案1:在退款回调中处理代理订单(推荐)
-
-在退款成功回调中,检查代理订单并撤销收益:
-
-```go
-// 在 handleQueryOrderRefund 中添加代理订单处理
-if status == refunddomestic.STATUS_SUCCESS {
- // 更新订单状态
- order.Status = orderStatus
- // ...
-
- // 检查并处理代理订单
- agentOrder, err := l.svcCtx.AgentOrderModel.FindOneByOrderId(ctx, order.Id)
- if err == nil && agentOrder.ProcessStatus == 1 {
- // 代理订单已处理,需要撤销收益
- err = l.svcCtx.AgentService.CancelAgentCommission(ctx, order.Id)
- if err != nil {
- logx.Errorf("撤销代理收益失败,订单ID: %d, 错误: %v", order.Id, err)
- // 不阻断退款流程,只记录日志
- }
- }
-}
-```
-
-### 方案2:在管理员退款时处理代理订单
-
-在 `AdminRefundOrderLogic` 中添加代理订单检查和处理。
-
-### 方案3:在代理处理任务中增加订单状态检查(已有保护)
-
-当前已有保护机制(`agentProcess.go:43-46`),如果订单状态不是 `paid`,会跳过处理。
-
----
-
-## 当前处理是否正确?
-
-### ✅ 对于 API 调用失败的情况
-
-**完全正确**:
-- 如果API调用失败(第164-167行),会进入退款流程
-- 此时代理处理任务还没发送(因为查询未成功)
-- 代理订单 `ProcessStatus = 0`,代理没有收到收益
-- **处理正确,无需修改**
-
-### ⚠️ 对于已处理代理订单的退款情况
-
-**存在问题**:
-- 如果代理订单已经处理(`ProcessStatus = 1`),代理已收到佣金和返佣
-- 此时订单退款,**没有撤销代理收益的逻辑**
-- 这会导致:
- - 用户收到退款
- - 但代理仍然保留佣金和返佣
- - **资金不一致**
-
----
-
-## 建议修改
-
-### 1. 在退款回调中添加代理订单检查
-
-### 2. 在管理员退款中添加代理订单检查
-
-### 3. 创建撤销代理收益的方法
-
-```go
-// CancelAgentCommission 撤销代理收益(订单退款时调用)
-func (s *AgentService) CancelAgentCommission(ctx context.Context, orderId int64) error {
- // 1. 查找代理订单
- // 2. 检查是否已处理
- // 3. 撤销佣金(从钱包扣除)
- // 4. 撤销返佣(从上级钱包扣除)
- // 5. 更新代理订单状态
- // 6. 创建撤销记录
-}
-```
-
----
-
-## 总结
-
-### 当前情况(API调用失败退款)
-
-✅ **处理正确**:
-- API调用失败时,代理处理任务还没发送
-- 代理订单未处理,代理没有收益
-- **无需修改**
-
-### 需要补充的场景
-
-⚠️ **需要处理**:
-- 代理订单已处理(`ProcessStatus = 1`)后订单退款的情况
-- 需要在退款回调和管理员退款中添加撤销代理收益的逻辑
-
diff --git a/邀请码使用历史功能说明.md b/邀请码使用历史功能说明.md
deleted file mode 100644
index ce2c789..0000000
--- a/邀请码使用历史功能说明.md
+++ /dev/null
@@ -1,107 +0,0 @@
-# 邀请码使用历史功能说明
-
-## 问题描述
-
-当前系统中,`agent_invite_code` 表只记录了最后一次使用情况(`used_user_id`, `used_agent_id`, `used_time`)。对于普通邀请码(可以无限使用),每次使用都会覆盖之前的记录,导致:
-
-1. **无法统计**:无法统计每个邀请码总共邀请了多少代理
-2. **无法查询**:无法查询某个代理是通过哪个邀请码成为代理的
-3. **历史丢失**:无法保留完整的使用历史记录
-
-## 解决方案
-
-### 1. 创建使用历史表
-
-创建 `agent_invite_code_usage` 表,记录每次邀请码使用的详细信息。
-
-### 2. 在 agent 表中添加字段
-
-在 `agent` 表中添加 `invite_code_id` 字段,便于直接查询代理是通过哪个邀请码成为的。
-
-## 实施步骤
-
-### 第一步:执行 SQL 迁移
-
-```bash
-# 执行 SQL 文件
-mysql -u root -p your_database < deploy/sql/add_invite_code_usage_table.sql
-```
-
-### 第二步:生成 Model
-
-```bash
-# 生成 AgentInviteCodeUsage Model
-goctl model mysql datasource -url="root:password@tcp(localhost:3306)/database" \
- -table="agent_invite_code_usage" \
- -dir="app/main/model" \
- -cache=true \
- --style=goZero \
- --home="./deploy/template"
-```
-
-### 第三步:更新 ServiceContext
-
-在 `app/main/api/internal/svc/servicecontext.go` 中添加:
-
-```go
-AgentInviteCodeUsageModel model.AgentInviteCodeUsageModel
-```
-
-并在 `NewServiceContext` 中初始化:
-
-```go
-AgentInviteCodeUsageModel: model.NewAgentInviteCodeUsageModel(db, cacheConf),
-```
-
-### 第四步:更新逻辑代码
-
-更新以下文件,在使用邀请码时记录使用历史:
-
-1. `app/main/api/internal/logic/agent/registerbyinvitecodelogic.go`
-2. `app/main/api/internal/logic/agent/applyforagentlogic.go`
-
-### 第五步:更新 Agent Model(可选)
-
-如果 agent 表添加了 `invite_code_id` 字段,需要重新生成 Agent Model 或手动更新。
-
-## 功能说明
-
-### 统计功能
-
-- **统计邀请码邀请数量**:通过 `agent_invite_code_usage` 表,可以统计每个邀请码邀请了多少代理
-- **查询代理来源**:可以通过 `agent.invite_code_id` 或 `agent_invite_code_usage.agent_id` 查询代理是通过哪个邀请码成为的
-
-### 数据完整性
-
-- **保留完整历史**:每次使用邀请码都会记录一条使用历史,不会丢失
-- **支持多次使用**:普通邀请码可以多次使用,每次使用都会记录
-
-## API 查询示例
-
-### 查询某个邀请码邀请了多少代理
-
-```sql
-SELECT COUNT(*) FROM agent_invite_code_usage
-WHERE invite_code_id = ? AND del_state = 0;
-```
-
-### 查询某个代理是通过哪个邀请码成为的
-
-```sql
--- 方式1:通过 agent 表(如果添加了 invite_code_id 字段)
-SELECT invite_code_id FROM agent WHERE id = ?;
-
--- 方式2:通过使用历史表
-SELECT invite_code_id, code FROM agent_invite_code_usage
-WHERE agent_id = ? AND del_state = 0
-ORDER BY used_time DESC LIMIT 1;
-```
-
-### 查询某个邀请码的所有使用记录
-
-```sql
-SELECT * FROM agent_invite_code_usage
-WHERE invite_code_id = ? AND del_state = 0
-ORDER BY used_time DESC;
-```
-
diff --git a/邀请链接和二维码生成逻辑说明.md b/邀请链接和二维码生成逻辑说明.md
deleted file mode 100644
index 1864d0f..0000000
--- a/邀请链接和二维码生成逻辑说明.md
+++ /dev/null
@@ -1,211 +0,0 @@
-# 邀请链接和二维码生成逻辑说明
-
-## 一、邀请链接生成逻辑
-
-### 1. API 端点
-- **路径**: `GET /agent/invite_link`
-- **处理器**: `GetInviteLinkHandler`
-- **逻辑**: `GetInviteLinkLogic`
-
-### 2. 生成流程
-
-#### 步骤 1: 验证代理身份
-```go
-// 获取当前用户ID
-userID := ctxdata.GetUidFromCtx(ctx)
-
-// 查询代理信息
-agent := AgentModel.FindOneByUserId(userID)
-```
-
-#### 步骤 2: 生成邀请码
-- 生成一个8位随机邀请码(使用 `tool.Krand(8, tool.KC_RAND_KIND_ALL)`)
-- 检查邀请码是否已存在(最多重试10次)
-- 创建邀请码记录:
- - `agent_id`: 当前代理ID
- - `target_level`: 1(普通代理)
- - `status`: 0(未使用)
- - `expire_time`: NULL(不过期)
- - `remark`: "邀请链接生成"
-
-#### 步骤 3: 构建邀请链接
-```go
-frontendDomain := "https://example.com" // TODO: 需要配置
-inviteLink := fmt.Sprintf("%s/register?invite_code=%s", frontendDomain, inviteCode)
-```
-
-#### 步骤 4: 生成二维码URL
-```go
-qrCodeUrl := fmt.Sprintf("%s/api/v1/image/qrcode?type=invitation&content=%s", frontendDomain, inviteLink)
-```
-
-### 3. 当前问题
-- ❌ `frontendDomain` 硬编码为 `"https://example.com"`,需要配置化
-- ❌ 二维码API (`/api/v1/image/qrcode`) 可能还未实现
-
-## 二、二维码生成逻辑
-
-### 1. 两种生成方式
-
-#### 方式 A: 前端生成(当前使用)
-- **位置**: `ycc-proxy-webview/src/components/QRcode.vue`
-- **库**: `qrcode` (npm)
-- **逻辑**:
- ```javascript
- // 如果提供了后端返回的 qrCodeUrl,优先使用
- if (mode === "invitation" && qrCodeUrl) {
- // 加载后端生成的二维码图片
- qrImg.src = qrCodeUrl;
- } else {
- // 前端生成二维码
- QRCode.toDataURL(url, { width: 150, margin: 0 });
- }
- ```
-
-#### 方式 B: 后端生成(已实现服务但可能缺少API端点)
-- **服务**: `ImageService.ProcessImageWithQRCode()`
-- **功能**:
- - 支持两种类型:`promote`(推广)和 `invitation`(邀请)
- - 加载背景图片(`static/images/yq_qrcode_1.png` 或 `tg_qrcode_1.png`)
- - 生成二维码并合成到背景图上
- - 返回 PNG 格式图片
-
-### 2. 二维码海报生成流程
-
-#### 推广海报(promote模式)
-- **背景图**: `static/images/tg_qrcode_1.png` - `tg_qrcode_8.jpg`(8张轮播图)
-- **二维码位置**: 左下角
- - 尺寸: 280px
- - 位置: X=192px, Y=距离底部190px
-- **用途**: 推广产品查询服务
-
-#### 邀请海报(invitation模式)
-- **背景图**: `static/images/yq_qrcode_1.png`
-- **二维码位置**: 中间偏上
- - 尺寸: 360px
- - 位置: 水平居中,垂直位置Y=555px
-- **用途**: 邀请好友成为下级代理
-
-### 3. 前端海报合成逻辑
-
-```javascript
-// 1. 加载海报背景图
-posterImg.src = posterImages[index];
-
-// 2. 生成或加载二维码
-if (mode === "invitation" && qrCodeUrl) {
- // 使用后端返回的二维码
- qrImg.src = qrCodeUrl;
-} else {
- // 前端生成二维码
- QRCode.toDataURL(url);
-}
-
-// 3. 在Canvas上绘制
-ctx.drawImage(posterImg, 0, 0); // 背景
-ctx.drawImage(qrCodeImg, x, y, size, size); // 二维码
-```
-
-## 三、数据流程
-
-### 邀请链接流程
-
-```
-用户点击"生成邀请链接"
- ↓
-前端调用 GET /agent/invite_link
- ↓
-后端逻辑 (GetInviteLinkLogic):
- 1. 验证代理身份
- 2. 生成新的邀请码(8位随机,不过期)
- 3. 保存到 agent_invite_code 表
- 4. 构建邀请链接: https://domain/register?invite_code=XXXXX
- 5. 构建二维码URL: https://domain/api/v1/image/qrcode?type=invitation&content=...
- ↓
-返回 { invite_link, qr_code_url }
- ↓
-前端显示链接和二维码
-```
-
-### 二维码使用流程
-
-#### 邀请模式 (invitation)
-```
-后端返回 qrCodeUrl
- ↓
-前端 QRcode 组件加载二维码图片
- ↓
-如果加载成功: 直接使用后端生成的二维码海报
-如果加载失败: 降级到前端生成二维码
- ↓
-合成到邀请海报背景图 (yq_qrcode_1.png)
- ↓
-用户可以保存或分享海报
-```
-
-#### 推广模式 (promote)
-```
-前端生成二维码 (使用 qrcode 库)
- ↓
-合成到推广海报背景图 (tg_qrcode_1.png - 8.png)
- ↓
-用户可以在多张海报中切换
- ↓
-用户可以保存或分享海报
-```
-
-## 四、相关数据库表
-
-### agent_invite_code
-- 存储邀请码信息
-- 每个邀请链接对应一个邀请码记录
-- 备注:`"邀请链接生成"` 表示这是通过链接生成的
-
-### agent_invite_code_usage
-- 存储邀请码使用历史
-- 记录每个代理是通过哪个邀请码成为的
-- 支持统计和查询
-
-## 五、待完善的问题
-
-### 1. 前端域名配置
-- ❌ 当前硬编码为 `"https://example.com"`
-- ✅ 应该从配置文件读取
-
-### 2. 二维码API实现
-- ❓ `/api/v1/image/qrcode` 端点是否存在?
-- ✅ `ImageService.ProcessImageWithQRCode()` 已实现
-- ❓ 是否需要创建对应的 Handler 和路由?
-
-### 3. 邀请码复用
-- 当前每次调用都生成新的邀请码
-- 是否应该复用已有的邀请码?
-
-## 六、链接格式对比
-
-### 邀请链接(成为代理)
-```
-格式: https://domain/register?invite_code=XXXXX
-参数: invite_code (8位邀请码)
-用途: 用户通过此链接注册成为代理
-```
-
-### 推广链接(推广产品)
-```
-格式: https://domain/agent/promotionInquire/{linkIdentifier}
-参数: linkIdentifier (加密的JSON字符串,包含agent_id, product_id, set_price)
-用途: 用户通过此链接查询产品
-```
-
-## 七、文件位置
-
-### 后端
-- **邀请链接逻辑**: `app/main/api/internal/logic/agent/getinvitelinklogic.go`
-- **二维码服务**: `app/main/api/internal/service/imageService.go`
-- **推广链接逻辑**: `app/main/api/internal/logic/agent/generatinglinklogic.go`
-
-### 前端
-- **二维码组件**: `ycc-proxy-webview/src/components/QRcode.vue`
-- **邀请页面**: `ycc-proxy-webview/src/views/Invitation.vue`
-- **推广查询页**: `ycc-proxy-webview/src/views/PromotionInquire.vue`
-