package sys import ( "context" "encoding/json" "net/url" "strconv" "strings" "time" "github.com/gogf/gf/v2/encoding/gjson" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gctx" "github.com/gogf/gf/v2/os/gtime" "hotgo/internal/library/contexts" "hotgo/internal/library/cultivateadmin" "hotgo/internal/model/input/form" "hotgo/internal/model/input/sysin" "hotgo/internal/service" ) type sSysCultivateFlower struct{} func NewSysCultivateFlower() *sSysCultivateFlower { return &sSysCultivateFlower{} } func init() { service.RegisterSysCultivateFlower(NewSysCultivateFlower()) } type cultivateFlowerDTO struct { ID string `json:"id"` FlowerCode string `json:"flowerCode"` FlowerName string `json:"flowerName"` FlowerDesc string `json:"flowerDesc"` IconFileID string `json:"iconFileId"` Status string `json:"status"` ConfigVersion string `json:"configVersion"` DailyWaterLimit uint `json:"dailyWaterLimit"` DailyFertilizeLimit uint `json:"dailyFertilizeLimit"` ShareConfig any `json:"shareConfig"` SortNo int `json:"sortNo"` StageCount int `json:"stageCount"` StageRules []*cultivateStageRuleDTO `json:"stageRules"` CreatedAt *gtime.Time `json:"createdAt"` UpdatedAt *gtime.Time `json:"updatedAt"` } type cultivateStageRuleDTO struct { ID string `json:"id"` Stage string `json:"stage"` NextStage string `json:"nextStage"` RequiredPoints uint `json:"requiredPoints"` WaterPoints uint `json:"waterPoints"` FertilizePoints uint `json:"fertilizePoints"` WaitHours uint `json:"waitHours"` AllowedActions []string `json:"allowedActions"` ImageFileID string `json:"imageFileId"` AnimationFileID string `json:"animationFileId"` ProgressTextTemplate string `json:"progressTextTemplate"` CompletionText string `json:"completionText"` SortNo int `json:"sortNo"` } func cultivateTenantID(ctx context.Context) int64 { id := contexts.GetTenantId(ctx) if id <= 0 { id = g.Cfg().MustGet(ctx, "cultivate.defaultTenantId", 1).Int64() } return id } func cultivateAdminScope(ctx context.Context) (cultivateadmin.Scope, error) { tenant, err := resolveImAdminTenant(ctx, cultivateTenantID(ctx)) if err != nil { return cultivateadmin.Scope{}, err } operatorID := contexts.GetUserId(ctx) if operatorID <= 0 { return cultivateadmin.Scope{}, gerror.New("当前管理员身份无效") } return cultivateadmin.Scope{ TenantID: tenant.Id, TenantCode: tenant.Code, OperatorID: operatorID, RoleKey: contexts.GetRoleKey(ctx), RequestID: gctx.CtxId(ctx), }, nil } func (s *sSysCultivateFlower) List(ctx context.Context, in *sysin.CultivateFlowerListInp) (list []*sysin.CultivateFlowerListModel, totalCount int, err error) { if err = in.Filter(ctx); err != nil { return nil, 0, err } scope, err := cultivateAdminScope(ctx) if err != nil { return nil, 0, err } normalizeCultivatePage(&in.PageReq) values := cultivatePageValues(in.Page, in.PerPage) cultivateSetQuery(values, "flower_code", in.FlowerCode) cultivateSetQuery(values, "flower_name", in.FlowerName) cultivateSetQuery(values, "status", in.Status) cultivateSetQuery(values, "config_version", in.ConfigVersion) setTimeRange(values, in.CreatedAt) var page cultivateadmin.Page[cultivateFlowerDTO] if err = cultivateadmin.DefaultClient(ctx).Get(ctx, scope, "/flowers", values, &page); err != nil { return nil, 0, err } list = make([]*sysin.CultivateFlowerListModel, 0, len(page.Items)) for index := range page.Items { item, convertErr := flowerListModel(&page.Items[index]) if convertErr != nil { return nil, 0, convertErr } list = append(list, item) } return list, page.Total, nil } func (s *sSysCultivateFlower) View(ctx context.Context, in *sysin.CultivateFlowerViewInp) (*sysin.CultivateFlowerViewModel, error) { if err := in.Filter(ctx); err != nil { return nil, err } scope, err := cultivateAdminScope(ctx) if err != nil { return nil, err } var item cultivateFlowerDTO if err = cultivateadmin.DefaultClient(ctx).Get(ctx, scope, "/flowers/"+strconv.FormatUint(in.Id, 10), nil, &item); err != nil { return nil, err } return flowerViewModel(&item) } func (s *sSysCultivateFlower) Edit(ctx context.Context, in *sysin.CultivateFlowerEditInp) error { if err := in.Filter(ctx); err != nil { return err } scope, err := cultivateAdminScope(ctx) if err != nil { return err } payload := flowerPayload(in) var response struct { ID string `json:"id"` } if in.Id == 0 { err = cultivateadmin.DefaultClient(ctx).Post(ctx, scope, "/flowers", payload, &response) } else { err = cultivateadmin.DefaultClient(ctx).Put(ctx, scope, "/flowers/"+strconv.FormatUint(in.Id, 10), payload, &response) } if err != nil { return err } if in.Id == 0 && response.ID != "" { in.Id, err = strconv.ParseUint(response.ID, 10, 64) if err != nil { return gerror.Wrap(err, "解析养成服务花种ID失败") } } return nil } func (s *sSysCultivateFlower) Status(ctx context.Context, in *sysin.CultivateFlowerStatusInp) error { if err := in.Filter(ctx); err != nil { return err } scope, err := cultivateAdminScope(ctx) if err != nil { return err } return cultivateadmin.DefaultClient(ctx).Post(ctx, scope, "/flowers/"+strconv.FormatUint(in.Id, 10)+"/status", map[string]any{ "status": in.Status, "reason": strings.TrimSpace(in.Reason), }, nil) } func flowerPayload(in *sysin.CultivateFlowerEditInp) map[string]any { shareConfig := map[string]any{} if in.ShareConfig != nil { _ = json.Unmarshal([]byte(in.ShareConfig.MustToJsonString()), &shareConfig) } rules := make([]map[string]any, 0, len(in.StageRules)) for _, rule := range in.StageRules { if rule == nil { continue } rules = append(rules, map[string]any{ "id": strconv.FormatUint(rule.Id, 10), "stage": rule.Stage, "next_stage": rule.NextStage, "required_points": rule.RequiredPoints, "water_points": rule.WaterPoints, "fertilize_points": rule.FertilizePoints, "wait_hours": rule.WaitHours, "allowed_actions": rule.AllowedActions, "image_file_id": rule.ImageFileId, "animation_file_id": rule.AnimationFileId, "progress_text_template": rule.ProgressTextTemplate, "completion_text": rule.CompletionText, "sort_no": rule.SortNo, }) } return map[string]any{ "flower_code": in.FlowerCode, "flower_name": in.FlowerName, "flower_desc": in.FlowerDesc, "icon_file_id": in.IconFileId, "status": in.Status, "daily_water_limit": in.DailyWaterLimit, "daily_fertilize_limit": in.DailyFertilizeLimit, "share_config": shareConfig, "sort_no": in.SortNo, "stage_rules": rules, } } func flowerListModel(item *cultivateFlowerDTO) (*sysin.CultivateFlowerListModel, error) { id, err := parseCultivateUint(item.ID, "花种ID") if err != nil { return nil, err } return &sysin.CultivateFlowerListModel{ Id: id, FlowerCode: item.FlowerCode, FlowerName: item.FlowerName, FlowerDesc: item.FlowerDesc, IconFileId: item.IconFileID, Status: item.Status, ConfigVersion: item.ConfigVersion, DailyWaterLimit: item.DailyWaterLimit, DailyFertilizeLimit: item.DailyFertilizeLimit, SortNo: item.SortNo, StageCount: item.StageCount, CreatedAt: item.CreatedAt, UpdatedAt: item.UpdatedAt, }, nil } func flowerViewModel(item *cultivateFlowerDTO) (*sysin.CultivateFlowerViewModel, error) { list, err := flowerListModel(item) if err != nil { return nil, err } shareConfig, _ := json.Marshal(item.ShareConfig) result := &sysin.CultivateFlowerViewModel{ CultivateFlowerEditInp: sysin.CultivateFlowerEditInp{ Id: list.Id, FlowerCode: list.FlowerCode, FlowerName: list.FlowerName, FlowerDesc: list.FlowerDesc, IconFileId: list.IconFileId, Status: list.Status, DailyWaterLimit: list.DailyWaterLimit, DailyFertilizeLimit: list.DailyFertilizeLimit, ShareConfig: gjson.New(string(shareConfig)), SortNo: list.SortNo, }, ConfigVersion: list.ConfigVersion, CreatedAt: list.CreatedAt, UpdatedAt: list.UpdatedAt, } for _, rule := range item.StageRules { id, parseErr := parseCultivateUint(rule.ID, "成长阶段ID") if parseErr != nil { return nil, parseErr } result.StageRules = append(result.StageRules, &sysin.CultivateStageRuleEditInp{ Id: id, Stage: rule.Stage, NextStage: rule.NextStage, RequiredPoints: rule.RequiredPoints, WaterPoints: rule.WaterPoints, FertilizePoints: rule.FertilizePoints, WaitHours: rule.WaitHours, AllowedActions: rule.AllowedActions, ImageFileId: rule.ImageFileID, AnimationFileId: rule.AnimationFileID, ProgressTextTemplate: rule.ProgressTextTemplate, CompletionText: rule.CompletionText, SortNo: rule.SortNo, }) } return result, nil } func parseCultivateUint(value, field string) (uint64, error) { id, err := strconv.ParseUint(strings.TrimSpace(value), 10, 64) if err != nil || id == 0 { return 0, gerror.Newf("养成服务返回的%s不正确", field) } return id, nil } func normalizeCultivatePage(page *form.PageReq) { if page.Page <= 0 { page.Page = 1 } if page.PerPage <= 0 { page.PerPage = 10 } if page.PerPage > 100 { page.PerPage = 100 } } func cultivatePageValues(page, perPage int) url.Values { return url.Values{"page": []string{strconv.Itoa(page)}, "per_page": []string{strconv.Itoa(perPage)}} } func cultivateSetQuery(values url.Values, key, value string) { if value = strings.TrimSpace(value); value != "" { values.Set(key, value) } } func setTimeRange(values url.Values, value []*gtime.Time) { if len(value) != 2 { return } if value[0] != nil { values.Set("created_from", value[0].Format(time.RFC3339)) } if value[1] != nil { values.Set("created_to", value[1].Format(time.RFC3339)) } }