micro-bundle/internal/logic/bundleLogic.go
2025-06-10 14:03:17 +08:00

463 lines
15 KiB
Go

package logic
import (
"encoding/json"
"errors"
"fmt"
"micro-bundle/internal/dao"
"micro-bundle/pkg/app"
"micro-bundle/pkg/msg"
"time"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/utils"
"github.com/jinzhu/copier"
"gorm.io/datatypes"
"gorm.io/gorm"
"micro-bundle/internal/model"
"micro-bundle/pb/bundle"
)
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
}
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)
var selectValueAddServiceCount = make(map[int]struct{})
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 == "" {
//中文套餐创建时,币种不一致直接返回错误
return res, errors.New(fmt.Sprintf("所选增值服务[%s]%s币种与套餐币种不一致", detail.ServiceName, req.Language))
} else {
//更新时,判断是否已存在,存在则取消关联
_, ok := existValueService[v.ValueAddUuid]
if ok {
cancelValueAddService[v.ValueAddUuid] = detail.ServiceName
continue
} else {
//币种不一致,新加币种时返回错误
return res, errors.New(fmt.Sprintf("所选增值服务[%s]%s币种与套餐币种不一致", detail.ServiceName, req.Language))
}
}
}
if _, exists := selectValueAddServiceCount[int(detail.ServiceType)]; exists {
return res, errors.New(fmt.Sprintf("所选增值服务[%s]类型存在多个", detail.ServiceName))
}
selectValueAddServiceCount[int(detail.ServiceType)] = struct{}{}
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.Uuid != "" {
return res, errors.New("套餐不存在")
}
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 {
//可以改成批量获取
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
}
} else { // 已存在,进行更新
// 更新前保存历史记录
if saveErr := saveBundleHistory(tx, req.Uuid, "", 0); err != nil {
return res, saveErr
}
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
}
} 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))
}
}
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)
bundleProfile := &bundle.BundleProfile{}
bundleProfileLangs := make([]*bundle.BundleProfileLang, 0)
selectValueAddService := make([]*bundle.SelectValueAddService, 0) //已选增值服务
if req.Uuid == "" {
return res, errors.New("uuid不能为空")
}
// if req.Language == "" {
// return res, errors.New("language不能为空")
// }
detail, err := dao.BundleDetailV2(req)
if err != nil {
res.Msg = err.Error()
}
if detail != nil && detail.BundleProfileLang != nil && len(detail.BundleProfileLang) > 0 {
bundleProfile.Uuid = detail.UUID
bundleProfile.Name = detail.Name
bundleProfile.Content = detail.Content
bundleProfile.Price = detail.Price
bundleProfile.PriceType = detail.PriceType
bundleProfile.BgImg1 = detail.BgImg1
bundleProfile.BgImg2 = detail.BgImg2
bundleProfile.Sort = detail.Sort
for _, lang := range detail.BundleProfileLang {
bundleProfileLang := &bundle.BundleProfileLang{
Uuid: lang.UUID,
Name: lang.Name,
Price: lang.Price,
PriceType: lang.PriceType,
Content: lang.Content,
Language: lang.Language,
CreatedAt: time.Unix(lang.CreatedAt, 0).Format("2006-01-02 15:04:05"),
UpdatedAt: time.Unix(int64(lang.UpdatedAt), 0).Format("2006-01-02 15:04:05"),
}
// 通过中间表拼接增值服务数据
if detail.BundleToValueAddService != nil && len(detail.BundleToValueAddService) > 0 {
for _, valueAddService := range detail.BundleToValueAddService {
valueAddDeatilData, err := dao.ValueAddServiceDetailByUuidAndLanguage(valueAddService.ValueUid, bundleProfileLang.Language)
if err != nil {
return res, err
}
//if valueAddService.IsDisplay{}
ValueAddServiceLang := &bundle.ValueAddServiceLang{
Uuid: valueAddDeatilData.UUID,
ServiceName: valueAddDeatilData.ServiceName,
ServiceType: int32(valueAddDeatilData.ServiceType),
PriceMode: int32(valueAddDeatilData.PriceMode),
PriceType: int64(valueAddDeatilData.PriceType),
OriginalPrice: fmt.Sprintf("%.2f", float32(valueAddDeatilData.OriginalPrice)),
Unit: string(valueAddDeatilData.Unit),
Language: valueAddDeatilData.Language,
CreatedAt: time.Unix(valueAddDeatilData.CreatedAt, 0).Format("2006-01-02 15:04:05"),
UpdatedAt: time.Unix(valueAddDeatilData.UpdatedAt, 0).Format("2006-01-02 15:04:05"),
}
bundleProfileLang.ValueAddServiceLang = append(bundleProfileLang.ValueAddServiceLang, ValueAddServiceLang)
selectValueAddService = append(selectValueAddService, &bundle.SelectValueAddService{
ValueAddUuid: valueAddService.ValueUid,
ServiceName: valueAddDeatilData.ServiceName,
IsDisplay: valueAddService.IsDisplay,
})
}
}
bundleProfileLangs = append(bundleProfileLangs, bundleProfileLang)
}
}
if selectValueAddService != nil && len(selectValueAddService) > 0 {
bundleProfile.SelectValueAddService = selectValueAddService
}
bundleProfile.BundleProfileLang = bundleProfileLangs
res.Bundle = bundleProfile
return
}
func HandleShelf(req *bundle.HandShelfRequest) (res *bundle.CommonResponse, err error) {
res = new(bundle.CommonResponse)
if req.Uuid == "" {
return res, errors.New("uuid不能为空")
}
if req.ShelfStatus != 1 && req.ShelfStatus != 2 {
return res, errors.New("Invalid shelf status")
}
res, err = dao.HandShelf(req.Uuid, req.ShelfStatus)
if err != nil {
res.Msg = err.Error()
return res, err
}
res.Msg = "Operation success"
return res, nil
}
// 差异更新套餐与增值服务中间表
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
}
func saveBundleHistory(tx *gorm.DB, bundleUuid string, operator string, operatorId uint64) error {
// 保存历史记录
data, err := dao.GetBundleDetailByUuid(bundleUuid)
if err != nil {
return errors.New("查询套餐详情失败: " + err.Error())
}
j, err := json.MarshalIndent(data, "", " ")
if err != nil {
return errors.New("json转换失败: " + err.Error())
}
history := &model.BundleProfileHistory{
Uuid: bundleUuid,
BundleProfile: datatypes.JSON(j),
Operator: operator,
OperatorId: operatorId,
}
if err := tx.Create(history).Error; err != nil {
return errors.New("保存套餐历史记录失败: " + err.Error())
}
return nil
}