micro-bundle/internal/logic/bundleLogic.go

538 lines
17 KiB
Go
Raw Normal View History

2025-02-20 12:40:39 +00:00
package logic
import (
2025-05-28 01:34:43 +00:00
"errors"
"fmt"
"micro-bundle/internal/dao"
"micro-bundle/pkg/app"
"micro-bundle/pkg/msg"
2025-06-12 09:51:24 +00:00
"time"
2025-05-28 01:34:43 +00:00
2025-06-12 09:51:24 +00:00
"micro-bundle/internal/model"
"micro-bundle/pb/bundle"
"dubbo.apache.org/dubbo-go/v3/common/logger"
2025-02-20 12:40:39 +00:00
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/utils"
"github.com/jinzhu/copier"
2025-06-12 09:51:24 +00:00
"github.com/samber/lo"
2025-05-28 01:34:43 +00:00
"gorm.io/gorm"
2025-02-20 12:40:39 +00:00
)
func CreateBundle(req *bundle.BundleProfile) (res *bundle.CommonResponse, err error) {
res = new(bundle.CommonResponse)
bundleProfile := new(model.BundleProfile)
_ = copier.CopyWithOption(&bundleProfile, req, copier.Option{DeepCopy: true})
bundleProfile.UUID = utils.GetUUID()
res, err = dao.CreateBundle(bundleProfile)
return
}
func UpdateBundle(req *bundle.BundleProfile) (res *bundle.CommonResponse, err error) {
res = new(bundle.CommonResponse)
bundleProfile := new(model.BundleProfile)
_ = copier.CopyWithOption(&bundleProfile, req, copier.Option{DeepCopy: true})
res, err = dao.UpdateBundle(bundleProfile)
return
}
func DeleteBundle(req *bundle.DelBundleRequest) (res *bundle.CommonResponse, err error) {
res = new(bundle.CommonResponse)
res, err = dao.DeleteBundle(req.Uuid)
return
}
func BundleList(req *bundle.BundleListRequest) (res *bundle.BundleListResponse, err error) {
res = new(bundle.BundleListResponse)
res, err = dao.BundleList(req)
return
}
func BundleDetail(req *bundle.BundleDetailRequest) (res *bundle.BundleDetailResponse, err error) {
res = new(bundle.BundleDetailResponse)
res.Bundle = new(bundle.BundleProfile)
res.Bundle, err = dao.BundleDetail(req.Uuid)
if err != nil {
res.Msg = err.Error()
}
return
}
2025-05-28 01:34:43 +00:00
func SaveBundle(req *bundle.BundleProfile) (res *bundle.SaveResponse, err error) {
res = &bundle.SaveResponse{}
if req.Language == "" {
return res, errors.New("语言参数不能为空")
}
if req.Sort <= 0 {
return res, errors.New("排序参数需为正整数")
}
bundleProfile := &model.BundleProfile{
Name: req.Name,
Sort: req.Sort,
Content: req.Content,
Price: req.Price,
PriceType: req.PriceType,
BgImg1: req.BgImg1,
BgImg2: req.BgImg2,
ShelfStatus: 2, //默认初始状态为2-下架
}
bundleLang := &model.BundleProfileLang{
Name: req.Name,
Content: req.Content,
Price: req.Price,
PriceType: req.PriceType,
Language: req.Language,
}
if req.Uuid == "" && req.Language != msg.ZH_CN {
return res, errors.New("请先创建中文版本套餐")
}
var existValueService = make(map[string]string)
if req.Uuid != "" {
valueService, existErr := dao.GetValueAddServiceUuidsByBundleUuid(req.Uuid)
if existErr != nil {
return res, existErr
}
if valueService != nil && len(valueService) > 0 {
for _, v := range valueService {
existValueService[v] = v
}
}
}
var cancelValueAddService = make(map[string]string)
selectService := make([]*model.BundleToValueAddService, 0)
if req.Language == msg.ZH_CN && req.SelectValueAddService != nil && len(req.SelectValueAddService) > 0 {
for _, v := range req.SelectValueAddService {
detail, checkErr := dao.ValueAddServiceDetailByUuidAndLanguage(v.ValueAddUuid, req.Language)
if checkErr != nil {
if checkErr == gorm.ErrRecordNotFound {
return res, errors.New(fmt.Sprintf("所选增值服务[%s]%s版不存在,请先创建对应增值套餐", v.ServiceName, req.Language))
} else {
return res, checkErr
}
}
if detail.PriceType != req.PriceType {
if req.Uuid == "" {
2025-06-06 07:27:06 +00:00
//中文套餐创建时,币种不一致直接返回错误
2025-05-28 01:34:43 +00:00
return res, errors.New(fmt.Sprintf("所选增值服务[%s]%s币种与套餐币种不一致", detail.ServiceName, req.Language))
} else {
2025-06-06 07:27:06 +00:00
//更新时,判断是否已存在,存在则取消关联
2025-05-28 01:34:43 +00:00
_, ok := existValueService[v.ValueAddUuid]
if ok {
cancelValueAddService[v.ValueAddUuid] = detail.ServiceName
continue
} else {
2025-06-06 07:27:06 +00:00
//币种不一致,新加币种时返回错误
2025-05-28 01:34:43 +00:00
return res, errors.New(fmt.Sprintf("所选增值服务[%s]%s币种与套餐币种不一致", detail.ServiceName, req.Language))
}
}
}
selectService = append(selectService, &model.BundleToValueAddService{
ValueUid: v.ValueAddUuid,
IsDisplay: v.IsDisplay,
})
}
}
tx := app.ModuleClients.BundleDB.Begin()
defer func() {
if err != nil {
tx.Rollback()
} else {
tx.Commit()
}
}()
_, err = dao.BundleDetailByUuidAndLanguage(req.Uuid, req.Language)
if err != nil {
if err == gorm.ErrRecordNotFound {
if req.Language != msg.ZH_CN {
_, err = dao.BundleDetailByUuidAndLanguage(req.Uuid, msg.ZH_CN)
if err != nil {
if err == gorm.ErrRecordNotFound {
res.Msg = "请先创建中文版本套餐"
return res, errors.New("请先创建中文版本套餐")
} else {
return res, err
}
}
}
if req.Language == msg.ZH_CN {
bundleProfile.UUID = utils.GetUUID()
if err = dao.TxCreateBundle(tx, bundleProfile); err != nil {
return res, errors.New("保存中文语言套餐失败: " + err.Error())
}
bundleLang.UUID = bundleProfile.UUID
res.Uuid = bundleProfile.UUID
// 新建套餐时插入中间表
for _, s := range selectService {
s.BundleUuid = bundleProfile.UUID
}
if len(selectService) > 0 {
if err = dao.CreateBundleToValueAddService(tx, selectService); err != nil {
return res, errors.New("保存套餐与增值服务关联失败: " + err.Error())
}
}
} else {
bundleLang.UUID = req.Uuid
res.Uuid = req.Uuid
valueUuid, err1 := dao.GetValueAddServiceUuidsByBundleUuid(bundleLang.UUID)
if err1 != nil {
return res, err1
}
count := 0
if valueUuid != nil && len(valueUuid) > 0 {
for _, v := range valueUuid {
2025-06-06 07:27:06 +00:00
//可以改成批量获取
2025-05-28 01:34:43 +00:00
valueDetail, err2 := dao.ValueAddServiceDetailByUuidAndLanguage(v, req.Language)
if err2 != nil {
return res, err2
}
if valueDetail.PriceType != req.PriceType {
if err = tx.Where("bundle_uuid =? AND value_uid =?", bundleLang.UUID, v).Delete(&model.BundleToValueAddService{}).Error; err != nil {
return res, errors.New("删除套餐与增值服务关联失败: " + err.Error())
}
count++
}
}
}
res.CancelNum = int64(count)
}
if err = dao.TxCreateBundleLang(tx, bundleLang); err != nil {
return res, errors.New("保存语言套餐失败: " + err.Error())
}
res.Msg = "保存成功"
return res, nil
} else {
return
}
2025-06-06 07:27:06 +00:00
} else { // 已存在,进行更新
// 更新前保存历史记录
// if err = dao.CreateBundleProfileHistory(tx,req.Uuid); err != nil {
// return res, errors.New("保存套餐历史记录失败: " + err.Error())
// }
2025-05-28 01:34:43 +00:00
if req.Language == msg.ZH_CN {
if len(cancelValueAddService) > 0 {
cancel := "以下增值服务:"
for _, v := range cancelValueAddService {
cancel += fmt.Sprintf("[%s]%s", v, req.Language)
if err = tx.Where("bundle_uuid = ? AND value_uid = ?", req.Uuid, v).Delete(&model.BundleToValueAddService{}).Error; err != nil {
return res, errors.New("删除套餐与增值服务关联失败: " + err.Error())
}
}
cancel += "版币种与套餐币种不一致"
res.Msg = "保存cn成功 " + cancel
}
res.CancelNum = int64(len(cancelValueAddService))
updateBundle := map[string]interface{}{
"name": req.Name,
"sort": req.Sort,
"content": req.Content,
"price": req.Price,
"price_type": req.PriceType,
"bg_img1": req.BgImg1,
"bg_img2": req.BgImg2,
}
if err = dao.TxUpdateBundle(tx, req.Uuid, updateBundle); err != nil {
return res, err
}
// 更新中间表函数
if err = diffUpdateBundleToValueAddService(tx, req.Uuid, selectService); err != nil {
tx.Rollback()
return res, err
}
2025-06-06 07:27:06 +00:00
} else {
//更新其他语言时 先获取所有关联增值服务,判断币种是否一致,不一致则取消关联
valueAddService, err := dao.GetBundleToValueAddServiceByBundleUuid(req.Uuid)
if err != nil {
return res, err
}
cancelValueService := make(map[string]string)
for _, v := range valueAddService {
detail, checkErr := dao.ValueAddServiceDetailByUuidAndLanguage(v.ValueUid, req.Language)
if checkErr != nil {
if checkErr == gorm.ErrRecordNotFound {
continue
} else {
return res, checkErr
}
}
if detail.PriceType != req.PriceType {
cancelValueService[v.ValueUid] = detail.ServiceName
continue
}
}
if int64(len(cancelValueService)) > 0 {
cancel := "以下增值服务:"
for k, v := range cancelValueService {
cancel += fmt.Sprintf("[%s]%s", v, req.Language)
if err = tx.Where("bundle_uuid = ? AND value_uid = ?", req.Uuid, k).Delete(&model.BundleToValueAddService{}).Error; err != nil {
return res, errors.New("删除套餐与增值服务关联失败: " + err.Error())
}
}
cancel += "版币种与套餐币种不一致,已取消相关关联"
res.Msg = "保存成功 " + cancel
res.CancelNum = int64(len(cancelValueService))
}
2025-05-28 01:34:43 +00:00
}
updateBundleLang := map[string]interface{}{
"name": req.Name,
"content": req.Content,
"price": req.Price,
"price_type": req.PriceType,
}
if err = dao.TxUpdateBundleLang(tx, req.Uuid, req.Language, updateBundleLang); err != nil {
return res, err
}
res.Uuid = req.Uuid
if res.Msg == "" {
res.Msg = "保存成功"
}
}
return res, nil
}
func BundleListV2(req *bundle.BundleListRequest) (res *bundle.BundleListResponse, err error) {
res = new(bundle.BundleListResponse)
res, err = dao.BundleListV2(req)
return
}
func BundleDetailV2(req *bundle.BundleDetailRequest) (res *bundle.BundleDetailResponseV2, err error) {
res = new(bundle.BundleDetailResponseV2)
if req.Uuid == "" {
2025-06-06 07:27:06 +00:00
res.Msg = ""
2025-05-28 01:34:43 +00:00
return res, errors.New("uuid不能为空")
}
if req.Language == "" {
2025-06-06 07:27:06 +00:00
res.Msg = ""
2025-05-28 01:34:43 +00:00
return res, errors.New("language不能为空")
}
res, err = dao.BundleDetailV2(req)
if err != nil {
res.Msg = err.Error()
}
return
}
func HandShelf(req *bundle.HandShelfRequest) (res *bundle.CommonResponse, err error) {
res = new(bundle.CommonResponse)
if req.ShelfStatus != 1 && req.ShelfStatus != 2 {
2025-06-06 07:27:06 +00:00
res.Msg = ""
return res, errors.New("无效的上下架状态")
2025-05-28 01:34:43 +00:00
}
res, err = dao.HandShelf(req.Uuid, req.ShelfStatus)
return
}
// 差异更新套餐与增值服务中间表
func diffUpdateBundleToValueAddService(tx *gorm.DB, bundleUuid string, selectService []*model.BundleToValueAddService) error {
oldUuids, err := dao.GetValueAddServiceUuidsByBundleUuid(bundleUuid)
if err != nil {
return errors.New("查询旧套餐与增值服务关联失败: " + err.Error())
}
newUuids := make(map[string]*model.BundleToValueAddService)
for _, s := range selectService {
newUuids[s.ValueUid] = s
}
oldSet := make(map[string]struct{})
for _, uid := range oldUuids {
oldSet[uid] = struct{}{}
}
// 需要新增的
toAdd := make([]*model.BundleToValueAddService, 0)
for uid, s := range newUuids {
if _, exist := oldSet[uid]; !exist {
s.BundleUuid = bundleUuid
toAdd = append(toAdd, s)
}
}
// 需要删除的
toDel := make([]string, 0)
for _, uid := range oldUuids {
if _, exist := newUuids[uid]; !exist {
toDel = append(toDel, uid)
}
}
if len(toDel) > 0 {
if err = tx.Where("bundle_uuid = ? AND value_uid IN ?", bundleUuid, toDel).Delete(&model.BundleToValueAddService{}).Error; err != nil {
return errors.New("删除套餐与增值服务关联失败: " + err.Error())
}
}
if len(toAdd) > 0 {
if err = dao.CreateBundleToValueAddService(tx, toAdd); err != nil {
return errors.New("保存套餐与增值服务关联失败: " + err.Error())
}
}
return nil
}
2025-06-09 05:58:27 +00:00
func BundleExtend(req *bundle.BundleExtendRequest) (*bundle.BundleExtendResponse, error) {
data := model.BundleExtensionRecords{}
if err := copier.CopyWithOption(&data, req, copier.Option{DeepCopy: true}); err != nil {
return nil, err
}
return nil, dao.AddBundleExtendRecord(data)
}
func BundleExtendRecordsList(req *bundle.BundleExtendRecordsListRequest) (*bundle.BundleExtendRecordsListResponse, error) {
data, total, err := dao.GetBundleExtendRecordList(req)
if err != nil {
return nil, err
}
2025-06-13 03:01:57 +00:00
var result = &bundle.BundleExtendRecordsListResponse{}
result.Data = lo.Map(data, func(data model.BundleExtendRecordItemPo, _ int) (ber *bundle.BundleExtendRecordItem) {
ber = &bundle.BundleExtendRecordItem{}
ber.CreatedAt = uint64(data.CreatedAt.UnixMilli())
ber.UserName = data.UserName
ber.UserPhoneNumber = data.UserPhoneNumber
if data.ServiceType == 0 {
ber.AccountAdditional += uint32(data.AccountAdditional)
ber.VideoAdditional += uint32(data.VideoAdditional)
ber.ImagesAdditional += uint32(data.ImagesAdditional)
ber.DataAdditional += uint32(data.DataAdditional)
ber.OperatorName = data.OperatorName
ber.OperatorPhoneNumber = data.OperatorPhoneNumber
} else {
switch data.ServiceType {
case 1:
ber.VideoAdditional += uint32(data.ServiceNum)
case 2:
ber.ImagesAdditional += uint32(data.ServiceNum)
case 3:
ber.DataAdditional += uint32(data.ServiceNum)
case 4:
ber.AccountAdditional += uint32(data.ServiceNum)
}
ber.AssociatedOrderNumber = data.OrderUUID
}
return
})
2025-06-09 05:58:27 +00:00
result.Total = total
return result, err
}
2025-06-12 09:51:24 +00:00
func GetBundleBalance(req *bundle.GetBundleBalanceReq) (*bundle.GetBundleBalanceResp, error) {
2025-06-12 10:46:10 +00:00
// start := time.Now()
// defer func() {
// fmt.Printf("(time.Now().UnixMilli() - start.UnixMilli()): %v\n", (time.Now().UnixMilli() - start.UnixMilli()))
// }()
data, total, err := dao.GetBundleBalance(req)
2025-06-12 09:51:24 +00:00
if err != nil {
return nil, err
}
type userDataDto struct {
2025-06-12 10:46:10 +00:00
UserID int
UserName string
UserPhoneNumber string
BundleName string
ExpirationTime time.Time
BundleStatus int
VideoCap int
ImageCap int
DataAnalysisCap int
AccountCap int
VideoUsed int
ImageUsed int
DataAnalysisUsed int
AccountUsed int
ExpansionPacksNumber int
2025-06-12 09:51:24 +00:00
}
usersMap := map[int]*userDataDto{}
lo.ForEach(data, func(data model.BundleBalancePo, index int) {
_, ok := usersMap[data.UserID]
if !ok {
usersMap[data.UserID] = &userDataDto{
UserID: data.UserID,
UserName: data.UserName,
UserPhoneNumber: data.UserPhoneNumber,
BundleStatus: data.BundleStatus,
ExpirationTime: data.ExpirationTime,
}
}
})
for _, user := range usersMap {
userData := lo.Filter(data, func(data model.BundleBalancePo, _ int) bool {
return data.UserID == user.UserID
})
2025-06-12 10:46:10 +00:00
// 去重bundle_order_value_add.uuid 计算可用量
2025-06-12 09:51:24 +00:00
lo.ForEach(lo.UniqBy(lo.Filter(userData, func(data model.BundleBalancePo, _ int) bool {
return data.BovaUUID != nil
}), func(data model.BundleBalancePo) string {
return *data.BovaUUID
}), func(data model.BundleBalancePo, index int) {
2025-06-12 10:46:10 +00:00
if data.OderSource == 3 {
user.ExpansionPacksNumber++
}
2025-06-12 09:51:24 +00:00
logger.Infof("增加数据类型%v(%v),user_id:%v,uuid:%v", data.ServiceType, data.ServiceNum, data.UserID, *data.BovaUUID)
switch data.ServiceType {
case 1:
user.VideoCap += data.ServiceNum
case 2:
user.ImageCap += data.ServiceNum
case 3:
user.DataAnalysisCap += data.ServiceNum
case 4:
user.AccountCap += data.ServiceNum
2025-06-12 10:46:10 +00:00
default:
logger.Warn("不存在的数据类型:", data.ServiceType)
2025-06-12 09:51:24 +00:00
}
})
2025-06-12 10:46:10 +00:00
// 去重work_category.uuid 统计绑定的发布数
2025-06-12 09:51:24 +00:00
lo.ForEach(lo.UniqBy(lo.Filter(userData, func(data model.BundleBalancePo, _ int) bool {
return data.CwUUID != nil
}), func(data model.BundleBalancePo) string {
return *data.CwUUID
}), func(data model.BundleBalancePo, index int) {
logger.Infof("减少数据类型%v,user_id:%v,uuid:%v", data.ServiceType, data.UserID, *data.CwUUID)
switch data.WorkCategory {
case 1:
2025-06-12 10:46:10 +00:00
user.ImageUsed++
2025-06-12 09:51:24 +00:00
case 2:
2025-06-12 10:46:10 +00:00
user.VideoUsed++
2025-06-12 09:51:24 +00:00
default:
logger.Warn("不存在的数据类型:", data.WorkCategory)
}
})
2025-06-12 10:46:10 +00:00
// 去重cast_media_account.uuid 统计绑定的账号数
user.AccountUsed += len(lo.UniqBy(lo.Filter(userData, func(data model.BundleBalancePo, _ int) bool {
2025-06-12 09:51:24 +00:00
return data.CmaUUID != nil
}), func(data model.BundleBalancePo) string {
return *data.CmaUUID
2025-06-12 10:46:10 +00:00
}))
2025-06-12 09:51:24 +00:00
2025-06-12 10:46:10 +00:00
// 去重bundle_extension_records.id 统计绑定的账号数
2025-06-13 03:01:57 +00:00
lo.ForEach(lo.UniqBy(userData, func(data model.BundleBalancePo) int {
2025-06-12 10:46:10 +00:00
return data.BerID
2025-06-13 03:01:57 +00:00
}), func(data model.BundleBalancePo, index int) {
user.ExpansionPacksNumber++
user.AccountCap += data.AccountAdditional
user.VideoCap += data.VideAadditiona
user.DataAnalysisCap += data.DataAdditional
user.ImageCap += data.ImagesAdditional
})
2025-06-12 10:46:10 +00:00
}
result := &bundle.GetBundleBalanceResp{}
result.Total = total
result.Data = lo.MapToSlice(usersMap, func(_ int, user *userDataDto) *bundle.BundleBalanceItem {
return &bundle.BundleBalanceItem{
UserName: user.UserName,
UserPhoneNumber: user.UserPhoneNumber,
BundleName: user.BundleName,
Status: int32(user.BundleStatus),
ExpiredTime: user.ExpirationTime.UnixMilli(),
AccountNumber: int32(user.AccountCap),
AccountConsumptionNumber: int32(user.AccountUsed),
ImageNumber: int32(user.ImageCap),
ImageConsumptionNumber: int32(user.ImageUsed),
VideoNumber: int32(user.VideoCap),
VideoConsumptionNumber: int32(user.VideoUsed),
DataAnalysisNumber: int32(user.DataAnalysisCap),
DataAnalysisConsumptionNumber: int32(user.DataAnalysisUsed),
ExpansionPacksNumber: int32(user.ExpansionPacksNumber),
}
})
return result, nil
2025-06-12 09:51:24 +00:00
}