diff --git a/server/api/admin/cultivatecontent/cultivate_content.go b/server/api/admin/cultivatecontent/cultivate_content.go
new file mode 100644
index 0000000..d0cfae7
--- /dev/null
+++ b/server/api/admin/cultivatecontent/cultivate_content.go
@@ -0,0 +1,42 @@
+package cultivatecontent
+
+import (
+ "github.com/gogf/gf/v2/frame/g"
+ "hotgo/internal/model/input/form"
+ "hotgo/internal/model/input/sysin"
+)
+
+type ListReq struct {
+ g.Meta `path:"/cultivate/content/list" method:"get" tags:"养成管理" summary:"获取动态或评论审核列表"`
+ sysin.CultivateContentListInp
+}
+type ListRes struct {
+ form.PageRes
+ List []*sysin.CultivateContentListModel `json:"list"`
+}
+type ReviewReq struct {
+ g.Meta `path:"/cultivate/content/review" method:"post" tags:"养成管理" summary:"审核或下架动态评论"`
+ sysin.CultivateContentReviewInp
+}
+type ReviewRes struct{}
+type ReportListReq struct {
+ g.Meta `path:"/cultivate/reports/list" method:"get" tags:"养成管理" summary:"获取举报列表"`
+ sysin.CultivateReportListInp
+}
+type ReportListRes struct {
+ form.PageRes
+ List []*sysin.CultivateReportListModel `json:"list"`
+}
+type ReportHandleReq struct {
+ g.Meta `path:"/cultivate/reports/handle" method:"post" tags:"养成管理" summary:"处理举报"`
+ sysin.CultivateReportHandleInp
+}
+type ReportHandleRes struct{}
+type LogListReq struct {
+ g.Meta `path:"/cultivate/logs/list" method:"get" tags:"养成管理" summary:"获取养成管理操作日志"`
+ sysin.CultivateOperationLogListInp
+}
+type LogListRes struct {
+ form.PageRes
+ List []*sysin.CultivateOperationLogListModel `json:"list"`
+}
diff --git a/server/api/admin/cultivateflower/cultivate_flower.go b/server/api/admin/cultivateflower/cultivate_flower.go
new file mode 100644
index 0000000..9a9d21c
--- /dev/null
+++ b/server/api/admin/cultivateflower/cultivate_flower.go
@@ -0,0 +1,33 @@
+package cultivateflower
+
+import (
+ "github.com/gogf/gf/v2/frame/g"
+ "hotgo/internal/model/input/form"
+ "hotgo/internal/model/input/sysin"
+)
+
+type ListReq struct {
+ g.Meta `path:"/cultivate/flowers/list" method:"get" tags:"养成管理" summary:"获取花种列表"`
+ sysin.CultivateFlowerListInp
+}
+type ListRes struct {
+ form.PageRes
+ List []*sysin.CultivateFlowerListModel `json:"list"`
+}
+type ViewReq struct {
+ g.Meta `path:"/cultivate/flowers/view" method:"get" tags:"养成管理" summary:"获取花种详情"`
+ sysin.CultivateFlowerViewInp
+}
+type ViewRes struct {
+ *sysin.CultivateFlowerViewModel
+}
+type EditReq struct {
+ g.Meta `path:"/cultivate/flowers/edit" method:"post" tags:"养成管理" summary:"新建或编辑花种及阶段规则"`
+ sysin.CultivateFlowerEditInp
+}
+type EditRes struct{}
+type StatusReq struct {
+ g.Meta `path:"/cultivate/flowers/status" method:"post" tags:"养成管理" summary:"更新花种上下架状态"`
+ sysin.CultivateFlowerStatusInp
+}
+type StatusRes struct{}
diff --git a/server/hack/config.yaml b/server/hack/config.yaml
index d0e5c50..5b08599 100644
--- a/server/hack/config.yaml
+++ b/server/hack/config.yaml
@@ -34,6 +34,16 @@ gfcli:
gJsonSupport: true
clear: false
+ - link: "mysql:root:root@tcp(127.0.0.1:3306)/cultivate?loc=Local&parseTime=true&charset=utf8mb4"
+ group: "cultivate"
+ tables: "cv_flower_catalog,cv_flower_stage_rule,cv_feed_post,cv_feed_media,cv_feed_comment,cv_feed_report,cv_content_audit_log,cv_admin_operation_log"
+ removePrefix: "cv_"
+ descriptionTag: true
+ noModelComment: true
+ jsonCase: "CamelLower"
+ gJsonSupport: true
+ clear: false
+
# service: # 生成业务配置
# srcFolder: "internal/logic"
diff --git a/server/internal/consts/cultivate.go b/server/internal/consts/cultivate.go
new file mode 100644
index 0000000..1635457
--- /dev/null
+++ b/server/internal/consts/cultivate.go
@@ -0,0 +1,54 @@
+package consts
+
+import (
+ "hotgo/internal/library/dict"
+ "hotgo/internal/model"
+)
+
+const (
+ CultivateFlowerOnline = "online"
+ CultivateFlowerOffline = "offline"
+ CultivateFlowerActivityOnly = "activity_only"
+ CultivateFlowerMaintenance = "maintenance"
+)
+
+var CultivateFlowerStatusOptions = []*model.Option{
+ dict.GenSuccessOption(CultivateFlowerOnline, "已上架"),
+ dict.GenDefaultOption(CultivateFlowerOffline, "已下架"),
+ dict.GenPrimaryOption(CultivateFlowerActivityOnly, "仅活动"),
+ dict.GenWarningOption(CultivateFlowerMaintenance, "维护中"),
+}
+
+var CultivateContentStatusOptions = []*model.Option{
+ dict.GenWarningOption("reviewing", "待审核"),
+ dict.GenSuccessOption("published", "已发布"),
+ dict.GenErrorOption("rejected", "已拒绝"),
+ dict.GenDefaultOption("taken_down", "已下架"),
+ dict.GenDefaultOption("deleted", "已删除"),
+}
+
+var CultivateReportStatusOptions = []*model.Option{
+ dict.GenWarningOption("submitted", "待处理"),
+ dict.GenPrimaryOption("processing", "处理中"),
+ dict.GenSuccessOption("resolved", "已处理"),
+ dict.GenDefaultOption("rejected", "已驳回"),
+}
+
+var CultivateContentTypeOptions = []*model.Option{
+ dict.GenPrimaryOption("feed_post", "动态"),
+ dict.GenInfoOption("feed_comment", "评论"),
+}
+
+var CultivateReportObjectTypeOptions = []*model.Option{
+ dict.GenPrimaryOption("feed_post", "动态"),
+ dict.GenInfoOption("feed_comment", "评论"),
+ dict.GenDefaultOption("user", "用户"),
+}
+
+func init() {
+ dict.RegisterEnums("CultivateFlowerStatusOptions", "养成花种状态", CultivateFlowerStatusOptions)
+ dict.RegisterEnums("CultivateContentStatusOptions", "养成内容状态", CultivateContentStatusOptions)
+ dict.RegisterEnums("CultivateReportStatusOptions", "养成举报状态", CultivateReportStatusOptions)
+ dict.RegisterEnums("CultivateContentTypeOptions", "养成内容类型", CultivateContentTypeOptions)
+ dict.RegisterEnums("CultivateReportObjectTypeOptions", "养成举报对象类型", CultivateReportObjectTypeOptions)
+}
diff --git a/server/internal/controller/admin/sys/cultivate_content.go b/server/internal/controller/admin/sys/cultivate_content.go
new file mode 100644
index 0000000..6841c32
--- /dev/null
+++ b/server/internal/controller/admin/sys/cultivate_content.go
@@ -0,0 +1,57 @@
+package sys
+
+import (
+ "context"
+ "hotgo/api/admin/cultivatecontent"
+ "hotgo/internal/model/input/sysin"
+ "hotgo/internal/service"
+)
+
+var CultivateContent = cCultivateContent{}
+
+type cCultivateContent struct{}
+
+func (*cCultivateContent) List(ctx context.Context, req *cultivatecontent.ListReq) (res *cultivatecontent.ListRes, err error) {
+ list, total, err := service.SysCultivateContent().List(ctx, &req.CultivateContentListInp)
+ if err != nil {
+ return nil, err
+ }
+ if list == nil {
+ list = []*sysin.CultivateContentListModel{}
+ }
+ res = &cultivatecontent.ListRes{List: list}
+ res.PageRes.Pack(req, total)
+ return
+}
+func (*cCultivateContent) Review(ctx context.Context, req *cultivatecontent.ReviewReq) (res *cultivatecontent.ReviewRes, err error) {
+ err = service.SysCultivateContent().Review(ctx, &req.CultivateContentReviewInp)
+ return
+}
+func (*cCultivateContent) ReportList(ctx context.Context, req *cultivatecontent.ReportListReq) (res *cultivatecontent.ReportListRes, err error) {
+ list, total, err := service.SysCultivateContent().ReportList(ctx, &req.CultivateReportListInp)
+ if err != nil {
+ return nil, err
+ }
+ if list == nil {
+ list = []*sysin.CultivateReportListModel{}
+ }
+ res = &cultivatecontent.ReportListRes{List: list}
+ res.PageRes.Pack(req, total)
+ return
+}
+func (*cCultivateContent) ReportHandle(ctx context.Context, req *cultivatecontent.ReportHandleReq) (res *cultivatecontent.ReportHandleRes, err error) {
+ err = service.SysCultivateContent().ReportHandle(ctx, &req.CultivateReportHandleInp)
+ return
+}
+func (*cCultivateContent) LogList(ctx context.Context, req *cultivatecontent.LogListReq) (res *cultivatecontent.LogListRes, err error) {
+ list, total, err := service.SysCultivateContent().LogList(ctx, &req.CultivateOperationLogListInp)
+ if err != nil {
+ return nil, err
+ }
+ if list == nil {
+ list = []*sysin.CultivateOperationLogListModel{}
+ }
+ res = &cultivatecontent.LogListRes{List: list}
+ res.PageRes.Pack(req, total)
+ return
+}
diff --git a/server/internal/controller/admin/sys/cultivate_flower.go b/server/internal/controller/admin/sys/cultivate_flower.go
new file mode 100644
index 0000000..ca7c259
--- /dev/null
+++ b/server/internal/controller/admin/sys/cultivate_flower.go
@@ -0,0 +1,40 @@
+package sys
+
+import (
+ "context"
+ "hotgo/api/admin/cultivateflower"
+ "hotgo/internal/model/input/sysin"
+ "hotgo/internal/service"
+)
+
+var CultivateFlower = cCultivateFlower{}
+
+type cCultivateFlower struct{}
+
+func (*cCultivateFlower) List(ctx context.Context, req *cultivateflower.ListReq) (res *cultivateflower.ListRes, err error) {
+ list, total, err := service.SysCultivateFlower().List(ctx, &req.CultivateFlowerListInp)
+ if err != nil {
+ return nil, err
+ }
+ if list == nil {
+ list = []*sysin.CultivateFlowerListModel{}
+ }
+ res = &cultivateflower.ListRes{List: list}
+ res.PageRes.Pack(req, total)
+ return
+}
+func (*cCultivateFlower) View(ctx context.Context, req *cultivateflower.ViewReq) (res *cultivateflower.ViewRes, err error) {
+ data, err := service.SysCultivateFlower().View(ctx, &req.CultivateFlowerViewInp)
+ if err != nil {
+ return nil, err
+ }
+ return &cultivateflower.ViewRes{CultivateFlowerViewModel: data}, nil
+}
+func (*cCultivateFlower) Edit(ctx context.Context, req *cultivateflower.EditReq) (res *cultivateflower.EditRes, err error) {
+ err = service.SysCultivateFlower().Edit(ctx, &req.CultivateFlowerEditInp)
+ return
+}
+func (*cCultivateFlower) Status(ctx context.Context, req *cultivateflower.StatusReq) (res *cultivateflower.StatusRes, err error) {
+ err = service.SysCultivateFlower().Status(ctx, &req.CultivateFlowerStatusInp)
+ return
+}
diff --git a/server/internal/dao/admin_operation_log.go b/server/internal/dao/admin_operation_log.go
new file mode 100644
index 0000000..634f918
--- /dev/null
+++ b/server/internal/dao/admin_operation_log.go
@@ -0,0 +1,22 @@
+// =================================================================================
+// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed.
+// =================================================================================
+
+package dao
+
+import (
+ "hotgo/internal/dao/internal"
+)
+
+// adminOperationLogDao is the data access object for the table cv_admin_operation_log.
+// You can define custom methods on it to extend its functionality as needed.
+type adminOperationLogDao struct {
+ *internal.AdminOperationLogDao
+}
+
+var (
+ // AdminOperationLog is a globally accessible object for table cv_admin_operation_log operations.
+ AdminOperationLog = adminOperationLogDao{internal.NewAdminOperationLogDao()}
+)
+
+// Add your custom methods and functionality below.
diff --git a/server/internal/dao/content_audit_log.go b/server/internal/dao/content_audit_log.go
new file mode 100644
index 0000000..437e3c6
--- /dev/null
+++ b/server/internal/dao/content_audit_log.go
@@ -0,0 +1,22 @@
+// =================================================================================
+// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed.
+// =================================================================================
+
+package dao
+
+import (
+ "hotgo/internal/dao/internal"
+)
+
+// contentAuditLogDao is the data access object for the table cv_content_audit_log.
+// You can define custom methods on it to extend its functionality as needed.
+type contentAuditLogDao struct {
+ *internal.ContentAuditLogDao
+}
+
+var (
+ // ContentAuditLog is a globally accessible object for table cv_content_audit_log operations.
+ ContentAuditLog = contentAuditLogDao{internal.NewContentAuditLogDao()}
+)
+
+// Add your custom methods and functionality below.
diff --git a/server/internal/dao/feed_comment.go b/server/internal/dao/feed_comment.go
new file mode 100644
index 0000000..22dda10
--- /dev/null
+++ b/server/internal/dao/feed_comment.go
@@ -0,0 +1,22 @@
+// =================================================================================
+// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed.
+// =================================================================================
+
+package dao
+
+import (
+ "hotgo/internal/dao/internal"
+)
+
+// feedCommentDao is the data access object for the table cv_feed_comment.
+// You can define custom methods on it to extend its functionality as needed.
+type feedCommentDao struct {
+ *internal.FeedCommentDao
+}
+
+var (
+ // FeedComment is a globally accessible object for table cv_feed_comment operations.
+ FeedComment = feedCommentDao{internal.NewFeedCommentDao()}
+)
+
+// Add your custom methods and functionality below.
diff --git a/server/internal/dao/feed_media.go b/server/internal/dao/feed_media.go
new file mode 100644
index 0000000..80af1b8
--- /dev/null
+++ b/server/internal/dao/feed_media.go
@@ -0,0 +1,22 @@
+// =================================================================================
+// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed.
+// =================================================================================
+
+package dao
+
+import (
+ "hotgo/internal/dao/internal"
+)
+
+// feedMediaDao is the data access object for the table cv_feed_media.
+// You can define custom methods on it to extend its functionality as needed.
+type feedMediaDao struct {
+ *internal.FeedMediaDao
+}
+
+var (
+ // FeedMedia is a globally accessible object for table cv_feed_media operations.
+ FeedMedia = feedMediaDao{internal.NewFeedMediaDao()}
+)
+
+// Add your custom methods and functionality below.
diff --git a/server/internal/dao/feed_post.go b/server/internal/dao/feed_post.go
new file mode 100644
index 0000000..87a4432
--- /dev/null
+++ b/server/internal/dao/feed_post.go
@@ -0,0 +1,22 @@
+// =================================================================================
+// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed.
+// =================================================================================
+
+package dao
+
+import (
+ "hotgo/internal/dao/internal"
+)
+
+// feedPostDao is the data access object for the table cv_feed_post.
+// You can define custom methods on it to extend its functionality as needed.
+type feedPostDao struct {
+ *internal.FeedPostDao
+}
+
+var (
+ // FeedPost is a globally accessible object for table cv_feed_post operations.
+ FeedPost = feedPostDao{internal.NewFeedPostDao()}
+)
+
+// Add your custom methods and functionality below.
diff --git a/server/internal/dao/feed_report.go b/server/internal/dao/feed_report.go
new file mode 100644
index 0000000..a8124e5
--- /dev/null
+++ b/server/internal/dao/feed_report.go
@@ -0,0 +1,22 @@
+// =================================================================================
+// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed.
+// =================================================================================
+
+package dao
+
+import (
+ "hotgo/internal/dao/internal"
+)
+
+// feedReportDao is the data access object for the table cv_feed_report.
+// You can define custom methods on it to extend its functionality as needed.
+type feedReportDao struct {
+ *internal.FeedReportDao
+}
+
+var (
+ // FeedReport is a globally accessible object for table cv_feed_report operations.
+ FeedReport = feedReportDao{internal.NewFeedReportDao()}
+)
+
+// Add your custom methods and functionality below.
diff --git a/server/internal/dao/flower_catalog.go b/server/internal/dao/flower_catalog.go
new file mode 100644
index 0000000..48858d6
--- /dev/null
+++ b/server/internal/dao/flower_catalog.go
@@ -0,0 +1,22 @@
+// =================================================================================
+// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed.
+// =================================================================================
+
+package dao
+
+import (
+ "hotgo/internal/dao/internal"
+)
+
+// flowerCatalogDao is the data access object for the table cv_flower_catalog.
+// You can define custom methods on it to extend its functionality as needed.
+type flowerCatalogDao struct {
+ *internal.FlowerCatalogDao
+}
+
+var (
+ // FlowerCatalog is a globally accessible object for table cv_flower_catalog operations.
+ FlowerCatalog = flowerCatalogDao{internal.NewFlowerCatalogDao()}
+)
+
+// Add your custom methods and functionality below.
diff --git a/server/internal/dao/flower_stage_rule.go b/server/internal/dao/flower_stage_rule.go
new file mode 100644
index 0000000..6e6957d
--- /dev/null
+++ b/server/internal/dao/flower_stage_rule.go
@@ -0,0 +1,22 @@
+// =================================================================================
+// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed.
+// =================================================================================
+
+package dao
+
+import (
+ "hotgo/internal/dao/internal"
+)
+
+// flowerStageRuleDao is the data access object for the table cv_flower_stage_rule.
+// You can define custom methods on it to extend its functionality as needed.
+type flowerStageRuleDao struct {
+ *internal.FlowerStageRuleDao
+}
+
+var (
+ // FlowerStageRule is a globally accessible object for table cv_flower_stage_rule operations.
+ FlowerStageRule = flowerStageRuleDao{internal.NewFlowerStageRuleDao()}
+)
+
+// Add your custom methods and functionality below.
diff --git a/server/internal/dao/internal/admin_operation_log.go b/server/internal/dao/internal/admin_operation_log.go
new file mode 100644
index 0000000..640b6d1
--- /dev/null
+++ b/server/internal/dao/internal/admin_operation_log.go
@@ -0,0 +1,103 @@
+// ==========================================================================
+// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
+// ==========================================================================
+
+package internal
+
+import (
+ "context"
+
+ "github.com/gogf/gf/v2/database/gdb"
+ "github.com/gogf/gf/v2/frame/g"
+)
+
+// AdminOperationLogDao is the data access object for the table cv_admin_operation_log.
+type AdminOperationLogDao struct {
+ table string // table is the underlying table name of the DAO.
+ group string // group is the database configuration group name of the current DAO.
+ columns AdminOperationLogColumns // columns contains all the column names of Table for convenient usage.
+ handlers []gdb.ModelHandler // handlers for customized model modification.
+}
+
+// AdminOperationLogColumns defines and stores column names for the table cv_admin_operation_log.
+type AdminOperationLogColumns struct {
+ Id string //
+ TenantId string //
+ OperatorId string //
+ OperationType string //
+ TargetType string //
+ TargetId string //
+ RequestId string //
+ BeforeSnapshot string //
+ AfterSnapshot string //
+ Reason string //
+ IpAddress string //
+ UserAgent string //
+ CreatedAt string //
+}
+
+// adminOperationLogColumns holds the columns for the table cv_admin_operation_log.
+var adminOperationLogColumns = AdminOperationLogColumns{
+ Id: "id",
+ TenantId: "tenant_id",
+ OperatorId: "operator_id",
+ OperationType: "operation_type",
+ TargetType: "target_type",
+ TargetId: "target_id",
+ RequestId: "request_id",
+ BeforeSnapshot: "before_snapshot",
+ AfterSnapshot: "after_snapshot",
+ Reason: "reason",
+ IpAddress: "ip_address",
+ UserAgent: "user_agent",
+ CreatedAt: "created_at",
+}
+
+// NewAdminOperationLogDao creates and returns a new DAO object for table data access.
+func NewAdminOperationLogDao(handlers ...gdb.ModelHandler) *AdminOperationLogDao {
+ return &AdminOperationLogDao{
+ group: "cultivate",
+ table: "cv_admin_operation_log",
+ columns: adminOperationLogColumns,
+ handlers: handlers,
+ }
+}
+
+// DB retrieves and returns the underlying raw database management object of the current DAO.
+func (dao *AdminOperationLogDao) DB() gdb.DB {
+ return g.DB(dao.group)
+}
+
+// Table returns the table name of the current DAO.
+func (dao *AdminOperationLogDao) Table() string {
+ return dao.table
+}
+
+// Columns returns all column names of the current DAO.
+func (dao *AdminOperationLogDao) Columns() AdminOperationLogColumns {
+ return dao.columns
+}
+
+// Group returns the database configuration group name of the current DAO.
+func (dao *AdminOperationLogDao) Group() string {
+ return dao.group
+}
+
+// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation.
+func (dao *AdminOperationLogDao) Ctx(ctx context.Context) *gdb.Model {
+ model := dao.DB().Model(dao.table)
+ for _, handler := range dao.handlers {
+ model = handler(model)
+ }
+ return model.Safe().Ctx(ctx)
+}
+
+// Transaction wraps the transaction logic using function f.
+// It rolls back the transaction and returns the error if function f returns a non-nil error.
+// It commits the transaction and returns nil if function f returns nil.
+//
+// Note: Do not commit or roll back the transaction in function f,
+// as it is automatically handled by this function.
+func (dao *AdminOperationLogDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
+ return dao.Ctx(ctx).Transaction(ctx, f)
+}
diff --git a/server/internal/dao/internal/content_audit_log.go b/server/internal/dao/internal/content_audit_log.go
new file mode 100644
index 0000000..0a44a75
--- /dev/null
+++ b/server/internal/dao/internal/content_audit_log.go
@@ -0,0 +1,103 @@
+// ==========================================================================
+// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
+// ==========================================================================
+
+package internal
+
+import (
+ "context"
+
+ "github.com/gogf/gf/v2/database/gdb"
+ "github.com/gogf/gf/v2/frame/g"
+)
+
+// ContentAuditLogDao is the data access object for the table cv_content_audit_log.
+type ContentAuditLogDao struct {
+ table string // table is the underlying table name of the DAO.
+ group string // group is the database configuration group name of the current DAO.
+ columns ContentAuditLogColumns // columns contains all the column names of Table for convenient usage.
+ handlers []gdb.ModelHandler // handlers for customized model modification.
+}
+
+// ContentAuditLogColumns defines and stores column names for the table cv_content_audit_log.
+type ContentAuditLogColumns struct {
+ Id string //
+ TenantId string //
+ ObjectType string //
+ ObjectId string //
+ AuditRequestId string //
+ AuditSource string //
+ StatusBefore string //
+ StatusAfter string //
+ ResultCode string //
+ RiskLabels string //
+ OperatorId string //
+ OccurredAt string //
+ CreatedAt string //
+}
+
+// contentAuditLogColumns holds the columns for the table cv_content_audit_log.
+var contentAuditLogColumns = ContentAuditLogColumns{
+ Id: "id",
+ TenantId: "tenant_id",
+ ObjectType: "object_type",
+ ObjectId: "object_id",
+ AuditRequestId: "audit_request_id",
+ AuditSource: "audit_source",
+ StatusBefore: "status_before",
+ StatusAfter: "status_after",
+ ResultCode: "result_code",
+ RiskLabels: "risk_labels",
+ OperatorId: "operator_id",
+ OccurredAt: "occurred_at",
+ CreatedAt: "created_at",
+}
+
+// NewContentAuditLogDao creates and returns a new DAO object for table data access.
+func NewContentAuditLogDao(handlers ...gdb.ModelHandler) *ContentAuditLogDao {
+ return &ContentAuditLogDao{
+ group: "cultivate",
+ table: "cv_content_audit_log",
+ columns: contentAuditLogColumns,
+ handlers: handlers,
+ }
+}
+
+// DB retrieves and returns the underlying raw database management object of the current DAO.
+func (dao *ContentAuditLogDao) DB() gdb.DB {
+ return g.DB(dao.group)
+}
+
+// Table returns the table name of the current DAO.
+func (dao *ContentAuditLogDao) Table() string {
+ return dao.table
+}
+
+// Columns returns all column names of the current DAO.
+func (dao *ContentAuditLogDao) Columns() ContentAuditLogColumns {
+ return dao.columns
+}
+
+// Group returns the database configuration group name of the current DAO.
+func (dao *ContentAuditLogDao) Group() string {
+ return dao.group
+}
+
+// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation.
+func (dao *ContentAuditLogDao) Ctx(ctx context.Context) *gdb.Model {
+ model := dao.DB().Model(dao.table)
+ for _, handler := range dao.handlers {
+ model = handler(model)
+ }
+ return model.Safe().Ctx(ctx)
+}
+
+// Transaction wraps the transaction logic using function f.
+// It rolls back the transaction and returns the error if function f returns a non-nil error.
+// It commits the transaction and returns nil if function f returns nil.
+//
+// Note: Do not commit or roll back the transaction in function f,
+// as it is automatically handled by this function.
+func (dao *ContentAuditLogDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
+ return dao.Ctx(ctx).Transaction(ctx, f)
+}
diff --git a/server/internal/dao/internal/feed_comment.go b/server/internal/dao/internal/feed_comment.go
new file mode 100644
index 0000000..ca3fc1f
--- /dev/null
+++ b/server/internal/dao/internal/feed_comment.go
@@ -0,0 +1,105 @@
+// ==========================================================================
+// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
+// ==========================================================================
+
+package internal
+
+import (
+ "context"
+
+ "github.com/gogf/gf/v2/database/gdb"
+ "github.com/gogf/gf/v2/frame/g"
+)
+
+// FeedCommentDao is the data access object for the table cv_feed_comment.
+type FeedCommentDao struct {
+ table string // table is the underlying table name of the DAO.
+ group string // group is the database configuration group name of the current DAO.
+ columns FeedCommentColumns // columns contains all the column names of Table for convenient usage.
+ handlers []gdb.ModelHandler // handlers for customized model modification.
+}
+
+// FeedCommentColumns defines and stores column names for the table cv_feed_comment.
+type FeedCommentColumns struct {
+ Id string //
+ TenantId string //
+ PostId string //
+ AuthorId string //
+ ContentText string //
+ Status string //
+ AuditStatus string //
+ AuditRequestId string //
+ DeletedBy string //
+ DeleteReason string //
+ PublishedAt string //
+ DeletedAt string //
+ CreatedAt string //
+ UpdatedAt string //
+}
+
+// feedCommentColumns holds the columns for the table cv_feed_comment.
+var feedCommentColumns = FeedCommentColumns{
+ Id: "id",
+ TenantId: "tenant_id",
+ PostId: "post_id",
+ AuthorId: "author_id",
+ ContentText: "content_text",
+ Status: "status",
+ AuditStatus: "audit_status",
+ AuditRequestId: "audit_request_id",
+ DeletedBy: "deleted_by",
+ DeleteReason: "delete_reason",
+ PublishedAt: "published_at",
+ DeletedAt: "deleted_at",
+ CreatedAt: "created_at",
+ UpdatedAt: "updated_at",
+}
+
+// NewFeedCommentDao creates and returns a new DAO object for table data access.
+func NewFeedCommentDao(handlers ...gdb.ModelHandler) *FeedCommentDao {
+ return &FeedCommentDao{
+ group: "cultivate",
+ table: "cv_feed_comment",
+ columns: feedCommentColumns,
+ handlers: handlers,
+ }
+}
+
+// DB retrieves and returns the underlying raw database management object of the current DAO.
+func (dao *FeedCommentDao) DB() gdb.DB {
+ return g.DB(dao.group)
+}
+
+// Table returns the table name of the current DAO.
+func (dao *FeedCommentDao) Table() string {
+ return dao.table
+}
+
+// Columns returns all column names of the current DAO.
+func (dao *FeedCommentDao) Columns() FeedCommentColumns {
+ return dao.columns
+}
+
+// Group returns the database configuration group name of the current DAO.
+func (dao *FeedCommentDao) Group() string {
+ return dao.group
+}
+
+// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation.
+func (dao *FeedCommentDao) Ctx(ctx context.Context) *gdb.Model {
+ model := dao.DB().Model(dao.table)
+ for _, handler := range dao.handlers {
+ model = handler(model)
+ }
+ return model.Safe().Ctx(ctx)
+}
+
+// Transaction wraps the transaction logic using function f.
+// It rolls back the transaction and returns the error if function f returns a non-nil error.
+// It commits the transaction and returns nil if function f returns nil.
+//
+// Note: Do not commit or roll back the transaction in function f,
+// as it is automatically handled by this function.
+func (dao *FeedCommentDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
+ return dao.Ctx(ctx).Transaction(ctx, f)
+}
diff --git a/server/internal/dao/internal/feed_media.go b/server/internal/dao/internal/feed_media.go
new file mode 100644
index 0000000..ea5e7c2
--- /dev/null
+++ b/server/internal/dao/internal/feed_media.go
@@ -0,0 +1,105 @@
+// ==========================================================================
+// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
+// ==========================================================================
+
+package internal
+
+import (
+ "context"
+
+ "github.com/gogf/gf/v2/database/gdb"
+ "github.com/gogf/gf/v2/frame/g"
+)
+
+// FeedMediaDao is the data access object for the table cv_feed_media.
+type FeedMediaDao struct {
+ table string // table is the underlying table name of the DAO.
+ group string // group is the database configuration group name of the current DAO.
+ columns FeedMediaColumns // columns contains all the column names of Table for convenient usage.
+ handlers []gdb.ModelHandler // handlers for customized model modification.
+}
+
+// FeedMediaColumns defines and stores column names for the table cv_feed_media.
+type FeedMediaColumns struct {
+ Id string //
+ TenantId string //
+ PostId string //
+ UserId string //
+ MediaType string //
+ FileId string //
+ ThumbnailFileId string //
+ Width string //
+ Height string //
+ SortNo string //
+ AuditStatus string //
+ AuditRequestId string //
+ CreatedAt string //
+ UpdatedAt string //
+}
+
+// feedMediaColumns holds the columns for the table cv_feed_media.
+var feedMediaColumns = FeedMediaColumns{
+ Id: "id",
+ TenantId: "tenant_id",
+ PostId: "post_id",
+ UserId: "user_id",
+ MediaType: "media_type",
+ FileId: "file_id",
+ ThumbnailFileId: "thumbnail_file_id",
+ Width: "width",
+ Height: "height",
+ SortNo: "sort_no",
+ AuditStatus: "audit_status",
+ AuditRequestId: "audit_request_id",
+ CreatedAt: "created_at",
+ UpdatedAt: "updated_at",
+}
+
+// NewFeedMediaDao creates and returns a new DAO object for table data access.
+func NewFeedMediaDao(handlers ...gdb.ModelHandler) *FeedMediaDao {
+ return &FeedMediaDao{
+ group: "cultivate",
+ table: "cv_feed_media",
+ columns: feedMediaColumns,
+ handlers: handlers,
+ }
+}
+
+// DB retrieves and returns the underlying raw database management object of the current DAO.
+func (dao *FeedMediaDao) DB() gdb.DB {
+ return g.DB(dao.group)
+}
+
+// Table returns the table name of the current DAO.
+func (dao *FeedMediaDao) Table() string {
+ return dao.table
+}
+
+// Columns returns all column names of the current DAO.
+func (dao *FeedMediaDao) Columns() FeedMediaColumns {
+ return dao.columns
+}
+
+// Group returns the database configuration group name of the current DAO.
+func (dao *FeedMediaDao) Group() string {
+ return dao.group
+}
+
+// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation.
+func (dao *FeedMediaDao) Ctx(ctx context.Context) *gdb.Model {
+ model := dao.DB().Model(dao.table)
+ for _, handler := range dao.handlers {
+ model = handler(model)
+ }
+ return model.Safe().Ctx(ctx)
+}
+
+// Transaction wraps the transaction logic using function f.
+// It rolls back the transaction and returns the error if function f returns a non-nil error.
+// It commits the transaction and returns nil if function f returns nil.
+//
+// Note: Do not commit or roll back the transaction in function f,
+// as it is automatically handled by this function.
+func (dao *FeedMediaDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
+ return dao.Ctx(ctx).Transaction(ctx, f)
+}
diff --git a/server/internal/dao/internal/feed_post.go b/server/internal/dao/internal/feed_post.go
new file mode 100644
index 0000000..f70fbaf
--- /dev/null
+++ b/server/internal/dao/internal/feed_post.go
@@ -0,0 +1,117 @@
+// ==========================================================================
+// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
+// ==========================================================================
+
+package internal
+
+import (
+ "context"
+
+ "github.com/gogf/gf/v2/database/gdb"
+ "github.com/gogf/gf/v2/frame/g"
+)
+
+// FeedPostDao is the data access object for the table cv_feed_post.
+type FeedPostDao struct {
+ table string // table is the underlying table name of the DAO.
+ group string // group is the database configuration group name of the current DAO.
+ columns FeedPostColumns // columns contains all the column names of Table for convenient usage.
+ handlers []gdb.ModelHandler // handlers for customized model modification.
+}
+
+// FeedPostColumns defines and stores column names for the table cv_feed_post.
+type FeedPostColumns struct {
+ Id string //
+ TenantId string //
+ AuthorId string //
+ ContentText string //
+ FlowerCode string //
+ SourceType string //
+ SourceId string //
+ Visibility string //
+ Status string //
+ AuditStatus string //
+ AuditRequestId string //
+ RejectReasonCode string //
+ LikeCount string //
+ CommentCount string //
+ Version string //
+ PublishedAt string //
+ DeletedAt string //
+ TakenDownAt string //
+ CreatedAt string //
+ UpdatedAt string //
+}
+
+// feedPostColumns holds the columns for the table cv_feed_post.
+var feedPostColumns = FeedPostColumns{
+ Id: "id",
+ TenantId: "tenant_id",
+ AuthorId: "author_id",
+ ContentText: "content_text",
+ FlowerCode: "flower_code",
+ SourceType: "source_type",
+ SourceId: "source_id",
+ Visibility: "visibility",
+ Status: "status",
+ AuditStatus: "audit_status",
+ AuditRequestId: "audit_request_id",
+ RejectReasonCode: "reject_reason_code",
+ LikeCount: "like_count",
+ CommentCount: "comment_count",
+ Version: "version",
+ PublishedAt: "published_at",
+ DeletedAt: "deleted_at",
+ TakenDownAt: "taken_down_at",
+ CreatedAt: "created_at",
+ UpdatedAt: "updated_at",
+}
+
+// NewFeedPostDao creates and returns a new DAO object for table data access.
+func NewFeedPostDao(handlers ...gdb.ModelHandler) *FeedPostDao {
+ return &FeedPostDao{
+ group: "cultivate",
+ table: "cv_feed_post",
+ columns: feedPostColumns,
+ handlers: handlers,
+ }
+}
+
+// DB retrieves and returns the underlying raw database management object of the current DAO.
+func (dao *FeedPostDao) DB() gdb.DB {
+ return g.DB(dao.group)
+}
+
+// Table returns the table name of the current DAO.
+func (dao *FeedPostDao) Table() string {
+ return dao.table
+}
+
+// Columns returns all column names of the current DAO.
+func (dao *FeedPostDao) Columns() FeedPostColumns {
+ return dao.columns
+}
+
+// Group returns the database configuration group name of the current DAO.
+func (dao *FeedPostDao) Group() string {
+ return dao.group
+}
+
+// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation.
+func (dao *FeedPostDao) Ctx(ctx context.Context) *gdb.Model {
+ model := dao.DB().Model(dao.table)
+ for _, handler := range dao.handlers {
+ model = handler(model)
+ }
+ return model.Safe().Ctx(ctx)
+}
+
+// Transaction wraps the transaction logic using function f.
+// It rolls back the transaction and returns the error if function f returns a non-nil error.
+// It commits the transaction and returns nil if function f returns nil.
+//
+// Note: Do not commit or roll back the transaction in function f,
+// as it is automatically handled by this function.
+func (dao *FeedPostDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
+ return dao.Ctx(ctx).Transaction(ctx, f)
+}
diff --git a/server/internal/dao/internal/feed_report.go b/server/internal/dao/internal/feed_report.go
new file mode 100644
index 0000000..9da350d
--- /dev/null
+++ b/server/internal/dao/internal/feed_report.go
@@ -0,0 +1,109 @@
+// ==========================================================================
+// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
+// ==========================================================================
+
+package internal
+
+import (
+ "context"
+
+ "github.com/gogf/gf/v2/database/gdb"
+ "github.com/gogf/gf/v2/frame/g"
+)
+
+// FeedReportDao is the data access object for the table cv_feed_report.
+type FeedReportDao struct {
+ table string // table is the underlying table name of the DAO.
+ group string // group is the database configuration group name of the current DAO.
+ columns FeedReportColumns // columns contains all the column names of Table for convenient usage.
+ handlers []gdb.ModelHandler // handlers for customized model modification.
+}
+
+// FeedReportColumns defines and stores column names for the table cv_feed_report.
+type FeedReportColumns struct {
+ Id string //
+ TenantId string //
+ ReporterId string //
+ ReportedUserId string //
+ ObjectType string //
+ ObjectId string //
+ ReasonCode string //
+ Description string //
+ EvidenceFileIds string //
+ SourcePage string //
+ Status string //
+ HandlerId string //
+ HandleResult string //
+ HandledAt string //
+ CreatedAt string //
+ UpdatedAt string //
+}
+
+// feedReportColumns holds the columns for the table cv_feed_report.
+var feedReportColumns = FeedReportColumns{
+ Id: "id",
+ TenantId: "tenant_id",
+ ReporterId: "reporter_id",
+ ReportedUserId: "reported_user_id",
+ ObjectType: "object_type",
+ ObjectId: "object_id",
+ ReasonCode: "reason_code",
+ Description: "description",
+ EvidenceFileIds: "evidence_file_ids",
+ SourcePage: "source_page",
+ Status: "status",
+ HandlerId: "handler_id",
+ HandleResult: "handle_result",
+ HandledAt: "handled_at",
+ CreatedAt: "created_at",
+ UpdatedAt: "updated_at",
+}
+
+// NewFeedReportDao creates and returns a new DAO object for table data access.
+func NewFeedReportDao(handlers ...gdb.ModelHandler) *FeedReportDao {
+ return &FeedReportDao{
+ group: "cultivate",
+ table: "cv_feed_report",
+ columns: feedReportColumns,
+ handlers: handlers,
+ }
+}
+
+// DB retrieves and returns the underlying raw database management object of the current DAO.
+func (dao *FeedReportDao) DB() gdb.DB {
+ return g.DB(dao.group)
+}
+
+// Table returns the table name of the current DAO.
+func (dao *FeedReportDao) Table() string {
+ return dao.table
+}
+
+// Columns returns all column names of the current DAO.
+func (dao *FeedReportDao) Columns() FeedReportColumns {
+ return dao.columns
+}
+
+// Group returns the database configuration group name of the current DAO.
+func (dao *FeedReportDao) Group() string {
+ return dao.group
+}
+
+// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation.
+func (dao *FeedReportDao) Ctx(ctx context.Context) *gdb.Model {
+ model := dao.DB().Model(dao.table)
+ for _, handler := range dao.handlers {
+ model = handler(model)
+ }
+ return model.Safe().Ctx(ctx)
+}
+
+// Transaction wraps the transaction logic using function f.
+// It rolls back the transaction and returns the error if function f returns a non-nil error.
+// It commits the transaction and returns nil if function f returns nil.
+//
+// Note: Do not commit or roll back the transaction in function f,
+// as it is automatically handled by this function.
+func (dao *FeedReportDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
+ return dao.Ctx(ctx).Transaction(ctx, f)
+}
diff --git a/server/internal/dao/internal/flower_catalog.go b/server/internal/dao/internal/flower_catalog.go
new file mode 100644
index 0000000..fc0012e
--- /dev/null
+++ b/server/internal/dao/internal/flower_catalog.go
@@ -0,0 +1,109 @@
+// ==========================================================================
+// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
+// ==========================================================================
+
+package internal
+
+import (
+ "context"
+
+ "github.com/gogf/gf/v2/database/gdb"
+ "github.com/gogf/gf/v2/frame/g"
+)
+
+// FlowerCatalogDao is the data access object for the table cv_flower_catalog.
+type FlowerCatalogDao struct {
+ table string // table is the underlying table name of the DAO.
+ group string // group is the database configuration group name of the current DAO.
+ columns FlowerCatalogColumns // columns contains all the column names of Table for convenient usage.
+ handlers []gdb.ModelHandler // handlers for customized model modification.
+}
+
+// FlowerCatalogColumns defines and stores column names for the table cv_flower_catalog.
+type FlowerCatalogColumns struct {
+ Id string //
+ TenantId string //
+ FlowerCode string //
+ FlowerName string //
+ FlowerDesc string //
+ IconFileId string //
+ Status string //
+ ConfigVersion string //
+ DailyWaterLimit string //
+ DailyFertilizeLimit string //
+ ShareConfig string //
+ SortNo string //
+ OnlineAt string //
+ OfflineAt string //
+ CreatedAt string //
+ UpdatedAt string //
+}
+
+// flowerCatalogColumns holds the columns for the table cv_flower_catalog.
+var flowerCatalogColumns = FlowerCatalogColumns{
+ Id: "id",
+ TenantId: "tenant_id",
+ FlowerCode: "flower_code",
+ FlowerName: "flower_name",
+ FlowerDesc: "flower_desc",
+ IconFileId: "icon_file_id",
+ Status: "status",
+ ConfigVersion: "config_version",
+ DailyWaterLimit: "daily_water_limit",
+ DailyFertilizeLimit: "daily_fertilize_limit",
+ ShareConfig: "share_config",
+ SortNo: "sort_no",
+ OnlineAt: "online_at",
+ OfflineAt: "offline_at",
+ CreatedAt: "created_at",
+ UpdatedAt: "updated_at",
+}
+
+// NewFlowerCatalogDao creates and returns a new DAO object for table data access.
+func NewFlowerCatalogDao(handlers ...gdb.ModelHandler) *FlowerCatalogDao {
+ return &FlowerCatalogDao{
+ group: "cultivate",
+ table: "cv_flower_catalog",
+ columns: flowerCatalogColumns,
+ handlers: handlers,
+ }
+}
+
+// DB retrieves and returns the underlying raw database management object of the current DAO.
+func (dao *FlowerCatalogDao) DB() gdb.DB {
+ return g.DB(dao.group)
+}
+
+// Table returns the table name of the current DAO.
+func (dao *FlowerCatalogDao) Table() string {
+ return dao.table
+}
+
+// Columns returns all column names of the current DAO.
+func (dao *FlowerCatalogDao) Columns() FlowerCatalogColumns {
+ return dao.columns
+}
+
+// Group returns the database configuration group name of the current DAO.
+func (dao *FlowerCatalogDao) Group() string {
+ return dao.group
+}
+
+// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation.
+func (dao *FlowerCatalogDao) Ctx(ctx context.Context) *gdb.Model {
+ model := dao.DB().Model(dao.table)
+ for _, handler := range dao.handlers {
+ model = handler(model)
+ }
+ return model.Safe().Ctx(ctx)
+}
+
+// Transaction wraps the transaction logic using function f.
+// It rolls back the transaction and returns the error if function f returns a non-nil error.
+// It commits the transaction and returns nil if function f returns nil.
+//
+// Note: Do not commit or roll back the transaction in function f,
+// as it is automatically handled by this function.
+func (dao *FlowerCatalogDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
+ return dao.Ctx(ctx).Transaction(ctx, f)
+}
diff --git a/server/internal/dao/internal/flower_stage_rule.go b/server/internal/dao/internal/flower_stage_rule.go
new file mode 100644
index 0000000..94d6177
--- /dev/null
+++ b/server/internal/dao/internal/flower_stage_rule.go
@@ -0,0 +1,109 @@
+// ==========================================================================
+// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
+// ==========================================================================
+
+package internal
+
+import (
+ "context"
+
+ "github.com/gogf/gf/v2/database/gdb"
+ "github.com/gogf/gf/v2/frame/g"
+)
+
+// FlowerStageRuleDao is the data access object for the table cv_flower_stage_rule.
+type FlowerStageRuleDao struct {
+ table string // table is the underlying table name of the DAO.
+ group string // group is the database configuration group name of the current DAO.
+ columns FlowerStageRuleColumns // columns contains all the column names of Table for convenient usage.
+ handlers []gdb.ModelHandler // handlers for customized model modification.
+}
+
+// FlowerStageRuleColumns defines and stores column names for the table cv_flower_stage_rule.
+type FlowerStageRuleColumns struct {
+ Id string //
+ TenantId string //
+ FlowerCatalogId string //
+ Stage string //
+ NextStage string //
+ RequiredPoints string //
+ WaterPoints string //
+ FertilizePoints string //
+ AllowedActions string //
+ ImageFileId string //
+ AnimationFileId string //
+ ProgressTextTemplate string //
+ CompletionText string //
+ SortNo string //
+ CreatedAt string //
+ UpdatedAt string //
+}
+
+// flowerStageRuleColumns holds the columns for the table cv_flower_stage_rule.
+var flowerStageRuleColumns = FlowerStageRuleColumns{
+ Id: "id",
+ TenantId: "tenant_id",
+ FlowerCatalogId: "flower_catalog_id",
+ Stage: "stage",
+ NextStage: "next_stage",
+ RequiredPoints: "required_points",
+ WaterPoints: "water_points",
+ FertilizePoints: "fertilize_points",
+ AllowedActions: "allowed_actions",
+ ImageFileId: "image_file_id",
+ AnimationFileId: "animation_file_id",
+ ProgressTextTemplate: "progress_text_template",
+ CompletionText: "completion_text",
+ SortNo: "sort_no",
+ CreatedAt: "created_at",
+ UpdatedAt: "updated_at",
+}
+
+// NewFlowerStageRuleDao creates and returns a new DAO object for table data access.
+func NewFlowerStageRuleDao(handlers ...gdb.ModelHandler) *FlowerStageRuleDao {
+ return &FlowerStageRuleDao{
+ group: "cultivate",
+ table: "cv_flower_stage_rule",
+ columns: flowerStageRuleColumns,
+ handlers: handlers,
+ }
+}
+
+// DB retrieves and returns the underlying raw database management object of the current DAO.
+func (dao *FlowerStageRuleDao) DB() gdb.DB {
+ return g.DB(dao.group)
+}
+
+// Table returns the table name of the current DAO.
+func (dao *FlowerStageRuleDao) Table() string {
+ return dao.table
+}
+
+// Columns returns all column names of the current DAO.
+func (dao *FlowerStageRuleDao) Columns() FlowerStageRuleColumns {
+ return dao.columns
+}
+
+// Group returns the database configuration group name of the current DAO.
+func (dao *FlowerStageRuleDao) Group() string {
+ return dao.group
+}
+
+// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation.
+func (dao *FlowerStageRuleDao) Ctx(ctx context.Context) *gdb.Model {
+ model := dao.DB().Model(dao.table)
+ for _, handler := range dao.handlers {
+ model = handler(model)
+ }
+ return model.Safe().Ctx(ctx)
+}
+
+// Transaction wraps the transaction logic using function f.
+// It rolls back the transaction and returns the error if function f returns a non-nil error.
+// It commits the transaction and returns nil if function f returns nil.
+//
+// Note: Do not commit or roll back the transaction in function f,
+// as it is automatically handled by this function.
+func (dao *FlowerStageRuleDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
+ return dao.Ctx(ctx).Transaction(ctx, f)
+}
diff --git a/server/internal/logic/sys/cultivate_content.go b/server/internal/logic/sys/cultivate_content.go
new file mode 100644
index 0000000..7ed2994
--- /dev/null
+++ b/server/internal/logic/sys/cultivate_content.go
@@ -0,0 +1,324 @@
+package sys
+
+import (
+ "context"
+ "database/sql"
+ "errors"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/gogf/gf/v2/database/gdb"
+ "github.com/gogf/gf/v2/encoding/gjson"
+ "github.com/gogf/gf/v2/errors/gerror"
+ "github.com/gogf/gf/v2/frame/g"
+
+ "hotgo/internal/dao"
+ "hotgo/internal/library/contexts"
+ "hotgo/internal/library/imtenant"
+ "hotgo/internal/model/entity"
+ "hotgo/internal/model/input/sysin"
+ "hotgo/internal/service"
+)
+
+type sSysCultivateContent struct{}
+
+func NewSysCultivateContent() *sSysCultivateContent { return &sSysCultivateContent{} }
+func init() { service.RegisterSysCultivateContent(NewSysCultivateContent()) }
+
+func (s *sSysCultivateContent) List(ctx context.Context, in *sysin.CultivateContentListInp) (list []*sysin.CultivateContentListModel, totalCount int, err error) {
+ tenantID := cultivateTenantID(ctx)
+ if in.ObjectType == "feed_comment" {
+ columns := dao.FeedComment.Columns()
+ mod := dao.FeedComment.Ctx(ctx).Where(columns.TenantId, tenantID)
+ if in.AuthorId != "" {
+ mod = mod.Where(columns.AuthorId, in.AuthorId)
+ }
+ if in.Status != "" {
+ mod = mod.Where(columns.Status, in.Status)
+ }
+ if in.Keyword != "" {
+ mod = mod.WhereLike(columns.ContentText, "%"+strings.TrimSpace(in.Keyword)+"%")
+ }
+ if len(in.CreatedAt) == 2 {
+ mod = mod.WhereBetween(columns.CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
+ }
+ var rows []*entity.FeedComment
+ err = mod.Page(in.Page, in.PerPage).OrderDesc(columns.Id).ScanAndCount(&rows, &totalCount, false)
+ if err != nil {
+ return nil, 0, gerror.Wrap(err, "获取评论审核列表失败")
+ }
+ list = make([]*sysin.CultivateContentListModel, 0, len(rows))
+ for _, row := range rows {
+ list = append(list, &sysin.CultivateContentListModel{Id: strconv.FormatUint(row.Id, 10), ObjectType: "feed_comment", PostId: strconv.FormatUint(row.PostId, 10), AuthorId: strconv.FormatUint(row.AuthorId, 10), ContentText: row.ContentText, Status: row.Status, AuditStatus: row.AuditStatus, CreatedAt: row.CreatedAt, UpdatedAt: row.UpdatedAt})
+ }
+ return
+ }
+ columns := dao.FeedPost.Columns()
+ mod := dao.FeedPost.Ctx(ctx).Where(columns.TenantId, tenantID)
+ if in.AuthorId != "" {
+ mod = mod.Where(columns.AuthorId, in.AuthorId)
+ }
+ if in.Status != "" {
+ mod = mod.Where(columns.Status, in.Status)
+ }
+ if in.Keyword != "" {
+ mod = mod.WhereLike(columns.ContentText, "%"+strings.TrimSpace(in.Keyword)+"%")
+ }
+ if len(in.CreatedAt) == 2 {
+ mod = mod.WhereBetween(columns.CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
+ }
+ var rows []*entity.FeedPost
+ err = mod.Page(in.Page, in.PerPage).OrderDesc(columns.Id).ScanAndCount(&rows, &totalCount, false)
+ if err != nil {
+ return nil, 0, gerror.Wrap(err, "获取动态审核列表失败")
+ }
+ list = make([]*sysin.CultivateContentListModel, 0, len(rows))
+ for _, row := range rows {
+ list = append(list, &sysin.CultivateContentListModel{Id: strconv.FormatUint(row.Id, 10), ObjectType: "feed_post", AuthorId: strconv.FormatUint(row.AuthorId, 10), ContentText: row.ContentText, FlowerCode: row.FlowerCode, Visibility: row.Visibility, Status: row.Status, AuditStatus: row.AuditStatus, RejectReason: row.RejectReasonCode, LikeCount: row.LikeCount, CommentCount: row.CommentCount, CreatedAt: row.CreatedAt, UpdatedAt: row.UpdatedAt})
+ }
+ return
+}
+
+func (s *sSysCultivateContent) Review(ctx context.Context, in *sysin.CultivateContentReviewInp) error {
+ tenantID, operatorID := cultivateTenantID(ctx), contexts.GetUserId(ctx)
+ return dao.FeedPost.Transaction(ctx, func(txCtx context.Context, tx gdb.TX) error {
+ if in.ObjectType == "feed_comment" {
+ return s.reviewComment(txCtx, tx, tenantID, operatorID, in)
+ }
+ return s.reviewPost(txCtx, tx, tenantID, operatorID, in)
+ })
+}
+
+func (s *sSysCultivateContent) reviewPost(ctx context.Context, tx gdb.TX, tenantID, operatorID int64, in *sysin.CultivateContentReviewInp) error {
+ columns := dao.FeedPost.Columns()
+ var before entity.FeedPost
+ err := tx.Model(dao.FeedPost.Table()).Where(g.Map{columns.Id: in.ObjectId, columns.TenantId: tenantID}).LockUpdate().Scan(&before)
+ if err != nil && !errors.Is(err, sql.ErrNoRows) {
+ return err
+ }
+ if before.Id == 0 {
+ return gerror.New("动态不存在")
+ }
+ now := time.Now().UTC()
+ data := g.Map{columns.UpdatedAt: now, columns.Version: gdb.Raw(columns.Version + "+1")}
+ afterStatus := before.Status
+ switch in.Action {
+ case "approve":
+ if before.Status != "reviewing" && before.Status != "rejected" {
+ return gerror.New("仅待审核或已拒绝动态可审核通过")
+ }
+ afterStatus = "published"
+ data[columns.Status] = afterStatus
+ data[columns.AuditStatus] = "approved"
+ data[columns.RejectReasonCode] = ""
+ if before.PublishedAt == nil {
+ data[columns.PublishedAt] = now
+ }
+ case "reject":
+ if before.Status != "reviewing" {
+ return gerror.New("仅待审核动态可拒绝")
+ }
+ afterStatus = "rejected"
+ data[columns.Status] = afterStatus
+ data[columns.AuditStatus] = "rejected"
+ data[columns.RejectReasonCode] = in.Reason
+ case "take_down":
+ if before.Status != "published" {
+ return gerror.New("仅已发布动态可下架")
+ }
+ afterStatus = "taken_down"
+ data[columns.Status] = afterStatus
+ data[columns.TakenDownAt] = now
+ data[columns.RejectReasonCode] = in.Reason
+ case "restore":
+ if before.Status != "taken_down" {
+ return gerror.New("仅已下架动态可恢复")
+ }
+ afterStatus = "published"
+ data[columns.Status] = afterStatus
+ data[columns.TakenDownAt] = nil
+ data[columns.RejectReasonCode] = ""
+ }
+ if _, err = tx.Model(dao.FeedPost.Table()).Where(columns.Id, before.Id).Data(data).Update(); err != nil {
+ return gerror.Wrap(err, "更新动态审核状态失败")
+ }
+ if err = writeContentAudit(ctx, tx, tenantID, operatorID, "feed_post", before.Id, before.Status, afterStatus, in.Action, now); err != nil {
+ return err
+ }
+ return writeCultivateOperation(ctx, tx, tenantID, operatorID, "content."+in.Action, "feed_post", in.Id, before, data, in.Reason, now)
+}
+
+func (s *sSysCultivateContent) reviewComment(ctx context.Context, tx gdb.TX, tenantID, operatorID int64, in *sysin.CultivateContentReviewInp) error {
+ columns := dao.FeedComment.Columns()
+ var before entity.FeedComment
+ err := tx.Model(dao.FeedComment.Table()).Where(g.Map{columns.Id: in.ObjectId, columns.TenantId: tenantID}).LockUpdate().Scan(&before)
+ if err != nil && !errors.Is(err, sql.ErrNoRows) {
+ return err
+ }
+ if before.Id == 0 {
+ return gerror.New("评论不存在")
+ }
+ now := time.Now().UTC()
+ data := g.Map{columns.UpdatedAt: now}
+ afterStatus := before.Status
+ counterDelta := 0
+ switch in.Action {
+ case "approve":
+ if before.Status != "reviewing" && before.Status != "rejected" {
+ return gerror.New("仅待审核或已拒绝评论可审核通过")
+ }
+ afterStatus = "published"
+ data[columns.Status] = afterStatus
+ data[columns.AuditStatus] = "approved"
+ data[columns.PublishedAt] = now
+ counterDelta = 1
+ case "reject":
+ if before.Status != "reviewing" {
+ return gerror.New("仅待审核评论可拒绝")
+ }
+ afterStatus = "rejected"
+ data[columns.Status] = afterStatus
+ data[columns.AuditStatus] = "rejected"
+ data[columns.DeleteReason] = in.Reason
+ case "take_down":
+ if before.Status != "published" {
+ return gerror.New("仅已发布评论可下架")
+ }
+ afterStatus = "taken_down"
+ data[columns.Status] = afterStatus
+ data[columns.DeleteReason] = in.Reason
+ counterDelta = -1
+ case "restore":
+ if before.Status != "taken_down" {
+ return gerror.New("仅已下架评论可恢复")
+ }
+ afterStatus = "published"
+ data[columns.Status] = afterStatus
+ data[columns.DeleteReason] = ""
+ counterDelta = 1
+ }
+ if _, err = tx.Model(dao.FeedComment.Table()).Where(columns.Id, before.Id).Data(data).Update(); err != nil {
+ return gerror.Wrap(err, "更新评论审核状态失败")
+ }
+ if counterDelta != 0 {
+ postColumns := dao.FeedPost.Columns()
+ expression := "`comment_count` + 1"
+ if counterDelta < 0 {
+ expression = "GREATEST(`comment_count` - 1, 0)"
+ }
+ if _, err = tx.Model(dao.FeedPost.Table()).Where(g.Map{postColumns.Id: before.PostId, postColumns.TenantId: tenantID}).Data(g.Map{postColumns.CommentCount: gdb.Raw(expression), postColumns.UpdatedAt: now}).Update(); err != nil {
+ return gerror.Wrap(err, "更新动态评论数失败")
+ }
+ }
+ if err = writeContentAudit(ctx, tx, tenantID, operatorID, "feed_comment", before.Id, before.Status, afterStatus, in.Action, now); err != nil {
+ return err
+ }
+ return writeCultivateOperation(ctx, tx, tenantID, operatorID, "content."+in.Action, "feed_comment", in.Id, before, data, in.Reason, now)
+}
+
+func writeContentAudit(ctx context.Context, tx gdb.TX, tenantID, operatorID int64, objectType string, objectID uint64, before, after, result string, now time.Time) error {
+ id, err := imtenant.NextSnowflakeID(ctx)
+ if err != nil {
+ return err
+ }
+ columns := dao.ContentAuditLog.Columns()
+ _, err = tx.Model(dao.ContentAuditLog.Table()).Data(g.Map{columns.Id: id, columns.TenantId: tenantID, columns.ObjectType: objectType, columns.ObjectId: objectID, columns.AuditSource: "manual", columns.StatusBefore: before, columns.StatusAfter: after, columns.ResultCode: result, columns.RiskLabels: "[]", columns.OperatorId: operatorID, columns.OccurredAt: now, columns.CreatedAt: now}).Insert()
+ return err
+}
+
+func (s *sSysCultivateContent) ReportList(ctx context.Context, in *sysin.CultivateReportListInp) (list []*sysin.CultivateReportListModel, totalCount int, err error) {
+ columns := dao.FeedReport.Columns()
+ mod := dao.FeedReport.Ctx(ctx).Where(columns.TenantId, cultivateTenantID(ctx))
+ if in.ObjectType != "" {
+ mod = mod.Where(columns.ObjectType, in.ObjectType)
+ }
+ if in.Status != "" {
+ mod = mod.Where(columns.Status, in.Status)
+ }
+ if in.ReporterId != "" {
+ mod = mod.Where(columns.ReporterId, in.ReporterId)
+ }
+ if len(in.CreatedAt) == 2 {
+ mod = mod.WhereBetween(columns.CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
+ }
+ var rows []*entity.FeedReport
+ if err = mod.Page(in.Page, in.PerPage).OrderDesc(columns.Id).ScanAndCount(&rows, &totalCount, false); err != nil {
+ return nil, 0, gerror.Wrap(err, "获取举报列表失败")
+ }
+ list = make([]*sysin.CultivateReportListModel, 0, len(rows))
+ for _, row := range rows {
+ list = append(list, &sysin.CultivateReportListModel{Id: strconv.FormatUint(row.Id, 10), ReporterId: strconv.FormatUint(row.ReporterId, 10), ReportedUserId: strconv.FormatUint(row.ReportedUserId, 10), ObjectType: row.ObjectType, ObjectId: row.ObjectId, ReasonCode: row.ReasonCode, Description: row.Description, EvidenceFileIds: row.EvidenceFileIds, SourcePage: row.SourcePage, Status: row.Status, HandlerId: optionalUint(row.HandlerId), HandleResult: row.HandleResult, HandledAt: row.HandledAt, CreatedAt: row.CreatedAt})
+ }
+ return
+}
+
+func (s *sSysCultivateContent) ReportHandle(ctx context.Context, in *sysin.CultivateReportHandleInp) error {
+ tenantID, operatorID := cultivateTenantID(ctx), contexts.GetUserId(ctx)
+ return dao.FeedReport.Transaction(ctx, func(txCtx context.Context, tx gdb.TX) error {
+ columns := dao.FeedReport.Columns()
+ var before entity.FeedReport
+ err := tx.Model(dao.FeedReport.Table()).Where(g.Map{columns.Id: in.ReportId, columns.TenantId: tenantID}).LockUpdate().Scan(&before)
+ if err != nil && !errors.Is(err, sql.ErrNoRows) {
+ return err
+ }
+ if before.Id == 0 {
+ return gerror.New("举报记录不存在")
+ }
+ if before.Status == "resolved" || before.Status == "rejected" {
+ return gerror.New("举报已办结,不能重复处理")
+ }
+ now := time.Now().UTC()
+ status := in.Action
+ if status == "resolve" {
+ status = "resolved"
+ }
+ data := g.Map{columns.Status: status, columns.HandlerId: operatorID, columns.HandleResult: in.HandleResult, columns.UpdatedAt: now}
+ if status == "resolved" || status == "rejected" {
+ data[columns.HandledAt] = now
+ }
+ if _, err = tx.Model(dao.FeedReport.Table()).Where(columns.Id, before.Id).Data(data).Update(); err != nil {
+ return gerror.Wrap(err, "更新举报处理状态失败")
+ }
+ return writeCultivateOperation(txCtx, tx, tenantID, operatorID, "report."+in.Action, "feed_report", in.Id, before, data, in.HandleResult, now)
+ })
+}
+
+func (s *sSysCultivateContent) LogList(ctx context.Context, in *sysin.CultivateOperationLogListInp) (list []*sysin.CultivateOperationLogListModel, totalCount int, err error) {
+ columns := dao.AdminOperationLog.Columns()
+ mod := dao.AdminOperationLog.Ctx(ctx).Where(columns.TenantId, cultivateTenantID(ctx))
+ if in.OperatorId != "" {
+ mod = mod.Where(columns.OperatorId, in.OperatorId)
+ }
+ if in.OperationType != "" {
+ mod = mod.WhereLike(columns.OperationType, "%"+strings.TrimSpace(in.OperationType)+"%")
+ }
+ if in.TargetType != "" {
+ mod = mod.Where(columns.TargetType, in.TargetType)
+ }
+ if len(in.CreatedAt) == 2 {
+ mod = mod.WhereBetween(columns.CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
+ }
+ var rows []*entity.AdminOperationLog
+ if err = mod.Page(in.Page, in.PerPage).OrderDesc(columns.Id).ScanAndCount(&rows, &totalCount, false); err != nil {
+ return nil, 0, gerror.Wrap(err, "获取操作日志失败")
+ }
+ list = make([]*sysin.CultivateOperationLogListModel, 0, len(rows))
+ for _, row := range rows {
+ list = append(list, &sysin.CultivateOperationLogListModel{Id: strconv.FormatUint(row.Id, 10), OperatorId: strconv.FormatUint(row.OperatorId, 10), OperationType: row.OperationType, TargetType: row.TargetType, TargetId: row.TargetId, RequestId: row.RequestId, BeforeSnapshot: jsonValue(row.BeforeSnapshot), AfterSnapshot: jsonValue(row.AfterSnapshot), Reason: row.Reason, CreatedAt: row.CreatedAt})
+ }
+ return
+}
+
+func optionalUint(value uint64) string {
+ if value == 0 {
+ return ""
+ }
+ return strconv.FormatUint(value, 10)
+}
+func jsonValue(value *gjson.Json) any {
+ if value == nil {
+ return nil
+ }
+ return value
+}
diff --git a/server/internal/logic/sys/cultivate_flower.go b/server/internal/logic/sys/cultivate_flower.go
new file mode 100644
index 0000000..46175b9
--- /dev/null
+++ b/server/internal/logic/sys/cultivate_flower.go
@@ -0,0 +1,191 @@
+package sys
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "strings"
+ "time"
+
+ "github.com/gogf/gf/v2/database/gdb"
+ "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"
+
+ "hotgo/internal/dao"
+ "hotgo/internal/library/contexts"
+ "hotgo/internal/library/imtenant"
+ "hotgo/internal/model/entity"
+ "hotgo/internal/model/input/sysin"
+ "hotgo/internal/service"
+)
+
+type sSysCultivateFlower struct{}
+
+func NewSysCultivateFlower() *sSysCultivateFlower { return &sSysCultivateFlower{} }
+func init() { service.RegisterSysCultivateFlower(NewSysCultivateFlower()) }
+
+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 (s *sSysCultivateFlower) List(ctx context.Context, in *sysin.CultivateFlowerListInp) (list []*sysin.CultivateFlowerListModel, totalCount int, err error) {
+ c := dao.FlowerCatalog.Columns()
+ mod := dao.FlowerCatalog.Ctx(ctx).Fields("cv_flower_catalog.*, COUNT(cv_flower_stage_rule.id) AS stage_count").LeftJoin(dao.FlowerStageRule.Table(), "cv_flower_stage_rule.flower_catalog_id=cv_flower_catalog.id AND cv_flower_stage_rule.tenant_id=cv_flower_catalog.tenant_id").Where("cv_flower_catalog."+c.TenantId, cultivateTenantID(ctx)).Group("cv_flower_catalog." + c.Id)
+ if in.FlowerCode != "" {
+ mod = mod.WhereLike("cv_flower_catalog."+c.FlowerCode, "%"+strings.TrimSpace(in.FlowerCode)+"%")
+ }
+ if in.FlowerName != "" {
+ mod = mod.WhereLike("cv_flower_catalog."+c.FlowerName, "%"+strings.TrimSpace(in.FlowerName)+"%")
+ }
+ if in.Status != "" {
+ mod = mod.Where("cv_flower_catalog."+c.Status, in.Status)
+ }
+ if in.ConfigVersion != "" {
+ mod = mod.Where("cv_flower_catalog."+c.ConfigVersion, in.ConfigVersion)
+ }
+ if len(in.CreatedAt) == 2 {
+ mod = mod.WhereBetween("cv_flower_catalog."+c.CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
+ }
+ mod = mod.Page(in.Page, in.PerPage).OrderAsc("cv_flower_catalog." + c.SortNo).OrderDesc("cv_flower_catalog." + c.Id)
+ if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
+ err = gerror.Wrap(err, "获取花种列表失败")
+ }
+ return
+}
+
+func (s *sSysCultivateFlower) View(ctx context.Context, in *sysin.CultivateFlowerViewInp) (res *sysin.CultivateFlowerViewModel, err error) {
+ c := dao.FlowerCatalog.Columns()
+ var catalog entity.FlowerCatalog
+ if err = dao.FlowerCatalog.Ctx(ctx).Where(g.Map{c.Id: in.Id, c.TenantId: cultivateTenantID(ctx)}).Scan(&catalog); err != nil {
+ return
+ }
+ if catalog.Id == 0 {
+ return nil, gerror.New("花种不存在")
+ }
+ res = &sysin.CultivateFlowerViewModel{CultivateFlowerEditInp: sysin.CultivateFlowerEditInp{Id: catalog.Id, FlowerCode: catalog.FlowerCode, FlowerName: catalog.FlowerName, FlowerDesc: catalog.FlowerDesc, IconFileId: catalog.IconFileId, Status: catalog.Status, DailyWaterLimit: catalog.DailyWaterLimit, DailyFertilizeLimit: catalog.DailyFertilizeLimit, ShareConfig: catalog.ShareConfig, SortNo: catalog.SortNo}, ConfigVersion: catalog.ConfigVersion, CreatedAt: catalog.CreatedAt, UpdatedAt: catalog.UpdatedAt}
+ rc := dao.FlowerStageRule.Columns()
+ var rules []*entity.FlowerStageRule
+ if err = dao.FlowerStageRule.Ctx(ctx).Where(g.Map{rc.TenantId: cultivateTenantID(ctx), rc.FlowerCatalogId: catalog.Id}).OrderAsc(rc.SortNo).OrderAsc(rc.Id).Scan(&rules); err != nil {
+ return
+ }
+ for _, rule := range rules {
+ actions := []string{}
+ if rule.AllowedActions != nil {
+ _ = json.Unmarshal(rule.AllowedActions.MustToJson(), &actions)
+ }
+ res.StageRules = append(res.StageRules, &sysin.CultivateStageRuleEditInp{Id: rule.Id, Stage: rule.Stage, NextStage: rule.NextStage, RequiredPoints: rule.RequiredPoints, WaterPoints: rule.WaterPoints, FertilizePoints: rule.FertilizePoints, AllowedActions: actions, ImageFileId: rule.ImageFileId, AnimationFileId: rule.AnimationFileId, ProgressTextTemplate: rule.ProgressTextTemplate, CompletionText: rule.CompletionText, SortNo: rule.SortNo})
+ }
+ return
+}
+
+func (s *sSysCultivateFlower) Edit(ctx context.Context, in *sysin.CultivateFlowerEditInp) (err error) {
+ tenantID := cultivateTenantID(ctx)
+ operatorID := contexts.GetUserId(ctx)
+ now := time.Now().UTC()
+ version := "cat_" + now.Format("20060102_150405")
+ return dao.FlowerCatalog.Transaction(ctx, func(txCtx context.Context, tx gdb.TX) error {
+ c := dao.FlowerCatalog.Columns()
+ var before entity.FlowerCatalog
+ if in.Id > 0 {
+ if e := tx.Model(dao.FlowerCatalog.Table()).Where(g.Map{c.Id: in.Id, c.TenantId: tenantID}).LockUpdate().Scan(&before); e != nil {
+ return e
+ }
+ if before.Id == 0 {
+ return gerror.New("花种不存在")
+ }
+ if before.FlowerCode != in.FlowerCode {
+ return gerror.New("花种编码创建后不可修改")
+ }
+ }
+ shareJSON := "{}"
+ if in.ShareConfig != nil {
+ shareJSON = in.ShareConfig.MustToJsonString()
+ }
+ data := g.Map{c.FlowerName: in.FlowerName, c.FlowerDesc: in.FlowerDesc, c.IconFileId: in.IconFileId, c.Status: in.Status, c.ConfigVersion: version, c.DailyWaterLimit: in.DailyWaterLimit, c.DailyFertilizeLimit: in.DailyFertilizeLimit, c.ShareConfig: shareJSON, c.SortNo: in.SortNo, c.UpdatedAt: now}
+ if in.Status == "online" {
+ data[c.OnlineAt] = now
+ data[c.OfflineAt] = nil
+ } else if in.Status == "offline" {
+ data[c.OfflineAt] = now
+ }
+ if in.Id == 0 {
+ id, e := imtenant.NextSnowflakeID(txCtx)
+ if e != nil {
+ return e
+ }
+ in.Id = uint64(id)
+ data[c.Id] = id
+ data[c.TenantId] = tenantID
+ data[c.FlowerCode] = in.FlowerCode
+ data[c.CreatedAt] = now
+ if _, e = tx.Model(dao.FlowerCatalog.Table()).Data(data).Insert(); e != nil {
+ return gerror.Wrap(e, "新增花种失败")
+ }
+ } else {
+ if _, e := tx.Model(dao.FlowerCatalog.Table()).Where(g.Map{c.Id: in.Id, c.TenantId: tenantID}).Data(data).Update(); e != nil {
+ return gerror.Wrap(e, "修改花种失败")
+ }
+ }
+ rc := dao.FlowerStageRule.Columns()
+ if _, e := tx.Model(dao.FlowerStageRule.Table()).Where(g.Map{rc.TenantId: tenantID, rc.FlowerCatalogId: in.Id}).Delete(); e != nil {
+ return e
+ }
+ for _, rule := range in.StageRules {
+ id, e := imtenant.NextSnowflakeID(txCtx)
+ if e != nil {
+ return e
+ }
+ actions, _ := json.Marshal(rule.AllowedActions)
+ _, e = tx.Model(dao.FlowerStageRule.Table()).Data(g.Map{rc.Id: id, rc.TenantId: tenantID, rc.FlowerCatalogId: in.Id, rc.Stage: rule.Stage, rc.NextStage: rule.NextStage, rc.RequiredPoints: rule.RequiredPoints, rc.WaterPoints: rule.WaterPoints, rc.FertilizePoints: rule.FertilizePoints, rc.AllowedActions: string(actions), rc.ImageFileId: rule.ImageFileId, rc.AnimationFileId: rule.AnimationFileId, rc.ProgressTextTemplate: rule.ProgressTextTemplate, rc.CompletionText: rule.CompletionText, rc.SortNo: rule.SortNo, rc.CreatedAt: now, rc.UpdatedAt: now}).Insert()
+ if e != nil {
+ return gerror.Wrap(e, "保存成长阶段失败")
+ }
+ }
+ return writeCultivateOperation(txCtx, tx, tenantID, operatorID, map[bool]string{true: "flower.edit", false: "flower.create"}[before.Id > 0], "flower", fmt.Sprint(in.Id), before, in, "", now)
+ })
+}
+
+func (s *sSysCultivateFlower) Status(ctx context.Context, in *sysin.CultivateFlowerStatusInp) error {
+ tenantID := cultivateTenantID(ctx)
+ operatorID := contexts.GetUserId(ctx)
+ now := time.Now().UTC()
+ return dao.FlowerCatalog.Transaction(ctx, func(txCtx context.Context, tx gdb.TX) error {
+ c := dao.FlowerCatalog.Columns()
+ var before entity.FlowerCatalog
+ if e := tx.Model(dao.FlowerCatalog.Table()).Where(g.Map{c.Id: in.Id, c.TenantId: tenantID}).LockUpdate().Scan(&before); e != nil {
+ return e
+ }
+ if before.Id == 0 {
+ return gerror.New("花种不存在")
+ }
+ data := g.Map{c.Status: in.Status, c.ConfigVersion: "cat_" + now.Format("20060102_150405"), c.UpdatedAt: now}
+ if in.Status == "online" {
+ data[c.OnlineAt] = now
+ data[c.OfflineAt] = nil
+ } else if in.Status == "offline" {
+ data[c.OfflineAt] = now
+ }
+ if _, e := tx.Model(dao.FlowerCatalog.Table()).Where(c.Id, in.Id).Data(data).Update(); e != nil {
+ return e
+ }
+ return writeCultivateOperation(txCtx, tx, tenantID, operatorID, "flower.status", "flower", fmt.Sprint(in.Id), before, data, in.Reason, now)
+ })
+}
+
+func writeCultivateOperation(ctx context.Context, tx gdb.TX, tenantID, operatorID int64, operation, targetType, targetID string, before, after any, reason string, now time.Time) error {
+ id, e := imtenant.NextSnowflakeID(ctx)
+ if e != nil {
+ return e
+ }
+ beforeJSON, _ := json.Marshal(before)
+ afterJSON, _ := json.Marshal(after)
+ c := dao.AdminOperationLog.Columns()
+ _, e = tx.Model(dao.AdminOperationLog.Table()).Data(g.Map{c.Id: id, c.TenantId: tenantID, c.OperatorId: operatorID, c.OperationType: operation, c.TargetType: targetType, c.TargetId: targetID, c.RequestId: gctx.CtxId(ctx), c.BeforeSnapshot: gjson.New(beforeJSON).MustToJsonString(), c.AfterSnapshot: gjson.New(afterJSON).MustToJsonString(), c.Reason: reason, c.IpAddress: "", c.UserAgent: "", c.CreatedAt: now}).Insert()
+ return e
+}
diff --git a/server/internal/model/do/admin_operation_log.go b/server/internal/model/do/admin_operation_log.go
new file mode 100644
index 0000000..c399a41
--- /dev/null
+++ b/server/internal/model/do/admin_operation_log.go
@@ -0,0 +1,29 @@
+// =================================================================================
+// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
+// =================================================================================
+
+package do
+
+import (
+ "github.com/gogf/gf/v2/encoding/gjson"
+ "github.com/gogf/gf/v2/frame/g"
+ "github.com/gogf/gf/v2/os/gtime"
+)
+
+// AdminOperationLog is the golang structure of table cv_admin_operation_log for DAO operations like Where/Data.
+type AdminOperationLog struct {
+ g.Meta `orm:"table:cv_admin_operation_log, do:true"`
+ Id any //
+ TenantId any //
+ OperatorId any //
+ OperationType any //
+ TargetType any //
+ TargetId any //
+ RequestId any //
+ BeforeSnapshot *gjson.Json //
+ AfterSnapshot *gjson.Json //
+ Reason any //
+ IpAddress any //
+ UserAgent any //
+ CreatedAt *gtime.Time //
+}
diff --git a/server/internal/model/do/content_audit_log.go b/server/internal/model/do/content_audit_log.go
new file mode 100644
index 0000000..fefee99
--- /dev/null
+++ b/server/internal/model/do/content_audit_log.go
@@ -0,0 +1,29 @@
+// =================================================================================
+// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
+// =================================================================================
+
+package do
+
+import (
+ "github.com/gogf/gf/v2/encoding/gjson"
+ "github.com/gogf/gf/v2/frame/g"
+ "github.com/gogf/gf/v2/os/gtime"
+)
+
+// ContentAuditLog is the golang structure of table cv_content_audit_log for DAO operations like Where/Data.
+type ContentAuditLog struct {
+ g.Meta `orm:"table:cv_content_audit_log, do:true"`
+ Id any //
+ TenantId any //
+ ObjectType any //
+ ObjectId any //
+ AuditRequestId any //
+ AuditSource any //
+ StatusBefore any //
+ StatusAfter any //
+ ResultCode any //
+ RiskLabels *gjson.Json //
+ OperatorId any //
+ OccurredAt *gtime.Time //
+ CreatedAt *gtime.Time //
+}
diff --git a/server/internal/model/do/feed_comment.go b/server/internal/model/do/feed_comment.go
new file mode 100644
index 0000000..d3813b0
--- /dev/null
+++ b/server/internal/model/do/feed_comment.go
@@ -0,0 +1,29 @@
+// =================================================================================
+// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
+// =================================================================================
+
+package do
+
+import (
+ "github.com/gogf/gf/v2/frame/g"
+ "github.com/gogf/gf/v2/os/gtime"
+)
+
+// FeedComment is the golang structure of table cv_feed_comment for DAO operations like Where/Data.
+type FeedComment struct {
+ g.Meta `orm:"table:cv_feed_comment, do:true"`
+ Id any //
+ TenantId any //
+ PostId any //
+ AuthorId any //
+ ContentText any //
+ Status any //
+ AuditStatus any //
+ AuditRequestId any //
+ DeletedBy any //
+ DeleteReason any //
+ PublishedAt *gtime.Time //
+ DeletedAt *gtime.Time //
+ CreatedAt *gtime.Time //
+ UpdatedAt *gtime.Time //
+}
diff --git a/server/internal/model/do/feed_media.go b/server/internal/model/do/feed_media.go
new file mode 100644
index 0000000..89740e3
--- /dev/null
+++ b/server/internal/model/do/feed_media.go
@@ -0,0 +1,29 @@
+// =================================================================================
+// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
+// =================================================================================
+
+package do
+
+import (
+ "github.com/gogf/gf/v2/frame/g"
+ "github.com/gogf/gf/v2/os/gtime"
+)
+
+// FeedMedia is the golang structure of table cv_feed_media for DAO operations like Where/Data.
+type FeedMedia struct {
+ g.Meta `orm:"table:cv_feed_media, do:true"`
+ Id any //
+ TenantId any //
+ PostId any //
+ UserId any //
+ MediaType any //
+ FileId any //
+ ThumbnailFileId any //
+ Width any //
+ Height any //
+ SortNo any //
+ AuditStatus any //
+ AuditRequestId any //
+ CreatedAt *gtime.Time //
+ UpdatedAt *gtime.Time //
+}
diff --git a/server/internal/model/do/feed_post.go b/server/internal/model/do/feed_post.go
new file mode 100644
index 0000000..2d29103
--- /dev/null
+++ b/server/internal/model/do/feed_post.go
@@ -0,0 +1,35 @@
+// =================================================================================
+// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
+// =================================================================================
+
+package do
+
+import (
+ "github.com/gogf/gf/v2/frame/g"
+ "github.com/gogf/gf/v2/os/gtime"
+)
+
+// FeedPost is the golang structure of table cv_feed_post for DAO operations like Where/Data.
+type FeedPost struct {
+ g.Meta `orm:"table:cv_feed_post, do:true"`
+ Id any //
+ TenantId any //
+ AuthorId any //
+ ContentText any //
+ FlowerCode any //
+ SourceType any //
+ SourceId any //
+ Visibility any //
+ Status any //
+ AuditStatus any //
+ AuditRequestId any //
+ RejectReasonCode any //
+ LikeCount any //
+ CommentCount any //
+ Version any //
+ PublishedAt *gtime.Time //
+ DeletedAt *gtime.Time //
+ TakenDownAt *gtime.Time //
+ CreatedAt *gtime.Time //
+ UpdatedAt *gtime.Time //
+}
diff --git a/server/internal/model/do/feed_report.go b/server/internal/model/do/feed_report.go
new file mode 100644
index 0000000..f728602
--- /dev/null
+++ b/server/internal/model/do/feed_report.go
@@ -0,0 +1,32 @@
+// =================================================================================
+// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
+// =================================================================================
+
+package do
+
+import (
+ "github.com/gogf/gf/v2/encoding/gjson"
+ "github.com/gogf/gf/v2/frame/g"
+ "github.com/gogf/gf/v2/os/gtime"
+)
+
+// FeedReport is the golang structure of table cv_feed_report for DAO operations like Where/Data.
+type FeedReport struct {
+ g.Meta `orm:"table:cv_feed_report, do:true"`
+ Id any //
+ TenantId any //
+ ReporterId any //
+ ReportedUserId any //
+ ObjectType any //
+ ObjectId any //
+ ReasonCode any //
+ Description any //
+ EvidenceFileIds *gjson.Json //
+ SourcePage any //
+ Status any //
+ HandlerId any //
+ HandleResult any //
+ HandledAt *gtime.Time //
+ CreatedAt *gtime.Time //
+ UpdatedAt *gtime.Time //
+}
diff --git a/server/internal/model/do/flower_catalog.go b/server/internal/model/do/flower_catalog.go
new file mode 100644
index 0000000..09f6bf0
--- /dev/null
+++ b/server/internal/model/do/flower_catalog.go
@@ -0,0 +1,32 @@
+// =================================================================================
+// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
+// =================================================================================
+
+package do
+
+import (
+ "github.com/gogf/gf/v2/encoding/gjson"
+ "github.com/gogf/gf/v2/frame/g"
+ "github.com/gogf/gf/v2/os/gtime"
+)
+
+// FlowerCatalog is the golang structure of table cv_flower_catalog for DAO operations like Where/Data.
+type FlowerCatalog struct {
+ g.Meta `orm:"table:cv_flower_catalog, do:true"`
+ Id any //
+ TenantId any //
+ FlowerCode any //
+ FlowerName any //
+ FlowerDesc any //
+ IconFileId any //
+ Status any //
+ ConfigVersion any //
+ DailyWaterLimit any //
+ DailyFertilizeLimit any //
+ ShareConfig *gjson.Json //
+ SortNo any //
+ OnlineAt *gtime.Time //
+ OfflineAt *gtime.Time //
+ CreatedAt *gtime.Time //
+ UpdatedAt *gtime.Time //
+}
diff --git a/server/internal/model/do/flower_stage_rule.go b/server/internal/model/do/flower_stage_rule.go
new file mode 100644
index 0000000..8c1bbd6
--- /dev/null
+++ b/server/internal/model/do/flower_stage_rule.go
@@ -0,0 +1,32 @@
+// =================================================================================
+// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
+// =================================================================================
+
+package do
+
+import (
+ "github.com/gogf/gf/v2/encoding/gjson"
+ "github.com/gogf/gf/v2/frame/g"
+ "github.com/gogf/gf/v2/os/gtime"
+)
+
+// FlowerStageRule is the golang structure of table cv_flower_stage_rule for DAO operations like Where/Data.
+type FlowerStageRule struct {
+ g.Meta `orm:"table:cv_flower_stage_rule, do:true"`
+ Id any //
+ TenantId any //
+ FlowerCatalogId any //
+ Stage any //
+ NextStage any //
+ RequiredPoints any //
+ WaterPoints any //
+ FertilizePoints any //
+ AllowedActions *gjson.Json //
+ ImageFileId any //
+ AnimationFileId any //
+ ProgressTextTemplate any //
+ CompletionText any //
+ SortNo any //
+ CreatedAt *gtime.Time //
+ UpdatedAt *gtime.Time //
+}
diff --git a/server/internal/model/entity/admin_operation_log.go b/server/internal/model/entity/admin_operation_log.go
new file mode 100644
index 0000000..8d00338
--- /dev/null
+++ b/server/internal/model/entity/admin_operation_log.go
@@ -0,0 +1,27 @@
+// =================================================================================
+// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
+// =================================================================================
+
+package entity
+
+import (
+ "github.com/gogf/gf/v2/encoding/gjson"
+ "github.com/gogf/gf/v2/os/gtime"
+)
+
+// AdminOperationLog is the golang structure for table admin_operation_log.
+type AdminOperationLog struct {
+ Id uint64 `json:"id" orm:"id" description:""`
+ TenantId uint64 `json:"tenantId" orm:"tenant_id" description:""`
+ OperatorId uint64 `json:"operatorId" orm:"operator_id" description:""`
+ OperationType string `json:"operationType" orm:"operation_type" description:""`
+ TargetType string `json:"targetType" orm:"target_type" description:""`
+ TargetId string `json:"targetId" orm:"target_id" description:""`
+ RequestId string `json:"requestId" orm:"request_id" description:""`
+ BeforeSnapshot *gjson.Json `json:"beforeSnapshot" orm:"before_snapshot" description:""`
+ AfterSnapshot *gjson.Json `json:"afterSnapshot" orm:"after_snapshot" description:""`
+ Reason string `json:"reason" orm:"reason" description:""`
+ IpAddress string `json:"ipAddress" orm:"ip_address" description:""`
+ UserAgent string `json:"userAgent" orm:"user_agent" description:""`
+ CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:""`
+}
diff --git a/server/internal/model/entity/content_audit_log.go b/server/internal/model/entity/content_audit_log.go
new file mode 100644
index 0000000..a2dacc5
--- /dev/null
+++ b/server/internal/model/entity/content_audit_log.go
@@ -0,0 +1,27 @@
+// =================================================================================
+// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
+// =================================================================================
+
+package entity
+
+import (
+ "github.com/gogf/gf/v2/encoding/gjson"
+ "github.com/gogf/gf/v2/os/gtime"
+)
+
+// ContentAuditLog is the golang structure for table content_audit_log.
+type ContentAuditLog struct {
+ Id uint64 `json:"id" orm:"id" description:""`
+ TenantId uint64 `json:"tenantId" orm:"tenant_id" description:""`
+ ObjectType string `json:"objectType" orm:"object_type" description:""`
+ ObjectId uint64 `json:"objectId" orm:"object_id" description:""`
+ AuditRequestId string `json:"auditRequestId" orm:"audit_request_id" description:""`
+ AuditSource string `json:"auditSource" orm:"audit_source" description:""`
+ StatusBefore string `json:"statusBefore" orm:"status_before" description:""`
+ StatusAfter string `json:"statusAfter" orm:"status_after" description:""`
+ ResultCode string `json:"resultCode" orm:"result_code" description:""`
+ RiskLabels *gjson.Json `json:"riskLabels" orm:"risk_labels" description:""`
+ OperatorId uint64 `json:"operatorId" orm:"operator_id" description:""`
+ OccurredAt *gtime.Time `json:"occurredAt" orm:"occurred_at" description:""`
+ CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:""`
+}
diff --git a/server/internal/model/entity/feed_comment.go b/server/internal/model/entity/feed_comment.go
new file mode 100644
index 0000000..1d1b250
--- /dev/null
+++ b/server/internal/model/entity/feed_comment.go
@@ -0,0 +1,27 @@
+// =================================================================================
+// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
+// =================================================================================
+
+package entity
+
+import (
+ "github.com/gogf/gf/v2/os/gtime"
+)
+
+// FeedComment is the golang structure for table feed_comment.
+type FeedComment struct {
+ Id uint64 `json:"id" orm:"id" description:""`
+ TenantId uint64 `json:"tenantId" orm:"tenant_id" description:""`
+ PostId uint64 `json:"postId" orm:"post_id" description:""`
+ AuthorId uint64 `json:"authorId" orm:"author_id" description:""`
+ ContentText string `json:"contentText" orm:"content_text" description:""`
+ Status string `json:"status" orm:"status" description:""`
+ AuditStatus string `json:"auditStatus" orm:"audit_status" description:""`
+ AuditRequestId string `json:"auditRequestId" orm:"audit_request_id" description:""`
+ DeletedBy uint64 `json:"deletedBy" orm:"deleted_by" description:""`
+ DeleteReason string `json:"deleteReason" orm:"delete_reason" description:""`
+ PublishedAt *gtime.Time `json:"publishedAt" orm:"published_at" description:""`
+ DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:""`
+ CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:""`
+ UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:""`
+}
diff --git a/server/internal/model/entity/feed_media.go b/server/internal/model/entity/feed_media.go
new file mode 100644
index 0000000..3fb3343
--- /dev/null
+++ b/server/internal/model/entity/feed_media.go
@@ -0,0 +1,27 @@
+// =================================================================================
+// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
+// =================================================================================
+
+package entity
+
+import (
+ "github.com/gogf/gf/v2/os/gtime"
+)
+
+// FeedMedia is the golang structure for table feed_media.
+type FeedMedia struct {
+ Id uint64 `json:"id" orm:"id" description:""`
+ TenantId uint64 `json:"tenantId" orm:"tenant_id" description:""`
+ PostId uint64 `json:"postId" orm:"post_id" description:""`
+ UserId uint64 `json:"userId" orm:"user_id" description:""`
+ MediaType string `json:"mediaType" orm:"media_type" description:""`
+ FileId string `json:"fileId" orm:"file_id" description:""`
+ ThumbnailFileId string `json:"thumbnailFileId" orm:"thumbnail_file_id" description:""`
+ Width uint `json:"width" orm:"width" description:""`
+ Height uint `json:"height" orm:"height" description:""`
+ SortNo uint `json:"sortNo" orm:"sort_no" description:""`
+ AuditStatus string `json:"auditStatus" orm:"audit_status" description:""`
+ AuditRequestId string `json:"auditRequestId" orm:"audit_request_id" description:""`
+ CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:""`
+ UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:""`
+}
diff --git a/server/internal/model/entity/feed_post.go b/server/internal/model/entity/feed_post.go
new file mode 100644
index 0000000..87be5d2
--- /dev/null
+++ b/server/internal/model/entity/feed_post.go
@@ -0,0 +1,33 @@
+// =================================================================================
+// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
+// =================================================================================
+
+package entity
+
+import (
+ "github.com/gogf/gf/v2/os/gtime"
+)
+
+// FeedPost is the golang structure for table feed_post.
+type FeedPost struct {
+ Id uint64 `json:"id" orm:"id" description:""`
+ TenantId uint64 `json:"tenantId" orm:"tenant_id" description:""`
+ AuthorId uint64 `json:"authorId" orm:"author_id" description:""`
+ ContentText string `json:"contentText" orm:"content_text" description:""`
+ FlowerCode string `json:"flowerCode" orm:"flower_code" description:""`
+ SourceType string `json:"sourceType" orm:"source_type" description:""`
+ SourceId uint64 `json:"sourceId" orm:"source_id" description:""`
+ Visibility string `json:"visibility" orm:"visibility" description:""`
+ Status string `json:"status" orm:"status" description:""`
+ AuditStatus string `json:"auditStatus" orm:"audit_status" description:""`
+ AuditRequestId string `json:"auditRequestId" orm:"audit_request_id" description:""`
+ RejectReasonCode string `json:"rejectReasonCode" orm:"reject_reason_code" description:""`
+ LikeCount uint `json:"likeCount" orm:"like_count" description:""`
+ CommentCount uint `json:"commentCount" orm:"comment_count" description:""`
+ Version uint `json:"version" orm:"version" description:""`
+ PublishedAt *gtime.Time `json:"publishedAt" orm:"published_at" description:""`
+ DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:""`
+ TakenDownAt *gtime.Time `json:"takenDownAt" orm:"taken_down_at" description:""`
+ CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:""`
+ UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:""`
+}
diff --git a/server/internal/model/entity/feed_report.go b/server/internal/model/entity/feed_report.go
new file mode 100644
index 0000000..68bd200
--- /dev/null
+++ b/server/internal/model/entity/feed_report.go
@@ -0,0 +1,30 @@
+// =================================================================================
+// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
+// =================================================================================
+
+package entity
+
+import (
+ "github.com/gogf/gf/v2/encoding/gjson"
+ "github.com/gogf/gf/v2/os/gtime"
+)
+
+// FeedReport is the golang structure for table feed_report.
+type FeedReport struct {
+ Id uint64 `json:"id" orm:"id" description:""`
+ TenantId uint64 `json:"tenantId" orm:"tenant_id" description:""`
+ ReporterId uint64 `json:"reporterId" orm:"reporter_id" description:""`
+ ReportedUserId uint64 `json:"reportedUserId" orm:"reported_user_id" description:""`
+ ObjectType string `json:"objectType" orm:"object_type" description:""`
+ ObjectId string `json:"objectId" orm:"object_id" description:""`
+ ReasonCode string `json:"reasonCode" orm:"reason_code" description:""`
+ Description string `json:"description" orm:"description" description:""`
+ EvidenceFileIds *gjson.Json `json:"evidenceFileIds" orm:"evidence_file_ids" description:""`
+ SourcePage string `json:"sourcePage" orm:"source_page" description:""`
+ Status string `json:"status" orm:"status" description:""`
+ HandlerId uint64 `json:"handlerId" orm:"handler_id" description:""`
+ HandleResult string `json:"handleResult" orm:"handle_result" description:""`
+ HandledAt *gtime.Time `json:"handledAt" orm:"handled_at" description:""`
+ CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:""`
+ UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:""`
+}
diff --git a/server/internal/model/entity/flower_catalog.go b/server/internal/model/entity/flower_catalog.go
new file mode 100644
index 0000000..3703e7c
--- /dev/null
+++ b/server/internal/model/entity/flower_catalog.go
@@ -0,0 +1,30 @@
+// =================================================================================
+// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
+// =================================================================================
+
+package entity
+
+import (
+ "github.com/gogf/gf/v2/encoding/gjson"
+ "github.com/gogf/gf/v2/os/gtime"
+)
+
+// FlowerCatalog is the golang structure for table flower_catalog.
+type FlowerCatalog struct {
+ Id uint64 `json:"id" orm:"id" description:""`
+ TenantId uint64 `json:"tenantId" orm:"tenant_id" description:""`
+ FlowerCode string `json:"flowerCode" orm:"flower_code" description:""`
+ FlowerName string `json:"flowerName" orm:"flower_name" description:""`
+ FlowerDesc string `json:"flowerDesc" orm:"flower_desc" description:""`
+ IconFileId string `json:"iconFileId" orm:"icon_file_id" description:""`
+ Status string `json:"status" orm:"status" description:""`
+ ConfigVersion string `json:"configVersion" orm:"config_version" description:""`
+ DailyWaterLimit uint `json:"dailyWaterLimit" orm:"daily_water_limit" description:""`
+ DailyFertilizeLimit uint `json:"dailyFertilizeLimit" orm:"daily_fertilize_limit" description:""`
+ ShareConfig *gjson.Json `json:"shareConfig" orm:"share_config" description:""`
+ SortNo int `json:"sortNo" orm:"sort_no" description:""`
+ OnlineAt *gtime.Time `json:"onlineAt" orm:"online_at" description:""`
+ OfflineAt *gtime.Time `json:"offlineAt" orm:"offline_at" description:""`
+ CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:""`
+ UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:""`
+}
diff --git a/server/internal/model/entity/flower_stage_rule.go b/server/internal/model/entity/flower_stage_rule.go
new file mode 100644
index 0000000..bd9bfbd
--- /dev/null
+++ b/server/internal/model/entity/flower_stage_rule.go
@@ -0,0 +1,30 @@
+// =================================================================================
+// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
+// =================================================================================
+
+package entity
+
+import (
+ "github.com/gogf/gf/v2/encoding/gjson"
+ "github.com/gogf/gf/v2/os/gtime"
+)
+
+// FlowerStageRule is the golang structure for table flower_stage_rule.
+type FlowerStageRule struct {
+ Id uint64 `json:"id" orm:"id" description:""`
+ TenantId uint64 `json:"tenantId" orm:"tenant_id" description:""`
+ FlowerCatalogId uint64 `json:"flowerCatalogId" orm:"flower_catalog_id" description:""`
+ Stage string `json:"stage" orm:"stage" description:""`
+ NextStage string `json:"nextStage" orm:"next_stage" description:""`
+ RequiredPoints uint `json:"requiredPoints" orm:"required_points" description:""`
+ WaterPoints uint `json:"waterPoints" orm:"water_points" description:""`
+ FertilizePoints uint `json:"fertilizePoints" orm:"fertilize_points" description:""`
+ AllowedActions *gjson.Json `json:"allowedActions" orm:"allowed_actions" description:""`
+ ImageFileId string `json:"imageFileId" orm:"image_file_id" description:""`
+ AnimationFileId string `json:"animationFileId" orm:"animation_file_id" description:""`
+ ProgressTextTemplate string `json:"progressTextTemplate" orm:"progress_text_template" description:""`
+ CompletionText string `json:"completionText" orm:"completion_text" description:""`
+ SortNo int `json:"sortNo" orm:"sort_no" description:""`
+ CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:""`
+ UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:""`
+}
diff --git a/server/internal/model/input/sysin/cultivate_content.go b/server/internal/model/input/sysin/cultivate_content.go
new file mode 100644
index 0000000..10946a1
--- /dev/null
+++ b/server/internal/model/input/sysin/cultivate_content.go
@@ -0,0 +1,149 @@
+package sysin
+
+import (
+ "context"
+ "strconv"
+ "strings"
+
+ "github.com/gogf/gf/v2/errors/gerror"
+ "github.com/gogf/gf/v2/os/gtime"
+
+ "hotgo/internal/model/input/form"
+)
+
+type CultivateContentListInp struct {
+ form.PageReq
+ ObjectType string `json:"objectType" d:"feed_post"`
+ AuthorId string `json:"authorId"`
+ Status string `json:"status"`
+ Keyword string `json:"keyword"`
+ CreatedAt []*gtime.Time `json:"createdAt"`
+}
+
+func (in *CultivateContentListInp) Filter(context.Context) error {
+ in.ObjectType = strings.TrimSpace(in.ObjectType)
+ if in.ObjectType != "feed_post" && in.ObjectType != "feed_comment" {
+ return gerror.New("内容类型不正确")
+ }
+ if in.AuthorId != "" {
+ if _, err := strconv.ParseUint(in.AuthorId, 10, 64); err != nil {
+ return gerror.New("作者ID不正确")
+ }
+ }
+ return nil
+}
+
+type CultivateContentListModel struct {
+ Id string `json:"id"`
+ ObjectType string `json:"objectType"`
+ PostId string `json:"postId"`
+ AuthorId string `json:"authorId"`
+ ContentText string `json:"contentText"`
+ FlowerCode string `json:"flowerCode"`
+ Visibility string `json:"visibility"`
+ Status string `json:"status"`
+ AuditStatus string `json:"auditStatus"`
+ RejectReason string `json:"rejectReason"`
+ LikeCount uint `json:"likeCount"`
+ CommentCount uint `json:"commentCount"`
+ CreatedAt *gtime.Time `json:"createdAt"`
+ UpdatedAt *gtime.Time `json:"updatedAt"`
+}
+
+type CultivateContentReviewInp struct {
+ ObjectType string `json:"objectType" v:"required|in:feed_post,feed_comment#内容类型不能为空|内容类型不正确"`
+ Id string `json:"id" v:"required#内容ID不能为空"`
+ Action string `json:"action" v:"required|in:approve,reject,take_down,restore#审核动作不能为空|审核动作不正确"`
+ Reason string `json:"reason"`
+ ObjectId uint64 `json:"-"`
+}
+
+func (in *CultivateContentReviewInp) Filter(context.Context) error {
+ id, err := strconv.ParseUint(strings.TrimSpace(in.Id), 10, 64)
+ if err != nil || id == 0 {
+ return gerror.New("内容ID不正确")
+ }
+ in.ObjectId = id
+ in.Reason = strings.TrimSpace(in.Reason)
+ if (in.Action == "reject" || in.Action == "take_down") && in.Reason == "" {
+ return gerror.New("拒绝或下架时必须填写原因")
+ }
+ return nil
+}
+
+type CultivateReportListInp struct {
+ form.PageReq
+ ObjectType string `json:"objectType"`
+ Status string `json:"status"`
+ ReporterId string `json:"reporterId"`
+ CreatedAt []*gtime.Time `json:"createdAt"`
+}
+
+func (in *CultivateReportListInp) Filter(context.Context) error {
+ if in.ReporterId != "" {
+ if _, err := strconv.ParseUint(in.ReporterId, 10, 64); err != nil {
+ return gerror.New("举报人ID不正确")
+ }
+ }
+ return nil
+}
+
+type CultivateReportListModel struct {
+ Id string `json:"id"`
+ ReporterId string `json:"reporterId"`
+ ReportedUserId string `json:"reportedUserId"`
+ ObjectType string `json:"objectType"`
+ ObjectId string `json:"objectId"`
+ ReasonCode string `json:"reasonCode"`
+ Description string `json:"description"`
+ EvidenceFileIds any `json:"evidenceFileIds"`
+ SourcePage string `json:"sourcePage"`
+ Status string `json:"status"`
+ HandlerId string `json:"handlerId"`
+ HandleResult string `json:"handleResult"`
+ HandledAt *gtime.Time `json:"handledAt"`
+ CreatedAt *gtime.Time `json:"createdAt"`
+}
+
+type CultivateReportHandleInp struct {
+ Id string `json:"id" v:"required#举报ID不能为空"`
+ Action string `json:"action" v:"required|in:processing,resolve,reject#处理动作不能为空|处理动作不正确"`
+ HandleResult string `json:"handleResult"`
+ ReportId uint64 `json:"-"`
+}
+
+func (in *CultivateReportHandleInp) Filter(context.Context) error {
+ id, err := strconv.ParseUint(strings.TrimSpace(in.Id), 10, 64)
+ if err != nil || id == 0 {
+ return gerror.New("举报ID不正确")
+ }
+ in.ReportId = id
+ in.HandleResult = strings.TrimSpace(in.HandleResult)
+ if in.Action != "processing" && in.HandleResult == "" {
+ return gerror.New("办结举报时必须填写处理结果")
+ }
+ return nil
+}
+
+type CultivateOperationLogListInp struct {
+ form.PageReq
+ OperatorId string `json:"operatorId"`
+ OperationType string `json:"operationType"`
+ TargetType string `json:"targetType"`
+ CreatedAt []*gtime.Time `json:"createdAt"`
+}
+
+func (*CultivateOperationLogListInp) Filter(context.Context) error { return nil }
+
+type CultivateOperationLogListModel struct {
+ Id string `json:"id"`
+ OperatorId string `json:"operatorId"`
+ OperationType string `json:"operationType"`
+ TargetType string `json:"targetType"`
+ TargetId string `json:"targetId"`
+ RequestId string `json:"requestId"`
+ BeforeSnapshot any `json:"beforeSnapshot"`
+ AfterSnapshot any `json:"afterSnapshot"`
+ Reason string `json:"reason"`
+ CreatedAt *gtime.Time `json:"createdAt"`
+}
diff --git a/server/internal/model/input/sysin/cultivate_flower.go b/server/internal/model/input/sysin/cultivate_flower.go
new file mode 100644
index 0000000..c0764b7
--- /dev/null
+++ b/server/internal/model/input/sysin/cultivate_flower.go
@@ -0,0 +1,155 @@
+package sysin
+
+import (
+ "context"
+ "strings"
+
+ "github.com/gogf/gf/v2/encoding/gjson"
+ "github.com/gogf/gf/v2/errors/gerror"
+ "github.com/gogf/gf/v2/os/gtime"
+
+ "hotgo/internal/model/input/form"
+)
+
+type CultivateStageRuleEditInp struct {
+ Id uint64 `json:"id"`
+ Stage string `json:"stage"`
+ NextStage string `json:"nextStage"`
+ RequiredPoints uint `json:"requiredPoints"`
+ WaterPoints uint `json:"waterPoints"`
+ FertilizePoints uint `json:"fertilizePoints"`
+ AllowedActions []string `json:"allowedActions"`
+ ImageFileId string `json:"imageFileId"`
+ AnimationFileId string `json:"animationFileId"`
+ ProgressTextTemplate string `json:"progressTextTemplate"`
+ CompletionText string `json:"completionText"`
+ SortNo int `json:"sortNo"`
+}
+
+type CultivateFlowerEditInp struct {
+ Id uint64 `json:"id"`
+ FlowerCode string `json:"flowerCode"`
+ FlowerName string `json:"flowerName"`
+ FlowerDesc string `json:"flowerDesc"`
+ IconFileId string `json:"iconFileId"`
+ Status string `json:"status"`
+ DailyWaterLimit uint `json:"dailyWaterLimit"`
+ DailyFertilizeLimit uint `json:"dailyFertilizeLimit"`
+ ShareConfig *gjson.Json `json:"shareConfig"`
+ SortNo int `json:"sortNo"`
+ StageRules []*CultivateStageRuleEditInp `json:"stageRules"`
+}
+
+func (in *CultivateFlowerEditInp) Filter(ctx context.Context) error {
+ in.FlowerCode = strings.TrimSpace(in.FlowerCode)
+ in.FlowerName = strings.TrimSpace(in.FlowerName)
+ if in.FlowerCode == "" || in.FlowerName == "" {
+ return gerror.New("花种编码和名称不能为空")
+ }
+ validStatus := map[string]bool{"online": true, "offline": true, "activity_only": true, "maintenance": true}
+ if !validStatus[in.Status] {
+ return gerror.New("花种状态不正确")
+ }
+ if len(in.StageRules) == 0 {
+ return gerror.New("至少配置一个成长阶段")
+ }
+ validStage := map[string]bool{"seed": true, "sprout": true, "bud": true, "bloom": true}
+ validAction := map[string]bool{"water": true, "fertilize": true}
+ seen := map[string]bool{}
+ for _, rule := range in.StageRules {
+ if rule == nil || !validStage[rule.Stage] {
+ return gerror.New("成长阶段不正确")
+ }
+ if seen[rule.Stage] {
+ return gerror.New("成长阶段不能重复")
+ }
+ seen[rule.Stage] = true
+ if rule.Stage != "bloom" && rule.RequiredPoints == 0 {
+ return gerror.New("非开花阶段升级阈值必须大于0")
+ }
+ if rule.NextStage != "" && !validStage[rule.NextStage] {
+ return gerror.New("下一阶段不正确")
+ }
+ if rule.Stage != "bloom" && len(rule.AllowedActions) == 0 {
+ return gerror.New("非开花阶段至少允许一种成长动作")
+ }
+ for _, action := range rule.AllowedActions {
+ if !validAction[action] {
+ return gerror.New("成长动作不正确")
+ }
+ }
+ }
+ stage := "seed"
+ visited := map[string]bool{}
+ for stage != "" {
+ if visited[stage] {
+ return gerror.New("成长阶段链不能形成循环")
+ }
+ visited[stage] = true
+ var next string
+ found := false
+ for _, r := range in.StageRules {
+ if r.Stage == stage {
+ next = r.NextStage
+ found = true
+ break
+ }
+ }
+ if !found {
+ return gerror.New("成长阶段链必须从 seed 连续配置")
+ }
+ stage = next
+ }
+ if !visited["bloom"] {
+ return gerror.New("成长阶段链必须以 bloom 结束")
+ }
+ return nil
+}
+
+type CultivateFlowerListInp struct {
+ form.PageReq
+ FlowerCode string `json:"flowerCode"`
+ FlowerName string `json:"flowerName"`
+ Status string `json:"status"`
+ ConfigVersion string `json:"configVersion"`
+ CreatedAt []*gtime.Time `json:"createdAt"`
+}
+
+func (*CultivateFlowerListInp) Filter(context.Context) error { return nil }
+
+type CultivateFlowerListModel struct {
+ Id uint64 `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"`
+ SortNo int `json:"sortNo"`
+ StageCount int `json:"stageCount"`
+ CreatedAt *gtime.Time `json:"createdAt"`
+ UpdatedAt *gtime.Time `json:"updatedAt"`
+}
+
+type CultivateFlowerViewInp struct {
+ Id uint64 `json:"id" v:"required#ID不能为空"`
+}
+
+func (*CultivateFlowerViewInp) Filter(context.Context) error { return nil }
+
+type CultivateFlowerViewModel struct {
+ CultivateFlowerEditInp
+ ConfigVersion string `json:"configVersion"`
+ CreatedAt *gtime.Time `json:"createdAt"`
+ UpdatedAt *gtime.Time `json:"updatedAt"`
+}
+
+type CultivateFlowerStatusInp struct {
+ Id uint64 `json:"id" v:"required#ID不能为空"`
+ Status string `json:"status" v:"required|in:online,offline,activity_only,maintenance#状态不能为空|状态不正确"`
+ Reason string `json:"reason"`
+}
+
+func (*CultivateFlowerStatusInp) Filter(context.Context) error { return nil }
diff --git a/server/internal/router/genrouter/cultivate_content.go b/server/internal/router/genrouter/cultivate_content.go
new file mode 100644
index 0000000..c92986a
--- /dev/null
+++ b/server/internal/router/genrouter/cultivate_content.go
@@ -0,0 +1,5 @@
+package genrouter
+
+import "hotgo/internal/controller/admin/sys"
+
+func init() { LoginRequiredRouter = append(LoginRequiredRouter, sys.CultivateContent) }
diff --git a/server/internal/router/genrouter/cultivate_flower.go b/server/internal/router/genrouter/cultivate_flower.go
new file mode 100644
index 0000000..d63341c
--- /dev/null
+++ b/server/internal/router/genrouter/cultivate_flower.go
@@ -0,0 +1,5 @@
+package genrouter
+
+import "hotgo/internal/controller/admin/sys"
+
+func init() { LoginRequiredRouter = append(LoginRequiredRouter, sys.CultivateFlower) }
diff --git a/server/internal/service/sys.go b/server/internal/service/sys.go
index 303f564..0ff1412 100644
--- a/server/internal/service/sys.go
+++ b/server/internal/service/sys.go
@@ -172,6 +172,19 @@ type (
// Switch 更新CURD列表开关
Switch(ctx context.Context, in *sysin.CurdDemoSwitchInp) (err error)
}
+ ISysCultivateFlower interface {
+ List(ctx context.Context, in *sysin.CultivateFlowerListInp) (list []*sysin.CultivateFlowerListModel, totalCount int, err error)
+ View(ctx context.Context, in *sysin.CultivateFlowerViewInp) (res *sysin.CultivateFlowerViewModel, err error)
+ Edit(ctx context.Context, in *sysin.CultivateFlowerEditInp) (err error)
+ Status(ctx context.Context, in *sysin.CultivateFlowerStatusInp) (err error)
+ }
+ ISysCultivateContent interface {
+ List(ctx context.Context, in *sysin.CultivateContentListInp) (list []*sysin.CultivateContentListModel, totalCount int, err error)
+ Review(ctx context.Context, in *sysin.CultivateContentReviewInp) error
+ ReportList(ctx context.Context, in *sysin.CultivateReportListInp) (list []*sysin.CultivateReportListModel, totalCount int, err error)
+ ReportHandle(ctx context.Context, in *sysin.CultivateReportHandleInp) error
+ LogList(ctx context.Context, in *sysin.CultivateOperationLogListInp) (list []*sysin.CultivateOperationLogListModel, totalCount int, err error)
+ }
ISysImTenant interface {
// Model IM租户ORM模型
Model(ctx context.Context, option ...*handler.Option) *gdb.Model
@@ -767,6 +780,8 @@ var (
localSysCron ISysCron
localSysCronGroup ISysCronGroup
localSysCurdDemo ISysCurdDemo
+ localSysCultivateFlower ISysCultivateFlower
+ localSysCultivateContent ISysCultivateContent
localSysImTenant ISysImTenant
localSysImRegistration ISysImRegistration
localSysImInvite ISysImInvite
@@ -901,6 +916,26 @@ func RegisterSysCurdDemo(i ISysCurdDemo) {
localSysCurdDemo = i
}
+func SysCultivateFlower() ISysCultivateFlower {
+ if localSysCultivateFlower == nil {
+ panic("implement not found for interface ISysCultivateFlower, forgot register?")
+ }
+ return localSysCultivateFlower
+}
+
+func RegisterSysCultivateFlower(i ISysCultivateFlower) {
+ localSysCultivateFlower = i
+}
+
+func SysCultivateContent() ISysCultivateContent {
+ if localSysCultivateContent == nil {
+ panic("implement not found for interface ISysCultivateContent, forgot register?")
+ }
+ return localSysCultivateContent
+}
+
+func RegisterSysCultivateContent(i ISysCultivateContent) { localSysCultivateContent = i }
+
func SysImTenant() ISysImTenant {
if localSysImTenant == nil {
panic("implement not found for interface ISysImTenant, forgot register?")
diff --git a/server/manifest/config/config.example.yaml b/server/manifest/config/config.example.yaml
index 2e19c79..b0e0584 100644
--- a/server/manifest/config/config.example.yaml
+++ b/server/manifest/config/config.example.yaml
@@ -240,6 +240,9 @@ database:
link: "mysql:root:${MYSQL_PASSWORD}@tcp(mysql.118im.com:30306)/hotgo?loc=Local&parseTime=true&charset=utf8mb4"
debug: true
Prefix: "hg_"
+ cultivate:
+ link: "mysql:root:${MYSQL_PASSWORD}@tcp(127.0.0.1:3306)/cultivate?loc=Local&parseTime=true&charset=utf8mb4"
+ debug: true
imRegistry:
link: "mysql:root:${MYSQL_PASSWORD}@tcp(mysql.118im.com:30306)/hotgo?loc=Local&parseTime=true&charset=utf8mb4"
debug: true
diff --git a/server/manifest/config/config.template b/server/manifest/config/config.template
index db4b688..18a38b5 100644
--- a/server/manifest/config/config.template
+++ b/server/manifest/config/config.template
@@ -240,8 +240,14 @@ database:
link: "$MYSQL_DSN"
debug: true
Prefix: "hg_"
+ cultivate:
+ link: "$CULTIVATE_MYSQL_DSN"
+ debug: true
+cultivate:
+ defaultTenantId: 1
+
# 链路追踪
jaeger:
switch: false # 链路追踪开关,可选:true|false,默认为false
diff --git a/server/storage/data/generate/cultivate_menu.sql b/server/storage/data/generate/cultivate_menu.sql
new file mode 100644
index 0000000..5a9c457
--- /dev/null
+++ b/server/storage/data/generate/cultivate_menu.sql
@@ -0,0 +1,47 @@
+-- 养成管理菜单与首期花种配置权限(可重复执行)
+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,'','养成管理','cultivateManager','/cultivate','ExperimentOutlined',1,'/cultivate/flowers','','','LAYOUT',1,'',0,0,'',0,0,0,124,'后院养成与物友动态管理',1,@now,@now
+WHERE NOT EXISTS (SELECT 1 FROM hg_admin_menu WHERE name='cultivateManager');
+SET @cultivateId := (SELECT id FROM hg_admin_menu WHERE name='cultivateManager' 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 @cultivateId,2,CONCAT('tr_',@cultivateId,' '),'花种配置','cultivateFlowers','flowers','',2,'','/cultivate/flowers/list','','/cultivate/flowers/index',1,'cultivateFlowers',0,0,'',1,0,0,40,'花种、成长阶段、每日限额与素材配置',1,@now,@now
+WHERE NOT EXISTS (SELECT 1 FROM hg_admin_menu WHERE name='cultivateFlowers');
+SET @flowersId := (SELECT id FROM hg_admin_menu WHERE name='cultivateFlowers' 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 @flowersId,3,CONCAT('tr_',@cultivateId,' tr_',@flowersId,' '),'查看花种','cultivateFlowersView','','',3,'','/cultivate/flowers/view','','',1,'',0,0,'',0,1,0,30,'',1,@now,@now
+WHERE NOT EXISTS (SELECT 1 FROM hg_admin_menu WHERE name='cultivateFlowersView');
+
+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 @flowersId,3,CONCAT('tr_',@cultivateId,' tr_',@flowersId,' '),'编辑花种','cultivateFlowersEdit','','',3,'','/cultivate/flowers/edit','','',1,'',0,0,'',0,1,0,20,'',1,@now,@now
+WHERE NOT EXISTS (SELECT 1 FROM hg_admin_menu WHERE name='cultivateFlowersEdit');
+
+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 @flowersId,3,CONCAT('tr_',@cultivateId,' tr_',@flowersId,' '),'上下架花种','cultivateFlowersStatus','','',3,'','/cultivate/flowers/status','','',1,'',0,0,'',0,1,0,10,'',1,@now,@now
+WHERE NOT EXISTS (SELECT 1 FROM hg_admin_menu WHERE name='cultivateFlowersStatus');
+
+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 @cultivateId,2,CONCAT('tr_',@cultivateId,' '),'内容审核','cultivateContent','content','',2,'','/cultivate/content/list','','/cultivate/content/index',1,'cultivateContent',0,0,'',1,0,0,30,'物友动态与评论审核管理',1,@now,@now
+WHERE NOT EXISTS (SELECT 1 FROM hg_admin_menu WHERE name='cultivateContent');
+SET @contentId := (SELECT id FROM hg_admin_menu WHERE name='cultivateContent' 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_',@cultivateId,' tr_',@contentId,' '),'审核内容','cultivateContentReview','','',3,'','/cultivate/content/review','','',1,'',0,0,'',0,1,0,20,'',1,@now,@now
+WHERE NOT EXISTS (SELECT 1 FROM hg_admin_menu WHERE name='cultivateContentReview');
+
+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 @cultivateId,2,CONCAT('tr_',@cultivateId,' '),'举报处理','cultivateReports','reports','',2,'','/cultivate/reports/list','','/cultivate/reports/index',1,'cultivateReports',0,0,'',1,0,0,20,'物友内容与用户举报处理',1,@now,@now
+WHERE NOT EXISTS (SELECT 1 FROM hg_admin_menu WHERE name='cultivateReports');
+SET @reportsId := (SELECT id FROM hg_admin_menu WHERE name='cultivateReports' 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_',@cultivateId,' tr_',@reportsId,' '),'处理举报','cultivateReportsHandle','','',3,'','/cultivate/reports/handle','','',1,'',0,0,'',0,1,0,20,'',1,@now,@now
+WHERE NOT EXISTS (SELECT 1 FROM hg_admin_menu WHERE name='cultivateReportsHandle');
+
+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 @cultivateId,2,CONCAT('tr_',@cultivateId,' '),'操作日志','cultivateLogs','logs','',2,'','/cultivate/logs/list','','/cultivate/logs/index',1,'cultivateLogs',0,0,'',1,0,0,10,'养成管理操作审计日志',1,@now,@now
+WHERE NOT EXISTS (SELECT 1 FROM hg_admin_menu WHERE name='cultivateLogs');
diff --git a/web/src/api/cultivate/content.ts b/web/src/api/cultivate/content.ts
new file mode 100644
index 0000000..ef1a63f
--- /dev/null
+++ b/web/src/api/cultivate/content.ts
@@ -0,0 +1,7 @@
+import { http } from '@/utils/http/axios';
+
+export function ContentList(params) { return http.request({ url: '/cultivate/content/list', method: 'get', params }); }
+export function ContentReview(params) { return http.request({ url: '/cultivate/content/review', method: 'POST', params }); }
+export function ReportList(params) { return http.request({ url: '/cultivate/reports/list', method: 'get', params }); }
+export function ReportHandle(params) { return http.request({ url: '/cultivate/reports/handle', method: 'POST', params }); }
+export function OperationLogList(params) { return http.request({ url: '/cultivate/logs/list', method: 'get', params }); }
diff --git a/web/src/api/cultivate/flowers.ts b/web/src/api/cultivate/flowers.ts
new file mode 100644
index 0000000..2fe02d5
--- /dev/null
+++ b/web/src/api/cultivate/flowers.ts
@@ -0,0 +1,6 @@
+import { http } from '@/utils/http/axios';
+
+export function List(params) { return http.request({ url: '/cultivate/flowers/list', method: 'get', params }); }
+export function View(params) { return http.request({ url: '/cultivate/flowers/view', method: 'get', params }); }
+export function Edit(params) { return http.request({ url: '/cultivate/flowers/edit', method: 'POST', params }); }
+export function Status(params) { return http.request({ url: '/cultivate/flowers/status', method: 'POST', params }); }
diff --git a/web/src/views/cultivate/content/index.vue b/web/src/views/cultivate/content/index.vue
new file mode 100644
index 0000000..1918026
--- /dev/null
+++ b/web/src/views/cultivate/content/index.vue
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/src/views/cultivate/content/model.ts b/web/src/views/cultivate/content/model.ts
new file mode 100644
index 0000000..6d8f470
--- /dev/null
+++ b/web/src/views/cultivate/content/model.ts
@@ -0,0 +1,43 @@
+import { computed, h, ref } from 'vue';
+import { NTag } from 'naive-ui';
+import { FormSchema } from '@/components/Form';
+import { useDictStore } from '@/store/modules/dict';
+
+const dict = useDictStore();
+
+export interface ContentState {
+ id: string;
+ objectType: string;
+ postId: string;
+ authorId: string;
+ contentText: string;
+ flowerCode: string;
+ visibility: string;
+ status: string;
+ auditStatus: string;
+ rejectReason: string;
+ likeCount: number;
+ commentCount: number;
+ createdAt: string;
+}
+
+export const schemas = ref([
+ { field: 'objectType', component: 'NSelect', label: '内容类型', defaultValue: 'feed_post', componentProps: { options: computed(() => dict.getOptions('CultivateContentTypeOptions')) } },
+ { field: 'status', component: 'NSelect', label: '状态', componentProps: { options: computed(() => dict.getOptions('CultivateContentStatusOptions')), clearable: true } },
+ { field: 'authorId', component: 'NInput', label: '作者ID', componentProps: { placeholder: '请输入作者ID' } },
+ { field: 'keyword', component: 'NInput', label: '内容关键词', componentProps: { placeholder: '请输入正文关键词' } },
+]);
+
+export const columns = [
+ { title: '内容ID', key: 'id', width: 190 },
+ { title: '类型', key: 'objectType', width: 90, render: (row: ContentState) => dict.getLabel('CultivateContentTypeOptions', row.objectType) },
+ { title: '作者ID', key: 'authorId', width: 150 },
+ { title: '正文', key: 'contentText', width: 300, ellipsis: { tooltip: true } },
+ { title: '可见范围', key: 'visibility', width: 100 },
+ { title: '状态', key: 'status', width: 110, render(row: ContentState) { return h(NTag, { type: row.status === 'published' ? 'success' : row.status === 'reviewing' ? 'warning' : row.status === 'rejected' ? 'error' : 'default' }, { default: () => dict.getLabel('CultivateContentStatusOptions', row.status) }); } },
+ { title: '点赞', key: 'likeCount', width: 80 },
+ { title: '评论', key: 'commentCount', width: 80 },
+ { title: '创建时间', key: 'createdAt', width: 180 },
+];
+
+export function loadOptions() { dict.loadOptions(['CultivateContentTypeOptions', 'CultivateContentStatusOptions']); }
diff --git a/web/src/views/cultivate/flowers/edit.vue b/web/src/views/cultivate/flowers/edit.vue
new file mode 100644
index 0000000..a24da02
--- /dev/null
+++ b/web/src/views/cultivate/flowers/edit.vue
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 成长阶段
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 取消保存并发布配置
+
+
+
+
+
+
diff --git a/web/src/views/cultivate/flowers/index.vue b/web/src/views/cultivate/flowers/index.vue
new file mode 100644
index 0000000..0a4cbe5
--- /dev/null
+++ b/web/src/views/cultivate/flowers/index.vue
@@ -0,0 +1,36 @@
+
+
+
+
+
diff --git a/web/src/views/cultivate/flowers/model.ts b/web/src/views/cultivate/flowers/model.ts
new file mode 100644
index 0000000..715accc
--- /dev/null
+++ b/web/src/views/cultivate/flowers/model.ts
@@ -0,0 +1,83 @@
+import { computed, h, ref } from 'vue';
+import { NTag } from 'naive-ui';
+import { cloneDeep } from 'lodash-es';
+import { FormSchema } from '@/components/Form';
+import { useDictStore } from '@/store/modules/dict';
+
+const dict = useDictStore();
+
+export interface StageRule {
+ id?: number;
+ stage: string;
+ nextStage: string;
+ requiredPoints: number;
+ waterPoints: number;
+ fertilizePoints: number;
+ allowedActions: string[];
+ imageFileId: string;
+ animationFileId: string;
+ progressTextTemplate: string;
+ completionText: string;
+ sortNo: number;
+}
+
+export class State {
+ id = 0;
+ flowerCode = '';
+ flowerName = '';
+ flowerDesc = '';
+ iconFileId = '';
+ status = 'offline';
+ configVersion = '';
+ dailyWaterLimit = 3;
+ dailyFertilizeLimit = 1;
+ shareConfig: Record = { can_share_snapshot: true, default_tag: '' };
+ sortNo = 0;
+ stageRules: StageRule[] = defaultStageRules();
+ createdAt = '';
+ updatedAt = '';
+ constructor(state?: Partial) { if (state) Object.assign(this, state); }
+}
+
+export function defaultStageRules(): StageRule[] {
+ return [
+ stageRule('seed', 'sprout', 12, 1, 0, ['water'], 10),
+ stageRule('sprout', 'bud', 60, 1, 0, ['water'], 20),
+ stageRule('bud', 'bloom', 90, 1, 5, ['water', 'fertilize'], 30),
+ stageRule('bloom', '', 0, 0, 0, [], 40),
+ ];
+}
+function stageRule(stage: string, nextStage: string, requiredPoints: number, waterPoints: number, fertilizePoints: number, allowedActions: string[], sortNo: number): StageRule {
+ return { stage, nextStage, requiredPoints, waterPoints, fertilizePoints, allowedActions, imageFileId: '', animationFileId: '', progressTextTemplate: '', completionText: '', sortNo };
+}
+export function newState(state: State | Record | null): State { return state ? new State(cloneDeep(state)) : new State(); }
+
+export const rules = {
+ flowerCode: { required: true, trigger: ['blur', 'input'], message: '请输入花种编码' },
+ flowerName: { required: true, trigger: ['blur', 'input'], message: '请输入花种名称' },
+ status: { required: true, trigger: ['change'], message: '请选择状态' },
+};
+
+export const schemas = ref([
+ { field: 'flowerCode', component: 'NInput', label: '花种编码', componentProps: { placeholder: '请输入花种编码' } },
+ { field: 'flowerName', component: 'NInput', label: '花种名称', componentProps: { placeholder: '请输入花种名称' } },
+ { field: 'status', component: 'NSelect', label: '状态', componentProps: { options: computed(() => dict.getOptions('CultivateFlowerStatusOptions')), clearable: true } },
+ { field: 'configVersion', component: 'NInput', label: '配置版本', componentProps: { placeholder: '请输入配置版本' } },
+]);
+
+export const columns = [
+ { title: '花种编码', key: 'flowerCode', width: 140 },
+ { title: '花种名称', key: 'flowerName', width: 140 },
+ { title: '花语/简介', key: 'flowerDesc', width: 240, ellipsis: { tooltip: true } },
+ { title: '成长阶段', key: 'stageCount', width: 100 },
+ { title: '浇水上限', key: 'dailyWaterLimit', width: 100 },
+ { title: '施肥上限', key: 'dailyFertilizeLimit', width: 100 },
+ { title: '配置版本', key: 'configVersion', width: 180 },
+ { title: '状态', key: 'status', width: 100, render(row: State) { return h(NTag, { type: row.status === 'online' ? 'success' : 'default' }, { default: () => dict.getLabel('CultivateFlowerStatusOptions', row.status) }); } },
+ { title: '排序', key: 'sortNo', width: 80 },
+ { title: '更新时间', key: 'updatedAt', width: 180 },
+];
+
+export function loadOptions() { dict.loadOptions(['CultivateFlowerStatusOptions']); }
+export const statusOptions = computed(() => dict.getOptions('CultivateFlowerStatusOptions'));
+export const actionOptions = [{ label: '浇水', value: 'water' }, { label: '施肥', value: 'fertilize' }];
diff --git a/web/src/views/cultivate/logs/index.vue b/web/src/views/cultivate/logs/index.vue
new file mode 100644
index 0000000..0f34527
--- /dev/null
+++ b/web/src/views/cultivate/logs/index.vue
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/web/src/views/cultivate/logs/model.ts b/web/src/views/cultivate/logs/model.ts
new file mode 100644
index 0000000..c261243
--- /dev/null
+++ b/web/src/views/cultivate/logs/model.ts
@@ -0,0 +1,12 @@
+import { ref } from 'vue';
+import { FormSchema } from '@/components/Form';
+export const schemas = ref([
+ { field: 'operatorId', component: 'NInput', label: '操作人ID', componentProps: { placeholder: '请输入操作人ID' } },
+ { field: 'operationType', component: 'NInput', label: '操作类型', componentProps: { placeholder: '如 content、report、flower' } },
+ { field: 'targetType', component: 'NInput', label: '对象类型', componentProps: { placeholder: '请输入对象类型' } },
+]);
+export const columns = [
+ { title: '日志ID', key: 'id', width: 190 }, { title: '操作人', key: 'operatorId', width: 150 }, { title: '操作类型', key: 'operationType', width: 160 },
+ { title: '对象类型', key: 'targetType', width: 130 }, { title: '对象ID', key: 'targetId', width: 190 }, { title: '原因/结果', key: 'reason', width: 260, ellipsis: { tooltip: true } },
+ { title: '请求ID', key: 'requestId', width: 220 }, { title: '操作时间', key: 'createdAt', width: 180 },
+];
diff --git a/web/src/views/cultivate/reports/index.vue b/web/src/views/cultivate/reports/index.vue
new file mode 100644
index 0000000..e6d1fd6
--- /dev/null
+++ b/web/src/views/cultivate/reports/index.vue
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/src/views/cultivate/reports/model.ts b/web/src/views/cultivate/reports/model.ts
new file mode 100644
index 0000000..5606d89
--- /dev/null
+++ b/web/src/views/cultivate/reports/model.ts
@@ -0,0 +1,20 @@
+import { computed, h, ref } from 'vue';
+import { NTag } from 'naive-ui';
+import { FormSchema } from '@/components/Form';
+import { useDictStore } from '@/store/modules/dict';
+
+const dict = useDictStore();
+export interface ReportState { id: string; reporterId: string; reportedUserId: string; objectType: string; objectId: string; reasonCode: string; description: string; status: string; handlerId: string; handleResult: string; handledAt: string; createdAt: string; }
+export const schemas = ref([
+ { field: 'objectType', component: 'NSelect', label: '对象类型', componentProps: { options: computed(() => dict.getOptions('CultivateReportObjectTypeOptions')), clearable: true } },
+ { field: 'status', component: 'NSelect', label: '处理状态', componentProps: { options: computed(() => dict.getOptions('CultivateReportStatusOptions')), clearable: true } },
+ { field: 'reporterId', component: 'NInput', label: '举报人ID', componentProps: { placeholder: '请输入举报人ID' } },
+]);
+export const columns = [
+ { title: '举报ID', key: 'id', width: 190 }, { title: '举报人', key: 'reporterId', width: 150 }, { title: '被举报人', key: 'reportedUserId', width: 150 },
+ { title: '对象', key: 'objectType', width: 90, render: (row: ReportState) => dict.getLabel('CultivateReportObjectTypeOptions', row.objectType) || row.objectType },
+ { title: '对象ID', key: 'objectId', width: 190 }, { title: '原因', key: 'reasonCode', width: 120 }, { title: '补充说明', key: 'description', width: 220, ellipsis: { tooltip: true } },
+ { title: '状态', key: 'status', width: 100, render(row: ReportState) { return h(NTag, { type: row.status === 'resolved' ? 'success' : row.status === 'submitted' ? 'warning' : 'default' }, { default: () => dict.getLabel('CultivateReportStatusOptions', row.status) }); } },
+ { title: '处理结果', key: 'handleResult', width: 220, ellipsis: { tooltip: true } }, { title: '提交时间', key: 'createdAt', width: 180 },
+];
+export function loadOptions() { dict.loadOptions(['CultivateReportObjectTypeOptions', 'CultivateReportStatusOptions']); }