236 lines
6.3 KiB
Go
236 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,
|
|
Name: data.Name,
|
|
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"`
|
|
Name *string `json:"name"`
|
|
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
|
|
}
|