init: web_hgdjc_server
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
package srv_rechargepackage
|
||||
|
||||
import (
|
||||
"time"
|
||||
"videoweb/pub/models/rechargepackagemod"
|
||||
"videoweb/pub/pubdef"
|
||||
)
|
||||
|
||||
// RechargePackageListReq Web后台购买套餐分页列表请求
|
||||
type RechargePackageListReq struct {
|
||||
pubdef.Page
|
||||
Id *int64 `json:"id"` // 套餐ID
|
||||
PackageType *rechargepackagemod.PackageType `json:"packageType"` // 套餐类型:1-会员畅享 2-金币充值
|
||||
Status *rechargepackagemod.Status `json:"status"` // 状态:1-启用 2-停用
|
||||
Name string `json:"name"` // 展示标题,模糊搜索
|
||||
}
|
||||
|
||||
// RechargePackageListResp Web后台购买套餐分页列表响应
|
||||
type RechargePackageListResp struct {
|
||||
Total int64 `json:"total"` // 总数量
|
||||
List []*RechargePackageItem `json:"list"` // 当前页列表
|
||||
}
|
||||
|
||||
// VipPackageOption 会员畅享下拉列表项。
|
||||
type VipPackageOption struct {
|
||||
Id int64 `json:"id"` // 套餐ID
|
||||
Name string `json:"name"` // 套餐名称
|
||||
}
|
||||
|
||||
// RechargePackageItem Web后台购买套餐列表项
|
||||
type RechargePackageItem struct {
|
||||
Id int64 `json:"id"` // 套餐ID
|
||||
PackageType rechargepackagemod.PackageType `json:"packageType"` // 套餐类型:1-会员畅享 2-金币充值
|
||||
Name string `json:"name"` // 展示标题
|
||||
SubTitle string `json:"subTitle"` // 展示副标题
|
||||
Price int64 `json:"price"` // 售价,单位分
|
||||
OriginalPrice int64 `json:"originalPrice"` // 原价,单位分
|
||||
DurationDays int64 `json:"durationDays"` // 会员有效天数
|
||||
DurationHours int64 `json:"durationHours"` // 会员有效小时数,由durationDays计算
|
||||
BaseCoin int64 `json:"baseCoin"` // 基础金币数
|
||||
GiftCoin int64 `json:"giftCoin"` // 赠送金币数
|
||||
TotalCoin int64 `json:"totalCoin"` // 到账总金币数,由baseCoin+giftCoin计算
|
||||
Sort int32 `json:"sort"` // 排序,越小越靠前
|
||||
Status rechargepackagemod.Status `json:"status"` // 状态:1-启用 2-停用
|
||||
Remark string `json:"remark"` // 后台备注
|
||||
CreatedAt time.Time `json:"createdAt"` // 创建时间
|
||||
UpdatedAt time.Time `json:"updatedAt"` // 更新时间
|
||||
}
|
||||
|
||||
// RechargePackageAddReq Web后台新增购买套餐请求
|
||||
type RechargePackageAddReq struct {
|
||||
PackageType rechargepackagemod.PackageType `json:"packageType" binding:"required"` // 套餐类型:1-会员畅享 2-金币充值
|
||||
Name string `json:"name" binding:"required"` // 展示标题
|
||||
Price int64 `json:"price" binding:"required"` // 售价,单位分
|
||||
OriginalPrice int64 `json:"originalPrice"` // 原价,单位分
|
||||
DurationDays int64 `json:"durationDays"` // 会员有效天数,金币套餐传0
|
||||
BaseCoin int64 `json:"baseCoin"` // 基础金币数,会员套餐传0
|
||||
GiftCoin int64 `json:"giftCoin"` // 赠送金币数
|
||||
Sort int32 `json:"sort"` // 排序,越小越靠前
|
||||
Status rechargepackagemod.Status `json:"status"` // 状态:1-启用 2-停用,默认1
|
||||
Remark string `json:"remark"` // 后台备注
|
||||
}
|
||||
|
||||
// RechargePackageUpdateReq Web后台更新购买套餐请求
|
||||
type RechargePackageUpdateReq struct {
|
||||
Id int64 `json:"id" binding:"required"` // 套餐ID
|
||||
PackageType *rechargepackagemod.PackageType `json:"packageType"` // 套餐类型:1-会员畅享 2-金币充值
|
||||
Name *string `json:"name"` // 展示标题
|
||||
Price *int64 `json:"price"` // 售价,单位分
|
||||
OriginalPrice *int64 `json:"originalPrice"` // 原价,单位分
|
||||
DurationDays *int64 `json:"durationDays"` // 会员有效天数
|
||||
BaseCoin *int64 `json:"baseCoin"` // 基础金币数
|
||||
GiftCoin *int64 `json:"giftCoin"` // 赠送金币数
|
||||
Sort *int32 `json:"sort"` // 排序
|
||||
Status *rechargepackagemod.Status `json:"status"` // 状态:1-启用 2-停用
|
||||
Remark *string `json:"remark"` // 后台备注
|
||||
}
|
||||
|
||||
// RechargePackageDelReq Web后台删除购买套餐请求。
|
||||
type RechargePackageDelReq struct {
|
||||
Id int64 `json:"id" binding:"required"` // 套餐ID
|
||||
}
|
||||
|
||||
// RechargePackageStatusSetReq Web后台单/批量设置购买套餐状态请求。
|
||||
type RechargePackageStatusSetReq struct {
|
||||
Ids []int64 `json:"ids"` // 套餐ID集合
|
||||
Status rechargepackagemod.Status `json:"status"` // 状态:1-启用 2-停用
|
||||
}
|
||||
@@ -0,0 +1,343 @@
|
||||
package srv_rechargepackage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
"videoweb/pub/common/srvdef"
|
||||
"videoweb/pub/models/rechargepackagemod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultPageNum int64 = 1
|
||||
defaultPageSize int64 = 10
|
||||
maxPageSize int64 = 100
|
||||
)
|
||||
|
||||
// ListRechargePackage 分页查询购买套餐配置。
|
||||
func ListRechargePackage(ctx context.Context, req *RechargePackageListReq) (*RechargePackageListResp, *srvdef.SrvErr) {
|
||||
if req == nil {
|
||||
return nil, srvdef.New(srvdef.ErrParamError, "参数不能为空", "")
|
||||
}
|
||||
filter := buildListFilter(req)
|
||||
pageNum, pageSize := normalizePage(req.PageNum, req.PageSize)
|
||||
total, err := rechargepackagemod.CountRechargePackage(ctx, filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp := &RechargePackageListResp{
|
||||
Total: total,
|
||||
List: make([]*RechargePackageItem, 0),
|
||||
}
|
||||
if total == 0 {
|
||||
return resp, nil
|
||||
}
|
||||
list, err := rechargepackagemod.FindManyRechargePackage(ctx, filter, options.Find().
|
||||
SetSort(bson.D{
|
||||
{Key: rechargepackagemod.FN.RechargePackage.PackageType, Value: 1},
|
||||
{Key: rechargepackagemod.FN.RechargePackage.Sort, Value: 1},
|
||||
{Key: rechargepackagemod.FN.RechargePackage.Id, Value: -1},
|
||||
}).
|
||||
SetSkip((pageNum-1)*pageSize).
|
||||
SetLimit(pageSize))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, item := range list {
|
||||
resp.List = append(resp.List, rechargePackageToWebItem(item))
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// ListVipPackageOptions 查询启用的会员畅享套餐选项,不分页。
|
||||
func ListVipPackageOptions(ctx context.Context) ([]*VipPackageOption, *srvdef.SrvErr) {
|
||||
list, err := rechargepackagemod.FindManyRechargePackage(ctx, bson.M{
|
||||
rechargepackagemod.FN.RechargePackage.PackageType: rechargepackagemod.PackageTypeVip,
|
||||
rechargepackagemod.FN.RechargePackage.Status: rechargepackagemod.StatusEnabled,
|
||||
}, options.Find().
|
||||
SetProjection(bson.D{
|
||||
{Key: rechargepackagemod.FN.RechargePackage.Id, Value: 1},
|
||||
{Key: rechargepackagemod.FN.RechargePackage.Name, Value: 1},
|
||||
}).
|
||||
SetSort(bson.D{
|
||||
{Key: rechargepackagemod.FN.RechargePackage.Sort, Value: 1},
|
||||
{Key: rechargepackagemod.FN.RechargePackage.Id, Value: -1},
|
||||
}))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]*VipPackageOption, 0, len(list))
|
||||
for _, item := range list {
|
||||
if item == nil {
|
||||
continue
|
||||
}
|
||||
result = append(result, &VipPackageOption{
|
||||
Id: item.Id,
|
||||
Name: item.Name,
|
||||
})
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// AddRechargePackage 新增购买套餐配置。
|
||||
func AddRechargePackage(ctx context.Context, req *RechargePackageAddReq) *srvdef.SrvErr {
|
||||
if req == nil {
|
||||
return srvdef.New(srvdef.ErrParamError, "参数不能为空", "")
|
||||
}
|
||||
doc := &rechargepackagemod.RechargePackage{
|
||||
PackageType: req.PackageType,
|
||||
Name: strings.TrimSpace(req.Name),
|
||||
Price: req.Price,
|
||||
OriginalPrice: req.OriginalPrice,
|
||||
DurationDays: req.DurationDays,
|
||||
BaseCoin: req.BaseCoin,
|
||||
GiftCoin: req.GiftCoin,
|
||||
Sort: req.Sort,
|
||||
Status: req.Status,
|
||||
Remark: strings.TrimSpace(req.Remark),
|
||||
}
|
||||
if doc.Status == 0 {
|
||||
doc.Status = rechargepackagemod.StatusEnabled
|
||||
}
|
||||
normalizePackageFields(doc)
|
||||
if err := validatePackage(doc); err != nil {
|
||||
return err
|
||||
}
|
||||
doc.SubTitle = buildSubTitle(doc)
|
||||
return rechargepackagemod.InsertOneRechargePackage(ctx, doc)
|
||||
}
|
||||
|
||||
// UpdateRechargePackage 更新购买套餐配置。
|
||||
func UpdateRechargePackage(ctx context.Context, req *RechargePackageUpdateReq) *srvdef.SrvErr {
|
||||
if req == nil || req.Id <= 0 {
|
||||
return srvdef.New(srvdef.ErrParamError, "套餐ID不能为空", "")
|
||||
}
|
||||
exist, err := rechargepackagemod.FindOneRechargePackage(ctx, bson.M{rechargepackagemod.FN.RechargePackage.Id: req.Id})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
doc := *exist
|
||||
applyUpdate(&doc, req)
|
||||
normalizePackageFields(&doc)
|
||||
if err := validatePackage(&doc); err != nil {
|
||||
return err
|
||||
}
|
||||
doc.SubTitle = buildSubTitle(&doc)
|
||||
now := time.Now()
|
||||
setDoc := bson.M{
|
||||
rechargepackagemod.FN.RechargePackage.PackageType: doc.PackageType,
|
||||
rechargepackagemod.FN.RechargePackage.Name: doc.Name,
|
||||
rechargepackagemod.FN.RechargePackage.SubTitle: doc.SubTitle,
|
||||
rechargepackagemod.FN.RechargePackage.Price: doc.Price,
|
||||
rechargepackagemod.FN.RechargePackage.OriginalPrice: doc.OriginalPrice,
|
||||
rechargepackagemod.FN.RechargePackage.DurationDays: doc.DurationDays,
|
||||
rechargepackagemod.FN.RechargePackage.BaseCoin: doc.BaseCoin,
|
||||
rechargepackagemod.FN.RechargePackage.GiftCoin: doc.GiftCoin,
|
||||
rechargepackagemod.FN.RechargePackage.Sort: doc.Sort,
|
||||
rechargepackagemod.FN.RechargePackage.Status: doc.Status,
|
||||
rechargepackagemod.FN.RechargePackage.Remark: doc.Remark,
|
||||
rechargepackagemod.FN.RechargePackage.UpdatedAt: now,
|
||||
}
|
||||
return rechargepackagemod.UpdateOneRechargePackage(ctx,
|
||||
bson.M{rechargepackagemod.FN.RechargePackage.Id: req.Id},
|
||||
bson.M{"$set": setDoc},
|
||||
)
|
||||
}
|
||||
|
||||
// DeleteRechargePackage 删除购买套餐配置。
|
||||
func DeleteRechargePackage(ctx context.Context, id int64) *srvdef.SrvErr {
|
||||
if id <= 0 {
|
||||
return srvdef.New(srvdef.ErrParamError, "套餐ID不能为空", "")
|
||||
}
|
||||
return rechargepackagemod.DeleteOneRechargePackage(ctx, bson.M{rechargepackagemod.FN.RechargePackage.Id: id})
|
||||
}
|
||||
|
||||
// SetRechargePackageStatus 单/批量设置购买套餐启停状态。
|
||||
func SetRechargePackageStatus(ctx context.Context, req *RechargePackageStatusSetReq) *srvdef.SrvErr {
|
||||
if req == nil {
|
||||
return srvdef.New(srvdef.ErrParamError, "参数不能为空", "")
|
||||
}
|
||||
ids := dedupePositiveInt64Ids(req.Ids)
|
||||
if len(ids) == 0 {
|
||||
return srvdef.New(srvdef.ErrParamError, "套餐ID不能为空", "")
|
||||
}
|
||||
if !isValidStatus(req.Status) {
|
||||
return srvdef.New(srvdef.ErrParamError, "状态无效", "")
|
||||
}
|
||||
return rechargepackagemod.UpdateManyRechargePackage(ctx,
|
||||
bson.M{rechargepackagemod.FN.RechargePackage.Id: bson.M{"$in": ids}},
|
||||
bson.M{"$set": bson.M{
|
||||
rechargepackagemod.FN.RechargePackage.Status: req.Status,
|
||||
rechargepackagemod.FN.RechargePackage.UpdatedAt: time.Now(),
|
||||
}},
|
||||
)
|
||||
}
|
||||
|
||||
func buildListFilter(req *RechargePackageListReq) bson.M {
|
||||
filter := bson.M{}
|
||||
if req.Id != nil {
|
||||
filter[rechargepackagemod.FN.RechargePackage.Id] = *req.Id
|
||||
}
|
||||
if req.PackageType != nil {
|
||||
filter[rechargepackagemod.FN.RechargePackage.PackageType] = *req.PackageType
|
||||
}
|
||||
if req.Status != nil {
|
||||
filter[rechargepackagemod.FN.RechargePackage.Status] = *req.Status
|
||||
}
|
||||
if strings.TrimSpace(req.Name) != "" {
|
||||
filter[rechargepackagemod.FN.RechargePackage.Name] = bson.M{
|
||||
"$regex": primitive.Regex{Pattern: strings.TrimSpace(req.Name), Options: "i"},
|
||||
}
|
||||
}
|
||||
return filter
|
||||
}
|
||||
|
||||
// normalizePage 统一分页兜底,限制 pageSize 防止后台误传过大导致慢查询。
|
||||
func normalizePage(pageNum, pageSize int64) (int64, int64) {
|
||||
if pageNum <= 0 {
|
||||
pageNum = defaultPageNum
|
||||
}
|
||||
if pageSize <= 0 {
|
||||
pageSize = defaultPageSize
|
||||
}
|
||||
if pageSize > maxPageSize {
|
||||
pageSize = maxPageSize
|
||||
}
|
||||
return pageNum, pageSize
|
||||
}
|
||||
|
||||
// normalizePackageFields 清理不同套餐类型不生效的字段。
|
||||
// 会员套餐不使用金币字段;金币套餐不使用会员有效期字段。
|
||||
func normalizePackageFields(doc *rechargepackagemod.RechargePackage) {
|
||||
switch doc.PackageType {
|
||||
case rechargepackagemod.PackageTypeVip:
|
||||
doc.BaseCoin = 0
|
||||
doc.GiftCoin = 0
|
||||
case rechargepackagemod.PackageTypeCoin:
|
||||
doc.DurationDays = 0
|
||||
}
|
||||
}
|
||||
|
||||
// validatePackage 校验购买套餐配置和套餐类型字段约束。
|
||||
func validatePackage(doc *rechargepackagemod.RechargePackage) *srvdef.SrvErr {
|
||||
if !isValidPackageType(doc.PackageType) {
|
||||
return srvdef.New(srvdef.ErrParamError, "套餐类型无效", "")
|
||||
}
|
||||
if strings.TrimSpace(doc.Name) == "" {
|
||||
return srvdef.New(srvdef.ErrParamError, "套餐标题不能为空", "")
|
||||
}
|
||||
if doc.Price <= 0 {
|
||||
return srvdef.New(srvdef.ErrParamError, "套餐价格必须大于0", "")
|
||||
}
|
||||
if !isValidStatus(doc.Status) {
|
||||
return srvdef.New(srvdef.ErrParamError, "状态无效", "")
|
||||
}
|
||||
if doc.PackageType == rechargepackagemod.PackageTypeVip && doc.DurationDays <= 0 {
|
||||
return srvdef.New(srvdef.ErrParamError, "会员套餐有效天数必须大于0", "")
|
||||
}
|
||||
if doc.PackageType == rechargepackagemod.PackageTypeCoin && doc.BaseCoin+doc.GiftCoin <= 0 {
|
||||
return srvdef.New(srvdef.ErrParamError, "金币套餐到账金币数必须大于0", "")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isValidPackageType(packageType rechargepackagemod.PackageType) bool {
|
||||
return packageType == rechargepackagemod.PackageTypeVip || packageType == rechargepackagemod.PackageTypeCoin
|
||||
}
|
||||
|
||||
func isValidStatus(status rechargepackagemod.Status) bool {
|
||||
return status == rechargepackagemod.StatusEnabled || status == rechargepackagemod.StatusDisabled
|
||||
}
|
||||
|
||||
// applyUpdate 将更新请求合并到现有文档副本,再统一做类型归一化和校验。
|
||||
func applyUpdate(doc *rechargepackagemod.RechargePackage, req *RechargePackageUpdateReq) {
|
||||
if req.PackageType != nil {
|
||||
doc.PackageType = *req.PackageType
|
||||
}
|
||||
if req.Name != nil {
|
||||
doc.Name = strings.TrimSpace(*req.Name)
|
||||
}
|
||||
if req.Price != nil {
|
||||
doc.Price = *req.Price
|
||||
}
|
||||
if req.OriginalPrice != nil {
|
||||
doc.OriginalPrice = *req.OriginalPrice
|
||||
}
|
||||
if req.DurationDays != nil {
|
||||
doc.DurationDays = *req.DurationDays
|
||||
}
|
||||
if req.BaseCoin != nil {
|
||||
doc.BaseCoin = *req.BaseCoin
|
||||
}
|
||||
if req.GiftCoin != nil {
|
||||
doc.GiftCoin = *req.GiftCoin
|
||||
}
|
||||
if req.Sort != nil {
|
||||
doc.Sort = *req.Sort
|
||||
}
|
||||
if req.Status != nil {
|
||||
doc.Status = *req.Status
|
||||
}
|
||||
if req.Remark != nil {
|
||||
doc.Remark = strings.TrimSpace(*req.Remark)
|
||||
}
|
||||
}
|
||||
|
||||
// buildSubTitle 根据套餐类型和核心配置生成展示副标题,不由后台请求手动传入。
|
||||
func buildSubTitle(doc *rechargepackagemod.RechargePackage) string {
|
||||
switch doc.PackageType {
|
||||
case rechargepackagemod.PackageTypeVip:
|
||||
return fmt.Sprintf("%d × 24 小时会员", doc.DurationDays)
|
||||
case rechargepackagemod.PackageTypeCoin:
|
||||
if doc.GiftCoin > 0 {
|
||||
return fmt.Sprintf("基础%d + 赠送%d", doc.BaseCoin, doc.GiftCoin)
|
||||
}
|
||||
return fmt.Sprintf("基础%d", doc.BaseCoin)
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// rechargePackageToWebItem 转换后台列表展示项,并补充派生字段。
|
||||
func rechargePackageToWebItem(item *rechargepackagemod.RechargePackage) *RechargePackageItem {
|
||||
return &RechargePackageItem{
|
||||
Id: item.Id,
|
||||
PackageType: item.PackageType,
|
||||
Name: item.Name,
|
||||
SubTitle: item.SubTitle,
|
||||
Price: item.Price,
|
||||
OriginalPrice: item.OriginalPrice,
|
||||
DurationDays: item.DurationDays,
|
||||
DurationHours: item.DurationDays * 24,
|
||||
BaseCoin: item.BaseCoin,
|
||||
GiftCoin: item.GiftCoin,
|
||||
TotalCoin: item.BaseCoin + item.GiftCoin,
|
||||
Sort: item.Sort,
|
||||
Status: item.Status,
|
||||
Remark: item.Remark,
|
||||
CreatedAt: item.CreatedAt,
|
||||
UpdatedAt: item.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
// dedupePositiveInt64Ids 去重并过滤非法ID,批量操作只处理正整数ID。
|
||||
func dedupePositiveInt64Ids(ids []int64) []int64 {
|
||||
result := make([]int64, 0, len(ids))
|
||||
seen := make(map[int64]struct{}, len(ids))
|
||||
for _, id := range ids {
|
||||
if id <= 0 {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[id]; ok {
|
||||
continue
|
||||
}
|
||||
seen[id] = struct{}{}
|
||||
result = append(result, id)
|
||||
}
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user