init: 初始化 web_hgdjb_server 项目
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
package srv_user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
"videoweb/pub/common/srvdef"
|
||||
"videoweb/pub/models/cointransmod"
|
||||
"videoweb/pub/models/rechargepackagemod"
|
||||
"videoweb/pub/models/usermod"
|
||||
"videoweb/pub/payment"
|
||||
"videoweb/pub/pubdef"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// GetUsers 分页查询用户列表
|
||||
func GetUsers(ctx context.Context, req *UserListReq) (*UserListResp, *srvdef.SrvErr) {
|
||||
filter := bson.M{}
|
||||
if req.Id != nil && *req.Id > 0 {
|
||||
filter[usermod.FN.User.Id] = *req.Id
|
||||
}
|
||||
if accountNo := strings.TrimSpace(req.AccountNo); accountNo != "" {
|
||||
filter[usermod.FN.User.AccountNo] = accountNo
|
||||
}
|
||||
if email := strings.TrimSpace(req.Email); email != "" {
|
||||
filter[usermod.FN.User.Email] = email
|
||||
}
|
||||
if req.NickName != "" {
|
||||
filter[usermod.FN.User.NickName] = bson.M{"$regex": req.NickName}
|
||||
}
|
||||
if districtCode := strings.TrimSpace(req.DistrictCode); districtCode != "" {
|
||||
filter[usermod.FN.User.DistrictCode] = districtCode
|
||||
}
|
||||
if req.Status != nil {
|
||||
filter[usermod.FN.User.Status] = *req.Status
|
||||
}
|
||||
if req.Type != nil {
|
||||
filter[usermod.FN.User.Type] = *req.Type
|
||||
}
|
||||
|
||||
opts := options.Find().
|
||||
SetSort(bson.M{usermod.FN.User.Id: -1}).
|
||||
SetSkip((req.PageNum - 1) * req.PageSize).
|
||||
SetLimit(req.PageSize)
|
||||
|
||||
resp := &UserListResp{}
|
||||
total, serr := usermod.CountUsers(ctx, filter)
|
||||
if serr != nil {
|
||||
return resp, serr
|
||||
}
|
||||
resp.Total = total
|
||||
if total == 0 {
|
||||
return resp, nil
|
||||
}
|
||||
list, serr := usermod.FindManyUser(ctx, filter, opts)
|
||||
if serr != nil {
|
||||
return resp, serr
|
||||
}
|
||||
resp.List = list
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// UpdateUser 更新指定用户信息
|
||||
func UpdateUser(ctx context.Context, id int64, param usermod.UserUpdate) *srvdef.SrvErr {
|
||||
now := time.Now()
|
||||
param.UpdatedAt = &now
|
||||
return usermod.UpdateOneUser(ctx,
|
||||
bson.M{usermod.FN.User.Id: id},
|
||||
bson.M{"$set": param},
|
||||
)
|
||||
}
|
||||
|
||||
// GiftCoin 给用户赠送/扣除金币,并写入流水记录
|
||||
// coin 为正数时增加余额,为负数时扣减(不校验余额是否充足,由调用方确保)
|
||||
func GiftCoin(ctx context.Context, req *GiftCoinReq) *srvdef.SrvErr {
|
||||
if req.Coin == 0 {
|
||||
return srvdef.New(srvdef.ErrParamError, "金币数量不能为 0", "")
|
||||
}
|
||||
|
||||
// 原子更新余额
|
||||
if serr := usermod.UpdateOneUser(ctx,
|
||||
bson.M{usermod.FN.User.Id: req.UserId},
|
||||
bson.M{
|
||||
"$inc": bson.M{usermod.FN.User.Recharge: req.Coin},
|
||||
"$set": bson.M{usermod.FN.User.UpdatedAt: time.Now()},
|
||||
},
|
||||
); serr != nil {
|
||||
return serr
|
||||
}
|
||||
|
||||
// 写流水(更新后再查余额,作为 balanceAfter)
|
||||
flowDir := cointransmod.FlowIn
|
||||
amount := req.Coin
|
||||
if req.Coin < 0 {
|
||||
flowDir = cointransmod.FlowOut
|
||||
amount = -req.Coin
|
||||
}
|
||||
remark := req.Remark
|
||||
if remark == "" {
|
||||
remark = payment.CoinTransRemark(cointransmod.TranTypeAdminAdjust, "")
|
||||
}
|
||||
_ = payment.WriteCoinTransByUser(ctx, payment.WriteCoinTransOpt{
|
||||
UserId: req.UserId,
|
||||
FlowDir: flowDir,
|
||||
TranType: cointransmod.TranTypeAdminAdjust,
|
||||
Amount: amount,
|
||||
Remark: remark,
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateVip 根据会员畅享套餐修改用户会员信息。
|
||||
func UpdateVip(ctx context.Context, req *UpdateVipReq) *srvdef.SrvErr {
|
||||
if req == nil || req.UserId <= 0 || req.PackageId <= 0 {
|
||||
return srvdef.New(srvdef.ErrParamError, "用户ID和套餐ID不能为空", "")
|
||||
}
|
||||
user, serr := usermod.FindOneUser(ctx, bson.M{usermod.FN.User.Id: req.UserId})
|
||||
if serr != nil || user == nil {
|
||||
return srvdef.New(srvdef.ErrUserNotExsit, "", "")
|
||||
}
|
||||
pkg, serr := rechargepackagemod.FindOneRechargePackage(ctx, bson.M{
|
||||
rechargepackagemod.FN.RechargePackage.Id: req.PackageId,
|
||||
rechargepackagemod.FN.RechargePackage.PackageType: rechargepackagemod.PackageTypeVip,
|
||||
rechargepackagemod.FN.RechargePackage.Status: rechargepackagemod.StatusEnabled,
|
||||
})
|
||||
if serr != nil || pkg == nil {
|
||||
return srvdef.New(srvdef.ErrNoData, "会员套餐不存在或未启用", "")
|
||||
}
|
||||
if pkg.DurationDays <= 0 {
|
||||
return srvdef.New(srvdef.ErrParamError, "会员套餐有效天数无效", "")
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
baseTime := now
|
||||
if user.CurrentVipExpire.After(now) {
|
||||
baseTime = user.CurrentVipExpire
|
||||
}
|
||||
newExpire := baseTime.Add(time.Duration(pkg.DurationDays) * 24 * time.Hour)
|
||||
updateDoc := bson.M{
|
||||
"$set": bson.M{
|
||||
usermod.FN.User.VipType: vipTypeFromDurationDays(pkg.DurationDays),
|
||||
usermod.FN.User.ShowCardId: pkg.Id,
|
||||
usermod.FN.User.ShowCardName: pkg.Name,
|
||||
usermod.FN.User.CurrentVipExpire: newExpire,
|
||||
usermod.FN.User.UpdatedAt: now,
|
||||
},
|
||||
}
|
||||
|
||||
if serr = usermod.UpdateOneUser(ctx, bson.M{usermod.FN.User.Id: req.UserId}, updateDoc); serr != nil {
|
||||
return serr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResetPassword 按用户ID重置登陆密码,入库为 bcrypt 哈希,与 app 登录校验一致。
|
||||
func ResetPassword(ctx context.Context, req *ResetPasswordReq) *srvdef.SrvErr {
|
||||
if req == nil || req.UserId <= 0 {
|
||||
return srvdef.New(srvdef.ErrParamError, "用户ID不能为空", "")
|
||||
}
|
||||
password := strings.TrimSpace(req.Password)
|
||||
if password == "" {
|
||||
return srvdef.New(srvdef.ErrParamError, "密码不能为空", "")
|
||||
}
|
||||
hashed, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return srvdef.New(srvdef.Failure, "密码处理失败", "")
|
||||
}
|
||||
return usermod.UpdateOneUser(ctx,
|
||||
bson.M{usermod.FN.User.Id: req.UserId},
|
||||
bson.M{"$set": bson.M{
|
||||
usermod.FN.User.Password: string(hashed),
|
||||
usermod.FN.User.UpdatedAt: time.Now(),
|
||||
}},
|
||||
)
|
||||
}
|
||||
|
||||
func vipTypeFromDurationDays(days int64) pubdef.VipType {
|
||||
if days >= 365 {
|
||||
return pubdef.VipTypeYear
|
||||
}
|
||||
if days > 0 {
|
||||
return pubdef.VipTypeMonth
|
||||
}
|
||||
return pubdef.VipTypeNone
|
||||
}
|
||||
Reference in New Issue
Block a user