From 64037863ef3c6949e9a67abe40758e8bc1b7f252 Mon Sep 17 00:00:00 2001 From: Austin Date: Thu, 15 Oct 2026 18:20:26 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=AF=9B=E9=9A=85?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E6=A8=A1=E5=9D=97=E7=9A=84=E5=90=8E=E5=8F=B0?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E4=B8=8E=E8=8F=9C=E5=8D=95=E6=9D=83=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/admin/maoyucontent/maoyu_content.go | 82 +++++++ server/api/admin/maoyupet/maoyu_pet.go | 82 +++++++ server/internal/controller/admin/sys/maoyu.go | 193 ++++++++++++++++ server/internal/logic/sys/maoyu_content.go | 206 +++++++++++++++++ server/internal/logic/sys/maoyu_pet.go | 181 +++++++++++++++ server/internal/model/input/sysin/maoyu.go | 209 ++++++++++++++++++ server/internal/router/genrouter/maoyu.go | 7 + server/internal/service/sys.go | 44 ++++ server/storage/data/generate/maoyu_menu.sql | 109 +++++++++ 9 files changed, 1113 insertions(+) create mode 100644 server/api/admin/maoyucontent/maoyu_content.go create mode 100644 server/api/admin/maoyupet/maoyu_pet.go create mode 100644 server/internal/controller/admin/sys/maoyu.go create mode 100644 server/internal/logic/sys/maoyu_content.go create mode 100644 server/internal/logic/sys/maoyu_pet.go create mode 100644 server/internal/model/input/sysin/maoyu.go create mode 100644 server/internal/router/genrouter/maoyu.go create mode 100644 server/storage/data/generate/maoyu_menu.sql diff --git a/server/api/admin/maoyucontent/maoyu_content.go b/server/api/admin/maoyucontent/maoyu_content.go new file mode 100644 index 0000000..7afa020 --- /dev/null +++ b/server/api/admin/maoyucontent/maoyu_content.go @@ -0,0 +1,82 @@ +package maoyucontent + +import ( + "github.com/gogf/gf/v2/frame/g" + + "hotgo/internal/model/input/form" + "hotgo/internal/model/input/sysin" +) + +// 毛隅内容治理页面:概览、动态/评论审核、举报、操作日志。 +// 页面协议与养成内容治理一致,通过 maoyu-server 内部接口处理毛隅业务。 + +type MaoyuContentDashboardReq struct { + g.Meta `path:"/maoyu/dashboard" method:"get" tags:"毛隅管理" summary:"获取毛隅管理概览"` + sysin.MaoyuDashboardInp +} +type MaoyuContentDashboardRes struct { + *sysin.MaoyuDashboardModel +} + +type MaoyuContentListReq struct { + g.Meta `path:"/maoyu/content/list" method:"get" tags:"毛隅管理" summary:"获取毛隅内容列表"` + *sysin.CultivateContentListInp +} +type MaoyuContentListRes struct { + List []*sysin.CultivateContentListModel `json:"list" dc:"内容列表"` + form.PageRes +} + +type MaoyuContentViewReq struct { + g.Meta `path:"/maoyu/content/view" method:"get" tags:"毛隅管理" summary:"获取毛隅内容详情"` + *sysin.CultivateContentViewInp +} +type MaoyuContentViewRes struct { + *sysin.CultivateContentViewModel +} + +type MaoyuContentReviewReq struct { + g.Meta `path:"/maoyu/content/review" method:"post" tags:"毛隅管理" summary:"毛隅内容单条审核"` + *sysin.CultivateContentReviewInp +} +type MaoyuContentReviewRes struct{} + +type MaoyuContentBatchReviewReq struct { + g.Meta `path:"/maoyu/content/batch-review" method:"post" tags:"毛隅管理" summary:"毛隅内容批量审核"` + *sysin.CultivateContentBatchReviewInp +} +type MaoyuContentBatchReviewRes struct { + *sysin.CultivateContentBatchReviewModel +} + +type MaoyuReportListReq struct { + g.Meta `path:"/maoyu/reports/list" method:"get" tags:"毛隅管理" summary:"获取毛隅举报列表"` + *sysin.CultivateReportListInp +} +type MaoyuReportListRes struct { + List []*sysin.CultivateReportListModel `json:"list" dc:"举报列表"` + form.PageRes +} + +type MaoyuReportViewReq struct { + g.Meta `path:"/maoyu/reports/view" method:"get" tags:"毛隅管理" summary:"获取毛隅举报详情"` + *sysin.CultivateReportViewInp +} +type MaoyuReportViewRes struct { + *sysin.CultivateReportViewModel +} + +type MaoyuReportHandleReq struct { + g.Meta `path:"/maoyu/reports/handle" method:"post" tags:"毛隅管理" summary:"处理毛隅举报"` + *sysin.CultivateReportHandleInp +} +type MaoyuReportHandleRes struct{} + +type MaoyuLogListReq struct { + g.Meta `path:"/maoyu/logs/list" method:"get" tags:"毛隅管理" summary:"获取毛隅操作日志列表"` + *sysin.CultivateOperationLogListInp +} +type MaoyuLogListRes struct { + List []*sysin.CultivateOperationLogListModel `json:"list" dc:"操作日志列表"` + form.PageRes +} diff --git a/server/api/admin/maoyupet/maoyu_pet.go b/server/api/admin/maoyupet/maoyu_pet.go new file mode 100644 index 0000000..274b69e --- /dev/null +++ b/server/api/admin/maoyupet/maoyu_pet.go @@ -0,0 +1,82 @@ +package maoyupet + +import ( + "github.com/gogf/gf/v2/frame/g" + + "hotgo/internal/model/input/form" + "hotgo/internal/model/input/sysin" +) + +// 毛隅萌宠管理页面:互动动作配置、宠物档案、场景生成任务、互动通知。 + +type MaoyuActionConfigListReq struct { + g.Meta `path:"/maoyu/action-configs/list" method:"get" tags:"毛隅管理" summary:"获取毛隅动作配置列表"` + *sysin.MaoyuActionConfigListInp +} +type MaoyuActionConfigListRes struct { + List []*sysin.MaoyuActionConfigListModel `json:"list" dc:"动作配置列表"` + form.PageRes +} + +type MaoyuActionConfigViewReq struct { + g.Meta `path:"/maoyu/action-configs/view" method:"get" tags:"毛隅管理" summary:"获取毛隅动作配置详情"` + *sysin.MaoyuActionConfigViewInp +} +type MaoyuActionConfigViewRes struct { + *sysin.MaoyuActionConfigViewModel +} + +type MaoyuActionConfigEditReq struct { + g.Meta `path:"/maoyu/action-configs/edit" method:"post" tags:"毛隅管理" summary:"新增或编辑毛隅动作配置"` + *sysin.MaoyuActionConfigEditInp +} +type MaoyuActionConfigEditRes struct{} + +type MaoyuActionConfigStatusReq struct { + g.Meta `path:"/maoyu/action-configs/status" method:"post" tags:"毛隅管理" summary:"启用/停用毛隅动作配置"` + *sysin.MaoyuActionConfigStatusInp +} +type MaoyuActionConfigStatusRes struct{} + +type MaoyuPetListReq struct { + g.Meta `path:"/maoyu/pets/list" method:"get" tags:"毛隅管理" summary:"获取毛隅宠物档案列表"` + *sysin.MaoyuPetListInp +} +type MaoyuPetListRes struct { + List []*sysin.MaoyuPetListModel `json:"list" dc:"宠物档案列表"` + form.PageRes +} + +type MaoyuPetViewReq struct { + g.Meta `path:"/maoyu/pets/view" method:"get" tags:"毛隅管理" summary:"获取毛隅宠物档案详情"` + *sysin.MaoyuPetViewInp +} +type MaoyuPetViewRes struct { + *sysin.MaoyuPetViewModel +} + +type MaoyuJobListReq struct { + g.Meta `path:"/maoyu/generation-jobs/list" method:"get" tags:"毛隅管理" summary:"获取毛隅生成任务列表"` + *sysin.MaoyuJobListInp +} +type MaoyuJobListRes struct { + List []*sysin.MaoyuJobListModel `json:"list" dc:"生成任务列表"` + form.PageRes +} + +type MaoyuJobViewReq struct { + g.Meta `path:"/maoyu/generation-jobs/view" method:"get" tags:"毛隅管理" summary:"获取毛隅生成任务详情"` + *sysin.MaoyuJobViewInp +} +type MaoyuJobViewRes struct { + *sysin.MaoyuJobViewModel +} + +type MaoyuNoticeListReq struct { + g.Meta `path:"/maoyu/notices/list" method:"get" tags:"毛隅管理" summary:"获取毛隅互动通知列表"` + *sysin.MaoyuNoticeListInp +} +type MaoyuNoticeListRes struct { + List []*sysin.MaoyuNoticeListModel `json:"list" dc:"互动通知列表"` + form.PageRes +} diff --git a/server/internal/controller/admin/sys/maoyu.go b/server/internal/controller/admin/sys/maoyu.go new file mode 100644 index 0000000..3b2620d --- /dev/null +++ b/server/internal/controller/admin/sys/maoyu.go @@ -0,0 +1,193 @@ +package sys + +import ( + "context" + + "hotgo/api/admin/maoyucontent" + "hotgo/api/admin/maoyupet" + "hotgo/internal/model/input/sysin" + "hotgo/internal/service" +) + +// 毛隅管理页面控制器:内容治理 + 萌宠管理,业务经 maoyu-server 内部接口处理。 + +var ( + MaoyuContent = cMaoyuContent{} + MaoyuPet = cMaoyuPet{} +) + +type ( + cMaoyuContent struct{} + cMaoyuPet struct{} +) + +// ---- 内容治理 ---- + +func (*cMaoyuContent) Dashboard(ctx context.Context, req *maoyucontent.MaoyuContentDashboardReq) (res *maoyucontent.MaoyuContentDashboardRes, err error) { + data, err := service.SysMaoyuContent().Dashboard(ctx, &req.MaoyuDashboardInp) + if err != nil { + return nil, err + } + return &maoyucontent.MaoyuContentDashboardRes{MaoyuDashboardModel: data}, nil +} + +func (*cMaoyuContent) List(ctx context.Context, req *maoyucontent.MaoyuContentListReq) (res *maoyucontent.MaoyuContentListRes, err error) { + list, total, err := service.SysMaoyuContent().List(ctx, req.CultivateContentListInp) + if err != nil { + return nil, err + } + if list == nil { + list = []*sysin.CultivateContentListModel{} + } + res = &maoyucontent.MaoyuContentListRes{List: list} + res.PageRes.Pack(req, total) + return +} + +func (*cMaoyuContent) View(ctx context.Context, req *maoyucontent.MaoyuContentViewReq) (res *maoyucontent.MaoyuContentViewRes, err error) { + data, err := service.SysMaoyuContent().View(ctx, req.CultivateContentViewInp) + if err != nil { + return nil, err + } + return &maoyucontent.MaoyuContentViewRes{CultivateContentViewModel: data}, nil +} + +func (*cMaoyuContent) Review(ctx context.Context, req *maoyucontent.MaoyuContentReviewReq) (res *maoyucontent.MaoyuContentReviewRes, err error) { + err = service.SysMaoyuContent().Review(ctx, req.CultivateContentReviewInp) + return +} + +func (*cMaoyuContent) BatchReview(ctx context.Context, req *maoyucontent.MaoyuContentBatchReviewReq) (res *maoyucontent.MaoyuContentBatchReviewRes, err error) { + data, err := service.SysMaoyuContent().BatchReview(ctx, req.CultivateContentBatchReviewInp) + if err != nil { + return nil, err + } + return &maoyucontent.MaoyuContentBatchReviewRes{CultivateContentBatchReviewModel: data}, nil +} + +func (*cMaoyuContent) ReportList(ctx context.Context, req *maoyucontent.MaoyuReportListReq) (res *maoyucontent.MaoyuReportListRes, err error) { + list, total, err := service.SysMaoyuContent().ReportList(ctx, req.CultivateReportListInp) + if err != nil { + return nil, err + } + if list == nil { + list = []*sysin.CultivateReportListModel{} + } + res = &maoyucontent.MaoyuReportListRes{List: list} + res.PageRes.Pack(req, total) + return +} + +func (*cMaoyuContent) ReportView(ctx context.Context, req *maoyucontent.MaoyuReportViewReq) (res *maoyucontent.MaoyuReportViewRes, err error) { + data, err := service.SysMaoyuContent().ReportView(ctx, req.CultivateReportViewInp) + if err != nil { + return nil, err + } + return &maoyucontent.MaoyuReportViewRes{CultivateReportViewModel: data}, nil +} + +func (*cMaoyuContent) ReportHandle(ctx context.Context, req *maoyucontent.MaoyuReportHandleReq) (res *maoyucontent.MaoyuReportHandleRes, err error) { + err = service.SysMaoyuContent().ReportHandle(ctx, req.CultivateReportHandleInp) + return +} + +func (*cMaoyuContent) LogList(ctx context.Context, req *maoyucontent.MaoyuLogListReq) (res *maoyucontent.MaoyuLogListRes, err error) { + list, total, err := service.SysMaoyuContent().LogList(ctx, req.CultivateOperationLogListInp) + if err != nil { + return nil, err + } + if list == nil { + list = []*sysin.CultivateOperationLogListModel{} + } + res = &maoyucontent.MaoyuLogListRes{List: list} + res.PageRes.Pack(req, total) + return +} + +// ---- 萌宠管理 ---- + +func (*cMaoyuPet) ActionConfigList(ctx context.Context, req *maoyupet.MaoyuActionConfigListReq) (res *maoyupet.MaoyuActionConfigListRes, err error) { + list, total, err := service.SysMaoyuPet().ActionConfigList(ctx, req.MaoyuActionConfigListInp) + if err != nil { + return nil, err + } + if list == nil { + list = []*sysin.MaoyuActionConfigListModel{} + } + res = &maoyupet.MaoyuActionConfigListRes{List: list} + res.PageRes.Pack(req, total) + return +} + +func (*cMaoyuPet) ActionConfigView(ctx context.Context, req *maoyupet.MaoyuActionConfigViewReq) (res *maoyupet.MaoyuActionConfigViewRes, err error) { + data, err := service.SysMaoyuPet().ActionConfigView(ctx, req.MaoyuActionConfigViewInp) + if err != nil { + return nil, err + } + return &maoyupet.MaoyuActionConfigViewRes{MaoyuActionConfigViewModel: data}, nil +} + +func (*cMaoyuPet) ActionConfigEdit(ctx context.Context, req *maoyupet.MaoyuActionConfigEditReq) (res *maoyupet.MaoyuActionConfigEditRes, err error) { + err = service.SysMaoyuPet().ActionConfigEdit(ctx, req.MaoyuActionConfigEditInp) + return +} + +func (*cMaoyuPet) ActionConfigStatus(ctx context.Context, req *maoyupet.MaoyuActionConfigStatusReq) (res *maoyupet.MaoyuActionConfigStatusRes, err error) { + err = service.SysMaoyuPet().ActionConfigStatus(ctx, req.MaoyuActionConfigStatusInp) + return +} + +func (*cMaoyuPet) PetList(ctx context.Context, req *maoyupet.MaoyuPetListReq) (res *maoyupet.MaoyuPetListRes, err error) { + list, total, err := service.SysMaoyuPet().PetList(ctx, req.MaoyuPetListInp) + if err != nil { + return nil, err + } + if list == nil { + list = []*sysin.MaoyuPetListModel{} + } + res = &maoyupet.MaoyuPetListRes{List: list} + res.PageRes.Pack(req, total) + return +} + +func (*cMaoyuPet) PetView(ctx context.Context, req *maoyupet.MaoyuPetViewReq) (res *maoyupet.MaoyuPetViewRes, err error) { + data, err := service.SysMaoyuPet().PetView(ctx, req.MaoyuPetViewInp) + if err != nil { + return nil, err + } + return &maoyupet.MaoyuPetViewRes{MaoyuPetViewModel: data}, nil +} + +func (*cMaoyuPet) JobList(ctx context.Context, req *maoyupet.MaoyuJobListReq) (res *maoyupet.MaoyuJobListRes, err error) { + list, total, err := service.SysMaoyuPet().JobList(ctx, req.MaoyuJobListInp) + if err != nil { + return nil, err + } + if list == nil { + list = []*sysin.MaoyuJobListModel{} + } + res = &maoyupet.MaoyuJobListRes{List: list} + res.PageRes.Pack(req, total) + return +} + +func (*cMaoyuPet) JobView(ctx context.Context, req *maoyupet.MaoyuJobViewReq) (res *maoyupet.MaoyuJobViewRes, err error) { + data, err := service.SysMaoyuPet().JobView(ctx, req.MaoyuJobViewInp) + if err != nil { + return nil, err + } + return &maoyupet.MaoyuJobViewRes{MaoyuJobViewModel: data}, nil +} + +func (*cMaoyuPet) NoticeList(ctx context.Context, req *maoyupet.MaoyuNoticeListReq) (res *maoyupet.MaoyuNoticeListRes, err error) { + list, total, err := service.SysMaoyuPet().NoticeList(ctx, req.MaoyuNoticeListInp) + if err != nil { + return nil, err + } + if list == nil { + list = []*sysin.MaoyuNoticeListModel{} + } + res = &maoyupet.MaoyuNoticeListRes{List: list} + res.PageRes.Pack(req, total) + return +} diff --git a/server/internal/logic/sys/maoyu_content.go b/server/internal/logic/sys/maoyu_content.go new file mode 100644 index 0000000..30b4767 --- /dev/null +++ b/server/internal/logic/sys/maoyu_content.go @@ -0,0 +1,206 @@ +package sys + +import ( + "context" + "net/url" + "strings" + + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/os/gctx" + + "hotgo/internal/library/contexts" + "hotgo/internal/library/maoyuadmin" + "hotgo/internal/model/input/sysin" + "hotgo/internal/service" +) + +// 毛隅内容治理:概览、动态/评论审核、举报、操作日志。 +// 与养成内容治理同一套页面协议,业务数据经 maoyu-server 内部接口处理,Manager 不直连毛隅业务库。 + +func init() { service.RegisterSysMaoyuContent(NewSysMaoyuContent()) } + +type sSysMaoyuContent struct{} + +func NewSysMaoyuContent() *sSysMaoyuContent { return &sSysMaoyuContent{} } + +// maoyuAdminScope 从管理员登录态构造毛隅内部接口调用范围。 +func maoyuAdminScope(ctx context.Context) (maoyuadmin.Scope, error) { + tenant, err := resolveImAdminTenant(ctx, cultivateTenantID(ctx)) + if err != nil { + return maoyuadmin.Scope{}, err + } + operatorID := contexts.GetUserId(ctx) + if operatorID <= 0 { + return maoyuadmin.Scope{}, gerror.New("当前管理员身份无效") + } + return maoyuadmin.Scope{ + TenantID: tenant.Id, TenantCode: tenant.Code, OperatorID: operatorID, + RoleKey: contexts.GetRoleKey(ctx), RequestID: gctx.CtxId(ctx), + }, nil +} + +func (s *sSysMaoyuContent) client(ctx context.Context) *maoyuadmin.Client { + return maoyuadmin.DefaultClient(ctx) +} + +func (s *sSysMaoyuContent) Dashboard(ctx context.Context, _ *sysin.MaoyuDashboardInp) (*sysin.MaoyuDashboardModel, error) { + scope, err := maoyuAdminScope(ctx) + if err != nil { + return nil, err + } + result := new(sysin.MaoyuDashboardModel) + if err = s.client(ctx).Get(ctx, scope, "/dashboard", nil, result); err != nil { + return nil, err + } + return result, nil +} + +func (s *sSysMaoyuContent) List(ctx context.Context, in *sysin.CultivateContentListInp) (list []*sysin.CultivateContentListModel, totalCount int, err error) { + if err = in.Filter(ctx); err != nil { + return nil, 0, err + } + scope, err := maoyuAdminScope(ctx) + if err != nil { + return nil, 0, err + } + normalizeCultivatePage(&in.PageReq) + values := cultivatePageValues(in.Page, in.PerPage) + cultivateSetQuery(values, "object_type", in.ObjectType) + cultivateSetQuery(values, "author_id", in.AuthorId) + cultivateSetQuery(values, "status", in.Status) + cultivateSetQuery(values, "keyword", strings.TrimSpace(in.Keyword)) + setTimeRange(values, in.CreatedAt) + var page maoyuadmin.Page[*sysin.CultivateContentListModel] + if err = s.client(ctx).Get(ctx, scope, "/content-reviews", values, &page); err != nil { + return nil, 0, err + } + if page.Items == nil { + page.Items = []*sysin.CultivateContentListModel{} + } + return page.Items, page.Total, nil +} + +func (s *sSysMaoyuContent) View(ctx context.Context, in *sysin.CultivateContentViewInp) (*sysin.CultivateContentViewModel, error) { + if err := in.Filter(ctx); err != nil { + return nil, err + } + scope, err := maoyuAdminScope(ctx) + if err != nil { + return nil, err + } + result := new(sysin.CultivateContentViewModel) + path := "/content-reviews/" + url.PathEscape(in.ObjectType) + "/" + url.PathEscape(in.Id) + if err = s.client(ctx).Get(ctx, scope, path, nil, result); err != nil { + return nil, err + } + return result, nil +} + +func (s *sSysMaoyuContent) Review(ctx context.Context, in *sysin.CultivateContentReviewInp) error { + if err := in.Filter(ctx); err != nil { + return err + } + scope, err := maoyuAdminScope(ctx) + if err != nil { + return err + } + path := "/content-reviews/" + url.PathEscape(in.ObjectType) + "/" + url.PathEscape(in.Id) + "/actions" + return s.client(ctx).Post(ctx, scope, path, map[string]any{"action": in.Action, "reason": strings.TrimSpace(in.Reason)}, nil) +} + +func (s *sSysMaoyuContent) BatchReview(ctx context.Context, in *sysin.CultivateContentBatchReviewInp) (*sysin.CultivateContentBatchReviewModel, error) { + if err := in.Filter(ctx); err != nil { + return nil, err + } + scope, err := maoyuAdminScope(ctx) + if err != nil { + return nil, err + } + result := new(sysin.CultivateContentBatchReviewModel) + err = s.client(ctx).Post(ctx, scope, "/content-reviews/batch-actions", map[string]any{ + "object_type": in.ObjectType, "ids": in.Ids, "action": in.Action, "reason": strings.TrimSpace(in.Reason), + }, result) + if err != nil { + return nil, err + } + return result, nil +} + +func (s *sSysMaoyuContent) ReportList(ctx context.Context, in *sysin.CultivateReportListInp) (list []*sysin.CultivateReportListModel, totalCount int, err error) { + if err = in.Filter(ctx); err != nil { + return nil, 0, err + } + scope, err := maoyuAdminScope(ctx) + if err != nil { + return nil, 0, err + } + normalizeCultivatePage(&in.PageReq) + values := cultivatePageValues(in.Page, in.PerPage) + cultivateSetQuery(values, "object_type", in.ObjectType) + cultivateSetQuery(values, "status", in.Status) + cultivateSetQuery(values, "reporter_id", in.ReporterId) + setTimeRange(values, in.CreatedAt) + var page maoyuadmin.Page[*sysin.CultivateReportListModel] + if err = s.client(ctx).Get(ctx, scope, "/reports", values, &page); err != nil { + return nil, 0, err + } + if page.Items == nil { + page.Items = []*sysin.CultivateReportListModel{} + } + return page.Items, page.Total, nil +} + +func (s *sSysMaoyuContent) ReportView(ctx context.Context, in *sysin.CultivateReportViewInp) (*sysin.CultivateReportViewModel, error) { + if err := in.Filter(ctx); err != nil { + return nil, err + } + scope, err := maoyuAdminScope(ctx) + if err != nil { + return nil, err + } + result := new(sysin.CultivateReportViewModel) + if err = s.client(ctx).Get(ctx, scope, "/reports/"+url.PathEscape(in.Id), nil, result); err != nil { + return nil, err + } + return result, nil +} + +func (s *sSysMaoyuContent) ReportHandle(ctx context.Context, in *sysin.CultivateReportHandleInp) error { + if err := in.Filter(ctx); err != nil { + return err + } + scope, err := maoyuAdminScope(ctx) + if err != nil { + return err + } + return s.client(ctx).Post(ctx, scope, "/reports/"+url.PathEscape(in.Id)+"/actions", map[string]any{ + "action": in.Action, "handle_result": in.HandleResult, "content_action": in.ContentAction, + "user_action": in.UserAction, "reason": in.Reason, + }, nil) +} + +func (s *sSysMaoyuContent) LogList(ctx context.Context, in *sysin.CultivateOperationLogListInp) (list []*sysin.CultivateOperationLogListModel, totalCount int, err error) { + if err = in.Filter(ctx); err != nil { + return nil, 0, err + } + scope, err := maoyuAdminScope(ctx) + if err != nil { + return nil, 0, err + } + normalizeCultivatePage(&in.PageReq) + values := cultivatePageValues(in.Page, in.PerPage) + cultivateSetQuery(values, "operator_id", in.OperatorId) + cultivateSetQuery(values, "operation_type", in.OperationType) + cultivateSetQuery(values, "target_type", in.TargetType) + cultivateSetQuery(values, "target_id", in.TargetId) + cultivateSetQuery(values, "request_id", in.RequestId) + setTimeRange(values, in.CreatedAt) + var page maoyuadmin.Page[*sysin.CultivateOperationLogListModel] + if err = s.client(ctx).Get(ctx, scope, "/operation-logs", values, &page); err != nil { + return nil, 0, err + } + if page.Items == nil { + page.Items = []*sysin.CultivateOperationLogListModel{} + } + return page.Items, page.Total, nil +} diff --git a/server/internal/logic/sys/maoyu_pet.go b/server/internal/logic/sys/maoyu_pet.go new file mode 100644 index 0000000..2366593 --- /dev/null +++ b/server/internal/logic/sys/maoyu_pet.go @@ -0,0 +1,181 @@ +package sys + +import ( + "context" + "net/url" + "strings" + + "hotgo/internal/library/maoyuadmin" + "hotgo/internal/model/input/sysin" + "hotgo/internal/service" +) + +// 毛隅萌宠管理:互动动作配置(新增/编辑/启用停用)、宠物档案(只读)、场景生成任务(只读)、互动通知(只读)。 + +func init() { service.RegisterSysMaoyuPet(NewSysMaoyuPet()) } + +type sSysMaoyuPet struct{} + +func NewSysMaoyuPet() *sSysMaoyuPet { return &sSysMaoyuPet{} } + +func (s *sSysMaoyuPet) ActionConfigList(ctx context.Context, in *sysin.MaoyuActionConfigListInp) (list []*sysin.MaoyuActionConfigListModel, totalCount int, err error) { + if err = in.Filter(ctx); err != nil { + return nil, 0, err + } + scope, err := maoyuAdminScope(ctx) + if err != nil { + return nil, 0, err + } + normalizeCultivatePage(&in.PageReq) + values := cultivatePageValues(in.Page, in.PerPage) + cultivateSetQuery(values, "keyword", in.Keyword) + cultivateSetQuery(values, "status", in.Status) + cultivateSetQuery(values, "version", in.Version) + setTimeRange(values, in.CreatedAt) + var page maoyuadmin.Page[*sysin.MaoyuActionConfigListModel] + if err = maoyuadmin.DefaultClient(ctx).Get(ctx, scope, "/action-configs", values, &page); err != nil { + return nil, 0, err + } + if page.Items == nil { + page.Items = []*sysin.MaoyuActionConfigListModel{} + } + return page.Items, page.Total, nil +} + +func (s *sSysMaoyuPet) ActionConfigView(ctx context.Context, in *sysin.MaoyuActionConfigViewInp) (*sysin.MaoyuActionConfigViewModel, error) { + scope, err := maoyuAdminScope(ctx) + if err != nil { + return nil, err + } + result := new(sysin.MaoyuActionConfigViewModel) + if err = maoyuadmin.DefaultClient(ctx).Get(ctx, scope, "/action-configs/"+url.PathEscape(in.Id), nil, result); err != nil { + return nil, err + } + return result, nil +} + +func (s *sSysMaoyuPet) ActionConfigEdit(ctx context.Context, in *sysin.MaoyuActionConfigEditInp) error { + scope, err := maoyuAdminScope(ctx) + if err != nil { + return err + } + body := map[string]any{ + "action_name": in.ActionName, "daily_limit": in.DailyLimit, "cooldown_seconds": in.CooldownSeconds, + "mutex_group": in.MutexGroup, "asset_key": in.AssetKey, "effect_config": in.EffectConfig, "sort_no": in.SortNo, + } + if id := strings.TrimSpace(in.Id); id != "" { + return maoyuadmin.DefaultClient(ctx).Put(ctx, scope, "/action-configs/"+url.PathEscape(id), body, nil) + } + body["action_code"] = in.ActionCode + return maoyuadmin.DefaultClient(ctx).Post(ctx, scope, "/action-configs", body, nil) +} + +func (s *sSysMaoyuPet) ActionConfigStatus(ctx context.Context, in *sysin.MaoyuActionConfigStatusInp) error { + scope, err := maoyuAdminScope(ctx) + if err != nil { + return err + } + return maoyuadmin.DefaultClient(ctx).Post(ctx, scope, "/action-configs/"+url.PathEscape(in.Id)+"/status", map[string]any{ + "reason": strings.TrimSpace(in.Reason), + }, nil) +} + +func (s *sSysMaoyuPet) PetList(ctx context.Context, in *sysin.MaoyuPetListInp) (list []*sysin.MaoyuPetListModel, totalCount int, err error) { + if err = in.Filter(ctx); err != nil { + return nil, 0, err + } + scope, err := maoyuAdminScope(ctx) + if err != nil { + return nil, 0, err + } + normalizeCultivatePage(&in.PageReq) + values := cultivatePageValues(in.Page, in.PerPage) + cultivateSetQuery(values, "user_id", in.UserId) + cultivateSetQuery(values, "pet_type", in.PetType) + cultivateSetQuery(values, "status", in.Status) + cultivateSetQuery(values, "keyword", in.Keyword) + setTimeRange(values, in.CreatedAt) + var page maoyuadmin.Page[*sysin.MaoyuPetListModel] + if err = maoyuadmin.DefaultClient(ctx).Get(ctx, scope, "/pets", values, &page); err != nil { + return nil, 0, err + } + if page.Items == nil { + page.Items = []*sysin.MaoyuPetListModel{} + } + return page.Items, page.Total, nil +} + +func (s *sSysMaoyuPet) PetView(ctx context.Context, in *sysin.MaoyuPetViewInp) (*sysin.MaoyuPetViewModel, error) { + scope, err := maoyuAdminScope(ctx) + if err != nil { + return nil, err + } + result := new(sysin.MaoyuPetViewModel) + if err = maoyuadmin.DefaultClient(ctx).Get(ctx, scope, "/pets/"+url.PathEscape(in.Id), nil, result); err != nil { + return nil, err + } + return result, nil +} + +func (s *sSysMaoyuPet) JobList(ctx context.Context, in *sysin.MaoyuJobListInp) (list []*sysin.MaoyuJobListModel, totalCount int, err error) { + if err = in.Filter(ctx); err != nil { + return nil, 0, err + } + scope, err := maoyuAdminScope(ctx) + if err != nil { + return nil, 0, err + } + normalizeCultivatePage(&in.PageReq) + values := cultivatePageValues(in.Page, in.PerPage) + cultivateSetQuery(values, "pet_id", in.PetId) + cultivateSetQuery(values, "user_id", in.UserId) + cultivateSetQuery(values, "status", in.Status) + cultivateSetQuery(values, "error_code", in.ErrorCode) + setTimeRange(values, in.CreatedAt) + var page maoyuadmin.Page[*sysin.MaoyuJobListModel] + if err = maoyuadmin.DefaultClient(ctx).Get(ctx, scope, "/generation-jobs", values, &page); err != nil { + return nil, 0, err + } + if page.Items == nil { + page.Items = []*sysin.MaoyuJobListModel{} + } + return page.Items, page.Total, nil +} + +func (s *sSysMaoyuPet) JobView(ctx context.Context, in *sysin.MaoyuJobViewInp) (*sysin.MaoyuJobViewModel, error) { + scope, err := maoyuAdminScope(ctx) + if err != nil { + return nil, err + } + result := new(sysin.MaoyuJobViewModel) + if err = maoyuadmin.DefaultClient(ctx).Get(ctx, scope, "/generation-jobs/"+url.PathEscape(in.Id), nil, result); err != nil { + return nil, err + } + return result, nil +} + +func (s *sSysMaoyuPet) NoticeList(ctx context.Context, in *sysin.MaoyuNoticeListInp) (list []*sysin.MaoyuNoticeListModel, totalCount int, err error) { + if err = in.Filter(ctx); err != nil { + return nil, 0, err + } + scope, err := maoyuAdminScope(ctx) + if err != nil { + return nil, 0, err + } + normalizeCultivatePage(&in.PageReq) + values := cultivatePageValues(in.Page, in.PerPage) + cultivateSetQuery(values, "recipient_id", in.RecipientId) + cultivateSetQuery(values, "actor_id", in.ActorId) + cultivateSetQuery(values, "event_type", in.EventType) + cultivateSetQuery(values, "status", in.Status) + cultivateSetQuery(values, "post_id", in.PostId) + setTimeRange(values, in.CreatedAt) + var page maoyuadmin.Page[*sysin.MaoyuNoticeListModel] + if err = maoyuadmin.DefaultClient(ctx).Get(ctx, scope, "/interaction-notices", values, &page); err != nil { + return nil, 0, err + } + if page.Items == nil { + page.Items = []*sysin.MaoyuNoticeListModel{} + } + return page.Items, page.Total, nil +} diff --git a/server/internal/model/input/sysin/maoyu.go b/server/internal/model/input/sysin/maoyu.go new file mode 100644 index 0000000..7598373 --- /dev/null +++ b/server/internal/model/input/sysin/maoyu.go @@ -0,0 +1,209 @@ +package sysin + +import ( + "context" + "strings" + + "github.com/gogf/gf/v2/os/gtime" + + "hotgo/internal/model/input/form" +) + +// 毛隅管理页面输入输出:概览、互动动作配置、宠物档案、场景生成任务、互动通知。 +// 内容审核、举报、操作日志协议与养成一致,直接复用 Cultivate* 输入输出,仅路由前缀不同。 + +type MaoyuDashboardInp struct{} +type MaoyuDashboardModel struct { + PetCount int64 `json:"petCount"` + ActivePetCount int64 `json:"activePetCount"` + BuildingPetCount int64 `json:"buildingPetCount"` + JobRunningCount int64 `json:"jobRunningCount"` + JobSucceededCount int64 `json:"jobSucceededCount"` + JobFailedCount int64 `json:"jobFailedCount"` + NoticeCount int64 `json:"noticeCount"` + UnreadNoticeCount int64 `json:"unreadNoticeCount"` + PostCount int64 `json:"postCount"` + PublicPostCount int64 `json:"publicPostCount"` + FriendsPostCount int64 `json:"friendsPostCount"` + PrivatePostCount int64 `json:"privatePostCount"` + CommentCount int64 `json:"commentCount"` + LikeCount int64 `json:"likeCount"` + TakenDownCount int64 `json:"takenDownCount"` + PendingAuditCount int64 `json:"pendingAuditCount"` + ApprovedAuditCount int64 `json:"approvedAuditCount"` + RejectedAuditCount int64 `json:"rejectedAuditCount"` + PendingReportCount int64 `json:"pendingReportCount"` + ProcessingCount int64 `json:"processingReportCount"` + ProcessedCount int64 `json:"processedReportCount"` +} + +type MaoyuActionConfigListInp struct { + form.PageReq + Keyword string `json:"keyword"` + Status string `json:"status" v:"in:,enabled,disabled#动作状态不正确"` + Version string `json:"version"` + CreatedAt []*gtime.Time `json:"createdAt"` +} + +func (in *MaoyuActionConfigListInp) Filter(ctx context.Context) (err error) { + if in.CreatedAt != nil && len(in.CreatedAt) == 2 && !in.CreatedAt[0].IsZero() && !in.CreatedAt[1].IsZero() { + in.CreatedAt = []*gtime.Time{in.CreatedAt[0].StartOfDay(), in.CreatedAt[1].EndOfDay()} + } else { + in.CreatedAt = nil + } + in.Keyword = strings.TrimSpace(in.Keyword) + return nil +} + +type MaoyuActionConfigListModel struct { + Id string `json:"id"` + ActionCode string `json:"actionCode"` + ActionName string `json:"actionName"` + Enabled bool `json:"enabled"` + DailyLimit int `json:"dailyLimit"` + CooldownSeconds int `json:"cooldownSeconds"` + MutexGroup string `json:"mutexGroup"` + AssetKey string `json:"assetKey"` + EffectConfig map[string]any `json:"effectConfig"` + ConfigVersion string `json:"configVersion"` + SortNo int `json:"sortNo"` + CreatedAt string `json:"createdAt"` + UpdatedAt string `json:"updatedAt"` +} + +type MaoyuActionConfigViewInp struct { + Id string `json:"id" v:"required#动作配置ID不能为空"` +} +type MaoyuActionConfigViewModel struct{ MaoyuActionConfigListModel } + +type MaoyuActionConfigEditInp struct { + Id string `json:"id"` + ActionCode string `json:"actionCode" v:"required|max-length:24|regex:^[a-z][a-z0-9_]*$#动作编码不能为空|动作编码过长|动作编码仅支持小写字母、数字和下划线"` + ActionName string `json:"actionName" v:"required|max-length:64#动作名称不能为空|动作名称过长"` + DailyLimit int `json:"dailyLimit" v:"min:0#每日次数不能为负"` + CooldownSeconds int `json:"cooldownSeconds" v:"min:0#冷却时间不能为负"` + MutexGroup string `json:"mutexGroup" v:"max-length:32#互斥组过长"` + AssetKey string `json:"assetKey" v:"required|max-length:64#素材键不能为空|素材键过长"` + EffectConfig map[string]any `json:"effectConfig"` + SortNo int `json:"sortNo"` +} +type MaoyuActionConfigStatusInp struct { + Id string `json:"id" v:"required#动作配置ID不能为空"` + Reason string `json:"reason"` +} + +type MaoyuPetListInp struct { + form.PageReq + UserId string `json:"userId"` + PetType string `json:"petType"` + Status string `json:"status" v:"in:,creating,active,deleted#宠物状态不正确"` + Keyword string `json:"keyword"` + CreatedAt []*gtime.Time `json:"createdAt"` +} + +func (in *MaoyuPetListInp) Filter(ctx context.Context) (err error) { + if in.CreatedAt != nil && len(in.CreatedAt) == 2 && !in.CreatedAt[0].IsZero() && !in.CreatedAt[1].IsZero() { + in.CreatedAt = []*gtime.Time{in.CreatedAt[0].StartOfDay(), in.CreatedAt[1].EndOfDay()} + } else { + in.CreatedAt = nil + } + in.Keyword = strings.TrimSpace(in.Keyword) + return nil +} + +type MaoyuPetListModel struct { + Id string `json:"id"` + UserId string `json:"userId"` + PetType string `json:"petType"` + CustomTypeName string `json:"customTypeName"` + PetName string `json:"petName"` + BirthDate string `json:"birthDate"` + AgeEstimated bool `json:"ageEstimated"` + WeightValue string `json:"weightValue"` + WeightUnit string `json:"weightUnit"` + AvatarFileId string `json:"avatarFileId"` + Status string `json:"status"` + CurrentSceneId string `json:"currentSceneId"` + Version uint `json:"version"` + CreatedAt string `json:"createdAt"` + UpdatedAt string `json:"updatedAt"` +} +type MaoyuPetViewInp struct { + Id string `json:"id" v:"required#宠物ID不能为空"` +} +type MaoyuPetViewModel struct{ MaoyuPetListModel } + +type MaoyuJobListInp struct { + form.PageReq + PetId string `json:"petId"` + UserId string `json:"userId"` + Status string `json:"status" v:"in:,queued,prechecking,generating,reviewing,succeeded,failed,canceled#任务状态不正确"` + ErrorCode string `json:"errorCode"` + CreatedAt []*gtime.Time `json:"createdAt"` +} + +func (in *MaoyuJobListInp) Filter(ctx context.Context) (err error) { + if in.CreatedAt != nil && len(in.CreatedAt) == 2 && !in.CreatedAt[0].IsZero() && !in.CreatedAt[1].IsZero() { + in.CreatedAt = []*gtime.Time{in.CreatedAt[0].StartOfDay(), in.CreatedAt[1].EndOfDay()} + } else { + in.CreatedAt = nil + } + return nil +} + +type MaoyuJobListModel struct { + Id string `json:"id"` + UserId string `json:"userId"` + PetId string `json:"petId"` + Status string `json:"status"` + Stage string `json:"stage"` + Progress int `json:"progress"` + ProgressMode string `json:"progressMode"` + InputFileId string `json:"inputFileId"` + ResultSceneId string `json:"resultSceneId"` + ErrorCode string `json:"errorCode"` + ErrorMessage string `json:"errorMessage"` + Provider string `json:"provider"` + StartedAt string `json:"startedAt"` + FinishedAt string `json:"finishedAt"` + CreatedAt string `json:"createdAt"` + UpdatedAt string `json:"updatedAt"` +} +type MaoyuJobViewInp struct { + Id string `json:"id" v:"required#任务ID不能为空"` +} +type MaoyuJobViewModel struct{ MaoyuJobListModel } + +type MaoyuNoticeListInp struct { + form.PageReq + RecipientId string `json:"recipientId"` + ActorId string `json:"actorId"` + EventType string `json:"eventType" v:"in:,post_liked,comment_liked,post_commented#通知类型不正确"` + Status string `json:"status" v:"in:,unread,read#通知状态不正确"` + PostId string `json:"postId"` + CreatedAt []*gtime.Time `json:"createdAt"` +} + +func (in *MaoyuNoticeListInp) Filter(ctx context.Context) (err error) { + if in.CreatedAt != nil && len(in.CreatedAt) == 2 && !in.CreatedAt[0].IsZero() && !in.CreatedAt[1].IsZero() { + in.CreatedAt = []*gtime.Time{in.CreatedAt[0].StartOfDay(), in.CreatedAt[1].EndOfDay()} + } else { + in.CreatedAt = nil + } + return nil +} + +type MaoyuNoticeListModel struct { + Id string `json:"id"` + RecipientId string `json:"recipientId"` + ActorId string `json:"actorId"` + EventType string `json:"eventType"` + EventKey string `json:"eventKey"` + PostId string `json:"postId"` + CommentId string `json:"commentId"` + TargetFileId string `json:"targetFileId"` + Status string `json:"status"` + OccurredAt string `json:"occurredAt"` + ReadAt string `json:"readAt"` + CreatedAt string `json:"createdAt"` +} diff --git a/server/internal/router/genrouter/maoyu.go b/server/internal/router/genrouter/maoyu.go new file mode 100644 index 0000000..fc13f58 --- /dev/null +++ b/server/internal/router/genrouter/maoyu.go @@ -0,0 +1,7 @@ +package genrouter + +import "hotgo/internal/controller/admin/sys" + +func init() { + LoginRequiredRouter = append(LoginRequiredRouter, sys.MaoyuContent, sys.MaoyuPet) +} diff --git a/server/internal/service/sys.go b/server/internal/service/sys.go index 7eae51b..d3ae4fa 100644 --- a/server/internal/service/sys.go +++ b/server/internal/service/sys.go @@ -189,6 +189,28 @@ type ( ReportHandle(ctx context.Context, in *sysin.CultivateReportHandleInp) error LogList(ctx context.Context, in *sysin.CultivateOperationLogListInp) (list []*sysin.CultivateOperationLogListModel, totalCount int, err error) } + ISysMaoyuContent interface { + Dashboard(ctx context.Context, in *sysin.MaoyuDashboardInp) (*sysin.MaoyuDashboardModel, error) + List(ctx context.Context, in *sysin.CultivateContentListInp) (list []*sysin.CultivateContentListModel, totalCount int, err error) + View(ctx context.Context, in *sysin.CultivateContentViewInp) (*sysin.CultivateContentViewModel, error) + Review(ctx context.Context, in *sysin.CultivateContentReviewInp) error + BatchReview(ctx context.Context, in *sysin.CultivateContentBatchReviewInp) (*sysin.CultivateContentBatchReviewModel, error) + ReportList(ctx context.Context, in *sysin.CultivateReportListInp) (list []*sysin.CultivateReportListModel, totalCount int, err error) + ReportView(ctx context.Context, in *sysin.CultivateReportViewInp) (*sysin.CultivateReportViewModel, error) + ReportHandle(ctx context.Context, in *sysin.CultivateReportHandleInp) error + LogList(ctx context.Context, in *sysin.CultivateOperationLogListInp) (list []*sysin.CultivateOperationLogListModel, totalCount int, err error) + } + ISysMaoyuPet interface { + ActionConfigList(ctx context.Context, in *sysin.MaoyuActionConfigListInp) (list []*sysin.MaoyuActionConfigListModel, totalCount int, err error) + ActionConfigView(ctx context.Context, in *sysin.MaoyuActionConfigViewInp) (*sysin.MaoyuActionConfigViewModel, error) + ActionConfigEdit(ctx context.Context, in *sysin.MaoyuActionConfigEditInp) error + ActionConfigStatus(ctx context.Context, in *sysin.MaoyuActionConfigStatusInp) error + PetList(ctx context.Context, in *sysin.MaoyuPetListInp) (list []*sysin.MaoyuPetListModel, totalCount int, err error) + PetView(ctx context.Context, in *sysin.MaoyuPetViewInp) (*sysin.MaoyuPetViewModel, error) + JobList(ctx context.Context, in *sysin.MaoyuJobListInp) (list []*sysin.MaoyuJobListModel, totalCount int, err error) + JobView(ctx context.Context, in *sysin.MaoyuJobViewInp) (*sysin.MaoyuJobViewModel, error) + NoticeList(ctx context.Context, in *sysin.MaoyuNoticeListInp) (list []*sysin.MaoyuNoticeListModel, totalCount int, err error) + } ISysImTenant interface { // Model IM租户ORM模型 Model(ctx context.Context, option ...*handler.Option) *gdb.Model @@ -967,6 +989,28 @@ func SysCultivateContent() ISysCultivateContent { func RegisterSysCultivateContent(i ISysCultivateContent) { localSysCultivateContent = i } +var localSysMaoyuContent ISysMaoyuContent + +func SysMaoyuContent() ISysMaoyuContent { + if localSysMaoyuContent == nil { + panic("implement not found for interface ISysMaoyuContent, forgot register?") + } + return localSysMaoyuContent +} + +func RegisterSysMaoyuContent(i ISysMaoyuContent) { localSysMaoyuContent = i } + +var localSysMaoyuPet ISysMaoyuPet + +func SysMaoyuPet() ISysMaoyuPet { + if localSysMaoyuPet == nil { + panic("implement not found for interface ISysMaoyuPet, forgot register?") + } + return localSysMaoyuPet +} + +func RegisterSysMaoyuPet(i ISysMaoyuPet) { localSysMaoyuPet = i } + func SysImTenant() ISysImTenant { if localSysImTenant == nil { panic("implement not found for interface ISysImTenant, forgot register?") diff --git a/server/storage/data/generate/maoyu_menu.sql b/server/storage/data/generate/maoyu_menu.sql new file mode 100644 index 0000000..d4e5d53 --- /dev/null +++ b/server/storage/data/generate/maoyu_menu.sql @@ -0,0 +1,109 @@ +-- 毛隅管理菜单与页面权限(可重复执行) +-- 对应 im_manager 页面接口 /maoyu/*,业务经 maoyu-server 内部接口 /internal/admin/maoyu/tenants/{tenant_code} 处理。 +-- 前端组件路径约定:web/src/views/maoyu/**(需前端按菜单路径放置页面组件)。 +SET NAMES utf8mb4; +SET @now := NOW(); + +-- 顶级目录 +INSERT INTO hg_admin_menu (pid,level,tree,title,name,path,icon,type,redirect,permissions,permission_name,component,always_show,active_menu,is_root,is_frame,frame_src,keep_alive,hidden,affix,sort,remark,status,created_at,updated_at) +SELECT 0,1,'','毛隅管理','maoyuManager','/maoyu','SmileOutlined',1,'/maoyu/dashboard','','','LAYOUT',1,'',0,0,'',0,0,0,126,'毛隅手记萌宠与爱宠社区管理',1,@now,@now +WHERE NOT EXISTS (SELECT 1 FROM hg_admin_menu WHERE name='maoyuManager'); +SET @maoyuId := (SELECT id FROM hg_admin_menu WHERE name='maoyuManager' LIMIT 1); +UPDATE hg_admin_menu SET redirect='/maoyu/dashboard', updated_at=@now WHERE id=@maoyuId; + +-- 数据概览 +INSERT INTO hg_admin_menu (pid,level,tree,title,name,path,icon,type,redirect,permissions,permission_name,component,always_show,active_menu,is_root,is_frame,frame_src,keep_alive,hidden,affix,sort,remark,status,created_at,updated_at) +SELECT @maoyuId,2,CONCAT('tr_',@maoyuId,' '),'数据概览','maoyuDashboard','dashboard','',2,'','/maoyu/dashboard','','/maoyu/dashboard/index',1,'maoyuDashboard',0,0,'',1,0,0,70,'萌宠、场景任务与内容治理核心指标',1,@now,@now +WHERE NOT EXISTS (SELECT 1 FROM hg_admin_menu WHERE name='maoyuDashboard'); + +-- 宠物档案(只读) +INSERT INTO hg_admin_menu (pid,level,tree,title,name,path,icon,type,redirect,permissions,permission_name,component,always_show,active_menu,is_root,is_frame,frame_src,keep_alive,hidden,affix,sort,remark,status,created_at,updated_at) +SELECT @maoyuId,2,CONCAT('tr_',@maoyuId,' '),'宠物档案','maoyuPets','pets','',2,'','/maoyu/pets/list','','/maoyu/pets/index',1,'maoyuPets',0,0,'',1,0,0,60,'宠物档案查询与详情(只读)',1,@now,@now +WHERE NOT EXISTS (SELECT 1 FROM hg_admin_menu WHERE name='maoyuPets'); +SET @petsId := (SELECT id FROM hg_admin_menu WHERE name='maoyuPets' LIMIT 1); + +INSERT INTO hg_admin_menu (pid,level,tree,title,name,path,icon,type,redirect,permissions,permission_name,component,always_show,active_menu,is_root,is_frame,frame_src,keep_alive,hidden,affix,sort,remark,status,created_at,updated_at) +SELECT @petsId,3,CONCAT('tr_',@maoyuId,' tr_',@petsId,' '),'查看宠物详情','maoyuPetsView','','',3,'','/maoyu/pets/view','','',1,'',0,0,'',0,1,0,30,'',1,@now,@now +WHERE NOT EXISTS (SELECT 1 FROM hg_admin_menu WHERE name='maoyuPetsView'); + +-- 场景生成任务(只读) +INSERT INTO hg_admin_menu (pid,level,tree,title,name,path,icon,type,redirect,permissions,permission_name,component,always_show,active_menu,is_root,is_frame,frame_src,keep_alive,hidden,affix,sort,remark,status,created_at,updated_at) +SELECT @maoyuId,2,CONCAT('tr_',@maoyuId,' '),'场景任务','maoyuJobs','jobs','',2,'','/maoyu/generation-jobs/list','','/maoyu/jobs/index',1,'maoyuJobs',0,0,'',1,0,0,50,'场景生成任务进度与失败排查(只读)',1,@now,@now +WHERE NOT EXISTS (SELECT 1 FROM hg_admin_menu WHERE name='maoyuJobs'); +SET @jobsId := (SELECT id FROM hg_admin_menu WHERE name='maoyuJobs' LIMIT 1); + +INSERT INTO hg_admin_menu (pid,level,tree,title,name,path,icon,type,redirect,permissions,permission_name,component,always_show,active_menu,is_root,is_frame,frame_src,keep_alive,hidden,affix,sort,remark,status,created_at,updated_at) +SELECT @jobsId,3,CONCAT('tr_',@maoyuId,' tr_',@jobsId,' '),'查看任务详情','maoyuJobsView','','',3,'','/maoyu/generation-jobs/view','','',1,'',0,0,'',0,1,0,30,'',1,@now,@now +WHERE NOT EXISTS (SELECT 1 FROM hg_admin_menu WHERE name='maoyuJobsView'); + +-- 动作配置 +INSERT INTO hg_admin_menu (pid,level,tree,title,name,path,icon,type,redirect,permissions,permission_name,component,always_show,active_menu,is_root,is_frame,frame_src,keep_alive,hidden,affix,sort,remark,status,created_at,updated_at) +SELECT @maoyuId,2,CONCAT('tr_',@maoyuId,' '),'动作配置','maoyuActionConfigs','action-configs','',2,'','/maoyu/action-configs/list','','/maoyu/action-configs/index',1,'maoyuActionConfigs',0,0,'',1,0,0,40,'玩球/梳毛/喂食/洗澡等互动动作玩法配置',1,@now,@now +WHERE NOT EXISTS (SELECT 1 FROM hg_admin_menu WHERE name='maoyuActionConfigs'); +SET @actionsId := (SELECT id FROM hg_admin_menu WHERE name='maoyuActionConfigs' LIMIT 1); + +INSERT INTO hg_admin_menu (pid,level,tree,title,name,path,icon,type,redirect,permissions,permission_name,component,always_show,active_menu,is_root,is_frame,frame_src,keep_alive,hidden,affix,sort,remark,status,created_at,updated_at) +SELECT @actionsId,3,CONCAT('tr_',@maoyuId,' tr_',@actionsId,' '),'查看动作配置','maoyuActionConfigsView','','',3,'','/maoyu/action-configs/view','','',1,'',0,0,'',0,1,0,30,'',1,@now,@now +WHERE NOT EXISTS (SELECT 1 FROM hg_admin_menu WHERE name='maoyuActionConfigsView'); + +INSERT INTO hg_admin_menu (pid,level,tree,title,name,path,icon,type,redirect,permissions,permission_name,component,always_show,active_menu,is_root,is_frame,frame_src,keep_alive,hidden,affix,sort,remark,status,created_at,updated_at) +SELECT @actionsId,3,CONCAT('tr_',@maoyuId,' tr_',@actionsId,' '),'编辑动作配置','maoyuActionConfigsEdit','','',3,'','/maoyu/action-configs/edit','','',1,'',0,0,'',0,1,0,20,'',1,@now,@now +WHERE NOT EXISTS (SELECT 1 FROM hg_admin_menu WHERE name='maoyuActionConfigsEdit'); + +INSERT INTO hg_admin_menu (pid,level,tree,title,name,path,icon,type,redirect,permissions,permission_name,component,always_show,active_menu,is_root,is_frame,frame_src,keep_alive,hidden,affix,sort,remark,status,created_at,updated_at) +SELECT @actionsId,3,CONCAT('tr_',@maoyuId,' tr_',@actionsId,' '),'启停动作配置','maoyuActionConfigsStatus','','',3,'','/maoyu/action-configs/status','','',1,'',0,0,'',0,1,0,10,'',1,@now,@now +WHERE NOT EXISTS (SELECT 1 FROM hg_admin_menu WHERE name='maoyuActionConfigsStatus'); + +-- 互动通知(只读) +INSERT INTO hg_admin_menu (pid,level,tree,title,name,path,icon,type,redirect,permissions,permission_name,component,always_show,active_menu,is_root,is_frame,frame_src,keep_alive,hidden,affix,sort,remark,status,created_at,updated_at) +SELECT @maoyuId,2,CONCAT('tr_',@maoyuId,' '),'互动通知','maoyuNotices','notices','',2,'','/maoyu/notices/list','','/maoyu/notices/index',1,'maoyuNotices',0,0,'',1,0,0,30,'点赞评论互动通知查询(只读)',1,@now,@now +WHERE NOT EXISTS (SELECT 1 FROM hg_admin_menu WHERE name='maoyuNotices'); + +-- 内容审核 +INSERT INTO hg_admin_menu (pid,level,tree,title,name,path,icon,type,redirect,permissions,permission_name,component,always_show,active_menu,is_root,is_frame,frame_src,keep_alive,hidden,affix,sort,remark,status,created_at,updated_at) +SELECT @maoyuId,2,CONCAT('tr_',@maoyuId,' '),'内容审核','maoyuContent','content','',2,'','/maoyu/content/list','','/maoyu/content/index',1,'maoyuContent',0,0,'',1,0,0,20,'爱宠动态与评论审核管理',1,@now,@now +WHERE NOT EXISTS (SELECT 1 FROM hg_admin_menu WHERE name='maoyuContent'); +SET @contentId := (SELECT id FROM hg_admin_menu WHERE name='maoyuContent' LIMIT 1); + +INSERT INTO hg_admin_menu (pid,level,tree,title,name,path,icon,type,redirect,permissions,permission_name,component,always_show,active_menu,is_root,is_frame,frame_src,keep_alive,hidden,affix,sort,remark,status,created_at,updated_at) +SELECT @contentId,3,CONCAT('tr_',@maoyuId,' tr_',@contentId,' '),'查看内容详情','maoyuContentView','','',3,'','/maoyu/content/view','','',1,'',0,0,'',0,1,0,30,'',1,@now,@now +WHERE NOT EXISTS (SELECT 1 FROM hg_admin_menu WHERE name='maoyuContentView'); + +INSERT INTO hg_admin_menu (pid,level,tree,title,name,path,icon,type,redirect,permissions,permission_name,component,always_show,active_menu,is_root,is_frame,frame_src,keep_alive,hidden,affix,sort,remark,status,created_at,updated_at) +SELECT @contentId,3,CONCAT('tr_',@maoyuId,' tr_',@contentId,' '),'审核内容','maoyuContentReview','','',3,'','/maoyu/content/review','','',1,'',0,0,'',0,1,0,20,'',1,@now,@now +WHERE NOT EXISTS (SELECT 1 FROM hg_admin_menu WHERE name='maoyuContentReview'); + +INSERT INTO hg_admin_menu (pid,level,tree,title,name,path,icon,type,redirect,permissions,permission_name,component,always_show,active_menu,is_root,is_frame,frame_src,keep_alive,hidden,affix,sort,remark,status,created_at,updated_at) +SELECT @contentId,3,CONCAT('tr_',@maoyuId,' tr_',@contentId,' '),'批量审核内容','maoyuContentBatchReview','','',3,'','/maoyu/content/batch-review','','',1,'',0,0,'',0,1,0,10,'',1,@now,@now +WHERE NOT EXISTS (SELECT 1 FROM hg_admin_menu WHERE name='maoyuContentBatchReview'); + +-- 举报处理 +INSERT INTO hg_admin_menu (pid,level,tree,title,name,path,icon,type,redirect,permissions,permission_name,component,always_show,active_menu,is_root,is_frame,frame_src,keep_alive,hidden,affix,sort,remark,status,created_at,updated_at) +SELECT @maoyuId,2,CONCAT('tr_',@maoyuId,' '),'举报处理','maoyuReports','reports','',2,'','/maoyu/reports/list','','/maoyu/reports/index',1,'maoyuReports',0,0,'',1,0,0,10,'爱宠内容与用户举报处理',1,@now,@now +WHERE NOT EXISTS (SELECT 1 FROM hg_admin_menu WHERE name='maoyuReports'); +SET @reportsId := (SELECT id FROM hg_admin_menu WHERE name='maoyuReports' LIMIT 1); + +INSERT INTO hg_admin_menu (pid,level,tree,title,name,path,icon,type,redirect,permissions,permission_name,component,always_show,active_menu,is_root,is_frame,frame_src,keep_alive,hidden,affix,sort,remark,status,created_at,updated_at) +SELECT @reportsId,3,CONCAT('tr_',@maoyuId,' tr_',@reportsId,' '),'查看举报详情','maoyuReportsView','','',3,'','/maoyu/reports/view','','',1,'',0,0,'',0,1,0,30,'',1,@now,@now +WHERE NOT EXISTS (SELECT 1 FROM hg_admin_menu WHERE name='maoyuReportsView'); + +INSERT INTO hg_admin_menu (pid,level,tree,title,name,path,icon,type,redirect,permissions,permission_name,component,always_show,active_menu,is_root,is_frame,frame_src,keep_alive,hidden,affix,sort,remark,status,created_at,updated_at) +SELECT @reportsId,3,CONCAT('tr_',@maoyuId,' tr_',@reportsId,' '),'处理举报','maoyuReportsHandle','','',3,'','/maoyu/reports/handle','','',1,'',0,0,'',0,1,0,20,'',1,@now,@now +WHERE NOT EXISTS (SELECT 1 FROM hg_admin_menu WHERE name='maoyuReportsHandle'); + +-- 操作日志 +INSERT INTO hg_admin_menu (pid,level,tree,title,name,path,icon,type,redirect,permissions,permission_name,component,always_show,active_menu,is_root,is_frame,frame_src,keep_alive,hidden,affix,sort,remark,status,created_at,updated_at) +SELECT @maoyuId,2,CONCAT('tr_',@maoyuId,' '),'操作日志','maoyuLogs','logs','',2,'','/maoyu/logs/list','','/maoyu/logs/index',1,'maoyuLogs',0,0,'',1,0,0,5,'毛隅管理操作审计日志',1,@now,@now +WHERE NOT EXISTS (SELECT 1 FROM hg_admin_menu WHERE name='maoyuLogs'); + +-- ===== 角色授权(hg_admin_role_menu)===== +-- 说明:超级管理员(hotgo 内置超管)无需记录即可访问全部菜单;以下将毛隅全部菜单授予指定角色。 +-- 使用方式:将 @roleId 替换为目标角色 ID 后执行(默认 2,可按需复制多份)。 +-- SET @roleId := 2; +-- INSERT INTO hg_admin_role_menu (role_id, menu_id) +-- SELECT @roleId, m.id FROM hg_admin_menu m +-- WHERE m.id = @maoyuId +-- OR m.pid = @maoyuId +-- OR m.pid IN (SELECT id FROM (SELECT id FROM hg_admin_menu WHERE pid = @maoyuId) t) +-- OR m.tree LIKE CONCAT('tr_', @maoyuId, ' %') +-- ON DUPLICATE KEY UPDATE menu_id = menu_id;