micro-bundle/internal/logic/bundleLogic.go
2025-09-11 14:54:21 +08:00

555 lines
18 KiB
Go
Raw 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 logic
import (
"encoding/json"
"errors"
"micro-bundle/internal/dao"
"micro-bundle/pb/bundle"
"micro-bundle/pkg/app"
"micro-bundle/pkg/msg"
"time"
"micro-bundle/internal/model"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/utils"
"github.com/jinzhu/copier"
"gorm.io/datatypes"
"gorm.io/gorm"
)
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,
Contract: "https://e-cdn.fontree.cn/fonchain-main/prod/file/saas/contract/template-25032801.pdf",
ImgOption: int8(req.ImgOption),
BgImg1: req.BgImg1,
BgImg2: req.BgImg2,
ShelfStatus: 2, //默认初始状态为2-下架
FontColor: req.FontColor,
}
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, errors.New("查询增值服务失败")
// }
// if valueService != nil && len(valueService) > 0 {
// for _, v := range valueService {
// existValueService[v] = v
// }
// }
// }
//现需要区分类型 分为 1套餐权益该类型必须关联一个可用时长类型的权益 2套餐可选附加权益非必选可为空
selectService := make([]*model.BundleToValueAddService, 0)
var pkgBenefitsValueAddServiceCount = make(map[int]struct{}) //套餐权益增值服务类型数量
var pkgOptionalValueAddServiceCount = 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("增值服务不存在")
} else {
return res, errors.New("查询增值服务失败")
}
}
if _, exists := pkgBenefitsValueAddServiceCount[int(detail.ServiceType)]; exists {
return res, errors.New("每种增值服务类型只可选择一个")
}
pkgBenefitsValueAddServiceCount[int(detail.ServiceType)] = struct{}{}
selectService = append(selectService, &model.BundleToValueAddService{
ValueUid: v.ValueAddUuid,
IsDisplay: v.IsDisplay,
BenefitsType: msg.Benefits,
})
}
if req.SelectValueAdditionalService != nil && len(req.SelectValueAdditionalService) > 0 {
for _, v := range req.SelectValueAdditionalService {
detail, checkErr := dao.ValueAddServiceDetailByUuidAndLanguage(v.ValueAddUuid, req.Language)
if checkErr != nil {
if checkErr == gorm.ErrRecordNotFound {
return res, errors.New("增值服务不存在")
} else {
return res, errors.New("查询增值服务失败")
}
}
if _, exists := pkgOptionalValueAddServiceCount[int(detail.ServiceType)]; exists {
return res, errors.New("每种增值服务类型只可选择一个")
}
pkgOptionalValueAddServiceCount[int(detail.ServiceType)] = struct{}{}
selectService = append(selectService, &model.BundleToValueAddService{
ValueUid: v.ValueAddUuid,
IsDisplay: v.IsDisplay,
BenefitsType: msg.OptionalBenefits,
})
}
}
}
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, errors.New("获取套餐信息失败")
}
}
}
if req.Language == msg.ZH_CN {
bundleProfile.UUID = utils.GetUUID()
if err = dao.TxCreateBundle(tx, bundleProfile); err != nil {
return res, errors.New("创建套餐信息失败")
}
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("保存套餐与增值服务关联失败")
}
}
if err = dao.TxCreateBundleLang(tx, bundleLang); err != nil {
return res, errors.New("创建套餐信息失败")
}
}
langList := []string{msg.ZH_TW, msg.EN, msg.DE_DE, msg.JA_JP}
for _, lang := range langList {
otherLang := &model.BundleProfileLang{
UUID: bundleLang.UUID,
Name: bundleLang.Name,
Content: bundleLang.Content,
Price: bundleLang.Price,
PriceType: bundleLang.PriceType,
}
otherLang.Language = lang
if err = dao.TxCreateBundleLang(tx, otherLang); err != nil {
return res, errors.New("创建套餐信息失败")
}
}
res.Msg = "创建套餐信息成功"
return
} else {
return
}
} else { // 已存在,进行更新
// 更新前保存历史记录
if saveErr := saveBundleHistory(tx, req.Uuid, "", 0); saveErr != nil {
return res, saveErr
}
if req.Language == msg.ZH_CN {
updateBundle := map[string]interface{}{
"name": req.Name,
"sort": req.Sort,
"content": req.Content,
"price": req.Price,
"price_type": req.PriceType,
"img_option": req.ImgOption,
"bg_img1": req.BgImg1,
"bg_img2": req.BgImg2,
"font_color": req.FontColor,
}
if err = dao.TxUpdateBundle(tx, req.Uuid, updateBundle); err != nil {
return res, errors.New("更新套餐信息失败")
}
//更新中间表函数
if err = diffUpdateBundleToValueAddService(tx, req.Uuid, selectService); err != nil {
tx.Rollback()
return res, errors.New("更新套餐与增值服务关联失败")
}
}
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, errors.New("更新套餐信息失败")
}
res.Uuid = req.Uuid
res.Msg = "保存成功"
}
return res, nil
}
func BundleListV2(req *bundle.BundleListRequest) (res *bundle.BundleListResponse, err error) {
res = new(bundle.BundleListResponse)
res, err = dao.BundleListV2(req)
if err != nil {
return res, errors.New("获取套餐列表失败")
}
return res, nil
}
func BundleDetailV2(req *bundle.BundleDetailRequest) (res *bundle.BundleDetailResponseV2, err error) {
res = new(bundle.BundleDetailResponseV2)
bundleProfile := &bundle.BundleProfile{}
bundleProfileLangs := make([]*bundle.BundleProfileLang, 0)
selectValueAddServices := make([]*bundle.SelectValueAddService, 0) //已选增值服务
if req.Uuid == "" {
return res, errors.New("缺少套餐UUID")
}
detail, err := dao.BundleDetailV2(req)
if err != nil {
return res, errors.New("获取套餐信息失败")
}
if detail != nil {
bundleProfile.Uuid = detail.UUID
bundleProfile.Name = detail.Name
bundleProfile.Content = detail.Content
bundleProfile.Price = detail.Price
bundleProfile.PriceType = detail.PriceType
bundleProfile.ImgOption = int32(detail.ImgOption)
bundleProfile.BgImg1 = detail.BgImg1
bundleProfile.BgImg2 = detail.BgImg2
bundleProfile.FontColor = detail.FontColor
bundleProfile.Sort = detail.Sort
bundleProfile.ShelfStatus = detail.ShelfStatus
bundleProfile.CreatedAt = detail.CreatedAt.Format("2006-01-02 15:04:05")
bundleProfile.UpdatedAt = detail.UpdatedAt.Format("2006-01-02 15:04:05")
bundleProfile.Contract = detail.Contract
if len(detail.BundleToValueAddService) > 0 {
for _, valueAddService := range detail.BundleToValueAddService {
valueAddDetail, err := dao.ValueAddServiceDetailByUuidAndLanguage(valueAddService.ValueUid, req.Language)
if err != nil {
return res, errors.New("查询增值服务失败")
}
selectValueAddService := &bundle.SelectValueAddService{
ValueAddUuid: valueAddService.ValueUid,
IsDisplay: valueAddService.IsDisplay,
ServiceName: valueAddDetail.ServiceName,
ServiceType: valueAddDetail.ServiceType,
}
selectValueAddServices = append(selectValueAddServices, selectValueAddService)
}
}
if len(detail.BundleProfileLang) > 0 {
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,
ImgOption: int32(detail.ImgOption),
BgImg1: detail.BgImg1,
BgImg2: detail.BgImg2,
FontColor: detail.FontColor,
Sort: detail.Sort,
ShelfStatus: detail.ShelfStatus,
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"),
}
bundleProfileLangs = append(bundleProfileLangs, bundleProfileLang)
}
}
}
if len(selectValueAddServices) > 0 {
bundleProfile.SelectValueAddService = selectValueAddServices
}
bundleProfile.BundleProfileLang = bundleProfileLangs
res.Bundle = bundleProfile
return
}
func BundleLangDetailV2(req *bundle.BundleDetailRequest) (res *bundle.BundleProfileLang, err error) {
res = new(bundle.BundleProfileLang)
if req.Uuid == "" {
return res, errors.New("缺少套餐UUID")
}
if req.Language == "" {
return res, errors.New("language不能为空")
}
detail, err := dao.BundleDetailV2(req)
if err != nil {
return res, errors.New("获取套餐信息失败")
}
lang, err := dao.BundleDetailByUuidAndLanguage(req.Uuid, req.Language)
if err != nil {
return res, errors.New("获取套餐信息失败")
}
if detail != nil {
res.Uuid = detail.UUID
res.ImgOption = int32(detail.ImgOption)
res.BgImg1 = detail.BgImg1
res.BgImg2 = detail.BgImg2
res.FontColor = detail.FontColor
res.Sort = detail.Sort
res.ShelfStatus = detail.ShelfStatus
res.Contract = detail.Contract
}
if lang != nil {
res.Name = lang.Name
res.Price = lang.Price
res.PriceType = lang.PriceType
res.Content = lang.Content
res.Language = lang.Language
res.CreatedAt = time.Unix(lang.CreatedAt, 0).Format("2006-01-02 15:04:05")
res.UpdatedAt = time.Unix(int64(lang.UpdatedAt), 0).Format("2006-01-02 15:04:05")
}
return
}
func HandleShelf(req *bundle.HandShelfRequest) (*bundle.CommonResponse, error) {
res := new(bundle.CommonResponse)
if req.Uuid == "" {
return res, errors.New("缺少套餐UUID")
}
if req.ShelfStatus != 1 && req.ShelfStatus != 2 {
return res, errors.New("状态值无效")
}
detail, err := dao.GetBundleDetailByUuid(req.Uuid)
if err != nil {
return res, errors.New("获取套餐信息失败")
}
if detail.ShelfStatus == req.ShelfStatus {
switch detail.ShelfStatus {
case 1:
return res, errors.New("套餐已上架,请勿重复操作")
case 2:
return res, errors.New("套餐已下架,请勿重复操作")
}
}
bundleDetailLangs, err := dao.GetBundleLangsByUuid(req.Uuid)
if err != nil {
return res, errors.New("获取套餐信息失败")
}
var langToPriceType = make(map[string]int64)
for _, bundleLang := range bundleDetailLangs {
langToPriceType[bundleLang.Language] = bundleLang.PriceType
}
tx := app.ModuleClients.BundleDB.Begin()
defer func() {
if err != nil {
tx.Rollback()
} else {
tx.Commit()
}
}()
if req.ShelfStatus == 1 {
valueAddUuids, err := dao.GetValueAddServiceUuidsByBundleUuid(req.Uuid)
if err != nil {
return res, errors.New("查询增值服务失败")
}
//判断时长类型是否存在
isExist := false
for _, valueUid := range valueAddUuids {
detail, err := dao.ValueAddServiceDetail(valueUid)
if err != nil {
return res, errors.New("查询增值服务失败")
}
if detail.ServiceType == 5 {
isExist = true
break
}
}
if !isExist {
return res, errors.New("缺失可用时长服务类型")
}
//校验币种
valueAddServices, er := dao.BatchValueAddServiceDetailTx(tx, valueAddUuids)
if er != nil {
return res, errors.New("查询增值服务详情失败")
}
for _, valueAddService := range valueAddServices {
if len(valueAddService.ValueAddServiceLang) > 0 {
for _, lang := range valueAddService.ValueAddServiceLang {
if langToPriceType[lang.Language] != lang.PriceType {
return res, errors.New("所选增值服务币种与套餐币种不一致")
}
}
}
}
}
res, err = dao.HandShelf(req.Uuid, req.ShelfStatus)
if err != nil {
return res, errors.New("更新套餐状态失败")
}
res.Msg = "更新成功"
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("查询套餐与增值服务关联失败")
}
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)
toDel := make([]string, 0)
toUpdate := make([]*model.BundleToValueAddService, 0)
for uid, s := range newUuids {
if _, exist := oldSet[uid]; !exist {
s.BundleUuid = bundleUuid
toAdd = append(toAdd, s)
} else {
s.BundleUuid = bundleUuid
toUpdate = append(toUpdate, s)
}
}
// 需要删除的
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("删除套餐与增值服务关联失败")
}
}
if len(toAdd) > 0 {
if err = dao.CreateBundleToValueAddService(tx, toAdd); err != nil {
return errors.New("保存套餐与增值服务关联失败")
}
}
if len(toUpdate) > 0 {
updates := make(map[string]interface{})
var uids []string
for _, record := range toUpdate {
uids = append(uids, record.ValueUid)
updates["is_display"] = record.IsDisplay
}
if err = dao.UpdateBundleToValueAddService(tx, toUpdate); err != nil {
return errors.New("保存套餐与增值服务关联失败")
}
}
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("获取套餐信息失败")
}
bundleToValueAddService := data.BundleToValueAddService
data.BundleToValueAddService = nil
var valueUuid []string
if len(bundleToValueAddService) > 0 {
for _, v := range bundleToValueAddService {
valueUuid = append(valueUuid, v.ValueUid)
}
}
valueData, err := dao.BatchValueAddServiceDetailTx(tx, valueUuid)
if err != nil {
return errors.New("查询增值服务详情失败")
}
j, err := json.Marshal(data)
if err != nil {
return errors.New("序列化转换失败")
}
vJ, err := json.Marshal(valueData)
if err != nil {
return errors.New("序列化转换失败")
}
history := &model.BundleProfileHistory{
Uuid: bundleUuid,
BundleProfile: datatypes.JSON(j),
ValueAddService: datatypes.JSON(vJ),
Operator: operator,
OperatorId: operatorId,
}
if err := tx.Create(history).Error; err != nil {
return errors.New("保存套餐历史记录失败")
}
return nil
}
// 增加h5套餐列表
func BundleListH5V2(req *bundle.BundleListRequest) (res *bundle.BundleListResponse, err error) {
res = new(bundle.BundleListResponse)
res, err = dao.BundleListH5V2(req)
if err != nil {
return res, errors.New("获取套餐列表失败")
}
return res, nil
}