feat: 新增养成管理后台

This commit is contained in:
richjohnrambo
2026-09-19 14:36:30 +08:00
parent 7c8b4d9b7c
commit f6766d3dbd
59 changed files with 3001 additions and 0 deletions
@@ -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"`
}
@@ -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{}
+10
View File
@@ -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"
+54
View File
@@ -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)
}
@@ -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
}
@@ -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
}
@@ -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.
+22
View File
@@ -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.
+22
View File
@@ -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.
+22
View File
@@ -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.
+22
View File
@@ -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.
+22
View File
@@ -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.
+22
View File
@@ -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.
+22
View File
@@ -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.
@@ -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)
}
@@ -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)
}
@@ -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)
}
+105
View File
@@ -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)
}
+117
View File
@@ -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)
}
+109
View File
@@ -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)
}
@@ -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)
}
@@ -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)
}
@@ -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
}
@@ -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
}
@@ -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 //
}
@@ -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 //
}
+29
View File
@@ -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 //
}
+29
View File
@@ -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 //
}
+35
View File
@@ -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 //
}
+32
View File
@@ -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 //
}
@@ -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 //
}
@@ -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 //
}
@@ -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:""`
}
@@ -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:""`
}
@@ -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:""`
}
@@ -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:""`
}
+33
View File
@@ -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:""`
}
@@ -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:""`
}
@@ -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:""`
}
@@ -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:""`
}
@@ -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"`
}
@@ -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 }
@@ -0,0 +1,5 @@
package genrouter
import "hotgo/internal/controller/admin/sys"
func init() { LoginRequiredRouter = append(LoginRequiredRouter, sys.CultivateContent) }
@@ -0,0 +1,5 @@
package genrouter
import "hotgo/internal/controller/admin/sys"
func init() { LoginRequiredRouter = append(LoginRequiredRouter, sys.CultivateFlower) }
+35
View File
@@ -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?")
@@ -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
+6
View File
@@ -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
@@ -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');
+7
View File
@@ -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 }); }
+6
View File
@@ -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 }); }
+37
View File
@@ -0,0 +1,37 @@
<template>
<div>
<div class="n-layout-page-header"><n-card :bordered="false" title="内容审核">审核物友动态与评论,支持拒绝、下架和恢复。</n-card></div>
<n-card :bordered="false" class="proCard">
<BasicForm ref="searchFormRef" @register="register" @submit="reloadTable" @reset="reloadTable" @keyup.enter="reloadTable" />
<BasicTable ref="actionRef" :columns="columns" :request="loadDataTable" :row-key="(row) => row.id" :actionColumn="actionColumn" :scroll-x="1450" />
</n-card>
<n-modal v-model:show="showReview" preset="dialog" title="内容审核" positive-text="确认" negative-text="取消" @positive-click="submitReview">
<n-space vertical>
<n-select v-model:value="reviewForm.action" :options="reviewOptions" placeholder="请选择审核动作" />
<n-input v-model:value="reviewForm.reason" type="textarea" placeholder="拒绝或下架时必须填写原因" />
</n-space>
</n-modal>
</div>
</template>
<script lang="ts" setup>
import { h, onMounted, reactive, ref } from 'vue';
import { useMessage } from 'naive-ui';
import { BasicTable, TableAction } from '@/components/Table';
import { BasicForm, useForm } from '@/components/Form';
import { usePermission } from '@/hooks/web/usePermission';
import { ContentList, ContentReview } from '@/api/cultivate/content';
import { columns, ContentState, loadOptions, schemas } from './model';
const actionRef = ref(); const searchFormRef = ref<any>({}); const showReview = ref(false); const selected = ref<ContentState>();
const message = useMessage(); const { hasPermission } = usePermission();
const reviewForm = reactive({ action: 'approve', reason: '' });
const reviewOptions = [{ label: '审核通过', value: 'approve' }, { label: '审核拒绝', value: 'reject' }, { label: '下架', value: 'take_down' }, { label: '恢复发布', value: 'restore' }];
const [register] = useForm({ gridProps: { cols: '1 s:1 m:2 l:4 xl:4 2xl:4' }, labelWidth: 90, schemas });
const loadDataTable = (res) => ContentList({ ...searchFormRef.value?.formModel, ...res });
const reloadTable = () => actionRef.value?.reload();
function openReview(row: ContentState) { selected.value = row; reviewForm.action = row.status === 'reviewing' ? 'approve' : row.status === 'taken_down' ? 'restore' : 'take_down'; reviewForm.reason = ''; showReview.value = true; }
async function submitReview() { if (!selected.value) return false; if ((reviewForm.action === 'reject' || reviewForm.action === 'take_down') && !reviewForm.reason.trim()) { message.warning('请填写原因'); return false; } await ContentReview({ objectType: selected.value.objectType, id: selected.value.id, ...reviewForm }); message.success('处理成功'); reloadTable(); return true; }
const actionColumn = reactive({ width: 120, title: '操作', key: 'action', fixed: 'right', render(row: ContentState) { return h(TableAction as any, { style: 'button', actions: [{ label: '审核处理', onClick: () => openReview(row), auth: ['/cultivate/content/review'], disabled: !hasPermission(['/cultivate/content/review']) }] }); } });
onMounted(loadOptions);
</script>
+43
View File
@@ -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<FormSchema[]>([
{ 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']); }
+49
View File
@@ -0,0 +1,49 @@
<template>
<n-modal v-model:show="showModal" :mask-closable="false" preset="dialog" :title="formValue.id ? '编辑花种配置' : '新建花种配置'" class="flower-modal">
<n-scrollbar class="flower-scroll"><n-spin :show="loading"><n-form ref="formRef" :model="formValue" :rules="rules" label-placement="left" :label-width="110">
<n-grid cols="1 s:1 m:2" responsive="screen" :x-gap="16">
<n-gi><n-form-item label="花种编码" path="flowerCode"><n-input v-model:value="formValue.flowerCode" :disabled="formValue.id > 0" placeholder="如 rose" /></n-form-item></n-gi>
<n-gi><n-form-item label="花种名称" path="flowerName"><n-input v-model:value="formValue.flowerName" /></n-form-item></n-gi>
<n-gi span="2"><n-form-item label="花语/简介"><n-input v-model:value="formValue.flowerDesc" type="textarea" /></n-form-item></n-gi>
<n-gi><n-form-item label="图标文件ID"><n-input v-model:value="formValue.iconFileId" /></n-form-item></n-gi>
<n-gi><n-form-item label="状态" path="status"><n-select v-model:value="formValue.status" :options="statusOptions" /></n-form-item></n-gi>
<n-gi><n-form-item label="每日浇水上限"><n-input-number v-model:value="formValue.dailyWaterLimit" :min="0" /></n-form-item></n-gi>
<n-gi><n-form-item label="每日施肥上限"><n-input-number v-model:value="formValue.dailyFertilizeLimit" :min="0" /></n-form-item></n-gi>
<n-gi><n-form-item label="排序"><n-input-number v-model:value="formValue.sortNo" /></n-form-item></n-gi>
<n-gi><n-form-item label="默认分享标签"><n-input v-model:value="formValue.shareConfig.default_tag" /></n-form-item></n-gi>
</n-grid>
<n-divider title-placement="left">成长阶段</n-divider>
<n-card v-for="rule in formValue.stageRules" :key="rule.stage" size="small" class="stage-card" :title="rule.stage">
<n-grid cols="1 s:1 m:3" responsive="screen" :x-gap="12">
<n-gi><n-form-item label="下一阶段"><n-input v-model:value="rule.nextStage" disabled /></n-form-item></n-gi>
<n-gi><n-form-item label="升级成长值"><n-input-number v-model:value="rule.requiredPoints" :min="0" /></n-form-item></n-gi>
<n-gi><n-form-item label="允许动作"><n-select v-model:value="rule.allowedActions" multiple :options="actionOptions" /></n-form-item></n-gi>
<n-gi><n-form-item label="浇水成长值"><n-input-number v-model:value="rule.waterPoints" :min="0" /></n-form-item></n-gi>
<n-gi><n-form-item label="施肥成长值"><n-input-number v-model:value="rule.fertilizePoints" :min="0" /></n-form-item></n-gi>
<n-gi><n-form-item label="排序"><n-input-number v-model:value="rule.sortNo" /></n-form-item></n-gi>
<n-gi><n-form-item label="静态图文件ID"><n-input v-model:value="rule.imageFileId" /></n-form-item></n-gi>
<n-gi><n-form-item label="动效文件ID"><n-input v-model:value="rule.animationFileId" /></n-form-item></n-gi>
<n-gi><n-form-item label="进度文案"><n-input v-model:value="rule.progressTextTemplate" /></n-form-item></n-gi>
</n-grid>
</n-card>
</n-form></n-spin></n-scrollbar>
<template #action><n-space><n-button @click="showModal=false">取消</n-button><n-button type="primary" :loading="submitting" @click="submit">保存并发布配置</n-button></n-space></template>
</n-modal>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { useMessage } from 'naive-ui';
import { Edit, View } from '@/api/cultivate/flowers';
import { actionOptions, newState, rules, State, statusOptions } from './model';
const emit=defineEmits(['reloadTable']);const message=useMessage();const showModal=ref(false);const loading=ref(false);const submitting=ref(false);const formRef=ref();const formValue=ref<State>(newState(null));
function openModal(state:State|null){showModal.value=true;formValue.value=newState(null);if(!state?.id)return;loading.value=true;View({id:state.id}).then((res)=>formValue.value=newState(res)).finally(()=>loading.value=false)}
function submit(){formRef.value?.validate((errors)=>{if(errors){message.error('请检查配置内容');return}submitting.value=true;Edit(formValue.value).then(()=>{message.success('配置已保存');showModal.value=false;emit('reloadTable')}).finally(()=>submitting.value=false)})}
defineExpose({openModal});
</script>
<style lang="less" scoped>
.flower-modal { width: min(1080px, 92vw); }
.flower-scroll { max-height: 78vh; padding-right: 16px; }
.stage-card { margin-bottom: 12px; }
</style>
+36
View File
@@ -0,0 +1,36 @@
<template>
<div>
<div class="n-layout-page-header"><n-card :bordered="false" title="花种与成长配置">维护花种、阶段链、成长值、每日限额和展示素材。</n-card></div>
<n-card :bordered="false" class="proCard">
<BasicForm ref="searchFormRef" @register="register" @submit="reloadTable" @reset="reloadTable" @keyup.enter="reloadTable" />
<BasicTable ref="actionRef" :columns="columns" :request="loadDataTable" :row-key="(row) => row.id" :actionColumn="actionColumn" :scroll-x="1450">
<template #tableTitle><n-button v-if="hasPermission(['/cultivate/flowers/edit'])" type="primary" @click="addTable"><template #icon><n-icon><PlusOutlined /></n-icon></template>新建花种</n-button></template>
</BasicTable>
</n-card>
<Edit ref="editRef" @reloadTable="reloadTable" />
</div>
</template>
<script lang="ts" setup>
import { h, onMounted, reactive, ref } from 'vue';
import { useDialog, useMessage } from 'naive-ui';
import { PlusOutlined } from '@vicons/antd';
import { BasicTable, TableAction } from '@/components/Table';
import { BasicForm, useForm } from '@/components/Form';
import { usePermission } from '@/hooks/web/usePermission';
import { List, Status } from '@/api/cultivate/flowers';
import { columns, loadOptions, schemas, State } from './model';
import Edit from './edit.vue';
const actionRef = ref(); const searchFormRef = ref<any>({}); const editRef = ref();
const dialog = useDialog(); const message = useMessage(); const { hasPermission } = usePermission();
const [register] = useForm({ gridProps: { cols: '1 s:1 m:2 l:4 xl:4 2xl:4' }, labelWidth: 90, schemas });
const loadDataTable = async (res) => List({ ...searchFormRef.value?.formModel, ...res });
const reloadTable = () => actionRef.value?.reload(); const addTable = () => editRef.value.openModal(null);
const actionColumn = reactive({ width: 220, title: '操作', key: 'action', fixed: 'right', render(record: State) { return h(TableAction as any, { style: 'button', actions: [
{ label: '编辑配置', onClick: () => editRef.value.openModal(record), auth: ['/cultivate/flowers/edit'] },
{ label: record.status === 'online' ? '下架' : '上架', onClick: () => changeStatus(record), auth: ['/cultivate/flowers/status'] },
] }); } });
function changeStatus(record: State) { const status = record.status === 'online' ? 'offline' : 'online'; dialog.warning({ title: '确认操作', content: `确定${status === 'online' ? '上架' : '下架'}“${record.flowerName}”吗?`, positiveText: '确定', negativeText: '取消', onPositiveClick: () => Status({ id: record.id, status }).then(() => { message.success('操作成功'); reloadTable(); }) }); }
onMounted(loadOptions);
</script>
+83
View File
@@ -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<string, any> = { can_share_snapshot: true, default_tag: '' };
sortNo = 0;
stageRules: StageRule[] = defaultStageRules();
createdAt = '';
updatedAt = '';
constructor(state?: Partial<State>) { 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<string, any> | 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<FormSchema[]>([
{ 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' }];
+13
View File
@@ -0,0 +1,13 @@
<template>
<div>
<div class="n-layout-page-header"><n-card :bordered="false" title="操作日志">留存花种配置、内容审核和举报处理的管理操作。</n-card></div>
<n-card :bordered="false" class="proCard">
<BasicForm ref="searchFormRef" @register="register" @submit="reloadTable" @reset="reloadTable" />
<BasicTable ref="actionRef" :columns="columns" :request="loadDataTable" :row-key="(row) => row.id" :scroll-x="1500" />
</n-card>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'; import { BasicTable } from '@/components/Table'; import { BasicForm, useForm } from '@/components/Form'; import { OperationLogList } from '@/api/cultivate/content'; import { columns, schemas } from './model';
const actionRef = ref(); const searchFormRef = ref<any>({}); const [register] = useForm({ gridProps: { cols: '1 s:1 m:2 l:4' }, labelWidth: 90, schemas }); const loadDataTable = (res) => OperationLogList({ ...searchFormRef.value?.formModel, ...res }); const reloadTable = () => actionRef.value?.reload();
</script>
+12
View File
@@ -0,0 +1,12 @@
import { ref } from 'vue';
import { FormSchema } from '@/components/Form';
export const schemas = ref<FormSchema[]>([
{ 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 },
];
+25
View File
@@ -0,0 +1,25 @@
<template>
<div>
<div class="n-layout-page-header"><n-card :bordered="false" title="举报处理">查看物友动态、评论及用户举报并记录处理结果。</n-card></div>
<n-card :bordered="false" class="proCard">
<BasicForm ref="searchFormRef" @register="register" @submit="reloadTable" @reset="reloadTable" />
<BasicTable ref="actionRef" :columns="columns" :request="loadDataTable" :row-key="(row) => row.id" :actionColumn="actionColumn" :scroll-x="1650" />
</n-card>
<n-modal v-model:show="showHandle" preset="dialog" title="处理举报" positive-text="确认" negative-text="取消" @positive-click="submitHandle">
<n-space vertical><n-select v-model:value="handleForm.action" :options="handleOptions" /><n-input v-model:value="handleForm.handleResult" type="textarea" placeholder="办结或驳回时必须填写处理结果" /></n-space>
</n-modal>
</div>
</template>
<script lang="ts" setup>
import { h, onMounted, reactive, ref } from 'vue';
import { useMessage } from 'naive-ui';
import { BasicTable, TableAction } from '@/components/Table'; import { BasicForm, useForm } from '@/components/Form';
import { ReportHandle, ReportList } from '@/api/cultivate/content'; import { columns, loadOptions, ReportState, schemas } from './model';
const actionRef = ref(); const searchFormRef = ref<any>({}); const showHandle = ref(false); const selected = ref<ReportState>(); const message = useMessage();
const handleForm = reactive({ action: 'processing', handleResult: '' }); const handleOptions = [{ label: '标记处理中', value: 'processing' }, { label: '确认处理', value: 'resolve' }, { label: '驳回举报', value: 'reject' }];
const [register] = useForm({ gridProps: { cols: '1 s:1 m:2 l:4' }, labelWidth: 90, schemas }); const loadDataTable = (res) => ReportList({ ...searchFormRef.value?.formModel, ...res }); const reloadTable = () => actionRef.value?.reload();
function openHandle(row: ReportState) { selected.value = row; handleForm.action = row.status === 'submitted' ? 'processing' : 'resolve'; handleForm.handleResult = ''; showHandle.value = true; }
async function submitHandle() { if (!selected.value) return false; if (handleForm.action !== 'processing' && !handleForm.handleResult.trim()) { message.warning('请填写处理结果'); return false; } await ReportHandle({ id: selected.value.id, ...handleForm }); message.success('处理成功'); reloadTable(); return true; }
const actionColumn = reactive({ width: 110, title: '操作', key: 'action', fixed: 'right', render(row: ReportState) { return h(TableAction as any, { style: 'button', actions: [{ label: '处理', onClick: () => openHandle(row), auth: ['/cultivate/reports/handle'], disabled: row.status === 'resolved' || row.status === 'rejected' }] }); } });
onMounted(loadOptions);
</script>
+20
View File
@@ -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<FormSchema[]>([
{ 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']); }