Files
platform/web/services/user.go

234 lines
6.3 KiB
Go

package services
import (
"errors"
"fmt"
"platform/pkg/u"
"platform/web/core"
m "platform/web/models"
q "platform/web/queries"
"time"
"github.com/shopspring/decimal"
"golang.org/x/crypto/bcrypt"
"gorm.io/gen/field"
"gorm.io/gorm"
)
var User = &userService{}
type userService struct{}
func (s *userService) Get(q *q.Query, uid int32) (*m.User, error) {
user, err := q.User.
Where(q.User.ID.Eq(uid)).Take()
if err != nil {
return nil, core.NewServErr("查询用户失败", err)
}
return user, nil
}
func (s *userService) UpdateBalanceByAdmin(user *m.User, newBalance decimal.Decimal, adminId *int32) error {
if user == nil {
return core.NewServErr("用户不存在")
}
amount := newBalance.Sub(user.Balance)
if amount.IsZero() {
return nil
}
return q.Q.Transaction(func(q *q.Query) error {
return s.UpdateBalance(q, user, amount, "管理员修改余额", adminId)
})
}
func (s *userService) UpdateBalance(q *q.Query, user *m.User, amount decimal.Decimal, remark string, adminId *int32) error {
balance := user.Balance.Add(amount)
if balance.IsNegative() {
return core.NewServErr("用户余额不足")
}
// 更新余额
_, err := q.User.
Where(
q.User.ID.Eq(user.ID),
q.User.Balance.Eq(user.Balance),
).
UpdateSimple(
q.User.Balance.Value(balance),
)
if err != nil {
return core.NewServErr("更新用户余额失败", err)
}
// 新增动账记录
err = q.BalanceActivity.Create(&m.BalanceActivity{
UserID: user.ID,
AdminID: adminId,
Amount: amount.StringFixed(2),
BalancePrev: user.Balance.StringFixed(2),
BalanceCurr: balance.StringFixed(2),
Remark: &remark,
})
if err != nil {
return core.NewServErr("新增动账记录失败", err)
}
return nil
}
func userBalanceKey(uid int32) string {
return fmt.Sprintf("user:%d:balance", uid)
}
type UpdateBalanceData struct {
Amount int `json:"amount"`
}
func (data *UpdateBalanceData) TradeDetail(user *m.User) (*TradeDetail, error) {
amount := decimal.NewFromInt(int64(data.Amount)).Div(decimal.NewFromInt(100))
return &TradeDetail{
data,
m.TradeTypeRecharge,
fmt.Sprintf("账户充值 - %s元", amount.StringFixed(2)),
amount, amount,
nil, nil,
nil, nil,
}, nil
}
// CreateByAdmin 管理员创建用户的数据
func (s *userService) CreateByAdmin(data CreateUserByAdminData) error {
var hashedPwd *string
if data.Password != nil {
hash, err := bcrypt.GenerateFromPassword([]byte(*data.Password), bcrypt.DefaultCost)
if err != nil {
return core.NewServErr("密码加密失败", err)
}
hashedPwd = u.P(string(hash))
}
source := m.UserSourceAdd
err := q.User.Create(&m.User{
Phone: data.Phone,
AdminID: data.AdminID,
DiscountID: data.DiscountID,
Username: data.Username,
Email: data.Email,
Password: hashedPwd,
Source: &source,
Avatar: data.Avatar,
Status: u.Else(data.Status, m.UserStatusEnabled),
ContactQQ: data.ContactQQ,
ContactWechat: data.ContactWechat,
})
if errors.Is(err, gorm.ErrDuplicatedKey) {
return core.NewBizErr("账号已存在,请检查手机号/用户名/邮箱是否重复")
}
if err != nil {
return err
}
return nil
}
type CreateUserByAdminData struct {
Phone string `json:"phone" validate:"required"`
AdminID *int32 `json:"admin_id"`
DiscountID *int32 `json:"discount_id"`
Username *string `json:"username"`
Email *string `json:"email"`
Password *string `json:"password"`
Avatar *string `json:"avatar"`
Status *m.UserStatus `json:"status"`
ContactQQ *string `json:"contact_qq"`
ContactWechat *string `json:"contact_wechat"`
}
// UpdateByAdmin 管理员更新用户的数据
func (s *userService) UpdateByAdmin(data UpdateUserByAdminData) error {
do := make([]field.AssignExpr, 0)
if data.Phone != nil {
do = append(do, q.User.Phone.Value(*data.Phone))
}
if data.AdminID != nil {
do = append(do, q.User.AdminID.Value(*data.AdminID))
}
if data.DiscountID != nil {
do = append(do, q.User.DiscountID.Value(*data.DiscountID))
}
if data.Username != nil {
do = append(do, q.User.Username.Value(*data.Username))
}
if data.Email != nil {
do = append(do, q.User.Email.Value(*data.Email))
}
if data.Password != nil {
hash, err := bcrypt.GenerateFromPassword([]byte(*data.Password), bcrypt.DefaultCost)
if err != nil {
return core.NewServErr("密码加密失败", err)
}
do = append(do, q.User.Password.Value(string(hash)))
}
if data.Name != nil {
do = append(do, q.User.Name.Value(*data.Name))
}
if data.Avatar != nil {
do = append(do, q.User.Avatar.Value(*data.Avatar))
}
if data.Status != nil {
do = append(do, q.User.Status.Value(int(*data.Status)))
}
if data.IDType != nil {
do = append(do, q.User.IDType.Value(int(*data.IDType)))
}
if data.IDNo != nil {
do = append(do, q.User.IDNo.Value(*data.IDNo))
}
if data.IDToken != nil {
do = append(do, q.User.IDToken.Value(*data.IDToken))
}
if data.ContactQQ != nil {
do = append(do, q.User.ContactQQ.Value(*data.ContactQQ))
}
if data.ContactWechat != nil {
do = append(do, q.User.ContactWechat.Value(*data.ContactWechat))
}
if len(do) == 0 {
return nil
}
_, err := q.User.Where(q.User.ID.Eq(data.ID)).UpdateSimple(do...)
if errors.Is(err, gorm.ErrDuplicatedKey) {
return core.NewBizErr("账号已存在,请检查手机号/用户名/邮箱是否重复")
}
return err
}
type UpdateUserByAdminData struct {
ID int32 `json:"id" validate:"required"`
Phone *string `json:"phone"`
AdminID *int32 `json:"admin_id"`
DiscountID *int32 `json:"discount_id"`
Username *string `json:"username"`
Email *string `json:"email"`
Password *string `json:"password"`
Name *string `json:"name"`
Avatar *string `json:"avatar"`
Status *m.UserStatus `json:"status"`
IDType *m.UserIDType `json:"id_type"`
IDNo *string `json:"id_no"`
IDToken *string `json:"id_token"`
ContactQQ *string `json:"contact_qq"`
ContactWechat *string `json:"contact_wechat"`
}
func (s *userService) RemoveByAdmin(id int32) error {
_, err := q.User.Where(q.User.ID.Eq(id)).UpdateColumn(q.User.DeletedAt, time.Now())
return err
}