Files
rootandClaude Opus 4.7 79ac89b458 Initial commit
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-11-07 10:47:18 +08:00

190 lines
5.5 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
}