can be joined table and its joined condition,
+// and also with its alias name, like:
+// Table("user").LeftJoin("user_detail", "user_detail.uid=user.uid")
+// Table("user", "u").LeftJoin("user_detail", "ud", "ud.uid=u.uid")
+func (m *arModel) LeftJoin(table ...string) *arModel {
+ return &arModel{m.M.LeftJoin(table...)}
+}
+
+// RightJoin does "RIGHT JOIN ... ON ..." statement on the model.
+// The parameter
can be joined table and its joined condition,
+// and also with its alias name, like:
+// Table("user").RightJoin("user_detail", "user_detail.uid=user.uid")
+// Table("user", "u").RightJoin("user_detail", "ud", "ud.uid=u.uid")
+func (m *arModel) RightJoin(table ...string) *arModel {
+ return &arModel{m.M.RightJoin(table...)}
+}
+
+// InnerJoin does "INNER JOIN ... ON ..." statement on the model.
+// The parameter
can be joined table and its joined condition,
+// and also with its alias name, like:
+// Table("user").InnerJoin("user_detail", "user_detail.uid=user.uid")
+// Table("user", "u").InnerJoin("user_detail", "ud", "ud.uid=u.uid")
+func (m *arModel) InnerJoin(table ...string) *arModel {
+ return &arModel{m.M.InnerJoin(table...)}
+}
+
+// Fields sets the operation fields of the model, multiple fields joined using char ','.
+func (m *arModel) Fields(fields string) *arModel {
+ return &arModel{m.M.Fields(fields)}
+}
+
+// FieldsEx sets the excluded operation fields of the model, multiple fields joined using char ','.
+func (m *arModel) FieldsEx(fields string) *arModel {
+ return &arModel{m.M.FieldsEx(fields)}
+}
+
+// Option sets the extra operation option for the model.
+func (m *arModel) Option(option int) *arModel {
+ return &arModel{m.M.Option(option)}
+}
+
+// OmitEmpty sets OPTION_OMITEMPTY option for the model, which automatically filers
+// the data and where attributes for empty values.
+func (m *arModel) OmitEmpty() *arModel {
+ return &arModel{m.M.OmitEmpty()}
+}
+
+// Filter marks filtering the fields which does not exist in the fields of the operated table.
+func (m *arModel) Filter() *arModel {
+ return &arModel{m.M.Filter()}
+}
+
+// Where sets the condition statement for the model. The parameter can be type of
+// string/map/gmap/slice/struct/*struct, etc. Note that, if it's called more than one times,
+// multiple conditions will be joined into where statement using "AND".
+// Eg:
+// Where("uid=10000")
+// Where("uid", 10000)
+// Where("money>? AND name like ?", 99999, "vip_%")
+// Where("uid", 1).Where("name", "john")
+// Where("status IN (?)", g.Slice{1,2,3})
+// Where("age IN(?,?)", 18, 50)
+// Where(User{ Id : 1, UserName : "john"})
+func (m *arModel) Where(where interface{}, args ...interface{}) *arModel {
+ return &arModel{m.M.Where(where, args...)}
+}
+
+// And adds "AND" condition to the where statement.
+func (m *arModel) And(where interface{}, args ...interface{}) *arModel {
+ return &arModel{m.M.And(where, args...)}
+}
+
+// Or adds "OR" condition to the where statement.
+func (m *arModel) Or(where interface{}, args ...interface{}) *arModel {
+ return &arModel{m.M.Or(where, args...)}
+}
+
+// Group sets the "GROUP BY" statement for the model.
+func (m *arModel) Group(groupBy string) *arModel {
+ return &arModel{m.M.Group(groupBy)}
+}
+
+// Order sets the "ORDER BY" statement for the model.
+func (m *arModel) Order(orderBy ...string) *arModel {
+ return &arModel{m.M.Order(orderBy...)}
+}
+
+// Limit sets the "LIMIT" statement for the model.
+// The parameter can be either one or two number, if passed two number is passed,
+// it then sets "LIMIT limit[0],limit[1]" statement for the model, or else it sets "LIMIT limit[0]"
+// statement.
+func (m *arModel) Limit(limit ...int) *arModel {
+ return &arModel{m.M.Limit(limit...)}
+}
+
+// Offset sets the "OFFSET" statement for the model.
+// It only makes sense for some databases like SQLServer, PostgreSQL, etc.
+func (m *arModel) Offset(offset int) *arModel {
+ return &arModel{m.M.Offset(offset)}
+}
+
+// Page sets the paging number for the model.
+// The parameter is started from 1 for paging.
+// Note that, it differs that the Limit function start from 0 for "LIMIT" statement.
+func (m *arModel) Page(page, limit int) *arModel {
+ return &arModel{m.M.Page(page, limit)}
+}
+
+// Batch sets the batch operation number for the model.
+func (m *arModel) Batch(batch int) *arModel {
+ return &arModel{m.M.Batch(batch)}
+}
+
+// Cache sets the cache feature for the model. It caches the result of the sql, which means
+// if there's another same sql request, it just reads and returns the result from cache, it
+// but not committed and executed into the database.
+//
+// If the parameter < 0, which means it clear the cache with given .
+// If the parameter = 0, which means it never expires.
+// If the parameter > 0, which means it expires after .
+//
+// The optional parameter is used to bind a name to the cache, which means you can later
+// control the cache like changing the or clearing the cache with specified .
+//
+// Note that, the cache feature is disabled if the model is operating on a transaction.
+func (m *arModel) Cache(duration time.Duration, name ...string) *arModel {
+ return &arModel{m.M.Cache(duration, name...)}
+}
+
+// Data sets the operation data for the model.
+// The parameter can be type of string/map/gmap/slice/struct/*struct, etc.
+// Eg:
+// Data("uid=10000")
+// Data("uid", 10000)
+// Data(g.Map{"uid": 10000, "name":"john"})
+// Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"})
+func (m *arModel) Data(data ...interface{}) *arModel {
+ return &arModel{m.M.Data(data...)}
+}
+
+// All does "SELECT FROM ..." statement for the model.
+// It retrieves the records from table and returns the result as []*Entity.
+// It returns nil if there's no record retrieved with the given conditions from table.
+//
+// The optional parameter is the same as the parameter of Model.Where function,
+// see Model.Where.
+func (m *arModel) All(where ...interface{}) ([]*Entity, error) {
+ all, err := m.M.All(where...)
+ if err != nil {
+ return nil, err
+ }
+ var entities []*Entity
+ if err = all.Structs(&entities); err != nil && err != sql.ErrNoRows {
+ return nil, err
+ }
+ return entities, nil
+}
+
+// One retrieves one record from table and returns the result as *Entity.
+// It returns nil if there's no record retrieved with the given conditions from table.
+//
+// The optional parameter is the same as the parameter of Model.Where function,
+// see Model.Where.
+func (m *arModel) One(where ...interface{}) (*Entity, error) {
+ one, err := m.M.One(where...)
+ if err != nil {
+ return nil, err
+ }
+ var entity *Entity
+ if err = one.Struct(&entity); err != nil && err != sql.ErrNoRows {
+ return nil, err
+ }
+ return entity, nil
+}
+
+// FindOne retrieves and returns a single Record by Model.WherePri and Model.One.
+// Also see Model.WherePri and Model.One.
+func (m *arModel) FindOne(where ...interface{}) (*Entity, error) {
+ one, err := m.M.FindOne(where...)
+ if err != nil {
+ return nil, err
+ }
+ var entity *Entity
+ if err = one.Struct(&entity); err != nil && err != sql.ErrNoRows {
+ return nil, err
+ }
+ return entity, nil
+}
+
+// FindAll retrieves and returns Result by by Model.WherePri and Model.All.
+// Also see Model.WherePri and Model.All.
+func (m *arModel) FindAll(where ...interface{}) ([]*Entity, error) {
+ all, err := m.M.FindAll(where...)
+ if err != nil {
+ return nil, err
+ }
+ var entities []*Entity
+ if err = all.Structs(&entities); err != nil && err != sql.ErrNoRows {
+ return nil, err
+ }
+ return entities, nil
+}
+
+// Chunk iterates the table with given size and callback function.
+func (m *arModel) Chunk(limit int, callback func(entities []*Entity, err error) bool) {
+ m.M.Chunk(limit, func(result gdb.Result, err error) bool {
+ var entities []*Entity
+ err = result.Structs(&entities)
+ if err == sql.ErrNoRows {
+ return false
+ }
+ return callback(entities, err)
+ })
+}
+
+// LockUpdate sets the lock for update for current operation.
+func (m *arModel) LockUpdate() *arModel {
+ return &arModel{m.M.LockUpdate()}
+}
+
+// LockShared sets the lock in share mode for current operation.
+func (m *arModel) LockShared() *arModel {
+ return &arModel{m.M.LockShared()}
+}
+
+// Unscoped enables/disables the soft deleting feature.
+func (m *arModel) Unscoped() *arModel {
+ return &arModel{m.M.Unscoped()}
+}
diff --git a/plugin/blog/model/blog_comment/blog_comment.go b/plugin/blog/model/blog_comment/blog_comment.go
new file mode 100644
index 0000000..81ca84d
--- /dev/null
+++ b/plugin/blog/model/blog_comment/blog_comment.go
@@ -0,0 +1,214 @@
+package blog_comment
+
+import (
+ "gfast/app/model/admin/user"
+ "gfast/plugin/blog/model/blog_log"
+ "github.com/gogf/gf/database/gdb"
+ "github.com/gogf/gf/errors/gerror"
+ "github.com/gogf/gf/frame/g"
+ "github.com/gogf/gf/os/gtime"
+)
+
+// AddReq 用于存储新增请求的请求参数
+type AddReq struct {
+ CommentLogId uint `p:"commentLogId" v:"required|min:1#评论所属日志ID不能为空|评论所属日志ID错误"` // 当前评论所属日志id
+ CommentNickname string `p:"commentNickname" v:"required#评论用户昵称不能为空"` // 评论用户的用户昵称
+ CommentContent string `p:"commentContent" v:"required#评论内容不能为空"` // 评论内容
+ CommentPid uint `p:"commentPid"` // 父评论id
+ ReplyName string `p:"replyName"` // 当前回复对象的昵称
+ ReplyId uint `p:"replyId"` // 当前回复对象的id
+}
+
+// EditReq 用于存储修改请求参数
+type EditReq struct {
+ CommentId int64 `p:"commentId" v:"required|min:1#评论ID不能为空|评论ID错误"`
+ CommentStatus uint `p:"commentStatus"` // 此评论的状态,0隐藏,1发布
+ CommentContent string `p:"commentContent" v:"required#评论内容不能为空"` // 评论内容
+}
+
+// SelectPageReq 用于存储分页查询的请求参数
+type SelectPageReq struct {
+ CommentNickname string `p:"commentNickname"` // 评论用户昵称
+ LogTitle string `p:"logTitle"` // 日志标题
+ CommentLogIds []int // 存储根据日志标题查出来的日志id
+ PageNum int64 `p:"pageNum"` // 当前页
+ PageSize int64 `p:"pageSize"` // 每页显示记录数
+ Status string // 状态:用于控制查询相应状态的评论,0停用,1正常
+ // 评论在博客页面回复中分两级一级是没有父评论的,二级是有父评论的,一条一级评论下的二级评论的父id值相同,均为该一级评论的id
+ Flag int // 用于判断查询模式,0:查询不区分评论等级(查询所有),1:只查询一级评论(父id为0的评论)
+}
+
+// 用于存储分页查询的数据
+type ListEntity struct {
+ Entity
+ LogTitle string `orm:"log_title" json:"log_title" ` // 所属日志标题
+ UserName string `orm:"user_name" json:"user_name" ` // 所属用户的用户名
+ Children []*Entity
+}
+
+// GetChildren 将分页查出的实体切片中每一个元素的子评论查出来并添加进去
+func GetChildren(listEntity []*ListEntity) ([]*ListEntity, error) {
+ for i, v := range listEntity {
+ children, err := GetChildrenByCommentId(v.CommentId)
+ if err != nil {
+ return nil, err
+ }
+ listEntity[i].Children = children
+ }
+ return listEntity, nil
+}
+
+// GetChildrenByCommentId 根据评论id查询该评论所有已审核的回复
+func GetChildrenByCommentId(id uint) ([]*Entity, error) {
+ list, err := Model.Where("comment_pid = ?", id).And("comment_status = ?", 1).All()
+ if err != nil {
+ g.Log().Error(err)
+ return nil, gerror.New("根据评论id查询回复评论出错")
+ }
+ return list, nil
+}
+
+// GetCommentByID 根据ID查询记录
+func GetCommentByID(id int64) (*Entity, error) {
+ entity, err := Model.FindOne("comment_id", id)
+ if err != nil {
+ g.Log().Error(err)
+ return nil, gerror.New("根据ID查询记录出错!")
+ }
+ if entity == nil {
+ return nil, gerror.New("根据ID未能查询到记录")
+ }
+ return entity, nil
+}
+
+// 查询并更新回复数
+func FindSonCommentCount(id int) error {
+ i, err := Model.Where("reply_id = ?", id).Count()
+ if err != nil {
+ g.Log().Debug(err)
+ return gerror.New("查询回复数出错")
+ }
+ entity, err := GetCommentByID(int64(id))
+ if err != nil {
+ return err
+ }
+ entity.CommentNum = uint(i)
+ Model.Save(entity)
+ return nil
+}
+
+// AddSave 添加的方法
+func AddSave(req *AddReq) error {
+ var entity Entity
+ entity.CommentLogId = req.CommentLogId // 所属日志
+ entity.CommentNickname = req.CommentNickname // 评论用户昵称
+ entity.CommentContent = req.CommentContent // 评论内容
+ entity.CommentPid = req.CommentPid // 父评论id,没有父则为0
+ entity.ReplyName = req.ReplyName
+ entity.ReplyId = req.ReplyId
+ entity.CreateTime = uint(gtime.Timestamp()) // 评论时间
+ // 保存实体
+ _, err := entity.Insert()
+ if err != nil {
+ g.Log().Error(err)
+ return gerror.New("添加记录入库失败!")
+ }
+ return nil
+}
+
+// 批量删除记录
+func DeleteByIDs(ids []int) error {
+ _, err := Model.Delete("comment_id in(?)", ids)
+ if err != nil {
+ g.Log().Error(err)
+ return gerror.New("删除记录失败!")
+ }
+ return nil
+}
+
+// 根据ID修改记录
+func EditSave(req *EditReq) error {
+ // 先根据ID来查询要修改的记录
+ entity, err := GetCommentByID(req.CommentId)
+ if err != nil {
+ return err
+ }
+ // 修改实体
+ entity.CommentContent = req.CommentContent
+ entity.CommentStatus = req.CommentStatus
+ _, err = Model.Save(entity)
+ if err != nil {
+ g.Log().Error(err)
+ return gerror.New("修改记录失败!")
+ }
+ return nil
+}
+
+// 根据日志标题查询对应的日志id切片(因为评论中只存有日志id没有日志标题)
+func FindUserAndLogIds(req *SelectPageReq) (*SelectPageReq, error) {
+ if req.LogTitle != "" {
+ logList, err := blog_log.Model.Where("log_title like ?", "%"+req.LogTitle+"%").Fields("log_id").All()
+ if err != nil {
+ g.Log().Error(err)
+ return req, gerror.New("根据日志标题查询日志id时出错")
+ }
+ for _, v := range logList {
+ req.CommentLogIds = append(req.CommentLogIds, v.LogId)
+ }
+ }
+ return req, nil
+}
+
+// 分页查询,返回值total总记录数,page当前页
+func SelectListByPage(req *SelectPageReq) (total int, page int64, list []*ListEntity, err error) {
+ model := g.DB().Table(Table + " comment")
+ if req != nil {
+ if req.Flag == 1 {
+ model.Where("comment_pid = ?", 0)
+ }
+ if req.Status == "0" {
+ model.Where("comment.comment_status = ?", 0)
+ }
+ if req.Status == "1" {
+ model.Where("comment.comment_status = ?", 1)
+ }
+ if req.CommentNickname != "" {
+ model.Where("comment.comment_nickname like ?", "%"+req.CommentNickname+"%")
+ }
+ if req.CommentLogIds != nil {
+ model.Where("comment.comment_log_id in(?)", req.CommentLogIds)
+ }
+ }
+ model = model.LeftJoin(blog_log.Table+" log", "log.log_id=comment.comment_log_id")
+ model = model.LeftJoin(user.Table+" u", "u.id=comment.comment_user_id")
+ // 查询广告位总记录数(总行数)
+ total, err = model.Count()
+ if err != nil {
+ g.Log().Error(err)
+ err = gerror.New("获取总记录数失败")
+ return 0, 0, nil, err
+ }
+ if req.PageNum == 0 {
+ req.PageNum = 1
+ }
+ page = req.PageNum
+ if req.PageSize == 0 {
+ req.PageSize = 10
+ }
+ // 分页排序查询
+ var res gdb.Result
+ res, err = model.Fields("comment.*,log.log_title,u.user_name").Page(int(page), int(req.PageSize)).Order("comment.create_time desc,comment.comment_id asc").All()
+ if err != nil {
+ g.Log().Error(err)
+ err = gerror.New("分页查询失败")
+ return 0, 0, nil, err
+ }
+
+ err = res.Structs(&list)
+ if err != nil {
+ g.Log().Error(err)
+ err = gerror.New("分页查询失败")
+ return 0, 0, nil, err
+ }
+ return total, page, list, nil
+}
diff --git a/plugin/blog/model/blog_comment/blog_comment_entity.go b/plugin/blog/model/blog_comment/blog_comment_entity.go
new file mode 100644
index 0000000..808eb90
--- /dev/null
+++ b/plugin/blog/model/blog_comment/blog_comment_entity.go
@@ -0,0 +1,67 @@
+// ==========================================================================
+// This is auto-generated by gf cli tool. You may not really want to edit it.
+// ==========================================================================
+
+package blog_comment
+
+import (
+ "database/sql"
+ "github.com/gogf/gf/database/gdb"
+)
+
+// Entity is the golang structure for table blog_comment.
+type Entity struct {
+ CommentId uint `orm:"comment_id,primary" json:"comment_id"` //
+ CommentUserId uint `orm:"comment_user_id" json:"comment_user_id"` // 评论用户的用户id
+ CommentContent string `orm:"comment_content" json:"comment_content"` // 评论内容
+ CommentPid uint `orm:"comment_pid" json:"comment_pid"` // 当前评论所回复的父评论的id
+ CommentNum uint `orm:"comment_num" json:"comment_num"` // 当前评论的回复数(下一级子评论数)
+ CommentStatus uint `orm:"comment_status" json:"comment_status"` // 此评论的状态,0隐藏,1发布
+ CommentLogId uint `orm:"comment_log_id" json:"comment_log_id"` // 当前评论所属日志id
+ CreateTime uint `orm:"create_time" json:"create_time"` // 评论创建时间
+ CommentNickname string `orm:"comment_nickname" json:"comment_nickname"` // 评论用户昵称
+ ReplyName string `orm:"reply_name" json:"reply_name"` // 当前所回复的对象昵称
+ ReplyId uint `orm:"reply_id" json:"reply_id"` // 当前回复对象的id
+}
+
+// OmitEmpty sets OPTION_OMITEMPTY option for the model, which automatically filers
+// the data and where attributes for empty values.
+func (r *Entity) OmitEmpty() *arModel {
+ return Model.Data(r).OmitEmpty()
+}
+
+// Inserts does "INSERT...INTO..." statement for inserting current object into table.
+func (r *Entity) Insert() (result sql.Result, err error) {
+ return Model.Data(r).Insert()
+}
+
+// InsertIgnore does "INSERT IGNORE INTO ..." statement for inserting current object into table.
+func (r *Entity) InsertIgnore() (result sql.Result, err error) {
+ return Model.Data(r).InsertIgnore()
+}
+
+// Replace does "REPLACE...INTO..." statement for inserting current object into table.
+// If there's already another same record in the table (it checks using primary key or unique index),
+// it deletes it and insert this one.
+func (r *Entity) Replace() (result sql.Result, err error) {
+ return Model.Data(r).Replace()
+}
+
+// Save does "INSERT...INTO..." statement for inserting/updating current object into table.
+// It updates the record if there's already another same record in the table
+// (it checks using primary key or unique index).
+func (r *Entity) Save() (result sql.Result, err error) {
+ return Model.Data(r).Save()
+}
+
+// Update does "UPDATE...WHERE..." statement for updating current object from table.
+// It updates the record if there's already another same record in the table
+// (it checks using primary key or unique index).
+func (r *Entity) Update() (result sql.Result, err error) {
+ return Model.Data(r).Where(gdb.GetWhereConditionOfStruct(r)).Update()
+}
+
+// Delete does "DELETE FROM...WHERE..." statement for deleting current object from table.
+func (r *Entity) Delete() (result sql.Result, err error) {
+ return Model.Where(gdb.GetWhereConditionOfStruct(r)).Delete()
+}
diff --git a/plugin/blog/model/blog_comment/blog_comment_model.go b/plugin/blog/model/blog_comment/blog_comment_model.go
new file mode 100644
index 0000000..623b473
--- /dev/null
+++ b/plugin/blog/model/blog_comment/blog_comment_model.go
@@ -0,0 +1,363 @@
+// ==========================================================================
+// This is auto-generated by gf cli tool. You may not really want to edit it.
+// ==========================================================================
+
+package blog_comment
+
+import (
+ "database/sql"
+ "github.com/gogf/gf/database/gdb"
+ "github.com/gogf/gf/frame/g"
+ "github.com/gogf/gf/frame/gmvc"
+ "time"
+)
+
+// arModel is a active record design model for table blog_comment operations.
+type arModel struct {
+ gmvc.M
+}
+
+var (
+ // Table is the table name of blog_comment.
+ Table = "blog_comment"
+ // Model is the model object of blog_comment.
+ Model = &arModel{g.DB("default").Table(Table).Safe()}
+ // Columns defines and stores column names for table blog_comment.
+ Columns = struct {
+ CommentId string //
+ CommentUserId string // 评论用户的用户id
+ CommentContent string // 评论内容
+ CommentPid string // 当前评论所回复的父评论的id
+ CommentNum string // 当前评论的回复数(下一级子评论数)
+ CommentStatus string // 此评论的状态,0隐藏,1发布
+ CommentLogId string // 当前评论所属日志id
+ CreateTime string // 评论创建时间
+ CommentNickname string // 评论用户昵称
+ ReplyName string // 当前所回复的对象昵称
+ ReplyId string // 当前回复对象的id
+ }{
+ CommentId: "comment_id",
+ CommentUserId: "comment_user_id",
+ CommentContent: "comment_content",
+ CommentPid: "comment_pid",
+ CommentNum: "comment_num",
+ CommentStatus: "comment_status",
+ CommentLogId: "comment_log_id",
+ CreateTime: "create_time",
+ CommentNickname: "comment_nickname",
+ ReplyName: "reply_name",
+ ReplyId: "reply_id",
+ }
+)
+
+// FindOne is a convenience method for Model.FindOne.
+// See Model.FindOne.
+func FindOne(where ...interface{}) (*Entity, error) {
+ return Model.FindOne(where...)
+}
+
+// FindAll is a convenience method for Model.FindAll.
+// See Model.FindAll.
+func FindAll(where ...interface{}) ([]*Entity, error) {
+ return Model.FindAll(where...)
+}
+
+// FindValue is a convenience method for Model.FindValue.
+// See Model.FindValue.
+func FindValue(fieldsAndWhere ...interface{}) (gdb.Value, error) {
+ return Model.FindValue(fieldsAndWhere...)
+}
+
+// FindArray is a convenience method for Model.FindArray.
+// See Model.FindArray.
+func FindArray(fieldsAndWhere ...interface{}) ([]gdb.Value, error) {
+ return Model.FindArray(fieldsAndWhere...)
+}
+
+// FindCount is a convenience method for Model.FindCount.
+// See Model.FindCount.
+func FindCount(where ...interface{}) (int, error) {
+ return Model.FindCount(where...)
+}
+
+// Insert is a convenience method for Model.Insert.
+func Insert(data ...interface{}) (result sql.Result, err error) {
+ return Model.Insert(data...)
+}
+
+// InsertIgnore is a convenience method for Model.InsertIgnore.
+func InsertIgnore(data ...interface{}) (result sql.Result, err error) {
+ return Model.InsertIgnore(data...)
+}
+
+// Replace is a convenience method for Model.Replace.
+func Replace(data ...interface{}) (result sql.Result, err error) {
+ return Model.Replace(data...)
+}
+
+// Save is a convenience method for Model.Save.
+func Save(data ...interface{}) (result sql.Result, err error) {
+ return Model.Save(data...)
+}
+
+// Update is a convenience method for Model.Update.
+func Update(dataAndWhere ...interface{}) (result sql.Result, err error) {
+ return Model.Update(dataAndWhere...)
+}
+
+// Delete is a convenience method for Model.Delete.
+func Delete(where ...interface{}) (result sql.Result, err error) {
+ return Model.Delete(where...)
+}
+
+// As sets an alias name for current table.
+func (m *arModel) As(as string) *arModel {
+ return &arModel{m.M.As(as)}
+}
+
+// TX sets the transaction for current operation.
+func (m *arModel) TX(tx *gdb.TX) *arModel {
+ return &arModel{m.M.TX(tx)}
+}
+
+// Master marks the following operation on master node.
+func (m *arModel) Master() *arModel {
+ return &arModel{m.M.Master()}
+}
+
+// Slave marks the following operation on slave node.
+// Note that it makes sense only if there's any slave node configured.
+func (m *arModel) Slave() *arModel {
+ return &arModel{m.M.Slave()}
+}
+
+// LeftJoin does "LEFT JOIN ... ON ..." statement on the model.
+// The parameter
can be joined table and its joined condition,
+// and also with its alias name, like:
+// Table("user").LeftJoin("user_detail", "user_detail.uid=user.uid")
+// Table("user", "u").LeftJoin("user_detail", "ud", "ud.uid=u.uid")
+func (m *arModel) LeftJoin(table ...string) *arModel {
+ return &arModel{m.M.LeftJoin(table...)}
+}
+
+// RightJoin does "RIGHT JOIN ... ON ..." statement on the model.
+// The parameter
can be joined table and its joined condition,
+// and also with its alias name, like:
+// Table("user").RightJoin("user_detail", "user_detail.uid=user.uid")
+// Table("user", "u").RightJoin("user_detail", "ud", "ud.uid=u.uid")
+func (m *arModel) RightJoin(table ...string) *arModel {
+ return &arModel{m.M.RightJoin(table...)}
+}
+
+// InnerJoin does "INNER JOIN ... ON ..." statement on the model.
+// The parameter
can be joined table and its joined condition,
+// and also with its alias name, like:
+// Table("user").InnerJoin("user_detail", "user_detail.uid=user.uid")
+// Table("user", "u").InnerJoin("user_detail", "ud", "ud.uid=u.uid")
+func (m *arModel) InnerJoin(table ...string) *arModel {
+ return &arModel{m.M.InnerJoin(table...)}
+}
+
+// Fields sets the operation fields of the model, multiple fields joined using char ','.
+func (m *arModel) Fields(fields string) *arModel {
+ return &arModel{m.M.Fields(fields)}
+}
+
+// FieldsEx sets the excluded operation fields of the model, multiple fields joined using char ','.
+func (m *arModel) FieldsEx(fields string) *arModel {
+ return &arModel{m.M.FieldsEx(fields)}
+}
+
+// Option sets the extra operation option for the model.
+func (m *arModel) Option(option int) *arModel {
+ return &arModel{m.M.Option(option)}
+}
+
+// OmitEmpty sets OPTION_OMITEMPTY option for the model, which automatically filers
+// the data and where attributes for empty values.
+func (m *arModel) OmitEmpty() *arModel {
+ return &arModel{m.M.OmitEmpty()}
+}
+
+// Filter marks filtering the fields which does not exist in the fields of the operated table.
+func (m *arModel) Filter() *arModel {
+ return &arModel{m.M.Filter()}
+}
+
+// Where sets the condition statement for the model. The parameter can be type of
+// string/map/gmap/slice/struct/*struct, etc. Note that, if it's called more than one times,
+// multiple conditions will be joined into where statement using "AND".
+// Eg:
+// Where("uid=10000")
+// Where("uid", 10000)
+// Where("money>? AND name like ?", 99999, "vip_%")
+// Where("uid", 1).Where("name", "john")
+// Where("status IN (?)", g.Slice{1,2,3})
+// Where("age IN(?,?)", 18, 50)
+// Where(User{ Id : 1, UserName : "john"})
+func (m *arModel) Where(where interface{}, args ...interface{}) *arModel {
+ return &arModel{m.M.Where(where, args...)}
+}
+
+// And adds "AND" condition to the where statement.
+func (m *arModel) And(where interface{}, args ...interface{}) *arModel {
+ return &arModel{m.M.And(where, args...)}
+}
+
+// Or adds "OR" condition to the where statement.
+func (m *arModel) Or(where interface{}, args ...interface{}) *arModel {
+ return &arModel{m.M.Or(where, args...)}
+}
+
+// Group sets the "GROUP BY" statement for the model.
+func (m *arModel) Group(groupBy string) *arModel {
+ return &arModel{m.M.Group(groupBy)}
+}
+
+// Order sets the "ORDER BY" statement for the model.
+func (m *arModel) Order(orderBy ...string) *arModel {
+ return &arModel{m.M.Order(orderBy...)}
+}
+
+// Limit sets the "LIMIT" statement for the model.
+// The parameter can be either one or two number, if passed two number is passed,
+// it then sets "LIMIT limit[0],limit[1]" statement for the model, or else it sets "LIMIT limit[0]"
+// statement.
+func (m *arModel) Limit(limit ...int) *arModel {
+ return &arModel{m.M.Limit(limit...)}
+}
+
+// Offset sets the "OFFSET" statement for the model.
+// It only makes sense for some databases like SQLServer, PostgreSQL, etc.
+func (m *arModel) Offset(offset int) *arModel {
+ return &arModel{m.M.Offset(offset)}
+}
+
+// Page sets the paging number for the model.
+// The parameter is started from 1 for paging.
+// Note that, it differs that the Limit function start from 0 for "LIMIT" statement.
+func (m *arModel) Page(page, limit int) *arModel {
+ return &arModel{m.M.Page(page, limit)}
+}
+
+// Batch sets the batch operation number for the model.
+func (m *arModel) Batch(batch int) *arModel {
+ return &arModel{m.M.Batch(batch)}
+}
+
+// Cache sets the cache feature for the model. It caches the result of the sql, which means
+// if there's another same sql request, it just reads and returns the result from cache, it
+// but not committed and executed into the database.
+//
+// If the parameter < 0, which means it clear the cache with given .
+// If the parameter = 0, which means it never expires.
+// If the parameter > 0, which means it expires after .
+//
+// The optional parameter is used to bind a name to the cache, which means you can later
+// control the cache like changing the or clearing the cache with specified .
+//
+// Note that, the cache feature is disabled if the model is operating on a transaction.
+func (m *arModel) Cache(duration time.Duration, name ...string) *arModel {
+ return &arModel{m.M.Cache(duration, name...)}
+}
+
+// Data sets the operation data for the model.
+// The parameter can be type of string/map/gmap/slice/struct/*struct, etc.
+// Eg:
+// Data("uid=10000")
+// Data("uid", 10000)
+// Data(g.Map{"uid": 10000, "name":"john"})
+// Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"})
+func (m *arModel) Data(data ...interface{}) *arModel {
+ return &arModel{m.M.Data(data...)}
+}
+
+// All does "SELECT FROM ..." statement for the model.
+// It retrieves the records from table and returns the result as []*Entity.
+// It returns nil if there's no record retrieved with the given conditions from table.
+//
+// The optional parameter is the same as the parameter of Model.Where function,
+// see Model.Where.
+func (m *arModel) All(where ...interface{}) ([]*Entity, error) {
+ all, err := m.M.All(where...)
+ if err != nil {
+ return nil, err
+ }
+ var entities []*Entity
+ if err = all.Structs(&entities); err != nil && err != sql.ErrNoRows {
+ return nil, err
+ }
+ return entities, nil
+}
+
+// One retrieves one record from table and returns the result as *Entity.
+// It returns nil if there's no record retrieved with the given conditions from table.
+//
+// The optional parameter is the same as the parameter of Model.Where function,
+// see Model.Where.
+func (m *arModel) One(where ...interface{}) (*Entity, error) {
+ one, err := m.M.One(where...)
+ if err != nil {
+ return nil, err
+ }
+ var entity *Entity
+ if err = one.Struct(&entity); err != nil && err != sql.ErrNoRows {
+ return nil, err
+ }
+ return entity, nil
+}
+
+// FindOne retrieves and returns a single Record by Model.WherePri and Model.One.
+// Also see Model.WherePri and Model.One.
+func (m *arModel) FindOne(where ...interface{}) (*Entity, error) {
+ one, err := m.M.FindOne(where...)
+ if err != nil {
+ return nil, err
+ }
+ var entity *Entity
+ if err = one.Struct(&entity); err != nil && err != sql.ErrNoRows {
+ return nil, err
+ }
+ return entity, nil
+}
+
+// FindAll retrieves and returns Result by by Model.WherePri and Model.All.
+// Also see Model.WherePri and Model.All.
+func (m *arModel) FindAll(where ...interface{}) ([]*Entity, error) {
+ all, err := m.M.FindAll(where...)
+ if err != nil {
+ return nil, err
+ }
+ var entities []*Entity
+ if err = all.Structs(&entities); err != nil && err != sql.ErrNoRows {
+ return nil, err
+ }
+ return entities, nil
+}
+
+// Chunk iterates the table with given size and callback function.
+func (m *arModel) Chunk(limit int, callback func(entities []*Entity, err error) bool) {
+ m.M.Chunk(limit, func(result gdb.Result, err error) bool {
+ var entities []*Entity
+ err = result.Structs(&entities)
+ if err == sql.ErrNoRows {
+ return false
+ }
+ return callback(entities, err)
+ })
+}
+
+// LockUpdate sets the lock for update for current operation.
+func (m *arModel) LockUpdate() *arModel {
+ return &arModel{m.M.LockUpdate()}
+}
+
+// LockShared sets the lock in share mode for current operation.
+func (m *arModel) LockShared() *arModel {
+ return &arModel{m.M.LockShared()}
+}
+
+// Unscoped enables/disables the soft deleting feature.
+func (m *arModel) Unscoped() *arModel {
+ return &arModel{m.M.Unscoped()}
+}
diff --git a/plugin/blog/model/blog_log/blog_log.go b/plugin/blog/model/blog_log/blog_log.go
new file mode 100644
index 0000000..d1fe53e
--- /dev/null
+++ b/plugin/blog/model/blog_log/blog_log.go
@@ -0,0 +1,253 @@
+package blog_log
+
+import (
+ "gfast/plugin/blog/model/blog_classification"
+ "github.com/gogf/gf/database/gdb"
+ "github.com/gogf/gf/errors/gerror"
+ "github.com/gogf/gf/frame/g"
+ "github.com/gogf/gf/os/gtime"
+)
+
+// AddReq 用于存储新增请求的请求参数
+type AddReq struct {
+ LogType int `p:"logType" v:"required#名称不能为空"` // 所属分类
+ LogSign int `p:"logSign"` // 0.一般 1.置顶,2.幻灯,3.推荐
+ LogTitle string `p:"logTitle" v:"required#标题不能为空"` // 日志标题
+ LogAuthor string `p:"logAuthor"` // 作者名
+ LogUrl string `p:"logUrl"` // 跳转地址
+ LogThumbnail string `p:"logThumbnail"` // 缩略图
+ LogStatus uint `p:"logStatus"` // 状态:1发布,0未发布
+ LogSort int `P:"logSort"` // 排序
+ LogContent string `p:"logContent" v:"required#内容不能为空"` // 内容
+}
+
+// EditReq 用于存储修改请求参数
+type EditReq struct {
+ LogId int64 `p:"logId" v:"required|min:1#日志ID不能为空|日志ID错误"`
+ AddReq
+}
+
+// SelectPageReq 用于存储分页查询的请求参数
+type SelectPageReq struct {
+ LogTitle string `p:"logTitle"` // 日志标题
+ PageNum int64 `p:"pageNum"` // 当前页
+ PageSize int64 `p:"pageSize"` // 每页显示记录数
+ CateTypeId int `p:"cateId"` // 分类类型id
+ Status int // 状态
+}
+
+// 用于存储联合查询的数据
+type ListEntity struct {
+ Entity
+ ClassificationName string `orm:"classification_name" json:"classification_name" ` // 所属分类名
+}
+
+// GetLogByID 根据ID查询记录
+func GetLogByID(id int64) (entity *ListEntity, err error) {
+ entity = new(ListEntity)
+ model := g.DB().Table(Table + " log")
+ model = model.Where("log_id = ?", id)
+ model = model.LeftJoin(blog_classification.Table+" cf", "cf.classification_id = log.log_type")
+ var res gdb.Record
+ res, err = model.Fields("log.*,cf.classification_name").FindOne()
+ if err != nil {
+ g.Log().Error(err)
+ return nil, gerror.New("根据ID查询记录出错!")
+ }
+ err = res.Struct(entity)
+ if err != nil {
+ g.Log().Error(err)
+ return nil, gerror.New("根据ID查询转换时出错")
+ }
+ if entity == nil {
+ return nil, gerror.New("根据ID未能查询到记录")
+ }
+ return entity, nil
+}
+
+// AddSave 添加的方法
+func AddSave(req *AddReq) error {
+ var entity Entity
+ entity.LogType = req.LogType
+ entity.LogSign = req.LogSign
+ entity.LogTitle = req.LogTitle
+ entity.LogAuthor = req.LogAuthor
+ entity.LogUrl = req.LogUrl
+ entity.LogThumbnail = req.LogThumbnail
+ entity.CreatTime = uint(gtime.Timestamp())
+ entity.LogStatus = req.LogStatus
+ entity.LogSort = req.LogSort
+ entity.LogContent = req.LogContent
+ // 保存实体
+ _, err := entity.Insert()
+ if err != nil {
+ g.Log().Error(err)
+ return gerror.New("添加记录入库失败!")
+ }
+ return nil
+}
+
+// 批量删除记录
+func DeleteByIDs(ids []int) error {
+ _, err := Model.Delete("log_id in(?)", ids)
+ if err != nil {
+ g.Log().Error(err)
+ return gerror.New("删除记录失败!")
+ }
+ return nil
+}
+
+// 根据ID修改记录
+func EditSave(req *EditReq) error {
+ // 先根据ID来查询要修改的记录
+ entity, err := GetLogByID(req.LogId)
+ if err != nil {
+ return err
+ }
+ // 修改实体
+ entity.LogType = req.LogType
+ entity.LogSign = req.LogSign
+ entity.LogTitle = req.LogTitle
+ entity.LogAuthor = req.LogAuthor
+ entity.LogUrl = req.LogUrl
+ entity.LogThumbnail = req.LogThumbnail
+ entity.LogStatus = req.LogStatus
+ entity.LogSort = req.LogSort
+ entity.LogContent = req.LogContent
+ _, err = Model.Filter().Save(entity)
+ if err != nil {
+ g.Log().Error(err)
+ return gerror.New("修改记录失败!")
+ }
+ return nil
+}
+
+// 分页查询,返回值total总记录数,page当前页
+func SelectListByPage(req *SelectPageReq) (total int, page int64, list []*ListEntity, err error) {
+ model := g.DB().Table(Table + " log")
+ if req != nil {
+ if req.LogTitle != "" {
+ model.Where("log.log_title like ?", "%"+req.LogTitle+"%")
+ }
+ if req.Status == 1 {
+ model.Where("log.log_status = 1")
+ }
+ if req.CateTypeId != 0 {
+ model.Where("log_type = ?", req.CateTypeId)
+ }
+ }
+ model = model.LeftJoin(blog_classification.Table+" cf", "cf.classification_id=log.log_type")
+ // 查询广告位总记录数(总行数)
+ total, err = model.Count()
+ if err != nil {
+ g.Log().Error(err)
+ err = gerror.New("获取总记录数失败")
+ return 0, 0, nil, err
+ }
+ if req.PageNum == 0 {
+ req.PageNum = 1
+ }
+ page = req.PageNum
+ if req.PageSize == 0 {
+ req.PageSize = 10
+ }
+ // 分页排序查询
+ var res gdb.Result
+ res, err = model.Fields("log.*,cf.classification_name").
+ Page(int(page), int(req.PageSize)).Order("log.log_sort asc,log.log_id desc").All()
+ if err != nil {
+ g.Log().Error(err)
+ err = gerror.New("分页查询失败")
+ return 0, 0, nil, err
+ }
+ err = res.Structs(&list)
+ if err != nil {
+ g.Log().Error(err)
+ err = gerror.New("分页查询失败")
+ return 0, 0, nil, err
+ }
+ return total, page, list, nil
+}
+
+// 按时间倒序查询size篇标志为sign分类id为typeId,状态为status的文章,标志值0.一般,1.置顶,2.幻灯,3.推荐,typeId等于0时不区分分类
+func FindSizeArticleBySign(size int, status int, sign int, typeId int) (list []*ListEntity, err error) {
+ model := g.DB().Table(Table + " log")
+ if status == 1 {
+ model = model.Where("log.log_status = ?", 1)
+ }
+ if status == 0 {
+ model = model.Where("log.log_status = ?", 0)
+ }
+ if sign == 0 {
+ model = model.Where("log.log_sign = 0")
+ }
+ if sign == 1 {
+ model = model.Where("log.log_sign = 1")
+ }
+ if sign == 2 {
+ model = model.Where("log.log_sign = 2")
+ }
+ if sign == 3 {
+ model = model.Where("log.log_sign = 3")
+ }
+ if typeId != 0 {
+ model = model.Where("log_type = ?", typeId)
+ }
+ model = model.LeftJoin(blog_classification.Table+" cf", "cf.classification_id=log.log_type")
+ // 分页排序查询
+ var res gdb.Result
+ res, err = model.Fields("log.*,cf.classification_name").
+ Order("log.log_sort asc,log.creat_time desc").Limit(size).All()
+ if err != nil {
+ g.Log().Error(err)
+ return nil, gerror.New("根据标志查询出错")
+ }
+ err = res.Structs(&list)
+ if err != nil {
+ g.Log().Error(err)
+ return nil, gerror.New("根据标志查询出错")
+ }
+ return
+}
+
+// 按时间倒序查询size篇分类id为typeId,状态为status的文章,typeId等于0时不区分分类
+func FindSizeArticle(size int, status int, typeId int) (list []*ListEntity, err error) {
+ model := g.DB().Table(Table + " log")
+ if status == 1 {
+ model = model.Where("log.log_status = ?", 1)
+ }
+ if status == 0 {
+ model = model.Where("log.log_status = ?", 0)
+ }
+ if typeId != 0 {
+ model = model.Where("log_type = ?", typeId)
+ }
+ model = model.LeftJoin(blog_classification.Table+" cf", "cf.classification_id=log.log_type")
+ // 分页排序查询
+ var res gdb.Result
+ res, err = model.Fields("log.*,cf.classification_name").Order("log.log_sort asc,log.creat_time desc").Limit(size).All()
+ if err != nil {
+ g.Log().Error(err)
+ return nil, gerror.New("根据标志查询出错")
+ }
+ err = res.Structs(&list)
+ if err != nil {
+ g.Log().Error(err)
+ return nil, gerror.New("根据标志查询出错")
+ }
+ return
+}
+
+// 查询size篇文章并根据点击数排序
+func FindArticleByHits(size int, status int) (list []*Entity, err error) {
+ model := Model
+ if status == 1 {
+ model.Where("log_status = 1")
+ }
+ list, err = model.Order("log_hits desc").FindAll()
+ if err != nil {
+ g.Log().Error(err)
+ return nil, gerror.New("根据点击数排序查询失败")
+ }
+ return
+}
diff --git a/plugin/blog/model/blog_log/blog_log_entity.go b/plugin/blog/model/blog_log/blog_log_entity.go
new file mode 100644
index 0000000..17f0e56
--- /dev/null
+++ b/plugin/blog/model/blog_log/blog_log_entity.go
@@ -0,0 +1,69 @@
+// ==========================================================================
+// This is auto-generated by gf cli tool. You may not really want to edit it.
+// ==========================================================================
+
+package blog_log
+
+import (
+ "database/sql"
+ "github.com/gogf/gf/database/gdb"
+)
+
+// Entity is the golang structure for table blog_log.
+type Entity struct {
+ LogId int `orm:"log_id,primary" json:"log_id"`
+ LogType int `orm:"log_type" json:"log_type"` // 所属分类
+ LogSign int `orm:"log_sign" json:"log_sign"` // 0.一般 1.置顶,2.幻灯,3.推荐
+ LogTitle string `orm:"log_title" json:"log_title"` // 日志标题
+ LogAuthor string `orm:"log_author" json:"log_author"` // 用户名
+ LogUrl string `orm:"log_url" json:"log_url"` // 跳转地址
+ LogThumbnail string `orm:"log_thumbnail" json:"log_thumbnail"` // 缩略图
+ LogHits uint64 `orm:"log_hits" json:"log_hits"` // 查看数点击数
+ LogComments int64 `orm:"log_comments" json:"log_comments"` // 评论数
+ CreatTime uint `orm:"creat_time" json:"creat_time"` // 创建时间
+ LogStatus uint `orm:"log_status" json:"log_status"` // 状态:1发布,0未发布
+ LogSort int `orm:"log_sort" json:"log_sort"` // 排序
+ LogContent string `orm:"log_content" json:"log_content"` // 内容
+}
+
+// OmitEmpty sets OPTION_OMITEMPTY option for the model, which automatically filers
+// the data and where attributes for empty values.
+func (r *Entity) OmitEmpty() *arModel {
+ return Model.Data(r).OmitEmpty()
+}
+
+// Inserts does "INSERT...INTO..." statement for inserting current object into table.
+func (r *Entity) Insert() (result sql.Result, err error) {
+ return Model.Data(r).Insert()
+}
+
+// InsertIgnore does "INSERT IGNORE INTO ..." statement for inserting current object into table.
+func (r *Entity) InsertIgnore() (result sql.Result, err error) {
+ return Model.Data(r).InsertIgnore()
+}
+
+// Replace does "REPLACE...INTO..." statement for inserting current object into table.
+// If there's already another same record in the table (it checks using primary key or unique index),
+// it deletes it and insert this one.
+func (r *Entity) Replace() (result sql.Result, err error) {
+ return Model.Data(r).Replace()
+}
+
+// Save does "INSERT...INTO..." statement for inserting/updating current object into table.
+// It updates the record if there's already another same record in the table
+// (it checks using primary key or unique index).
+func (r *Entity) Save() (result sql.Result, err error) {
+ return Model.Data(r).Save()
+}
+
+// Update does "UPDATE...WHERE..." statement for updating current object from table.
+// It updates the record if there's already another same record in the table
+// (it checks using primary key or unique index).
+func (r *Entity) Update() (result sql.Result, err error) {
+ return Model.Data(r).Where(gdb.GetWhereConditionOfStruct(r)).Update()
+}
+
+// Delete does "DELETE FROM...WHERE..." statement for deleting current object from table.
+func (r *Entity) Delete() (result sql.Result, err error) {
+ return Model.Where(gdb.GetWhereConditionOfStruct(r)).Delete()
+}
diff --git a/plugin/blog/model/blog_log/blog_log_model.go b/plugin/blog/model/blog_log/blog_log_model.go
new file mode 100644
index 0000000..b04f968
--- /dev/null
+++ b/plugin/blog/model/blog_log/blog_log_model.go
@@ -0,0 +1,367 @@
+// ==========================================================================
+// This is auto-generated by gf cli tool. You may not really want to edit it.
+// ==========================================================================
+
+package blog_log
+
+import (
+ "database/sql"
+ "github.com/gogf/gf/database/gdb"
+ "github.com/gogf/gf/frame/g"
+ "github.com/gogf/gf/frame/gmvc"
+ "time"
+)
+
+// arModel is a active record design model for table blog_log operations.
+type arModel struct {
+ gmvc.M
+}
+
+var (
+ // Table is the table name of blog_log.
+ Table = "blog_log"
+ // Model is the model object of blog_log.
+ Model = &arModel{g.DB("default").Table(Table).Safe()}
+ // Columns defines and stores column names for table blog_log.
+ Columns = struct {
+ LogId string //
+ LogType string // 分类, 1va
+ LogSign string // 0.一般 1.置顶,2.幻灯,3.推荐
+ LogTitle string // 日志标题
+ LogAuthor string // 作者名
+ LogUrl string // 跳转地址
+ LogThumbnail string // 缩略图
+ LogHits string // 查看数点击数
+ LogComments string // 评论数
+ CreatTime string // 创建时间
+ LogStatus string // 状态:1发布,0未发布
+ LogSort string // 排序
+ LogContent string // 内容
+ }{
+ LogId: "log_id",
+ LogType: "log_type",
+ LogSign: "log_sign",
+ LogTitle: "log_title",
+ LogAuthor: "log_author",
+ LogUrl: "log_url",
+ LogThumbnail: "log_thumbnail",
+ LogHits: "log_hits",
+ LogComments: "log_comments",
+ CreatTime: "creat_time",
+ LogStatus: "log_status",
+ LogSort: "log_sort",
+ LogContent: "log_content",
+ }
+)
+
+// FindOne is a convenience method for Model.FindOne.
+// See Model.FindOne.
+func FindOne(where ...interface{}) (*Entity, error) {
+ return Model.FindOne(where...)
+}
+
+// FindAll is a convenience method for Model.FindAll.
+// See Model.FindAll.
+func FindAll(where ...interface{}) ([]*Entity, error) {
+ return Model.FindAll(where...)
+}
+
+// FindValue is a convenience method for Model.FindValue.
+// See Model.FindValue.
+func FindValue(fieldsAndWhere ...interface{}) (gdb.Value, error) {
+ return Model.FindValue(fieldsAndWhere...)
+}
+
+// FindArray is a convenience method for Model.FindArray.
+// See Model.FindArray.
+func FindArray(fieldsAndWhere ...interface{}) ([]gdb.Value, error) {
+ return Model.FindArray(fieldsAndWhere...)
+}
+
+// FindCount is a convenience method for Model.FindCount.
+// See Model.FindCount.
+func FindCount(where ...interface{}) (int, error) {
+ return Model.FindCount(where...)
+}
+
+// Insert is a convenience method for Model.Insert.
+func Insert(data ...interface{}) (result sql.Result, err error) {
+ return Model.Insert(data...)
+}
+
+// InsertIgnore is a convenience method for Model.InsertIgnore.
+func InsertIgnore(data ...interface{}) (result sql.Result, err error) {
+ return Model.InsertIgnore(data...)
+}
+
+// Replace is a convenience method for Model.Replace.
+func Replace(data ...interface{}) (result sql.Result, err error) {
+ return Model.Replace(data...)
+}
+
+// Save is a convenience method for Model.Save.
+func Save(data ...interface{}) (result sql.Result, err error) {
+ return Model.Save(data...)
+}
+
+// Update is a convenience method for Model.Update.
+func Update(dataAndWhere ...interface{}) (result sql.Result, err error) {
+ return Model.Update(dataAndWhere...)
+}
+
+// Delete is a convenience method for Model.Delete.
+func Delete(where ...interface{}) (result sql.Result, err error) {
+ return Model.Delete(where...)
+}
+
+// As sets an alias name for current table.
+func (m *arModel) As(as string) *arModel {
+ return &arModel{m.M.As(as)}
+}
+
+// TX sets the transaction for current operation.
+func (m *arModel) TX(tx *gdb.TX) *arModel {
+ return &arModel{m.M.TX(tx)}
+}
+
+// Master marks the following operation on master node.
+func (m *arModel) Master() *arModel {
+ return &arModel{m.M.Master()}
+}
+
+// Slave marks the following operation on slave node.
+// Note that it makes sense only if there's any slave node configured.
+func (m *arModel) Slave() *arModel {
+ return &arModel{m.M.Slave()}
+}
+
+// LeftJoin does "LEFT JOIN ... ON ..." statement on the model.
+// The parameter
can be joined table and its joined condition,
+// and also with its alias name, like:
+// Table("user").LeftJoin("user_detail", "user_detail.uid=user.uid")
+// Table("user", "u").LeftJoin("user_detail", "ud", "ud.uid=u.uid")
+func (m *arModel) LeftJoin(table ...string) *arModel {
+ return &arModel{m.M.LeftJoin(table...)}
+}
+
+// RightJoin does "RIGHT JOIN ... ON ..." statement on the model.
+// The parameter
can be joined table and its joined condition,
+// and also with its alias name, like:
+// Table("user").RightJoin("user_detail", "user_detail.uid=user.uid")
+// Table("user", "u").RightJoin("user_detail", "ud", "ud.uid=u.uid")
+func (m *arModel) RightJoin(table ...string) *arModel {
+ return &arModel{m.M.RightJoin(table...)}
+}
+
+// InnerJoin does "INNER JOIN ... ON ..." statement on the model.
+// The parameter
can be joined table and its joined condition,
+// and also with its alias name, like:
+// Table("user").InnerJoin("user_detail", "user_detail.uid=user.uid")
+// Table("user", "u").InnerJoin("user_detail", "ud", "ud.uid=u.uid")
+func (m *arModel) InnerJoin(table ...string) *arModel {
+ return &arModel{m.M.InnerJoin(table...)}
+}
+
+// Fields sets the operation fields of the model, multiple fields joined using char ','.
+func (m *arModel) Fields(fields string) *arModel {
+ return &arModel{m.M.Fields(fields)}
+}
+
+// FieldsEx sets the excluded operation fields of the model, multiple fields joined using char ','.
+func (m *arModel) FieldsEx(fields string) *arModel {
+ return &arModel{m.M.FieldsEx(fields)}
+}
+
+// Option sets the extra operation option for the model.
+func (m *arModel) Option(option int) *arModel {
+ return &arModel{m.M.Option(option)}
+}
+
+// OmitEmpty sets OPTION_OMITEMPTY option for the model, which automatically filers
+// the data and where attributes for empty values.
+func (m *arModel) OmitEmpty() *arModel {
+ return &arModel{m.M.OmitEmpty()}
+}
+
+// Filter marks filtering the fields which does not exist in the fields of the operated table.
+func (m *arModel) Filter() *arModel {
+ return &arModel{m.M.Filter()}
+}
+
+// Where sets the condition statement for the model. The parameter can be type of
+// string/map/gmap/slice/struct/*struct, etc. Note that, if it's called more than one times,
+// multiple conditions will be joined into where statement using "AND".
+// Eg:
+// Where("uid=10000")
+// Where("uid", 10000)
+// Where("money>? AND name like ?", 99999, "vip_%")
+// Where("uid", 1).Where("name", "john")
+// Where("status IN (?)", g.Slice{1,2,3})
+// Where("age IN(?,?)", 18, 50)
+// Where(User{ Id : 1, UserName : "john"})
+func (m *arModel) Where(where interface{}, args ...interface{}) *arModel {
+ return &arModel{m.M.Where(where, args...)}
+}
+
+// And adds "AND" condition to the where statement.
+func (m *arModel) And(where interface{}, args ...interface{}) *arModel {
+ return &arModel{m.M.And(where, args...)}
+}
+
+// Or adds "OR" condition to the where statement.
+func (m *arModel) Or(where interface{}, args ...interface{}) *arModel {
+ return &arModel{m.M.Or(where, args...)}
+}
+
+// Group sets the "GROUP BY" statement for the model.
+func (m *arModel) Group(groupBy string) *arModel {
+ return &arModel{m.M.Group(groupBy)}
+}
+
+// Order sets the "ORDER BY" statement for the model.
+func (m *arModel) Order(orderBy ...string) *arModel {
+ return &arModel{m.M.Order(orderBy...)}
+}
+
+// Limit sets the "LIMIT" statement for the model.
+// The parameter can be either one or two number, if passed two number is passed,
+// it then sets "LIMIT limit[0],limit[1]" statement for the model, or else it sets "LIMIT limit[0]"
+// statement.
+func (m *arModel) Limit(limit ...int) *arModel {
+ return &arModel{m.M.Limit(limit...)}
+}
+
+// Offset sets the "OFFSET" statement for the model.
+// It only makes sense for some databases like SQLServer, PostgreSQL, etc.
+func (m *arModel) Offset(offset int) *arModel {
+ return &arModel{m.M.Offset(offset)}
+}
+
+// Page sets the paging number for the model.
+// The parameter is started from 1 for paging.
+// Note that, it differs that the Limit function start from 0 for "LIMIT" statement.
+func (m *arModel) Page(page, limit int) *arModel {
+ return &arModel{m.M.Page(page, limit)}
+}
+
+// Batch sets the batch operation number for the model.
+func (m *arModel) Batch(batch int) *arModel {
+ return &arModel{m.M.Batch(batch)}
+}
+
+// Cache sets the cache feature for the model. It caches the result of the sql, which means
+// if there's another same sql request, it just reads and returns the result from cache, it
+// but not committed and executed into the database.
+//
+// If the parameter < 0, which means it clear the cache with given .
+// If the parameter = 0, which means it never expires.
+// If the parameter > 0, which means it expires after .
+//
+// The optional parameter is used to bind a name to the cache, which means you can later
+// control the cache like changing the or clearing the cache with specified .
+//
+// Note that, the cache feature is disabled if the model is operating on a transaction.
+func (m *arModel) Cache(duration time.Duration, name ...string) *arModel {
+ return &arModel{m.M.Cache(duration, name...)}
+}
+
+// Data sets the operation data for the model.
+// The parameter can be type of string/map/gmap/slice/struct/*struct, etc.
+// Eg:
+// Data("uid=10000")
+// Data("uid", 10000)
+// Data(g.Map{"uid": 10000, "name":"john"})
+// Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"})
+func (m *arModel) Data(data ...interface{}) *arModel {
+ return &arModel{m.M.Data(data...)}
+}
+
+// All does "SELECT FROM ..." statement for the model.
+// It retrieves the records from table and returns the result as []*Entity.
+// It returns nil if there's no record retrieved with the given conditions from table.
+//
+// The optional parameter is the same as the parameter of Model.Where function,
+// see Model.Where.
+func (m *arModel) All(where ...interface{}) ([]*Entity, error) {
+ all, err := m.M.All(where...)
+ if err != nil {
+ return nil, err
+ }
+ var entities []*Entity
+ if err = all.Structs(&entities); err != nil && err != sql.ErrNoRows {
+ return nil, err
+ }
+ return entities, nil
+}
+
+// One retrieves one record from table and returns the result as *Entity.
+// It returns nil if there's no record retrieved with the given conditions from table.
+//
+// The optional parameter is the same as the parameter of Model.Where function,
+// see Model.Where.
+func (m *arModel) One(where ...interface{}) (*Entity, error) {
+ one, err := m.M.One(where...)
+ if err != nil {
+ return nil, err
+ }
+ var entity *Entity
+ if err = one.Struct(&entity); err != nil && err != sql.ErrNoRows {
+ return nil, err
+ }
+ return entity, nil
+}
+
+// FindOne retrieves and returns a single Record by Model.WherePri and Model.One.
+// Also see Model.WherePri and Model.One.
+func (m *arModel) FindOne(where ...interface{}) (*Entity, error) {
+ one, err := m.M.FindOne(where...)
+ if err != nil {
+ return nil, err
+ }
+ var entity *Entity
+ if err = one.Struct(&entity); err != nil && err != sql.ErrNoRows {
+ return nil, err
+ }
+ return entity, nil
+}
+
+// FindAll retrieves and returns Result by by Model.WherePri and Model.All.
+// Also see Model.WherePri and Model.All.
+func (m *arModel) FindAll(where ...interface{}) ([]*Entity, error) {
+ all, err := m.M.FindAll(where...)
+ if err != nil {
+ return nil, err
+ }
+ var entities []*Entity
+ if err = all.Structs(&entities); err != nil && err != sql.ErrNoRows {
+ return nil, err
+ }
+ return entities, nil
+}
+
+// Chunk iterates the table with given size and callback function.
+func (m *arModel) Chunk(limit int, callback func(entities []*Entity, err error) bool) {
+ m.M.Chunk(limit, func(result gdb.Result, err error) bool {
+ var entities []*Entity
+ err = result.Structs(&entities)
+ if err == sql.ErrNoRows {
+ return false
+ }
+ return callback(entities, err)
+ })
+}
+
+// LockUpdate sets the lock for update for current operation.
+func (m *arModel) LockUpdate() *arModel {
+ return &arModel{m.M.LockUpdate()}
+}
+
+// LockShared sets the lock in share mode for current operation.
+func (m *arModel) LockShared() *arModel {
+ return &arModel{m.M.LockShared()}
+}
+
+// Unscoped enables/disables the soft deleting feature.
+func (m *arModel) Unscoped() *arModel {
+ return &arModel{m.M.Unscoped()}
+}
diff --git a/plugin/blog/service/blog_service/classification.go b/plugin/blog/service/blog_service/classification.go
new file mode 100644
index 0000000..c4bed5f
--- /dev/null
+++ b/plugin/blog/service/blog_service/classification.go
@@ -0,0 +1,73 @@
+package blog_service
+
+import (
+ "gfast/plugin/blog/model/blog_classification"
+)
+
+//获取频道列表
+func GetMenuListChannel() (list []*blog_classification.Entity, err error) {
+ //获取频道列表
+ listAll, err := GetMenuList()
+ if err != nil {
+ return
+ }
+ list = make([]*blog_classification.Entity, 0, len(listAll))
+ for _, v := range listAll {
+ list = append(list, v)
+ }
+ return
+}
+
+//获取所有菜单列表
+func GetMenuList() (list []*blog_classification.Entity, err error) {
+ return blog_classification.GetList()
+}
+
+// 添加
+func AddClassificationSave(req *blog_classification.AddReq) (err error) {
+ // 判断名称是否已存在
+ err = blog_classification.CheakClassificationNameUnique(req.ClassificationName, 0)
+ if err != nil {
+ return err
+ }
+ // 不存在则调用AddSave()函数添加
+ err = blog_classification.AddSave(req)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// 批量删除
+func DeleteClassificationByIds(ids []int) error {
+ return blog_classification.DeleteClassificationByIds(ids)
+}
+
+// 修改
+func EditClassificationSave(editReq *blog_classification.EditReq) error {
+ // 判断修改后的名称的唯一性
+ err := blog_classification.CheakClassificationNameUnique(editReq.ClassificationName, editReq.ClassificationId)
+ if err != nil {
+ return err
+ }
+ err = blog_classification.EditSave(editReq)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// 根据ID查询
+func GetClassificationByID(id int64) (*blog_classification.Entity, error) {
+ return blog_classification.GetClassificationByID(id)
+}
+
+// 分页查询
+func SelectClassificationListByPage(req *blog_classification.SelectPageReq) (total int, page int64, list []*blog_classification.Entity, err error) {
+ return blog_classification.SelectListByPage(req)
+}
+
+// 查询所有状态为正常的分类
+func FindAllList() (list []*blog_classification.Entity, err error) {
+ return blog_classification.FindAllList()
+}
diff --git a/plugin/blog/service/blog_service/comment.go b/plugin/blog/service/blog_service/comment.go
new file mode 100644
index 0000000..cfa728c
--- /dev/null
+++ b/plugin/blog/service/blog_service/comment.go
@@ -0,0 +1,46 @@
+package blog_service
+
+import (
+ "gfast/plugin/blog/model/blog_comment"
+)
+
+// 添加
+func AddCommentSave(req *blog_comment.AddReq) error {
+ err := blog_comment.AddSave(req)
+ if err != nil {
+ return err
+ }
+ if req.CommentPid != 0 {
+ err = blog_comment.FindSonCommentCount(int(req.ReplyId)) // 每次添加评论都要更新所回复评论的回复数
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// 删除
+func DeleteCommentByIDs(Ids []int) error {
+ return blog_comment.DeleteByIDs(Ids)
+}
+
+//修改
+func EditCommentSave(editReq *blog_comment.EditReq) error {
+ return blog_comment.EditSave(editReq)
+}
+
+// 根据ID查询
+func GetCommentByID(id int64) (*blog_comment.Entity, error) {
+ return blog_comment.GetCommentByID(id)
+}
+
+// 分页查询
+func SelectCommentListByPage(req *blog_comment.SelectPageReq) (total int, page int64, list []*blog_comment.ListEntity, err error) {
+ blog_comment.FindUserAndLogIds(req)
+ return blog_comment.SelectListByPage(req)
+}
+
+// GetChildren 将分页查出的实体切片中每一个元素的子评论查出来并添加进去
+func GetChildren(listEntity []*blog_comment.ListEntity) ([]*blog_comment.ListEntity, error) {
+ return blog_comment.GetChildren(listEntity)
+}
diff --git a/plugin/blog/service/blog_service/log.go b/plugin/blog/service/blog_service/log.go
new file mode 100644
index 0000000..08a8b4c
--- /dev/null
+++ b/plugin/blog/service/blog_service/log.go
@@ -0,0 +1,45 @@
+package blog_service
+
+import (
+ "gfast/plugin/blog/model/blog_log"
+)
+
+// 添加
+func AddLogSave(req *blog_log.AddReq) error {
+ return blog_log.AddSave(req)
+}
+
+// 删除
+func DeleteLogByIDs(Ids []int) error {
+ return blog_log.DeleteByIDs(Ids)
+}
+
+//修改
+func EditLogSave(editReq *blog_log.EditReq) error {
+ return blog_log.EditSave(editReq)
+}
+
+// 根据ID查询
+func GetLogByID(id int64) (*blog_log.ListEntity, error) {
+ return blog_log.GetLogByID(id)
+}
+
+// 分页查询日志
+func SelectLogListByPage(req *blog_log.SelectPageReq) (total int, page int64, list []*blog_log.ListEntity, err error) {
+ return blog_log.SelectListByPage(req)
+}
+
+// 按时间倒序查询size篇标志为sign分类id为typeId,状态为status的文章,标志值0.一般,1.置顶,2.幻灯,3.推荐,typeId等于0时不区分分类
+func FindSizeArticleBySign(size int, status int, sign int, typeId int) ([]*blog_log.ListEntity, error) {
+ return blog_log.FindSizeArticleBySign(size, status, sign, typeId)
+}
+
+// 按时间倒序查询size篇分类id为typeId,状态为status的文章,typeId等于0时不区分分类
+func FindSizeArticle(size int, status int, typeId int) (list []*blog_log.ListEntity, err error) {
+ return blog_log.FindSizeArticle(size, status, typeId)
+}
+
+// 查询size篇文章并根据点击数排序
+func FindArticleByHits(size int, status int) ([]*blog_log.Entity, error) {
+ return blog_log.FindArticleByHits(size, status)
+}
diff --git a/public/resource/plugin/blog/css/base.css b/public/resource/plugin/blog/css/base.css
new file mode 100644
index 0000000..f92e9e3
--- /dev/null
+++ b/public/resource/plugin/blog/css/base.css
@@ -0,0 +1,1320 @@
+@charset "utf-8";
+/* CSS Document */
+* {
+ margin: 0;
+ padding: 0
+}
+
+img {
+ border: 0;
+ display: block
+}
+/*去除图片默认的1px边框;将图片标签变成块级元素,可清除默认的3px距离*/
+
+ul li {
+ list-style: none;
+}
+/*去除ul li标签前面小点*/
+
+a {
+ color: #222;
+ text-decoration: none;
+}
+/* 所有a 标签的 颜色为#222; 去除默认的下划线*/
+
+a:hover {
+ color: #e01109;
+}
+
+h1 {
+ font-size: 28px
+}
+
+h2 {
+ font-size: 18px
+}
+
+h3, h4, h5, h6 {
+ font-size: 16px
+}
+
+body {
+ font: 15px "Microsoft YaHei", Arial, Helvetica, sans-serif;
+ color: #222;
+ background: #f5f5f5
+}
+
+/*设置 文字大小、字体、颜色、背景*/
+header, main {
+ width: 1200px;
+ margin: auto
+}
+
+article {
+ width: 860px;
+ float: left;
+}
+
+aside {
+ width: 320px;
+ float: right;
+}
+
+footer {
+ width: 100%;
+ padding-bottom: 20px;
+ text-align: center;
+ clear: both
+}
+
+.nav {
+ /*float: left;*/
+ display: table;
+ width: 100%;
+ height: 105px;
+ margin: 20px 0;
+ box-shadow: rgba(0, 0, 0, .1) 3px 5px 5px;
+}
+
+.nav a img {
+ width: 90px;
+ height: 90px;
+ float: left;
+ /*background: #fff;*/
+ /*display: block;*/
+ /*margin-right: 20px;*/
+ /*border-radius: 100%;*/
+}
+
+.nav-list1 {
+ float: right;
+}
+
+.submenu {
+ float: left;
+ margin: 0px 20px 0px 0px;
+ font-size: 20px;
+ width: 86px;
+ height: 37px;
+ text-align: center;
+}
+
+.logo-name {
+ margin-top: 20px;
+}
+
+.describe {
+ margin-top: 10px;
+ float: left;
+}
+
+.submenu:hover .nav-submenu {
+ display: block;
+}
+
+.nav-submenu {
+ width: 125px;
+ display: none;
+ border: 1px solid #c7c3c3;
+ text-align: left;
+ margin: 10px 0 0 0;
+ background: #f2f2f4;
+ font-size: 17px;
+ position: relative;
+ z-index: 10;
+ box-shadow: rgba(0, 0, 0, 0.2) 3px 5px 5px;
+}
+
+.nav-submenu div {
+ background: #f2f2f4;
+ border-left: 1px solid #c7c3c3;
+ border-top: 1px solid #c7c3c3;
+ left: 38px;
+ transform: rotate(45deg);
+ top: -6px;
+ width: 8px;
+ height: 8px;
+ position: absolute;
+}
+
+.nav-submenu li {
+ margin: 7px 0px;
+ padding-left: 15px;
+}
+
+.nav-submenu li:hover {
+ background: #dedfe1;
+}
+
+.nav-submenu li a:hover {
+ color: black;
+}
+
+/*blogs*/
+.blogs {
+}
+
+.blogs li {
+ overflow: hidden;
+ margin-bottom: 20px;
+ background: #fff;
+ padding: 20px;
+ position: relative;
+}
+
+.top_blog .blogs li::after {
+ position: absolute;
+ content: "";
+ right: 10px;
+ top: 40px;
+ border-style: solid;
+ border-width: 0 13px 13px 13px;
+}
+
+.top_blog .blogs li::after {
+ border-color: #e01109 #e01109 transparent #e01109;
+}
+
+.top_blog .blogs li::before {
+ background: #e01109;
+ position: absolute;
+ content: "";
+ right: 10px;
+ top: 0;
+ width: 26px;
+ height: 30px;
+ text-align: center;
+ font-size: 16px;
+ font-weight: bold;
+ color: #fff;
+ padding: 10px 0 0 0;
+}
+
+.top_blog .blogs li:nth-child(1)::before {
+ content: "1"
+}
+
+.top_blog .blogs li:nth-child(2)::before {
+ content: "2"
+}
+
+.top_blog .blogs li:nth-child(3)::before {
+ content: "3"
+}
+
+.blogs li i {
+ display: block;
+ width: 160px;
+ height: 110px;
+ overflow: hidden;
+ float: left;
+ margin-right: 20px;
+}
+
+.blogs li i img {
+ width: 100%;
+ min-height: 100%;
+ transition: .5s;
+}
+
+.blogs h2 {
+ margin: 0 0 14px 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.blog_smalltext {
+ overflow: hidden;
+ -webkit-box-orient: vertical;
+ display: -webkit-box;
+ -webkit-line-clamp: 2;
+ font-size: 14px;
+ color: #666;
+ margin-bottom: 14px;
+}
+
+.blog_info {
+ color: #888;
+ font-size: 13px;
+}
+
+.blog_info span {
+ margin-right: 15px;
+ position: relative;
+ padding-left: 20px;
+ line-height: 20px;
+}
+
+.blog_info span a {
+ color: #518f97
+}
+
+.blog_info span a:hover {
+ color: #e01109;
+}
+
+.blog_info span:nth-child(n):before {
+ position: absolute;
+ content: "";
+ width: 20px;
+ height: 20px;
+ left: 0;
+ top: 0;
+}
+
+.blog_info span:nth-child(1) {
+ background: url(../image/icon.png) no-repeat 0 0;
+ background-size: 18px
+}
+
+.blog_info span:nth-child(2) {
+ background: url(../image/icon.png) no-repeat 0 -18px;
+ background-size: 18px
+}
+
+.blog_info span:nth-child(3) {
+ background: url(../image/icon.png) no-repeat 0 -36px;
+ background-size: 18px
+}
+
+.blog_info span:nth-child(4) {
+ background: url(../image/icon.png) no-repeat 0 -54px;
+ background-size: 18px
+}
+
+.blog_info span:nth-child(5) {
+ background: url(../image/icon.png) no-repeat 0 -72px;
+ background-size: 18px
+}
+
+.blogs li:hover {
+ box-shadow: rgba(0, 0, 0, .1) 3px 5px 5px;
+}
+
+.blogs li:hover h2 {
+ color: #F00
+}
+
+.blogs li:hover img {
+ transform: scale(1.1)
+}
+
+/*aside*/
+aside div {
+ background: #fff;
+ margin-bottom: 20px
+}
+
+.search {
+ background: #ed4040;
+ position: relative;
+ border: #ed4040 2px solid;
+ border-radius: 5px;
+ overflow: hidden;
+}
+
+.search input.input_submit {
+ border: 0;
+ color: #fff;
+ outline: none;
+ position: absolute;
+ top: 0;
+ right: 0;
+ width: 25%;
+ display: block;
+ font-size: 15px;
+ height: 36px;
+ line-height: 36px;
+ text-indent: 1em;
+ cursor: pointer;
+ background: url(../image/search.png) no-repeat left 10px center;
+ background-size: 21px;
+}
+
+.search input.input_text {
+ border: 0;
+ line-height: 36px;
+ height: 36px;
+ font-size: 14px;
+ width: 75%;
+ outline: none;
+ text-indent: 1em;
+}
+
+h2.aside_title {
+ padding: 20px;
+}
+
+.paihang {
+}
+
+.paihang ol {
+ padding: 0 20px 20px 40px;
+}
+
+.paihang ol li {
+ margin-bottom: 20px;
+ padding-left: 10px;
+}
+
+.coffee {
+ padding: 0px 20px 20px 20px;
+ width: 280px;
+}
+
+.recommend {
+ padding: 0px 20px 20px 20px;
+ height: 150px;
+ width: 280px;
+}
+
+.links span {
+ float: right;
+ font-size: 16px;
+ font-weight: normal
+}
+
+.links span a {
+ color: #666;
+}
+
+.links span a:hover {
+ color: #f00
+}
+
+.links ul {
+ padding: 0 20px 20px 20px;
+ overflow: hidden
+}
+
+.links li {
+ width: 50%;
+ float: left;
+ display: inline-block;
+ line-height: 30px;
+ text-align: center
+}
+
+.links li a {
+ margin: 5px;
+ border: #666 1px solid;
+ display: block;
+ border-radius: 3px;
+ color: #666
+}
+
+.links li a:hover {
+ border: #222 1px solid;
+ color: #000
+}
+
+/*footer*/
+footer img {
+ display: inline-block;
+ vertical-align: middle;
+ margin-right: 5px;
+}
+
+footer p {
+ line-height: 30px;
+ font-size: 14px;
+ color: #666
+}
+
+footer p span {
+ margin: 0 10px
+}
+
+footer a {
+ color: #666
+}
+
+/*weizhi*/
+.weizhi {
+ margin: 0 0 20px 0;
+ font-size: 14px;
+ color: #949494;
+ background: url(../image/weizhi.png) no-repeat left center;
+ background-size: 20px;
+ padding-left: 30px
+}
+
+.weizhi a {
+ color: #949494;
+}
+
+.weizhi a:hover {
+ color: #000
+}
+
+/*suiyan*/
+.suiyan_list {
+}
+
+.suiyan_list ul {
+ overflow: hidden;
+}
+
+.suiyan_list ul li {
+ width: 25%;
+ float: left;
+}
+
+.suiyan_list ul li a {
+ display: block;
+ padding: 20px;
+ margin: 30px auto;
+ background: #fff;
+ box-shadow: rgba(0, 0, 0, .1) 3px 5px 5px;
+ width: 200px;
+ height: 200px;
+ transform: rotate(-10deg);
+ position: relative;
+ color: #333
+}
+
+.suiyan_list ul li:nth-child(even) a {
+ -webkit-transform: rotate(10deg);
+ top: 10px;
+ background: rgb(255, 255, 205);;
+}
+
+.suiyan_list ul li:nth-child(3n) a {
+ -webkit-transform: rotate(-5deg);
+ top: -10px;
+ background: rgb(254, 201, 227);
+}
+
+.suiyan_list ul li:nth-child(5n) a {
+ -webkit-transform: rotate(8deg);
+ top: -10px;
+ background: rgb(210, 251, 253);
+}
+
+.suiyan_list ul li a:hover {
+ transform: scale(1.1);
+ transition: .2s;
+ box-shadow: rgba(0, 0, 0, .5) 3px 5px 5px;
+}
+
+.suiyan_time {
+ font-size: 14px;
+ color: #999;
+ margin-bottom: 10px;
+}
+
+.suiyan_text {
+ line-height: 30px;
+}
+
+.suiyan_list span {
+ position: absolute;
+ width: 50px;
+ height: 50px;
+ bottom: 5px;
+ right: 5px;
+}
+
+/*tuijian*/
+.tuijian {
+}
+
+.tuijian ul {
+ padding: 0 20px 20px 20px;
+}
+
+.tuijian ul li {
+ overflow: hidden;
+ margin-bottom: 20px;
+}
+
+.tuijian ul li a {
+}
+
+.tuijian i {
+ display: block;
+ width: 90px;
+ height: 60px;
+ overflow: hidden;
+ margin-right: 20px;
+ float: left;
+}
+
+.tuijian p {
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 2;
+ overflow: hidden;
+ margin-top: 8px
+}
+
+.tuijian img {
+ width: 100%;
+ min-height: 100%;
+}
+
+/*love*/
+.love {
+}
+
+.love ul {
+ padding: 0 20px 20px 20px;
+ overflow: hidden
+}
+
+.love ul li {
+ overflow: hidden;
+ width: 50%;
+ float: left;
+}
+
+.love ul li a {
+ margin: 0 3px 10px;
+ display: block;
+ overflow: hidden;
+}
+
+.love i {
+ display: block;
+ width: 100%;
+ height: 90px;
+ overflow: hidden;
+}
+
+.love p {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ font-size: 14px;
+}
+
+.love img {
+ width: 100%;
+ min-height: 100%;
+}
+
+/*tagsclous*/
+.tagsclous h2 {
+ padding-bottom: 0 !important
+}
+
+.tagsclous ul {
+ padding: 20px;
+ overflow: hidden;
+ position: relative;
+}
+
+.tagsclous ul:before {
+ animation: linescroll 5s;
+ animation-iteration-count: infinite;
+ content: "";
+ width: 20px;
+ height: 20px;
+ background: red;
+ position: absolute;
+ z-index: 9
+}
+
+.tagsclous a {
+ display: inline-block;
+ background: #ffffff;
+ width: 33.3333%;
+ font-size: 14px;
+ float: left;
+ line-height: 40px;
+ text-align: center;
+ position: relative;
+}
+
+.tagsclous a:nth-child(3n-1):before {
+ width: 1px;
+ height: 100%;
+ content: "";
+ position: absolute;
+ background: #eae5e5;
+ left: 0;
+ top: 0;
+}
+
+.tagsclous a:nth-child(3n-1):after {
+ width: 1px;
+ height: 100%;
+ content: "";
+ position: absolute;
+ background: #eae5e5;
+ right: 0;
+ top: 0;
+}
+
+.tagsclous a:nth-child(6n+1) {
+ background: #f5f5f5;
+}
+
+.tagsclous a:nth-child(6n+2) {
+ background: #f5f5f5;
+}
+
+.tagsclous a:nth-child(6n+3) {
+ background: #f5f5f5;
+}
+
+.tagsclous a:hover {
+ background: #e01109;
+ color: #fff
+}
+
+@keyframes linescroll {
+ 0% {
+ left: 0px;
+ top: 0px;
+ }
+ 25% {
+ left: 300px;
+ top: 0px;
+ }
+ 50% {
+ left: 300px;
+ top: 220px;
+ }
+ 75% {
+ left: 0px;
+ top: 220px;
+ }
+ 100% {
+ left: 0px;
+ top: 0px;
+ }
+}
+
+/*xiangce*/
+.xiangce_list ul {
+ width: 25%;
+ float: left;
+ overflow: hidden;
+}
+
+.xiangce_list li {
+ margin: 10px
+}
+
+.xiangce_list li a {
+ margin: 0 0 30px 0;
+ padding: 12px;
+ background: white;
+ border-radius: 3px;
+ box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.05);
+ display: block;
+}
+
+.xiangce_list li img {
+ width: 100%;
+ margin: 0 auto 10px
+}
+
+.xiangce_list li p {
+ color: #666;
+ text-align: center
+}
+
+.xiangce_list li a:hover {
+ box-shadow: 0px 2px 3px 1px rgba(0, 0, 0, 0.1);
+ transform: translateY(-5px);
+ transition: all .2s;
+}
+
+.time_list {
+ background: #fff;
+ padding: 20px;
+}
+
+.time_list li {
+ line-height: 36px;
+}
+
+.time_list li a {
+ display: block;
+ position: relative;
+}
+
+.time_list li span {
+ margin: 0 40px 0 0;
+ float: left;
+ color: #999;
+}
+
+.time_list ul {
+ position: relative;
+}
+
+.time_list ul:before {
+ position: absolute;
+ content: "";
+ width: 2px;
+ height: 100%;
+ background: #ccc;
+ left: 102px;
+ top: 0;
+}
+
+.time_list li a:before {
+ position: absolute;
+ content: "";
+ width: 12px;
+ height: 12px;
+ background: #fff;
+ border: #ccc 2px solid;
+ border-radius: 100%;
+ left: 95px;
+ top: 10px;
+}
+
+.time_list li a:hover:before {
+ background: #f00;
+}
+
+.about {
+ background: #fff;
+ padding: 20px;
+ font-size: 16px;
+ line-height: 24px;
+}
+
+.about p {
+ margin: 10px 0;
+}
+
+.about h2 {
+ line-height: 40px;
+ background: #f2f2f2;
+ border-left: #000 4px solid;
+ padding-left: 10px;
+ margin: 10px 0;
+}
+
+.about img {
+ width: 80% !important;
+ height: auto !important;
+ margin: auto;
+}
+
+.aside_right {
+ padding: 20px;
+}
+
+.aside_right h2 {
+ margin: 0 0 10px 0;
+}
+
+.aside_right ul {
+ overflow: hidden;
+ padding: 0 0 20px 0;
+}
+
+.aside_right ul li {
+ line-height: 30px;
+ background: #f2f2f2;
+ margin-bottom: 10px;
+ text-indent: 1em;
+ border-radius: 10px;
+}
+
+.aside_right ol {
+ line-height: 30px;
+ margin-left: 30px;
+}
+
+.container {
+ background: #fff;
+ padding: 20px;
+}
+
+.container h1 {
+ font-size: 24px;
+ margin-bottom: 20px;
+}
+
+.content {
+ margin-top: 15px;
+ font-size: 16px;
+ line-height: 24px;
+}
+
+.content p {
+ margin: 20px 0;
+}
+
+.content img {
+ margin: auto;
+ max-width: 100% !important;
+ height: auto !important;
+}
+
+.otherlink {
+ margin: 20px 0;
+ line-height: 26px;
+}
+
+.otherlink h2, .pinglun_box h2 {
+ margin-bottom: 10px;
+ position: relative;
+}
+
+.otherlink h2:before, .pinglun_box h2:before {
+ position: absolute;
+ content: "";
+ width: 4px;
+ height: 100%;
+ background: #f00;
+ left: -20px;
+}
+
+
+.relevant_article {
+ width: 820px;
+ height: 145px;
+}
+
+.relevant_article div {
+ float: left;
+ margin-right: 5px;
+ width: 200px;
+ height: 135px;
+}
+
+.otherlink img {
+ width: 200px;
+ height: 135px;
+}
+
+@media only screen and (max-width: 1200px) {
+ header, main {
+ width: 90%;
+ }
+
+ article {
+ width: 68%;
+ }
+
+ aside {
+ width: 30%;
+ }
+}
+
+@media only screen and (max-width: 768px) {
+ article {
+ width: 100%;
+ }
+
+ .touxiang {
+ width: 60%;
+ }
+
+ aside {
+ display: none;
+ }
+
+ nav ul li a {
+ padding: 0 15px;
+ }
+
+ img.weixin {
+ z-index: 999;
+ }
+
+ .suiyan_list ul {
+ padding: 30px;
+ }
+
+ .suiyan_list ul li {
+ width: 33.33333%;
+ }
+
+ .suiyan_list ul li a {
+ padding: 10px;
+ width: 180px;
+ }
+
+ .suiyan_text {
+ font-size: 14px;
+ }
+
+ .suiyan_list ul li a:hover {
+ z-index: 9;
+ }
+
+ .xiangce_list li p {
+ font-size: 14px;
+ }
+
+ .xiangce_list li a {
+ padding: 5px;
+ }
+
+ .xiangce_list li {
+ margin: 5px;
+ }
+}
+
+@media only screen and (max-width: 480px) {
+ header, main {
+ width: 96%;
+ }
+
+ .touxiang {
+ width: 100%;
+ margin: 5px 0;
+ }
+
+ .touxiang i {
+ width: 60px;
+ height: 60px;
+ margin-right: 10px;
+ }
+
+ .touxiang i img {
+ width: 100%;
+ }
+
+ .touxiang h2 {
+ margin: 5px 0;
+ }
+
+ .guanzhu {
+ position: fixed;
+ z-index: 9;
+ bottom: 0;
+ top: inherit;
+ width: 100%;
+ }
+
+ .guanzhu:hover .weixin {
+ bottom: 30px;
+ left: 33%;
+ box-shadow: #b8b1b1 2px 2px 10px;
+ }
+
+ footer {
+ padding-bottom: 50px;
+ margin-top: 20px
+ }
+
+ nav {
+ overflow-x: scroll;
+ height: 40px;
+ line-height: 40px;
+ }
+
+ nav ul {
+ width: max-content;
+ overflow-x: scroll;
+ }
+
+ .blogs li {
+ padding: 10px;
+ }
+
+ .blogs h2 {
+ font-size: 16px;
+ margin: 0 0 5px 0;
+ }
+
+ .blogs li i {
+ width: 90px;
+ height: 60px;
+ margin-right: 10px;
+ }
+
+ .top_blog .blogs li::after {
+ display: none;
+ }
+
+ .top_blog .blogs li::before {
+ display: none;
+ }
+
+ .blog_info span:last-child {
+ display: none;
+ }
+
+ .suiyan_list ul li {
+ width: 50%;
+ }
+
+ .xiangce_list ul {
+ width: 100%
+ }
+
+ .xiangce_list li a {
+ padding: 12px;
+ }
+
+ .time_list li span {
+ width: 100%;
+ }
+
+ .time_list ul:before {
+ left: 0;
+ }
+
+ .time_list li a:before {
+ left: -8px;
+ }
+
+ .time_list li a {
+ padding-left: 20px;
+ }
+
+ .time_list li span {
+ padding-left: 20px;
+ }
+
+ .about img {
+ width: 100% !important;
+ }
+
+ .container h1 {
+ font-size: 20px;
+ }
+
+ .info-pre-next p, .otherlink li {
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ }
+
+ footer p span:last-child {
+ display: block;
+ }
+
+ main {
+ overflow: hidden;
+ }
+}
+
+.swiper-container {
+ width: 860px;
+ height: 350px;
+ margin-bottom: 20px;
+ float: left;
+}
+
+.swiper-wrapper img {
+ width: 860px;
+ height: 350px;
+}
+
+.swiper-container {
+ --swiper-theme-color: #e5e4e4; /* 设置Swiper风格 */
+ --swiper-navigation-color: #8B8EAD; /* 单独设置按钮颜色 */
+ --swiper-navigation-size: 30px; /* 设置按钮大小 */
+}
+
+/*留言*/
+.guest_tit {
+ height: 70px;
+ line-height: 50px;
+}
+
+.guest_tit input {
+ border-radius: 5px;
+ height: 40px;
+ border: 1px solid #dddddd;
+ font-size: 17px;
+ font-family: 微软雅黑;
+ width: 200px;
+ float: left;
+ padding: 0 10px;
+}
+
+.guest_tit label {
+ width: 100px;
+ line-height: 40px;
+ display: block;
+ float: left;
+ font-size: 18px;
+}
+
+.guest_txt {
+ height: 187px;
+}
+
+.guest_txt textarea {
+ border-radius: 5px;
+ width: 80%;
+ font-size: 17px;
+ font-family: 微软雅黑;
+ height: 150px;
+ padding: 5px 10px;
+ border: 1px solid #dddddd;
+}
+
+.guest_s {
+ line-height: 50px;
+ width: 450px;
+}
+
+.btn {
+ width: 20%;
+ border-radius: 5px;
+ background-color: #e01109;
+ display: inline-block;
+ color: #fff;
+ border: none;
+ line-height: 47px;
+ font-size: 20px;
+ cursor: pointer;
+ padding: 0 10px;
+ margin-right: 15px;
+}
+
+/* 分页样式 */
+.paging {
+ margin: 15px;
+ text-align: center;
+ font-size: 15px;
+}
+
+.paging a, .paging span {
+ margin: 0 1px;
+ border: solid 1px #ccc;
+ padding: 5px;
+}
+
+.GPageSpan {
+ color: red;
+}
+
+/* 没有评论的样式 */
+.no_comment {
+ height: 100px;
+ color: #7d8293;
+ padding-top: 6px;
+}
+
+/* 评论样式 */
+.comment {
+ margin-top: 10px;
+ border-bottom: 1px solid #dee2e6;
+}
+
+.comment_nickname {
+ float: left;
+ font-size: 15px;
+ font-weight: 700;
+ line-height: 18px;
+ padding-bottom: 4px;
+}
+
+.comment_create_time {
+ color: #99a2aa;
+ line-height: 18px;
+ font-size: 13px;
+}
+
+.comment_content {
+ padding-left: 5px;
+ margin-top: 10px;
+ color: #666;
+}
+
+.comment_reply {
+ color: #99a2aa;
+ font-size: 14px;
+ margin: 7px 0px;
+}
+
+.comment_reply span {
+ padding: 5px;
+ border-radius: 4px;
+ display: inline-block;
+}
+
+.comment_reply span:hover {
+ color: #508af5;
+ background: #dcdee3;
+ border-radius: 4px;
+ cursor: pointer;
+ display: inline-block;
+}
+
+/* 友情链接 */
+.link_content {
+ width: 860px;
+ float: left;
+}
+
+.link_content h2 {
+ margin: 0 auto;
+ width: 100px;
+}
+
+.link_content div {
+ margin: 30px auto;
+ width: 470px;
+}
+
+.link_content div input {
+ height: 35px;
+ width: 438px;
+ padding: 2px 15px;
+ font-size: 16px;
+ border-radius: 4px;
+ border: 1px solid #c8d0d0;
+}
+
+.link_content div textarea {
+ width: 438px;
+ padding: 10px 15px;
+ font-size: 16px;
+ font-family: 微软雅黑;
+ border-radius: 4px;
+ border: 1px solid #c8d0d0;
+}
+
+::-webkit-input-placeholder {
+ color: #d1d3da;
+}
+
+.comment_button {
+ border: none;
+ background: #e01109;
+ color: #fff;
+ border-radius: 5px;
+ width: 100%;
+ cursor: pointer;
+ line-height: 50px;
+ font-size: 20px;
+}
+
+.reply_box {
+ margin-left: 25px;
+}
+
+.reply_id {
+ display: none;
+}
+
+.reply_item {
+ display: none;
+}
+
+.view_more {
+ color: #99a2aa;
+ font-size: 14px;
+ margin-left: 29px;
+}
+
+.view_more span {
+ padding: 5px;
+ border-radius: 4px;
+ display: inline-block;
+}
+
+.view_more span:hover {
+ color: #508af5;
+ background: #dcdee3;
+ border-radius: 4px;
+ cursor: pointer;
+ display: inline-block;
+}
\ No newline at end of file
diff --git a/public/resource/plugin/blog/image/001.jpg b/public/resource/plugin/blog/image/001.jpg
new file mode 100644
index 0000000..d1ac5a2
Binary files /dev/null and b/public/resource/plugin/blog/image/001.jpg differ
diff --git a/public/resource/plugin/blog/image/002.png b/public/resource/plugin/blog/image/002.png
new file mode 100644
index 0000000..2b7e8bb
Binary files /dev/null and b/public/resource/plugin/blog/image/002.png differ
diff --git a/public/resource/plugin/blog/image/003.jpg b/public/resource/plugin/blog/image/003.jpg
new file mode 100644
index 0000000..03d26e0
Binary files /dev/null and b/public/resource/plugin/blog/image/003.jpg differ
diff --git a/public/resource/plugin/blog/image/004.jpg b/public/resource/plugin/blog/image/004.jpg
new file mode 100644
index 0000000..c96f9ba
Binary files /dev/null and b/public/resource/plugin/blog/image/004.jpg differ
diff --git a/public/resource/plugin/blog/image/005.jpg b/public/resource/plugin/blog/image/005.jpg
new file mode 100644
index 0000000..6f317b9
Binary files /dev/null and b/public/resource/plugin/blog/image/005.jpg differ
diff --git a/public/resource/plugin/blog/image/006.jpg b/public/resource/plugin/blog/image/006.jpg
new file mode 100644
index 0000000..9c7d513
Binary files /dev/null and b/public/resource/plugin/blog/image/006.jpg differ
diff --git a/public/resource/plugin/blog/image/bq1.png b/public/resource/plugin/blog/image/bq1.png
new file mode 100644
index 0000000..66ee2c1
Binary files /dev/null and b/public/resource/plugin/blog/image/bq1.png differ
diff --git a/public/resource/plugin/blog/image/bq2.png b/public/resource/plugin/blog/image/bq2.png
new file mode 100644
index 0000000..de4c2bb
Binary files /dev/null and b/public/resource/plugin/blog/image/bq2.png differ
diff --git a/public/resource/plugin/blog/image/bq3.png b/public/resource/plugin/blog/image/bq3.png
new file mode 100644
index 0000000..20e7527
Binary files /dev/null and b/public/resource/plugin/blog/image/bq3.png differ
diff --git a/public/resource/plugin/blog/image/bq4.png b/public/resource/plugin/blog/image/bq4.png
new file mode 100644
index 0000000..db35e2d
Binary files /dev/null and b/public/resource/plugin/blog/image/bq4.png differ
diff --git a/public/resource/plugin/blog/image/bq5.png b/public/resource/plugin/blog/image/bq5.png
new file mode 100644
index 0000000..2994698
Binary files /dev/null and b/public/resource/plugin/blog/image/bq5.png differ
diff --git a/public/resource/plugin/blog/image/ga.png b/public/resource/plugin/blog/image/ga.png
new file mode 100644
index 0000000..41870d1
Binary files /dev/null and b/public/resource/plugin/blog/image/ga.png differ
diff --git a/public/resource/plugin/blog/image/gfastLogo.png b/public/resource/plugin/blog/image/gfastLogo.png
new file mode 100644
index 0000000..2c10f1f
Binary files /dev/null and b/public/resource/plugin/blog/image/gfastLogo.png differ
diff --git a/public/resource/plugin/blog/image/icon.png b/public/resource/plugin/blog/image/icon.png
new file mode 100644
index 0000000..49bb327
Binary files /dev/null and b/public/resource/plugin/blog/image/icon.png differ
diff --git a/public/resource/plugin/blog/image/search.png b/public/resource/plugin/blog/image/search.png
new file mode 100644
index 0000000..6b16a86
Binary files /dev/null and b/public/resource/plugin/blog/image/search.png differ
diff --git a/public/resource/plugin/blog/image/weizhi.png b/public/resource/plugin/blog/image/weizhi.png
new file mode 100644
index 0000000..5dd6306
Binary files /dev/null and b/public/resource/plugin/blog/image/weizhi.png differ
diff --git a/public/resource/plugin/blog/image/wx.png b/public/resource/plugin/blog/image/wx.png
new file mode 100644
index 0000000..6b0a522
Binary files /dev/null and b/public/resource/plugin/blog/image/wx.png differ
diff --git a/public/resource/plugin/blog/image/yangqq.png b/public/resource/plugin/blog/image/yangqq.png
new file mode 100644
index 0000000..1a03daa
Binary files /dev/null and b/public/resource/plugin/blog/image/yangqq.png differ
diff --git a/public/resource/plugin/blog/js/nav.js b/public/resource/plugin/blog/js/nav.js
new file mode 100644
index 0000000..a58e1ab
--- /dev/null
+++ b/public/resource/plugin/blog/js/nav.js
@@ -0,0 +1,11 @@
+// JavaScript Document
+
+$(document).ready(function(){
+ var myNav = $("nav a"),i;
+ for(i=0;i
+