package product import ( "context" "crypto/rand" "encoding/hex" "fmt" "time" "perms-system-server/internal/consts" authHelper "perms-system-server/internal/logic/auth" productModel "perms-system-server/internal/model/product" "perms-system-server/internal/model/productmember" userModel "perms-system-server/internal/model/user" "perms-system-server/internal/response" "perms-system-server/internal/svc" "perms-system-server/internal/types" "github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/stores/sqlx" "golang.org/x/crypto/bcrypt" ) type CreateProductLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } func NewCreateProductLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateProductLogic { return &CreateProductLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } func (l *CreateProductLogic) CreateProduct(req *types.CreateProductReq) (resp *types.CreateProductResp, err error) { if err := authHelper.RequireSuperAdmin(l.ctx); err != nil { return nil, err } if len(req.Code) > 64 { return nil, response.ErrBadRequest("产品编码长度不能超过64个字符") } if len(req.Name) > 64 { return nil, response.ErrBadRequest("产品名称长度不能超过64个字符") } if len(req.Remark) > 255 { return nil, response.ErrBadRequest("备注长度不能超过255个字符") } _, findErr := l.svcCtx.SysProductModel.FindOneByCode(l.ctx, req.Code) if findErr == nil { return nil, response.ErrConflict("产品编码已存在") } appKey, err := generateRandomHex(32) if err != nil { return nil, err } rawAppSecret, err := generateRandomHex(64) if err != nil { return nil, err } appSecretHash, err := bcrypt.GenerateFromPassword([]byte(rawAppSecret), bcrypt.DefaultCost) if err != nil { return nil, err } now := time.Now().Unix() adminUsername := fmt.Sprintf("admin_%s", req.Code) if _, err := l.svcCtx.SysUserModel.FindOneByUsername(l.ctx, adminUsername); err == nil { return nil, response.ErrConflict(fmt.Sprintf("用户名 %s 已存在,无法自动创建管理员账号", adminUsername)) } adminPassword, err := generateRandomHex(8) if err != nil { return nil, err } hashedPwd, err := bcrypt.GenerateFromPassword([]byte(adminPassword), bcrypt.DefaultCost) if err != nil { return nil, err } var productId int64 err = l.svcCtx.SysProductModel.TransactCtx(l.ctx, func(ctx context.Context, session sqlx.Session) error { result, err := l.svcCtx.SysProductModel.InsertWithTx(ctx, session, &productModel.SysProduct{ Code: req.Code, Name: req.Name, AppKey: appKey, AppSecret: string(appSecretHash), Remark: req.Remark, Status: consts.StatusEnabled, CreateTime: now, UpdateTime: now, }) if err != nil { return err } productId, _ = result.LastInsertId() userResult, err := l.svcCtx.SysUserModel.InsertWithTx(ctx, session, &userModel.SysUser{ Username: adminUsername, Password: string(hashedPwd), Nickname: fmt.Sprintf("%s管理员", req.Name), IsSuperAdmin: consts.IsSuperAdminNo, MustChangePassword: consts.MustChangePasswordYes, Status: consts.StatusEnabled, CreateTime: now, UpdateTime: now, }) if err != nil { return err } userId, _ := userResult.LastInsertId() _, err = l.svcCtx.SysProductMemberModel.InsertWithTx(ctx, session, &productmember.SysProductMember{ ProductCode: req.Code, UserId: userId, MemberType: consts.MemberTypeAdmin, Status: consts.StatusEnabled, CreateTime: now, UpdateTime: now, }) return err }) if err != nil { return nil, err } return &types.CreateProductResp{ Id: productId, Code: req.Code, AppKey: appKey, AppSecret: rawAppSecret, AdminUser: adminUsername, AdminPassword: adminPassword, }, nil } func generateRandomHex(length int) (string, error) { b := make([]byte, length) if _, err := rand.Read(b); err != nil { return "", fmt.Errorf("generate random bytes failed: %w", err) } return hex.EncodeToString(b)[:length], nil }