This commit is contained in:
2026-04-18 12:05:21 +08:00
parent 55fcaf62dc
commit 957976db31
21 changed files with 331 additions and 140 deletions

View File

@@ -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-启用
}
// 列表响应

View File

@@ -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-启用
}
// 列表响应

View File

@@ -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"`

View File

@@ -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"`
}
)

View File

@@ -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:

View File

@@ -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)

View File

@@ -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

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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 {

View File

@@ -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

View File

@@ -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(&notifications, &notificationsModelList)
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
}

View File

@@ -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)

View File

@@ -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,

View File

@@ -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)

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)

View File

@@ -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"`
}

View File

@@ -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

View File

@@ -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 {