|
|
package provider
|
|
|
|
|
|
import (
|
|
|
"context"
|
|
|
"joybuywork/internal/app"
|
|
|
"joybuywork/internal/repository/cache"
|
|
|
"joybuywork/internal/repository/dao"
|
|
|
"joybuywork/internal/repository/model"
|
|
|
"joybuywork/internal/repository/mtype"
|
|
|
"joybuywork/internal/utils"
|
|
|
"joybuywork/internal/utils/merr"
|
|
|
"fmt"
|
|
|
)
|
|
|
|
|
|
type RewardAutoLang struct {
|
|
|
KvData []*mtype.RewardKV `json:"kv_data,omitempty"` //奖品自定义参数数据
|
|
|
ReceiveType int32 `json:"receive_type"` //发放类型(1-系统发放,2-联系客服,3-客户端发放,4-实物)
|
|
|
Contact string `json:"contact,omitempty"` //联系方式
|
|
|
Id int64 `json:"id"`
|
|
|
Url string `json:"url,omitempty"` //回调地址
|
|
|
Title string `json:"title"` // 显示的奖品名称
|
|
|
ImgUrl string `json:"img_url"` // 显示的奖品图片
|
|
|
Mark string `json:"mark"` // 语言标识
|
|
|
IsDefault int32 `json:"is_default"`
|
|
|
FindDefault bool `json:"find_default"` //只能找到默认数据
|
|
|
RewardTitle string `json:"reward_title"` //奖品标题
|
|
|
FormTitle string `json:"form_title"` //表单标题
|
|
|
OpenToUse int32 `json:"open_to_use"` //是否开启了“去使用”
|
|
|
AppParam string `json:"app_param"` //app参数
|
|
|
PcParam string `json:"pc_param"` //pc参数
|
|
|
WebUrl string `json:"web_url"` //web端跳转参数
|
|
|
DyRewardType int32 `json:"dy_reward_type"` //动态奖品内容类型(0-无,1-兑换码)
|
|
|
UrlId int64 `json:"url_id"` //回调地址
|
|
|
FormId int64 `json:"form_id"` //表单id
|
|
|
}
|
|
|
|
|
|
type RewardInfo struct {
|
|
|
Id int64 `json:"id"`
|
|
|
KvData []*mtype.RewardKV `json:"kv_data,omitempty"` //奖品自定义参数数据
|
|
|
ReceiveType int32 `json:"receive_type"` //发放类型(0-不做任何操作,1-系统发放,2-联系客服,3-客户端发放,4-实物)
|
|
|
RewardTitle string `json:"reward_title,omitempty"` //奖品标题
|
|
|
Contact string `json:"contact,omitempty"` //联系方式
|
|
|
Url string `json:"url,omitempty"` //回调地址
|
|
|
Langs []*RewardLang `json:"langs,omitempty"` //语言数据
|
|
|
FormTitle string `json:"form_title"` //表单标题
|
|
|
DyRewardType int32 `json:"dy_reward_type"` //动态奖品内容类型(0-无,1-兑换码)
|
|
|
UrlId int64 `json:"url_id"` //回调地址
|
|
|
FormId int64 `json:"form_id"` //表单id
|
|
|
}
|
|
|
|
|
|
type RewardLang struct {
|
|
|
RewardId int64 `json:"reward_id"`
|
|
|
Title string `json:"title"`
|
|
|
ImgUrl string `json:"img_url"`
|
|
|
Mark string `json:"mark"`
|
|
|
IsDefault int32 `json:"is_default"`
|
|
|
FindDefault bool `json:"find_default"` //只能找到默认数据
|
|
|
OpenToUse int32 `json:"open_to_use"` //是否开启了“去使用”
|
|
|
AppParam string `json:"app_param"` //app参数
|
|
|
WebUrl string `json:"web_url"` //web端跳转参数
|
|
|
PcParam string `json:"pc_param"` //pc参数
|
|
|
}
|
|
|
|
|
|
type Rewards struct {
|
|
|
ctx context.Context
|
|
|
be *merr.BusinessError
|
|
|
projectId int64
|
|
|
|
|
|
find struct {
|
|
|
list []*model.Reward
|
|
|
} // 找到的数据
|
|
|
|
|
|
fmap map[int64]struct{}
|
|
|
urlMap map[int64]string //地址id对应的地址信息
|
|
|
formMap map[int64]string //表单id对应的表单标题
|
|
|
rMap map[int64]*RewardInfo //奖品信息临时map
|
|
|
rlMap map[string]RewardLang //奖品的语言信息临时map
|
|
|
rls map[int64][]*model.RewardLang //奖品的语言信息列表临时map
|
|
|
ralMap map[string]RewardAutoLang //奖品信息并且自动选匹配语种后的数据临时map
|
|
|
}
|
|
|
|
|
|
// NewRewards 奖品管理
|
|
|
func NewRewards(ctx context.Context, projectId int64, ids ...int64) (*Rewards, error) {
|
|
|
p := &Rewards{
|
|
|
ctx: ctx,
|
|
|
be: merr.NewBE(ctx),
|
|
|
projectId: projectId,
|
|
|
fmap: map[int64]struct{}{},
|
|
|
rlMap: map[string]RewardLang{},
|
|
|
rMap: map[int64]*RewardInfo{},
|
|
|
rls: map[int64][]*model.RewardLang{},
|
|
|
ralMap: map[string]RewardAutoLang{},
|
|
|
}
|
|
|
|
|
|
err := p._find(ids...)
|
|
|
|
|
|
return p, err
|
|
|
}
|
|
|
|
|
|
func (s *Rewards) Append(ids ...int64) (*Rewards, error) {
|
|
|
err := s._find(ids...)
|
|
|
return s, err
|
|
|
}
|
|
|
|
|
|
// InfoListWithLang 奖品列表信息带语言
|
|
|
func (s *Rewards) InfoListWithLang(ids ...int64) (list []*RewardInfo, err error) {
|
|
|
list, err = s.InfoList(ids...)
|
|
|
for _, v := range list {
|
|
|
if len(v.Langs) != 0 {
|
|
|
continue
|
|
|
}
|
|
|
|
|
|
langs := s.rls[v.Id]
|
|
|
for _, lang := range langs {
|
|
|
v.Langs = append(v.Langs, &RewardLang{
|
|
|
RewardId: lang.RewardID,
|
|
|
Title: lang.Title,
|
|
|
ImgUrl: lang.ImgURL,
|
|
|
Mark: lang.Mark,
|
|
|
IsDefault: int32(lang.IsDefault),
|
|
|
})
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return
|
|
|
}
|
|
|
|
|
|
// InfoWithLang 奖品信息带语言
|
|
|
func (s *Rewards) InfoWithLang(ids ...int64) (info *RewardInfo, err error) {
|
|
|
info, err = s.Info(ids...)
|
|
|
if err != nil {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
if len(info.Langs) != 0 {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
langs := s.rls[info.Id]
|
|
|
for _, lang := range langs {
|
|
|
info.Langs = append(info.Langs, &RewardLang{
|
|
|
RewardId: lang.RewardID,
|
|
|
Title: lang.Title,
|
|
|
ImgUrl: lang.ImgURL,
|
|
|
Mark: lang.Mark,
|
|
|
IsDefault: int32(lang.IsDefault),
|
|
|
FindDefault: false,
|
|
|
OpenToUse: int32(lang.OpenToUse),
|
|
|
AppParam: lang.AppParam,
|
|
|
WebUrl: lang.WebURL,
|
|
|
PcParam: lang.PcParam,
|
|
|
})
|
|
|
}
|
|
|
|
|
|
return
|
|
|
}
|
|
|
|
|
|
// InfoList 奖品列表信息
|
|
|
func (s *Rewards) InfoList(ids ...int64) (list []*RewardInfo, err error) {
|
|
|
|
|
|
urlMap, err := s._url()
|
|
|
if err != nil {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
formMap, err := s._form()
|
|
|
if err != nil {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
if len(ids) > 0 {
|
|
|
// 找id
|
|
|
list = make([]*RewardInfo, 0, len(ids))
|
|
|
var inMap = make(map[int64]struct{})
|
|
|
for _, id := range ids {
|
|
|
inMap[id] = struct{}{}
|
|
|
}
|
|
|
|
|
|
for _, v := range s.find.list {
|
|
|
if _, ok := inMap[v.ID]; ok {
|
|
|
list = append(list, s.one(v, urlMap[v.URLID], formMap[v.FormID]))
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return
|
|
|
}
|
|
|
|
|
|
// 全都找
|
|
|
list = make([]*RewardInfo, 0, len(s.find.list))
|
|
|
for _, v := range s.find.list {
|
|
|
list = append(list, s.one(v, urlMap[v.URLID], formMap[v.FormID]))
|
|
|
}
|
|
|
|
|
|
return
|
|
|
}
|
|
|
|
|
|
// InfoMap 奖品列表信息
|
|
|
func (s *Rewards) InfoMap(ids ...int64) (list map[int64]*RewardInfo, err error) {
|
|
|
|
|
|
urlMap, err := s._url()
|
|
|
if err != nil {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
formMap, err := s._form()
|
|
|
if err != nil {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
if len(ids) > 0 {
|
|
|
// 找id的
|
|
|
list = make(map[int64]*RewardInfo, len(ids))
|
|
|
var inMap = make(map[int64]struct{})
|
|
|
for _, id := range ids {
|
|
|
inMap[id] = struct{}{}
|
|
|
}
|
|
|
|
|
|
for _, v := range s.find.list {
|
|
|
if _, ok := inMap[v.ID]; ok {
|
|
|
list[v.ID] = s.one(v, urlMap[v.URLID], formMap[v.FormID])
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return
|
|
|
}
|
|
|
|
|
|
// 全都找
|
|
|
list = make(map[int64]*RewardInfo, len(s.find.list))
|
|
|
for _, v := range s.find.list {
|
|
|
list[v.ID] = s.one(v, urlMap[v.URLID], formMap[v.FormID])
|
|
|
}
|
|
|
|
|
|
return
|
|
|
}
|
|
|
|
|
|
// Info 奖品信息
|
|
|
func (s *Rewards) Info(ids ...int64) (info *RewardInfo, err error) {
|
|
|
|
|
|
for _, id := range ids {
|
|
|
var ok bool
|
|
|
info, ok = s.rMap[id]
|
|
|
if ok {
|
|
|
return
|
|
|
}
|
|
|
}
|
|
|
|
|
|
defer func() {
|
|
|
if err == nil && info != nil {
|
|
|
s.rMap[info.Id] = info
|
|
|
}
|
|
|
}()
|
|
|
|
|
|
urlMap, err := s._url()
|
|
|
if err != nil {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
formMap, err := s._form()
|
|
|
if err != nil {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
if len(ids) == 0 {
|
|
|
for _, v := range s.find.list {
|
|
|
return s.one(v, urlMap[v.URLID], formMap[v.FormID]), nil
|
|
|
}
|
|
|
return
|
|
|
}
|
|
|
|
|
|
for _, v := range s.find.list {
|
|
|
for _, id := range ids {
|
|
|
if v.ID == id {
|
|
|
return s.one(v, urlMap[v.URLID], formMap[v.FormID]), nil
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return
|
|
|
}
|
|
|
|
|
|
// AutoLang 自动匹配语言
|
|
|
func (s *Rewards) AutoLang(rewardId int64, langMarks ...string) (lang RewardLang) {
|
|
|
|
|
|
key := fmt.Sprint(rewardId)
|
|
|
if len(langMarks) > 0 {
|
|
|
key += "_" + langMarks[0]
|
|
|
}
|
|
|
|
|
|
if info, ok := s.rlMap[key]; ok {
|
|
|
return info
|
|
|
}
|
|
|
|
|
|
defer func() {
|
|
|
s.rlMap[key] = lang
|
|
|
}()
|
|
|
|
|
|
langs := s.rls[rewardId]
|
|
|
for _, v := range langs {
|
|
|
for _, mark := range langMarks {
|
|
|
if v.Mark == mark {
|
|
|
return RewardLang{
|
|
|
RewardId: v.RewardID,
|
|
|
Title: v.Title,
|
|
|
ImgUrl: v.ImgURL,
|
|
|
Mark: v.Mark,
|
|
|
IsDefault: int32(v.IsDefault),
|
|
|
FindDefault: false,
|
|
|
OpenToUse: int32(v.OpenToUse),
|
|
|
AppParam: v.AppParam,
|
|
|
WebUrl: v.WebURL,
|
|
|
PcParam: v.PcParam,
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
for _, v := range langs {
|
|
|
if v.IsDefault == mtype.Yes {
|
|
|
return RewardLang{
|
|
|
RewardId: v.RewardID,
|
|
|
Title: v.Title,
|
|
|
ImgUrl: v.ImgURL,
|
|
|
Mark: v.Mark,
|
|
|
IsDefault: int32(v.IsDefault),
|
|
|
FindDefault: true,
|
|
|
OpenToUse: int32(v.OpenToUse),
|
|
|
AppParam: v.AppParam,
|
|
|
WebUrl: v.WebURL,
|
|
|
PcParam: v.PcParam,
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return
|
|
|
}
|
|
|
|
|
|
func (s *Rewards) InfoAutoLang(rewardId int64, langMarks ...string) (data RewardAutoLang, err error) {
|
|
|
key := fmt.Sprint(rewardId)
|
|
|
if len(langMarks) > 0 {
|
|
|
key += "_" + langMarks[0]
|
|
|
}
|
|
|
|
|
|
if cd, ok := s.ralMap[key]; ok {
|
|
|
return cd, nil
|
|
|
}
|
|
|
|
|
|
defer func() {
|
|
|
if err == nil {
|
|
|
s.ralMap[key] = data
|
|
|
}
|
|
|
}()
|
|
|
|
|
|
info, err := s.Info(rewardId)
|
|
|
if err != nil {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
if info == nil {
|
|
|
err = s.be.VerifyError("not found reward info")
|
|
|
return
|
|
|
}
|
|
|
|
|
|
lang := s.AutoLang(rewardId, langMarks...)
|
|
|
|
|
|
return RewardAutoLang{
|
|
|
KvData: info.KvData,
|
|
|
ReceiveType: info.ReceiveType,
|
|
|
Contact: info.Contact,
|
|
|
Id: info.Id,
|
|
|
Url: info.Url,
|
|
|
Title: lang.Title,
|
|
|
ImgUrl: lang.ImgUrl,
|
|
|
Mark: lang.Mark,
|
|
|
IsDefault: lang.IsDefault,
|
|
|
FindDefault: lang.FindDefault,
|
|
|
RewardTitle: info.RewardTitle,
|
|
|
FormTitle: info.FormTitle,
|
|
|
OpenToUse: lang.OpenToUse,
|
|
|
AppParam: lang.AppParam,
|
|
|
PcParam: lang.PcParam,
|
|
|
WebUrl: lang.WebUrl,
|
|
|
DyRewardType: info.DyRewardType,
|
|
|
UrlId: info.UrlId,
|
|
|
FormId: info.FormId,
|
|
|
}, nil
|
|
|
}
|
|
|
|
|
|
func (s *Rewards) InfoAutoLangMap(langMarks ...string) (data map[int64]*RewardAutoLang, err error) {
|
|
|
list, err := s.InfoList()
|
|
|
if err != nil {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
data = make(map[int64]*RewardAutoLang, len(list))
|
|
|
|
|
|
for _, info := range list {
|
|
|
lang := s.AutoLang(info.Id, langMarks...)
|
|
|
data[info.Id] = &RewardAutoLang{
|
|
|
KvData: info.KvData,
|
|
|
ReceiveType: info.ReceiveType,
|
|
|
Contact: info.Contact,
|
|
|
Id: info.Id,
|
|
|
Url: info.Url,
|
|
|
Title: lang.Title,
|
|
|
ImgUrl: lang.ImgUrl,
|
|
|
Mark: lang.Mark,
|
|
|
IsDefault: lang.IsDefault,
|
|
|
FindDefault: lang.FindDefault,
|
|
|
RewardTitle: info.RewardTitle,
|
|
|
FormTitle: info.FormTitle,
|
|
|
OpenToUse: lang.OpenToUse,
|
|
|
AppParam: lang.AppParam,
|
|
|
PcParam: lang.PcParam,
|
|
|
WebUrl: lang.WebUrl,
|
|
|
DyRewardType: info.DyRewardType,
|
|
|
UrlId: info.UrlId,
|
|
|
FormId: info.FormId,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return
|
|
|
}
|
|
|
|
|
|
// 找到对应的数据
|
|
|
func (s *Rewards) _find(fids ...int64) (err error) {
|
|
|
var rewardIds []int64
|
|
|
if len(fids) == 0 {
|
|
|
rewardIds, err = s._findByProjectId()
|
|
|
} else {
|
|
|
rewardIds, err = s._findByIds(fids...)
|
|
|
}
|
|
|
|
|
|
if err != nil {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
return s._findRewardLang(rewardIds)
|
|
|
}
|
|
|
|
|
|
func (s *Rewards) _findByProjectId() (rewardIds []int64, err error) {
|
|
|
m := dao.Reward
|
|
|
list, err := m.WithContext(s.ctx).
|
|
|
Where(
|
|
|
m.ProjectID.Eq(s.projectId),
|
|
|
m.OldData.Eq(mtype.No),
|
|
|
m.IsDel.Eq(mtype.No)).
|
|
|
Order(m.ID.Desc()).
|
|
|
Find()
|
|
|
if err != nil {
|
|
|
err = s.be.MysqlError(err)
|
|
|
return
|
|
|
}
|
|
|
|
|
|
for _, v := range list {
|
|
|
if _, ok := s.fmap[v.ID]; ok {
|
|
|
continue
|
|
|
}
|
|
|
s.find.list = append(s.find.list, v)
|
|
|
s.fmap[v.ID] = struct{}{}
|
|
|
rewardIds = append(rewardIds, v.ID)
|
|
|
}
|
|
|
|
|
|
return
|
|
|
}
|
|
|
|
|
|
func (s *Rewards) _findByIds(fids ...int64) (rewardIds []int64, err error) {
|
|
|
var fixIds, ids []int64
|
|
|
for _, v := range fids {
|
|
|
if _, ok := s.fmap[v]; ok {
|
|
|
continue
|
|
|
}
|
|
|
|
|
|
if v < 0 {
|
|
|
fixIds = append(fixIds, v)
|
|
|
continue
|
|
|
}
|
|
|
|
|
|
ids = append(ids, v)
|
|
|
}
|
|
|
|
|
|
if len(fixIds) > 0 {
|
|
|
for _, reward := range app.FissionFixRewards {
|
|
|
s.find.list = append(s.find.list, &model.Reward{
|
|
|
ID: reward.Id,
|
|
|
RewardTitle: reward.RewardTitle,
|
|
|
})
|
|
|
|
|
|
for _, lang := range reward.Langs {
|
|
|
s.rls[reward.Id] = append(s.rls[reward.Id], &model.RewardLang{
|
|
|
RewardID: reward.Id,
|
|
|
Mark: lang.Mark,
|
|
|
IsDefault: utils.Ternary[int8](lang.IsDefault, mtype.Yes, mtype.No),
|
|
|
Title: lang.Title,
|
|
|
ImgURL: lang.ImgUrl,
|
|
|
})
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if len(ids) == 0 {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
c := cache.NewReward(s.ctx)
|
|
|
list, err := c.FindByIds(ids)
|
|
|
if err != nil {
|
|
|
err = s.be.CacheError(err)
|
|
|
return
|
|
|
}
|
|
|
|
|
|
for _, v := range list {
|
|
|
s.find.list = append(s.find.list, v)
|
|
|
s.fmap[v.ID] = struct{}{}
|
|
|
rewardIds = append(rewardIds, v.ID)
|
|
|
}
|
|
|
|
|
|
return
|
|
|
}
|
|
|
|
|
|
func (s *Rewards) _findRewardLang(rewardIds []int64) (err error) {
|
|
|
rlm := dao.RewardLang
|
|
|
list, err := rlm.WithContext(s.ctx).Where(rlm.RewardID.In(rewardIds...), rlm.IsDel.Eq(mtype.No)).Find()
|
|
|
if err != nil {
|
|
|
err = s.be.MysqlError(err)
|
|
|
return
|
|
|
}
|
|
|
|
|
|
for _, v := range list {
|
|
|
s.rls[v.RewardID] = append(s.rls[v.RewardID], v)
|
|
|
}
|
|
|
|
|
|
return
|
|
|
}
|
|
|
|
|
|
func (s *Rewards) _url() (urlMap map[int64]string, err error) {
|
|
|
if len(s.urlMap) != 0 {
|
|
|
return s.urlMap, nil
|
|
|
}
|
|
|
|
|
|
var ids []int64
|
|
|
urlMap = make(map[int64]string)
|
|
|
for _, v := range s.find.list {
|
|
|
if _, ok := urlMap[v.URLID]; !ok {
|
|
|
ids = append(ids, v.URLID)
|
|
|
urlMap[v.URLID] = ""
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if len(ids) == 0 {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
m := dao.NotifyURL
|
|
|
list, err := m.WithContext(s.ctx).Where(m.ID.In(ids...)).Find()
|
|
|
if err != nil {
|
|
|
err = s.be.MysqlError(err)
|
|
|
return
|
|
|
}
|
|
|
|
|
|
for _, url := range list {
|
|
|
urlMap[url.ID] = url.URL
|
|
|
}
|
|
|
|
|
|
s.urlMap = urlMap
|
|
|
|
|
|
return
|
|
|
}
|
|
|
|
|
|
func (s *Rewards) _form() (formMap map[int64]string, err error) {
|
|
|
if len(s.formMap) != 0 {
|
|
|
return s.formMap, nil
|
|
|
}
|
|
|
|
|
|
var ids []int64
|
|
|
formMap = make(map[int64]string)
|
|
|
for _, v := range s.find.list {
|
|
|
if _, ok := formMap[v.FormID]; !ok && v.FormID > 0 {
|
|
|
ids = append(ids, v.FormID)
|
|
|
formMap[v.FormID] = ""
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if len(ids) == 0 {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
m := dao.Form
|
|
|
list, err := m.WithContext(s.ctx).Where(m.ID.In(ids...)).Find()
|
|
|
if err != nil {
|
|
|
err = s.be.MysqlError(err)
|
|
|
return
|
|
|
}
|
|
|
|
|
|
for _, form := range list {
|
|
|
formMap[form.ID] = form.Title
|
|
|
}
|
|
|
|
|
|
s.formMap = formMap
|
|
|
|
|
|
return
|
|
|
}
|
|
|
|
|
|
func (s *Rewards) one(val *model.Reward, url, formTitle string) *RewardInfo {
|
|
|
return &RewardInfo{
|
|
|
Id: val.ID,
|
|
|
KvData: val.GetKvData(),
|
|
|
ReceiveType: int32(val.ReceiveType),
|
|
|
RewardTitle: val.RewardTitle,
|
|
|
Contact: val.Contact,
|
|
|
Url: url,
|
|
|
Langs: nil,
|
|
|
FormTitle: formTitle,
|
|
|
DyRewardType: int32(val.DyRewardType),
|
|
|
UrlId: val.URLID,
|
|
|
FormId: val.FormID,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
func (s *Rewards) GetProjectId() int64 {
|
|
|
return s.projectId
|
|
|
}
|