diff --git a/app/main/api/desc/admin/admin_user.api b/app/main/api/desc/admin/admin_user.api index 8df538e..ca2e404 100644 --- a/app/main/api/desc/admin/admin_user.api +++ b/app/main/api/desc/admin/admin_user.api @@ -44,11 +44,13 @@ service main { type ( // 列表请求 AdminGetUserListReq { - Page int64 `form:"page,default=1"` // 页码 - PageSize int64 `form:"pageSize,default=20"` // 每页数量 - Username string `form:"username,optional"` // 用户名 - RealName string `form:"real_name,optional"` // 真实姓名 - Status int64 `form:"status,optional,default=-1"` // 状态:0-禁用,1-启用 + Page int64 `form:"page,default=1"` // 页码 + PageSize int64 `form:"pageSize,default=20"` // 每页数量 + Username string `form:"username,optional"` // 用户名 + RealName string `form:"real_name,optional"` // 真实姓名 + StartTime string `form:"startTime,optional"` // 创建时间开始(yyyy-MM-dd HH:mm:ss) + EndTime string `form:"endTime,optional"` // 创建时间结束(yyyy-MM-dd HH:mm:ss) + Status int64 `form:"status,optional,default=-1"` // 状态:0-禁用,1-启用 } // 列表响应 diff --git a/app/main/api/desc/admin/role.api b/app/main/api/desc/admin/role.api index 99dd121..3b01668 100644 --- a/app/main/api/desc/admin/role.api +++ b/app/main/api/desc/admin/role.api @@ -36,11 +36,16 @@ service main { type ( // 列表请求 GetRoleListReq { - Page int64 `form:"page,default=1"` // 页码 - PageSize int64 `form:"pageSize,default=20"` // 每页数量 - Name string `form:"name,optional"` // 角色名称 - Code string `form:"code,optional"` // 角色编码 - Status int64 `form:"status,optional,default=-1"` // 状态:0-禁用,1-启用 + Page int64 `form:"page,default=1"` // 页码 + PageSize int64 `form:"pageSize,default=20"` // 每页数量 + RoleName string `form:"role_name,optional"` // 角色名称(前端字段) + Name string `form:"name,optional"` // 角色名称(兼容字段) + RoleCode string `form:"role_code,optional"` // 角色编码(前端字段) + Code string `form:"code,optional"` // 角色编码(兼容字段) + Description string `form:"description,optional"` // 角色描述 + StartTime string `form:"startTime,optional"` // 创建时间开始(yyyy-MM-dd HH:mm:ss) + EndTime string `form:"endTime,optional"` // 创建时间结束(yyyy-MM-dd HH:mm:ss) + Status int64 `form:"status,optional,default=-1"` // 状态:0-禁用,1-启用 } // 列表响应 diff --git a/app/main/api/desc/front/agent.api b/app/main/api/desc/front/agent.api index 12ac05d..c3f5e02 100644 --- a/app/main/api/desc/front/agent.api +++ b/app/main/api/desc/front/agent.api @@ -54,6 +54,8 @@ type ( // 代理服务基本类型定义 type AgentProductConfig { ProductID int64 `json:"product_id"` + ProductName string `json:"product_name"` + ProductEn string `json:"product_en"` CostPrice float64 `json:"cost_price"` PriceRangeMin float64 `json:"price_range_min"` PriceRangeMax float64 `json:"price_range_max"` diff --git a/app/main/api/desc/front/user.api b/app/main/api/desc/front/user.api index 34b93ec..8dc69ee 100644 --- a/app/main/api/desc/front/user.api +++ b/app/main/api/desc/front/user.api @@ -141,9 +141,9 @@ service main { type ( sendSmsReq { - Mobile string `json:"mobile" validate:"required,mobile"` - CaptchaVerifyParam string `json:"captchaVerifyParam"` - ActionType string `json:"actionType,optional" validate:"oneof=login register query agentApply realName bindMobile"` + Mobile string `json:"mobile" validate:"required,mobile"` + CaptchaVerifyParam string `json:"captchaVerifyParam,optional,omitempty"` + ActionType string `json:"actionType,optional" validate:"oneof=login register query agentApply realName bindMobile"` } ) diff --git a/app/main/api/etc/main.example.yaml b/app/main/api/etc/main.example.yaml index f4428fe..d5888cd 100644 --- a/app/main/api/etc/main.example.yaml +++ b/app/main/api/etc/main.example.yaml @@ -68,8 +68,6 @@ WechatH5: WechatMini: AppID: "xxxx" # 小程序的AppID AppSecret: "xxxx" # 小程序的AppSecret - TycAppID: "xxxx" - TycAppSecret: "xxxx" Query: ShareLinkExpire: 604800 # 7天 = 7 * 24 * 60 * 60 = 604800秒 AdminConfig: diff --git a/app/main/api/internal/logic/admin_notification/admingetnotificationlistlogic.go b/app/main/api/internal/logic/admin_notification/admingetnotificationlistlogic.go index a7f4530..c45299f 100644 --- a/app/main/api/internal/logic/admin_notification/admingetnotificationlistlogic.go +++ b/app/main/api/internal/logic/admin_notification/admingetnotificationlistlogic.go @@ -2,6 +2,7 @@ package admin_notification import ( "context" + "strings" "time" "bdrp-server/app/main/api/internal/svc" @@ -32,7 +33,10 @@ func (l *AdminGetNotificationListLogic) AdminGetNotificationList(req *types.Admi builder = builder.Where("title LIKE ?", "%"+*req.Title+"%") } if req.NotificationPage != nil { - builder = builder.Where("notification_page = ?", *req.NotificationPage) + s := strings.TrimSpace(*req.NotificationPage) + if s != "" { + builder = builder.Where("notification_page LIKE ?", "%"+s+"%") + } } if req.Status != nil { builder = builder.Where("status = ?", *req.Status) diff --git a/app/main/api/internal/logic/admin_notification/adminupdatenotificationlogic.go b/app/main/api/internal/logic/admin_notification/adminupdatenotificationlogic.go index 0dd6e6a..a79652b 100644 --- a/app/main/api/internal/logic/admin_notification/adminupdatenotificationlogic.go +++ b/app/main/api/internal/logic/admin_notification/adminupdatenotificationlogic.go @@ -3,6 +3,7 @@ package admin_notification import ( "context" "database/sql" + "strings" "time" "bdrp-server/app/main/api/internal/svc" @@ -32,13 +33,30 @@ func (l *AdminUpdateNotificationLogic) AdminUpdateNotification(req *types.AdminU if err != nil { return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查找通知失败, err: %v, id: %d", err, req.Id) } + // 空字符串表示「无日期」(如每天展示),须写 NULL;不能写零时间,否则 MySQL 会报 Incorrect date value if req.StartDate != nil { - startDate, _ := time.Parse("2006-01-02", *req.StartDate) - notification.StartDate = sql.NullTime{Time: startDate, Valid: true} + s := strings.TrimSpace(*req.StartDate) + if s == "" { + notification.StartDate = sql.NullTime{Valid: false} + } else { + startDate, err := time.Parse("2006-01-02", s) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.REUQEST_PARAM_ERROR), "start_date 格式错误: %v", err) + } + notification.StartDate = sql.NullTime{Time: startDate, Valid: true} + } } if req.EndDate != nil { - endDate, _ := time.Parse("2006-01-02", *req.EndDate) - notification.EndDate = sql.NullTime{Time: endDate, Valid: true} + s := strings.TrimSpace(*req.EndDate) + if s == "" { + notification.EndDate = sql.NullTime{Valid: false} + } else { + endDate, err := time.Parse("2006-01-02", s) + if err != nil { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.REUQEST_PARAM_ERROR), "end_date 格式错误: %v", err) + } + notification.EndDate = sql.NullTime{Time: endDate, Valid: true} + } } if req.Title != nil { notification.Title = *req.Title diff --git a/app/main/api/internal/logic/admin_role/getrolelistlogic.go b/app/main/api/internal/logic/admin_role/getrolelistlogic.go index b724ccc..c0874c9 100644 --- a/app/main/api/internal/logic/admin_role/getrolelistlogic.go +++ b/app/main/api/internal/logic/admin_role/getrolelistlogic.go @@ -2,12 +2,15 @@ package admin_role import ( "context" + "strings" "sync" + "time" "bdrp-server/app/main/api/internal/svc" "bdrp-server/app/main/api/internal/types" "bdrp-server/app/main/model" + "github.com/Masterminds/squirrel" "github.com/samber/lo" "github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/mr" @@ -34,15 +37,7 @@ func (l *GetRoleListLogic) GetRoleList(req *types.GetRoleListReq) (resp *types.G // 构建查询条件 builder := l.svcCtx.AdminRoleModel.SelectBuilder() - if len(req.Name) > 0 { - builder = builder.Where("role_name LIKE ?", "%"+req.Name+"%") - } - if len(req.Code) > 0 { - builder = builder.Where("role_code LIKE ?", "%"+req.Code+"%") - } - if req.Status != -1 { - builder = builder.Where("status = ?", req.Status) - } + builder = applyRoleListFilters(builder, req) // 设置分页 offset := (req.Page - 1) * req.PageSize @@ -73,15 +68,7 @@ func (l *GetRoleListLogic) GetRoleList(req *types.GetRoleListReq) (resp *types.G mutex.Unlock() } else if taskType == 2 { countBuilder := l.svcCtx.AdminRoleModel.SelectBuilder() - if len(req.Name) > 0 { - countBuilder = countBuilder.Where("role_name LIKE ?", "%"+req.Name+"%") - } - if len(req.Code) > 0 { - countBuilder = countBuilder.Where("role_code LIKE ?", "%"+req.Code+"%") - } - if req.Status != -1 { - countBuilder = countBuilder.Where("status = ?", req.Status) - } + countBuilder = applyRoleListFilters(countBuilder, req) count, err := l.svcCtx.AdminRoleModel.FindCount(l.ctx, countBuilder, "id") if err != nil { @@ -146,3 +133,56 @@ func (l *GetRoleListLogic) GetRoleList(req *types.GetRoleListReq) (resp *types.G return resp, nil } + +func applyRoleListFilters(builder squirrel.SelectBuilder, req *types.GetRoleListReq) squirrel.SelectBuilder { + roleName := strings.TrimSpace(req.RoleName) + if roleName == "" { + roleName = strings.TrimSpace(req.Name) + } + if roleName != "" { + builder = builder.Where("role_name LIKE ?", "%"+roleName+"%") + } + + roleCode := strings.TrimSpace(req.RoleCode) + if roleCode == "" { + roleCode = strings.TrimSpace(req.Code) + } + if roleCode != "" { + builder = builder.Where("role_code LIKE ?", "%"+roleCode+"%") + } + + description := strings.TrimSpace(req.Description) + if description != "" { + builder = builder.Where("description LIKE ?", "%"+description+"%") + } + + if req.Status != -1 { + builder = builder.Where("status = ?", req.Status) + } + + if startTime, ok := parseRoleListTime(req.StartTime, false); ok { + builder = builder.Where("create_time >= ?", startTime) + } + if endTime, ok := parseRoleListTime(req.EndTime, true); ok { + builder = builder.Where("create_time <= ?", endTime) + } + + return builder +} + +func parseRoleListTime(raw string, isEnd bool) (time.Time, bool) { + raw = strings.TrimSpace(raw) + if raw == "" { + return time.Time{}, false + } + + for _, layout := range []string{"2006-01-02 15:04:05", "2006-01-02", time.RFC3339} { + if t, err := time.Parse(layout, raw); err == nil { + if layout == "2006-01-02" && isEnd { + t = t.Add(23*time.Hour + 59*time.Minute + 59*time.Second) + } + return t, true + } + } + return time.Time{}, false +} diff --git a/app/main/api/internal/logic/admin_user/admingetuserlistlogic.go b/app/main/api/internal/logic/admin_user/admingetuserlistlogic.go index d013f08..82f6d89 100644 --- a/app/main/api/internal/logic/admin_user/admingetuserlistlogic.go +++ b/app/main/api/internal/logic/admin_user/admingetuserlistlogic.go @@ -2,12 +2,15 @@ package admin_user import ( "context" + "strings" "sync" + "time" "bdrp-server/app/main/api/internal/svc" "bdrp-server/app/main/api/internal/types" "bdrp-server/app/main/model" + "github.com/Masterminds/squirrel" "github.com/samber/lo" "github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/mr" @@ -36,15 +39,7 @@ func (l *AdminGetUserListLogic) AdminGetUserList(req *types.AdminGetUserListReq) // 构建查询条件 builder := l.svcCtx.AdminUserModel.SelectBuilder(). Where("del_state = ?", 0) - if len(req.Username) > 0 { - builder = builder.Where("username LIKE ?", "%"+req.Username+"%") - } - if len(req.RealName) > 0 { - builder = builder.Where("real_name LIKE ?", "%"+req.RealName+"%") - } - if req.Status != -1 { - builder = builder.Where("status = ?", req.Status) - } + builder = applyAdminUserListFilters(builder, req) // 设置分页 offset := (req.Page - 1) * req.PageSize @@ -76,15 +71,7 @@ func (l *AdminGetUserListLogic) AdminGetUserList(req *types.AdminGetUserListReq) } else if taskType == 2 { countBuilder := l.svcCtx.AdminUserModel.SelectBuilder(). Where("del_state = ?", 0) - if len(req.Username) > 0 { - countBuilder = countBuilder.Where("username LIKE ?", "%"+req.Username+"%") - } - if len(req.RealName) > 0 { - countBuilder = countBuilder.Where("real_name LIKE ?", "%"+req.RealName+"%") - } - if req.Status != -1 { - countBuilder = countBuilder.Where("status = ?", req.Status) - } + countBuilder = applyAdminUserListFilters(countBuilder, req) count, err := l.svcCtx.AdminUserModel.FindCount(l.ctx, countBuilder, "id") if err != nil { @@ -147,3 +134,38 @@ func (l *AdminGetUserListLogic) AdminGetUserList(req *types.AdminGetUserListReq) return resp, nil } + +func applyAdminUserListFilters(builder squirrel.SelectBuilder, req *types.AdminGetUserListReq) squirrel.SelectBuilder { + if username := strings.TrimSpace(req.Username); username != "" { + builder = builder.Where("username LIKE ?", "%"+username+"%") + } + if realName := strings.TrimSpace(req.RealName); realName != "" { + builder = builder.Where("real_name LIKE ?", "%"+realName+"%") + } + if req.Status != -1 { + builder = builder.Where("status = ?", req.Status) + } + if startTime, ok := parseAdminUserListTime(req.StartTime, false); ok { + builder = builder.Where("create_time >= ?", startTime) + } + if endTime, ok := parseAdminUserListTime(req.EndTime, true); ok { + builder = builder.Where("create_time <= ?", endTime) + } + return builder +} + +func parseAdminUserListTime(raw string, isEnd bool) (time.Time, bool) { + raw = strings.TrimSpace(raw) + if raw == "" { + return time.Time{}, false + } + for _, layout := range []string{"2006-01-02 15:04:05", "2006-01-02", time.RFC3339} { + if t, err := time.Parse(layout, raw); err == nil { + if layout == "2006-01-02" && isEnd { + t = t.Add(23*time.Hour + 59*time.Minute + 59*time.Second) + } + return t, true + } + } + return time.Time{}, false +} diff --git a/app/main/api/internal/logic/agent/getagentproductconfiglogic.go b/app/main/api/internal/logic/agent/getagentproductconfiglogic.go index 11dd056..36ac812 100644 --- a/app/main/api/internal/logic/agent/getagentproductconfiglogic.go +++ b/app/main/api/internal/logic/agent/getagentproductconfiglogic.go @@ -67,6 +67,13 @@ func (l *GetAgentProductConfigLogic) GetAgentProductConfig() (resp *types.AgentP // 将 item 转换为推广项目配置模型 config := item.(*model.AgentProductConfig) var agentProductConfig types.AgentProductConfig + productModel, findProductErr := l.svcCtx.ProductModel.FindOne(l.ctx, config.ProductId) + if findProductErr != nil { + cancel(findProductErr) + return + } + agentProductConfig.ProductName = productModel.ProductName + agentProductConfig.ProductEn = productModel.ProductEn // 配置平台成本价和定价成本 agentProductConfigModel, findAgentProductConfigErr := l.svcCtx.AgentProductConfigModel.FindOneByProductId(l.ctx, config.ProductId) if findAgentProductConfigErr != nil { diff --git a/app/main/api/internal/logic/auth/sendsmslogic.go b/app/main/api/internal/logic/auth/sendsmslogic.go index 660cb81..b8dda3e 100644 --- a/app/main/api/internal/logic/auth/sendsmslogic.go +++ b/app/main/api/internal/logic/auth/sendsmslogic.go @@ -37,17 +37,16 @@ func NewSendSmsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SendSmsLo func (l *SendSmsLogic) SendSms(req *types.SendSmsReq) error { cfg := l.svcCtx.Config.Captcha - // captcha 开关:关闭后直接跳过图形验证码校验 - if !cfg.Enable { - return nil - } - if err := captcha.VerifyWithRequest(captcha.Config{ - AccessKeyID: cfg.AccessKeyID, - AccessKeySecret: cfg.AccessKeySecret, - EndpointURL: cfg.EndpointURL, - SceneID: cfg.SceneID, - }, req.CaptchaVerifyParam, l.ctx); err != nil { - return err + // captcha 开关:仅控制是否做图形校验;关闭时不校验,短信照常发送 + if cfg.Enable { + if err := captcha.VerifyWithRequest(captcha.Config{ + AccessKeyID: cfg.AccessKeyID, + AccessKeySecret: cfg.AccessKeySecret, + EndpointURL: cfg.EndpointURL, + SceneID: cfg.SceneID, + }, req.CaptchaVerifyParam, l.ctx); err != nil { + return err + } } // 默认action类型:当未传入时,默认为login,便于小程序环境兼容 action := req.ActionType diff --git a/app/main/api/internal/logic/notification/getnotificationslogic.go b/app/main/api/internal/logic/notification/getnotificationslogic.go index bfe8bb7..fd49a29 100644 --- a/app/main/api/internal/logic/notification/getnotificationslogic.go +++ b/app/main/api/internal/logic/notification/getnotificationslogic.go @@ -2,14 +2,15 @@ package notification import ( "context" - "bdrp-server/common/xerr" "time" - "github.com/jinzhu/copier" + "bdrp-server/common/xerr" + "github.com/pkg/errors" "bdrp-server/app/main/api/internal/svc" "bdrp-server/app/main/api/internal/types" + "bdrp-server/app/main/model" "github.com/zeromicro/go-zero/core/logx" ) @@ -36,22 +37,42 @@ func (l *GetNotificationsLogic) GetNotifications() (resp *types.GetNotifications todayStart := now.Format("2006-01-02") + " 00:00:00" todayEnd := now.Format("2006-01-02") + " 23:59:59" - // 构建查询条件 + // 构建查询条件(与后台管理一致:status 1=启用 0=禁用;库中为整型,不能用字符串 "active") builder := l.svcCtx.GlobalNotificationsModel.SelectBuilder(). - Where("status = ?", "active"). + Where("status = ?", 1). Where("(start_date IS NULL OR start_date <= ?)", todayEnd). // start_date 是 NULL 或者小于等于今天结束时间 - Where("(end_date IS NULL OR end_date >= ?)", todayStart) // end_date 是 NULL 或者大于等于今天开始时间 + Where("(end_date IS NULL OR end_date >= ?)", todayStart) // end_date 是 NULL 或者大于等于今天开始时间 notificationsModelList, findErr := l.svcCtx.GlobalNotificationsModel.FindAll(l.ctx, builder, "") if findErr != nil { return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "全局通知, 查找通知失败, err:%+v", findErr) } - var notifications []types.Notification - copyErr := copier.Copy(¬ifications, ¬ificationsModelList) - if copyErr != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "全局通知, 复制结构体失败, err:%+v", copyErr) - } + notifications := toPublicNotifications(notificationsModelList) - return &types.GetNotificationsResp{Notifications: notifications}, nil + return &types.GetNotificationsResp{ + Notifications: notifications, + Total: int64(len(notifications)), + }, nil +} + +func toPublicNotifications(list []*model.GlobalNotifications) []types.Notification { + out := make([]types.Notification, 0, len(list)) + for _, m := range list { + n := types.Notification{ + Title: m.Title, + Content: m.Content, + NotificationPage: m.NotificationPage, + StartTime: m.StartTime, + EndTime: m.EndTime, + } + if m.StartDate.Valid { + n.StartDate = m.StartDate.Time.Format("2006-01-02") + } + if m.EndDate.Valid { + n.EndDate = m.EndDate.Time.Format("2006-01-02") + } + out = append(out, n) + } + return out } diff --git a/app/main/api/internal/logic/user/bindmobilelogic.go b/app/main/api/internal/logic/user/bindmobilelogic.go index d575f69..6d89ec2 100644 --- a/app/main/api/internal/logic/user/bindmobilelogic.go +++ b/app/main/api/internal/logic/user/bindmobilelogic.go @@ -73,6 +73,9 @@ func (l *BindMobileLogic) BindMobile(req *types.BindMobileReq) (resp *types.Bind if claims.UserType == model.UserTypeTemp { userTemp, err := l.svcCtx.UserTempModel.FindOne(l.ctx, claims.UserId) if err != nil { + if errors.Is(err, model.ErrNotFound) { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.USER_TEMP_INVALID), "绑定手机号, 临时用户不存在: %d", claims.UserId) + } return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "绑定手机号, 读取临时用户失败: %v", err) } userAuth, err := l.svcCtx.UserAuthModel.FindOneByUserIdAuthType(l.ctx, user.Id, userTemp.AuthType) diff --git a/app/main/api/internal/logic/user/detaillogic.go b/app/main/api/internal/logic/user/detaillogic.go index 1ca9d80..e7d3462 100644 --- a/app/main/api/internal/logic/user/detaillogic.go +++ b/app/main/api/internal/logic/user/detaillogic.go @@ -1,13 +1,13 @@ package user import ( - "context" "bdrp-server/app/main/api/internal/svc" "bdrp-server/app/main/api/internal/types" "bdrp-server/app/main/model" "bdrp-server/common/ctxdata" "bdrp-server/common/xerr" "bdrp-server/pkg/lzkit/crypto" + "context" "github.com/jinzhu/copier" "github.com/pkg/errors" @@ -38,6 +38,15 @@ func (l *DetailLogic) Detail() (resp *types.UserInfoResp, err error) { userID := claims.UserId userType := claims.UserType if userType != model.UserTypeNormal { + if userType == model.UserTypeTemp { + _, err = l.svcCtx.UserTempModel.FindOne(l.ctx, userID) + if err != nil { + if errors.Is(err, model.ErrNotFound) { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.USER_TEMP_INVALID), "用户信息, 临时用户不存在, %v", err) + } + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "用户信息, 查询临时用户失败, %v", err) + } + } return &types.UserInfoResp{ UserInfo: types.User{ Id: userID, diff --git a/app/main/api/internal/logic/user/gettokenlogic.go b/app/main/api/internal/logic/user/gettokenlogic.go index 9c99834..12e8c46 100644 --- a/app/main/api/internal/logic/user/gettokenlogic.go +++ b/app/main/api/internal/logic/user/gettokenlogic.go @@ -37,7 +37,10 @@ func (l *GetTokenLogic) GetToken() (resp *types.MobileCodeLoginResp, err error) if claims.UserType == model.UserTypeTemp { _, err := l.svcCtx.UserTempModel.FindOne(l.ctx, claims.UserId) if err != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "用户信息, %v", err) + if errors.Is(err, model.ErrNotFound) { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.USER_TEMP_INVALID), "用户信息, 临时用户不存在, %v", err) + } + return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "用户信息, 查询临时用户失败, %v", err) } } else { user, err := l.svcCtx.UserModel.FindOne(l.ctx, claims.UserId) diff --git a/app/main/api/internal/logic/user/wxh5authlogic.go b/app/main/api/internal/logic/user/wxh5authlogic.go index 9ba0118..26f9f36 100644 --- a/app/main/api/internal/logic/user/wxh5authlogic.go +++ b/app/main/api/internal/logic/user/wxh5authlogic.go @@ -1,9 +1,9 @@ package user import ( - "context" "bdrp-server/app/main/model" "bdrp-server/common/xerr" + "context" "encoding/json" "fmt" "io" @@ -104,6 +104,8 @@ func (l *WxH5AuthLogic) WxH5Auth(req *types.WXH5AuthReq) (resp *types.WXH5AuthRe type AccessTokenResp struct { AccessToken string `json:"access_token"` Openid string `json:"openid"` + ErrCode int `json:"errcode,omitempty"` + ErrMsg string `json:"errmsg,omitempty"` } // GetAccessToken 通过code获取access_token @@ -112,26 +114,46 @@ func (l *WxH5AuthLogic) GetAccessToken(code string) (*AccessTokenResp, error) { appSecret := l.svcCtx.Config.WechatH5.AppSecret url := fmt.Sprintf("https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code", appID, appSecret, code) + const ( + maxRetryTimes = 3 + httpTimeout = 5 * time.Second + retryDelay = 100 * time.Millisecond + ) - resp, err := http.Get(url) - if err != nil { - return nil, err - } - defer resp.Body.Close() + client := &http.Client{Timeout: httpTimeout} + var lastErr error - body, err := io.ReadAll(resp.Body) - if err != nil { - return nil, err + for attempt := 1; attempt <= maxRetryTimes; attempt++ { + resp, err := client.Get(url) + if err != nil { + lastErr = err + } else { + body, readErr := io.ReadAll(resp.Body) + resp.Body.Close() + if readErr != nil { + lastErr = readErr + } else { + var accessTokenResp AccessTokenResp + if unmarshalErr := json.Unmarshal(body, &accessTokenResp); unmarshalErr != nil { + lastErr = unmarshalErr + } else { + if accessTokenResp.ErrCode != 0 { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), + "微信接口返回错误: errcode=%d, errmsg=%s", + accessTokenResp.ErrCode, accessTokenResp.ErrMsg) + } + if accessTokenResp.AccessToken == "" || accessTokenResp.Openid == "" { + return nil, errors.New("微信接口返回数据不完整") + } + return &accessTokenResp, nil + } + } + } + + if attempt < maxRetryTimes { + time.Sleep(time.Duration(attempt) * retryDelay) + } } - var accessTokenResp AccessTokenResp - if err = json.Unmarshal(body, &accessTokenResp); err != nil { - return nil, err - } - - if accessTokenResp.AccessToken == "" || accessTokenResp.Openid == "" { - return nil, errors.New("accessTokenResp.AccessToken为空") - } - - return &accessTokenResp, nil + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "请求微信access_token接口失败(重试%d次): %v", maxRetryTimes, lastErr) } diff --git a/app/main/api/internal/logic/user/wxminiauthlogic.go b/app/main/api/internal/logic/user/wxminiauthlogic.go index 2c3d85d..4045bfe 100644 --- a/app/main/api/internal/logic/user/wxminiauthlogic.go +++ b/app/main/api/internal/logic/user/wxminiauthlogic.go @@ -119,36 +119,52 @@ func (l *WxMiniAuthLogic) GetSessionKey(code string) (*SessionKeyResp, error) { url := fmt.Sprintf("https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code", appID, appSecret, code) + const ( + maxRetryTimes = 3 + httpTimeout = 5 * time.Second + retryDelay = 100 * time.Millisecond + ) - resp, err := http.Get(url) - if err != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取session_key失败: %v", err) - } - defer resp.Body.Close() + client := &http.Client{Timeout: httpTimeout} + var lastErr error - body, err := io.ReadAll(resp.Body) - if err != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "读取响应失败: %v", err) + for attempt := 1; attempt <= maxRetryTimes; attempt++ { + resp, err := client.Get(url) + if err != nil { + lastErr = err + } else { + body, readErr := io.ReadAll(resp.Body) + resp.Body.Close() + if readErr != nil { + lastErr = readErr + } else { + var sessionKeyResp SessionKeyResp + if unmarshalErr := json.Unmarshal(body, &sessionKeyResp); unmarshalErr != nil { + lastErr = unmarshalErr + } else { + // 检查微信返回的错误码 + if sessionKeyResp.ErrCode != 0 { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), + "微信接口返回错误: errcode=%d, errmsg=%s", + sessionKeyResp.ErrCode, sessionKeyResp.ErrMsg) + } + + // 验证必要字段 + if sessionKeyResp.Openid == "" || sessionKeyResp.SessionKey == "" { + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), + "微信接口返回数据不完整: openid=%s, session_key=%s", + sessionKeyResp.Openid, sessionKeyResp.SessionKey) + } + + return &sessionKeyResp, nil + } + } + } + + if attempt < maxRetryTimes { + time.Sleep(time.Duration(attempt) * retryDelay) + } } - var sessionKeyResp SessionKeyResp - if err = json.Unmarshal(body, &sessionKeyResp); err != nil { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "解析响应失败: %v", err) - } - - // 检查微信返回的错误码 - if sessionKeyResp.ErrCode != 0 { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), - "微信接口返回错误: errcode=%d, errmsg=%s", - sessionKeyResp.ErrCode, sessionKeyResp.ErrMsg) - } - - // 验证必要字段 - if sessionKeyResp.Openid == "" || sessionKeyResp.SessionKey == "" { - return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), - "微信接口返回数据不完整: openid=%s, session_key=%s", - sessionKeyResp.Openid, sessionKeyResp.SessionKey) - } - - return &sessionKeyResp, nil + return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "请求微信session_key接口失败(重试%d次): %v", maxRetryTimes, lastErr) } diff --git a/app/main/api/internal/service/userService.go b/app/main/api/internal/service/userService.go index a82192e..98fb725 100644 --- a/app/main/api/internal/service/userService.go +++ b/app/main/api/internal/service/userService.go @@ -1,12 +1,12 @@ package service import ( - "context" "bdrp-server/app/main/api/internal/config" "bdrp-server/app/main/model" "bdrp-server/common/ctxdata" jwtx "bdrp-server/common/jwt" "bdrp-server/common/xerr" + "context" "database/sql" "github.com/google/uuid" @@ -210,6 +210,9 @@ func (s *UserService) TempUserBindUser(ctx context.Context, session sqlx.Session // 使用事务上下文查询临时用户 userTemp, err := s.userTempModel.FindOne(ctx, claims.UserId) if err != nil { + if errors.Is(err, model.ErrNotFound) { + return errors.Wrapf(xerr.NewErrCode(xerr.USER_TEMP_INVALID), "临时用户不存在, userId: %d", claims.UserId) + } return err } @@ -236,6 +239,9 @@ func (s *UserService) TempUserBindUser(ctx context.Context, session sqlx.Session // 重新获取最新的userTemp数据,确保版本号是最新的 latestUserTemp, err := s.userTempModel.FindOne(ctx, claims.UserId) if err != nil { + if errors.Is(err, model.ErrNotFound) { + return errors.Wrapf(xerr.NewErrCode(xerr.USER_TEMP_INVALID), "临时用户不存在, userId: %d", claims.UserId) + } return err } err = s.userTempModel.DeleteSoft(ctx, session, latestUserTemp) @@ -261,6 +267,9 @@ func (s *UserService) TempUserBindUser(ctx context.Context, session sqlx.Session // 重新获取最新的userTemp数据,确保版本号是最新的 latestUserTemp, err := s.userTempModel.FindOne(ctx, claims.UserId) if err != nil { + if errors.Is(err, model.ErrNotFound) { + return errors.Wrapf(xerr.NewErrCode(xerr.USER_TEMP_INVALID), "临时用户不存在, userId: %d", claims.UserId) + } return err } err = s.userTempModel.DeleteSoft(ctx, session, latestUserTemp) diff --git a/app/main/api/internal/types/types.go b/app/main/api/internal/types/types.go index f18ff43..9d902c4 100644 --- a/app/main/api/internal/types/types.go +++ b/app/main/api/internal/types/types.go @@ -728,11 +728,13 @@ type AdminGetUserDetailResp struct { } type AdminGetUserListReq struct { - Page int64 `form:"page,default=1"` // 页码 - PageSize int64 `form:"pageSize,default=20"` // 每页数量 - Username string `form:"username,optional"` // 用户名 - RealName string `form:"real_name,optional"` // 真实姓名 - Status int64 `form:"status,optional,default=-1"` // 状态:0-禁用,1-启用 + Page int64 `form:"page,default=1"` // 页码 + PageSize int64 `form:"pageSize,default=20"` // 每页数量 + Username string `form:"username,optional"` // 用户名 + RealName string `form:"real_name,optional"` // 真实姓名 + StartTime string `form:"startTime,optional"` // 创建时间开始(yyyy-MM-dd HH:mm:ss) + EndTime string `form:"endTime,optional"` // 创建时间结束(yyyy-MM-dd HH:mm:ss) + Status int64 `form:"status,optional,default=-1"` // 状态:0-禁用,1-启用 } type AdminGetUserListResp struct { @@ -1199,6 +1201,8 @@ type AgentPlatformDeductionListItem struct { type AgentProductConfig struct { ProductID int64 `json:"product_id"` + ProductName string `json:"product_name"` + ProductEn string `json:"product_en"` CostPrice float64 `json:"cost_price"` PriceRangeMin float64 `json:"price_range_min"` PriceRangeMax float64 `json:"price_range_max"` @@ -1678,11 +1682,16 @@ type GetRoleDetailResp struct { } type GetRoleListReq struct { - Page int64 `form:"page,default=1"` // 页码 - PageSize int64 `form:"pageSize,default=20"` // 每页数量 - Name string `form:"name,optional"` // 角色名称 - Code string `form:"code,optional"` // 角色编码 - Status int64 `form:"status,optional,default=-1"` // 状态:0-禁用,1-启用 + Page int64 `form:"page,default=1"` // 页码 + PageSize int64 `form:"pageSize,default=20"` // 每页数量 + RoleName string `form:"role_name,optional"` // 角色名称(前端字段) + Name string `form:"name,optional"` // 角色名称(兼容字段) + RoleCode string `form:"role_code,optional"` // 角色编码(前端字段) + Code string `form:"code,optional"` // 角色编码(兼容字段) + Description string `form:"description,optional"` // 角色描述 + StartTime string `form:"startTime,optional"` // 创建时间开始(yyyy-MM-dd HH:mm:ss) + EndTime string `form:"endTime,optional"` // 创建时间结束(yyyy-MM-dd HH:mm:ss) + Status int64 `form:"status,optional,default=-1"` // 状态:0-禁用,1-启用 } type GetRoleListResp struct { @@ -2221,6 +2230,6 @@ type GetAppVersionResp struct { type SendSmsReq struct { Mobile string `json:"mobile" validate:"required,mobile"` - CaptchaVerifyParam string `json:"captchaVerifyParam"` - ActionType string `json:"actionType" validate:"omitempty,oneof=login register query agentApply realName bindMobile"` + CaptchaVerifyParam string `json:"captchaVerifyParam,optional,omitempty"` + ActionType string `json:"actionType,optional" validate:"oneof=login register query agentApply realName bindMobile"` } diff --git a/common/xerr/errCode.go b/common/xerr/errCode.go index 396df56..b212fde 100644 --- a/common/xerr/errCode.go +++ b/common/xerr/errCode.go @@ -17,6 +17,7 @@ const CUSTOM_ERROR uint32 = 100008 const USER_NOT_FOUND uint32 = 100009 const USER_NEED_BIND_MOBILE uint32 = 100010 const USER_DISABLED uint32 = 100011 // 账号已被封禁 +const USER_TEMP_INVALID uint32 = 100012 const LOGIN_FAILED uint32 = 200001 const LOGIC_QUERY_WAIT uint32 = 200002 diff --git a/common/xerr/errMsg.go b/common/xerr/errMsg.go index 87cd63a..975252b 100644 --- a/common/xerr/errMsg.go +++ b/common/xerr/errMsg.go @@ -14,6 +14,7 @@ func init() { message[USER_NOT_FOUND] = "用户不存在" message[USER_NEED_BIND_MOBILE] = "请先绑定手机号" message[USER_DISABLED] = "账号已被封禁" + message[USER_TEMP_INVALID] = "用户状态异常" } func MapErrMsg(errcode uint32) string {