init: web_hgdjc_server

This commit is contained in:
2026-11-07 10:44:09 +08:00
commit 71a4f4e175
713 changed files with 147749 additions and 0 deletions
+43
View File
@@ -0,0 +1,43 @@
package srv_user
import (
"videoweb/pub/models/usermod"
"videoweb/pub/pubdef"
)
type UserListReq struct {
pubdef.Page
Id *int64 `json:"id"` // 用户ID
AccountNo string `json:"accountNo"` // 账号(精确)
Email string `json:"email"` // 邮箱(精确)
NickName string `json:"nickName"` // 昵称(模糊)
DistrictCode string `json:"districtCode"` // 渠道码
Status *pubdef.UserStatus `json:"status"` // 状态
Type *pubdef.UserType `json:"type"` // 用户类型
}
type UserListResp struct {
Total int64 `json:"total"`
List usermod.UserList `json:"list"`
}
type UserUpdateReq struct {
Id int64 `json:"id" binding:"required"`
Param usermod.UserUpdate `json:"param"`
}
type GiftCoinReq struct {
UserId int64 `json:"userId" binding:"required"` // 目标用户ID
Coin int64 `json:"coin" binding:"required"` // 赠送金币数(正数=增加,负数=扣除)
Remark string `json:"remark"` // 备注(记录在流水表)
}
type UpdateVipReq struct {
UserId int64 `json:"userId" binding:"required"` // 用户ID
PackageId int64 `json:"packageId" binding:"required"` // 会员畅享套餐ID
}
type ResetPasswordReq struct {
UserId int64 `json:"userId" binding:"required"` // 用户ID
Password string `json:"password" binding:"required"` // 明文密码
}
+189
View File
@@ -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
}