Kaynağa Gözat

新增模型与CMS

yxh 5 yıl önce
ebeveyn
işleme
289cf4d2ac
47 değiştirilmiş dosya ile 5840 ekleme ve 106 silme
  1. 8 0
      app/controller/admin/auth.go
  2. 194 0
      app/controller/admin/cms_menu.go
  3. 178 0
      app/controller/admin/cms_news.go
  4. 7 15
      app/controller/admin/config_dict.go
  5. 151 0
      app/controller/admin/model_category.go
  6. 106 0
      app/controller/admin/model_fields.go
  7. 170 0
      app/controller/admin/model_info.go
  8. 138 0
      app/controller/admin/plug_ad.go
  9. 151 0
      app/controller/admin/plug_ad_type.go
  10. 108 0
      app/controller/admin/plug_link.go
  11. 122 0
      app/controller/admin/plug_link_type.go
  12. 3 0
      app/model/admin/cms_category/cms_category.go
  13. 1 0
      app/model/admin/cms_category/cms_category_entity.go
  14. 20 39
      app/model/admin/cms_news/cms_news.go
  15. 0 3
      app/model/admin/cms_news/cms_news_entity.go
  16. 0 6
      app/model/admin/cms_news/cms_news_model.go
  17. 10 0
      app/model/admin/model_category/model_category.go
  18. 222 0
      app/model/admin/model_fields/model_fields.go
  19. 73 0
      app/model/admin/model_fields/model_fields_entity.go
  20. 375 0
      app/model/admin/model_fields/model_fields_model.go
  21. 24 0
      app/model/admin/model_fields/model_rules.go
  22. 264 0
      app/model/admin/model_info/model_info.go
  23. 73 0
      app/model/admin/model_info/model_info_entity.go
  24. 375 0
      app/model/admin/model_info/model_info_model.go
  25. 41 25
      app/model/admin/plug_ad/plug_ad.go
  26. 11 10
      app/model/admin/plug_adtype/plug_adtype.go
  27. 149 0
      app/model/admin/plug_link/plug_link.go
  28. 65 0
      app/model/admin/plug_link/plug_link_entity.go
  29. 359 0
      app/model/admin/plug_link/plug_link_model.go
  30. 142 0
      app/model/admin/plug_linktype/plug_linktype.go
  31. 59 0
      app/model/admin/plug_linktype/plug_linktype_entity.go
  32. 347 0
      app/model/admin/plug_linktype/plug_linktype_model.go
  33. 151 0
      app/service/admin/cms_service/menu.go
  34. 224 0
      app/service/admin/cms_service/news.go
  35. 44 0
      app/service/admin/model_service/category.go
  36. 165 0
      app/service/admin/model_service/field.go
  37. 373 0
      app/service/admin/model_service/info.go
  38. 30 0
      app/service/admin/plug_link_service/plugLink.go
  39. 48 0
      app/service/admin/plug_link_service/plugLinkType.go
  40. 47 0
      app/service/admin/plug_service/plug.go
  41. 30 0
      app/service/admin/plug_service/plugAd.go
  42. 4 0
      app/service/cache_service/cache_values.go
  43. 619 0
      app/service/common/com_model_service/model.go
  44. 3 1
      data/db.sql
  45. 73 5
      library/utils/slice_tree.go
  46. 57 2
      library/utils/tools.go
  47. 26 0
      router/routerAdmin.go

+ 8 - 0
app/controller/admin/auth.go

@@ -110,6 +110,10 @@ func (c *Auth) AddMenu(r *ghttp.Request) {
 		if !auth_service.CheckMenuNameUnique(menu.Name, 0) {
 			response.FailJson(true, r, "菜单规则名称已经存在")
 		}
+		//判断路由是否已经存在
+		if !auth_service.CheckMenuPathUnique(menu.Path, 0) {
+			response.FailJson(true, r, "路由地址已经存在")
+		}
 		//保存到数据库
 		err, _ := auth_service.AddMenu(menu)
 		if err != nil {
@@ -148,6 +152,10 @@ func (c *Auth) EditMenu(r *ghttp.Request) {
 		if !auth_service.CheckMenuNameUnique(menu.Name, id) {
 			response.FailJson(true, r, "菜单规则名称已经存在")
 		}
+		//判断路由是否已经存在
+		if !auth_service.CheckMenuPathUnique(menu.Path, id) {
+			response.FailJson(true, r, "路由地址已经存在")
+		}
 		//保存到数据库
 		err, _ := auth_service.EditMenu(menu, id)
 		if err != nil {

+ 194 - 0
app/controller/admin/cms_menu.go

@@ -0,0 +1,194 @@
+package admin
+
+import (
+	"gfast/app/model/admin/cms_category"
+	"gfast/app/service/admin/cms_service"
+	"gfast/app/service/admin/dict_service"
+	"gfast/app/service/admin/model_service"
+	"gfast/app/service/cache_service"
+	"gfast/library/response"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/net/ghttp"
+	"github.com/gogf/gf/util/gconv"
+	"github.com/gogf/gf/util/gvalid"
+)
+
+//cms栏目管理
+type CmsMenu struct{}
+
+// @Summary 栏目列表
+// @Description 分页列表
+// @Tags 栏目管理
+// @Param data body cms_category.ReqSearchList true "data"
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/cms/menu/list [get]
+// @Security
+func (c *CmsMenu) List(r *ghttp.Request) {
+	var req *cms_category.ReqSearchList
+	//获取参数
+	if err := r.Parse(&req); err != nil {
+		response.FailJson(true, r, err.(*gvalid.Error).FirstString())
+	}
+	menus, err := cms_service.GetMenuListSearch(req)
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	//栏目显示状态
+	statusOptions, err := dict_service.GetDictWithDataByType("sys_show_hide", "", "")
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	//栏目类型
+	typeOptions, err := dict_service.GetDictWithDataByType("cms_category_type", "", "")
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	res := g.Map{
+		"list":          menus,
+		"statusOptions": statusOptions,
+		"typeOptions":   typeOptions,
+	}
+	response.SusJson(true, r, "ok", res)
+}
+
+// @Summary 添加栏目
+// @Description 添加栏目
+// @Tags 栏目管理
+// @Param data body cms_category.ReqAdd true "data"
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/cms/menu/add [post]
+// @Security
+func (c *CmsMenu) Add(r *ghttp.Request) {
+	if r.Method == "POST" {
+		var req *cms_category.ReqAdd
+		//获取参数
+		if err := r.Parse(&req); err != nil {
+			response.FailJson(true, r, err.(*gvalid.Error).FirstString())
+		}
+		_, err := cms_service.AddSave(req)
+		if err != nil {
+			response.FailJson(true, r, err.Error())
+		}
+		cache_service.New().RemoveByTag(cache_service.AdminCmsTag)
+		response.SusJson(true, r, "栏目添加成功")
+	}
+	//获取上级分类(频道)
+	menus, err := cms_service.GetMenuListChannel()
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+
+	res := g.Map{
+		"parentList": menus,
+	}
+	response.SusJson(true, r, "添加栏目", res)
+}
+
+// @Summary 修改栏目
+// @Description 修改栏目
+// @Tags 栏目管理
+// @Param data body cms_category.ReqEdit true "data"
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/cms/menu/edit [post]
+// @Security
+func (c *CmsMenu) Edit(r *ghttp.Request) {
+	if r.Method == "POST" {
+		var req *cms_category.ReqEdit
+		//获取参数
+		if err := r.Parse(&req); err != nil {
+			response.FailJson(true, r, err.(*gvalid.Error).FirstString())
+		}
+		_, err := cms_service.EditSave(req)
+		if err != nil {
+			response.FailJson(true, r, err.Error())
+		}
+		cache_service.New().RemoveByTag(cache_service.AdminCmsTag)
+		response.SusJson(true, r, "栏目修改成功")
+	}
+	//获取栏目数据
+	id := r.GetInt("id")
+	menuInfo, err := cms_service.GetMenuInfoById(id)
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	if menuInfo == nil {
+		response.FailJson(true, r, "参数错误")
+	}
+	//获取上级分类(频道)
+	menus, err := cms_service.GetMenuListChannel()
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+
+	res := g.Map{
+		"menuInfo":   menuInfo,
+		"parentList": menus,
+	}
+	response.SusJson(true, r, "修改栏目", res)
+}
+
+// @Summary 栏目排序
+// @Description 栏目排序
+// @Tags 栏目管理
+// @Param data body string  true "data"
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/cms/menu/sort [post]
+// @Security
+func (c *CmsMenu) Sort(r *ghttp.Request) {
+	sorts := r.Get("sorts")
+	s := gconv.Map(sorts)
+	if s == nil {
+		response.FailJson(true, r, "排序失败")
+	}
+	for k, v := range s {
+		cms_category.Model.Where("id=?", k).Data("list_order", v).Update()
+	}
+	cache_service.New().RemoveByTag(cache_service.AdminCmsTag)
+	response.SusJson(true, r, "排序成功")
+}
+
+// @Summary 删除栏目
+// @Description 删除栏目
+// @Tags 栏目管理
+// @Param ids body integer  true "ids"
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/cms/menu/delete [delete]
+// @Security
+func (c *CmsMenu) Delete(r *ghttp.Request) {
+	ids := r.GetInts("ids")
+	if len(ids) == 0 {
+		response.FailJson(true, r, "删除失败")
+	}
+	err := cms_service.DeleteMenuByIds(ids)
+	if err != nil {
+		response.FailJson(true, r, "删除失败")
+	}
+	cache_service.New().RemoveByTag(cache_service.AdminCmsTag)
+	response.SusJson(true, r, "删除信息成功", ids)
+}
+
+// @Summary 栏目模型选项
+// @Description 栏目模型选项
+// @Tags 栏目管理
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/cms/menu/modelOptions [get]
+// @Security
+func (c *CmsMenu) ModelOptions(r *ghttp.Request) {
+	//栏目模型分类
+	modelOptions, err := dict_service.GetDictWithDataByType("cms_cate_models", "", "")
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	values := gconv.SliceAny(modelOptions["values"])
+	keys := make([]int, len(values))
+	for k, val := range values {
+		data := gconv.Map(val)
+		keys[k] = gconv.Int(data["key"])
+	}
+	//获取对应模型
+	models, err := model_service.GetModelsByCateIds(keys)
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	response.SusJson(true, r, "ok", models)
+}

+ 178 - 0
app/controller/admin/cms_news.go

@@ -0,0 +1,178 @@
+package admin
+
+import (
+	"gfast/app/model/admin/cms_news"
+	"gfast/app/service/admin/cms_service"
+	"gfast/app/service/admin/dict_service"
+	"gfast/app/service/admin/user_service"
+	"gfast/library/response"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/net/ghttp"
+	"github.com/gogf/gf/util/gvalid"
+)
+
+type CmsNews struct{}
+
+// @Summary 信息列表
+// @Description 信息列表
+// @Tags 文章管理
+// @Param data body cms_news.ReqListSearchParams true "data"
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/cms/news/list [get]
+// @Security
+func (c *CmsNews) List(r *ghttp.Request) {
+	var req *cms_news.ReqListSearchParams
+	//获取参数
+	if err := r.Parse(&req); err != nil {
+		response.FailJson(true, r, err.(*gvalid.Error).FirstString())
+	}
+	total, page, list, err := cms_service.NewsListByPage(req)
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	//获取可选栏目
+	menus, err := cms_service.GetPublishableMenuList(req.CateId...)
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+
+	//文章发布状态
+	statusOptions, err := dict_service.GetDictWithDataByType("cms_news_pub_type", "", "")
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	//文章附加状态
+	attrOptions, err := dict_service.GetDictWithDataByType("cms_news_attr", "", "")
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	//文章附加状态
+	typeOptions, err := dict_service.GetDictWithDataByType("cms_news_type", "", "")
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	result := g.Map{
+		"currentPage":   page,
+		"total":         total,
+		"list":          list,
+		"menus":         menus,
+		"statusOptions": statusOptions,
+		"attrOptions":   attrOptions,
+		"typeOptions":   typeOptions,
+	}
+	response.SusJson(true, r, "信息列表", result)
+}
+
+// @Summary 添加信息
+// @Description 添加信息
+// @Tags 文章管理
+// @Param data body cms_news.ReqAddParams true "data"
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/cms/news/add [post]
+// @Security
+func (c *CmsNews) Add(r *ghttp.Request) {
+	if r.Method == "POST" {
+		var req *cms_news.ReqAddParams
+		//获取参数
+		if err := r.Parse(&req); err != nil {
+			response.FailJson(true, r, err.(*gvalid.Error).FirstString())
+		}
+		cateIds := r.GetInts("cateIds")
+		userId := user_service.GetLoginID(r)
+		_, err := cms_service.AddNews(req, cateIds, userId)
+		if err != nil {
+			response.FailJson(true, r, err.Error())
+		}
+		response.SusJson(true, r, "添加信息成功")
+	}
+	//获取可选栏目
+	menus, err := cms_service.GetPublishableMenuList()
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	res := g.Map{
+		"menus": menus,
+	}
+	response.SusJson(true, r, "添加信息", res)
+}
+
+// @Summary 修改信息
+// @Description 修改信息
+// @Tags 文章管理
+// @Param data body cms_news.ReqEditParams true "data"
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/cms/news/edit [post]
+// @Security
+func (c *CmsNews) Edit(r *ghttp.Request) {
+	if r.Method == "POST" {
+		var req *cms_news.ReqEditParams
+		//获取参数
+		if err := r.Parse(&req); err != nil {
+			response.FailJson(true, r, err.(*gvalid.Error).FirstString())
+		}
+		cateIds := r.GetInts("cateIds")
+		err := cms_service.EditNews(req, cateIds)
+		if err != nil {
+			response.FailJson(true, r, err.Error())
+		}
+		response.SusJson(true, r, "修改信息成功")
+	}
+	id := r.GetInt("id")
+	if id == 0 {
+		response.FailJson(true, r, "参数错误")
+	}
+	//获取文章信息
+	news, err := cms_service.GetNewsById(id)
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	checkedCategoryId, err := cms_service.GetCheckedCategoryIdByNewsId(news.Id)
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+
+	res := g.Map{
+		"menus": checkedCategoryId,
+		"news":  news,
+	}
+	response.SusJson(true, r, "添加信息", res)
+}
+
+// @Summary 删除信息
+// @Description 删除信息
+// @Tags 文章管理
+// @Param ids body integer  true "ids[1,2,3..]"
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/cms/news/delete [delete]
+// @Security
+func (c *CmsNews) Delete(r *ghttp.Request) {
+	ids := r.GetInts("ids")
+	if len(ids) == 0 {
+		response.FailJson(true, r, "删除失败")
+	}
+	err := cms_service.DeleteCmsByIds(ids)
+	if err != nil {
+		response.FailJson(true, r, "删除失败")
+	}
+	response.SusJson(true, r, "删除信息成功")
+}
+
+// @Summary 获取模型字段
+// @Description 获取模型字段
+// @Tags 栏目管理
+// @Param cateIds body integer  true "cateIds[1,2,3...]"
+// @Param newsId body integer  true "newsId"
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/cms/news/getModelFields [get]
+// @Security
+func (c *CmsNews) GetModelFields(r *ghttp.Request) {
+	//获取栏目ID
+	cateIds := r.GetInts("cateIds")
+	//文章id
+	newsId := r.GetInt64("newsId")
+	res, err := cms_service.GetModelFieldsByCateIds(r, cateIds, newsId)
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	response.SusJson(true, r, "ok", res)
+}

+ 7 - 15
app/controller/admin/config_dict.go

@@ -242,30 +242,22 @@ func (c *Dict) DataDelete(r *ghttp.Request) {
 	response.SusJson(true, r, "删除成功")
 }
 
-// @Summary 字典状态
-// @Description 字典状态
+// @Summary 获取字典对应选项
+// @Description 获取字典对应选项
 // @Tags 字典管理
+// @Param dictType query string true "dictType"
 // @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
 // @Router /system/config/dict/sysNormalDisable [post]
 // @Security
-func (c *Dict) SysNormalDisable(r *ghttp.Request) {
+func (c *Dict) GetDicts(r *ghttp.Request) {
+	dictType := r.GetString("dictType")
 	//菜单正常or停用状态
-	statusOptions, err := dict_service.GetDictWithDataByType("sys_normal_disable", "", "")
-	if err != nil {
-		response.FailJson(true, r, err.Error())
-	}
-	response.SusJson(true, r, "", statusOptions)
-
-}
-
-func (c *Dict) SysCommonStatus(r *ghttp.Request) {
-	//获取相关选项
-	logStatus, err := dict_service.GetDictWithDataByType("sys_oper_log_status", "", "全部")
+	statusOptions, err := dict_service.GetDictWithDataByType(dictType, "", "")
 	if err != nil {
 		response.FailJson(true, r, err.Error())
 	}
+	response.SusJson(true, r, "ok", statusOptions)
 
-	response.SusJson(true, r, "ok", logStatus)
 }
 
 // 获取字典选择框列表

+ 151 - 0
app/controller/admin/model_category.go

@@ -0,0 +1,151 @@
+package admin
+
+import (
+	"gfast/app/model/admin/model_category"
+	"gfast/app/service/admin/model_service"
+	"gfast/app/service/admin/user_service"
+	"gfast/app/service/cache_service"
+	"gfast/library/response"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/net/ghttp"
+	"github.com/gogf/gf/util/gconv"
+	"github.com/gogf/gf/util/gvalid"
+)
+
+//模型分类
+type ModelCategory struct{}
+
+// @Summary 模型列表
+// @Description 模型列表
+// @Tags 模型分类
+// @Param data body model_category.SearchReq true "data"
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/model/category/list [get]
+// @Security
+func (c *ModelCategory) List(r *ghttp.Request) {
+	var req *model_category.SearchReq
+	//获取参数
+	if err := r.Parse(&req); err != nil {
+		response.FailJson(true, r, err.(*gvalid.Error).FirstString())
+	}
+	total, list, err := model_service.GetCategoryList(req)
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	res := g.Map{
+		"total": total,
+		"list":  list,
+	}
+	response.SusJson(true, r, "信息列表", res)
+}
+
+// @Summary 模型分类
+// @Description 模型分类
+// @Tags 模型分类
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/model/category/all [post]
+// @Security
+func (c *ModelCategory) All(r *ghttp.Request) {
+	list, err := model_service.GetCategoryAll()
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	response.SusJson(true, r, "ok", list)
+}
+
+// @Summary 添加模型分类
+// @Description 添加模型分类
+// @Tags 模型分类
+// @Param data body model_category.AddReq true "data"
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/model/category/add [post]
+// @Security
+func (c *ModelCategory) Add(r *ghttp.Request) {
+	var req *model_category.AddReq
+	//获取参数
+	if err := r.Parse(&req); err != nil {
+		response.FailJson(true, r, err.(*gvalid.Error).FirstString())
+	}
+
+	req.CreateBy = gconv.Uint(user_service.GetLoginID(r))
+	err := model_service.AddCategory(req)
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	cache_service.New().RemoveByTag(cache_service.AdminModelTag)
+	response.SusJson(true, r, "添加成功")
+}
+
+// @Summary 修改模型分类
+// @Description 修改模型分类
+// @Tags 模型分类
+// @Param data body model_category.EditReq true "data"
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/model/category/edit [post]
+// @Security
+func (c *ModelCategory) Edit(r *ghttp.Request) {
+	if r.Method == "POST" {
+		var req *model_category.EditReq
+		//获取参数
+		if err := r.Parse(&req); err != nil {
+			response.FailJson(true, r, err.(*gvalid.Error).FirstString())
+		}
+
+		req.UpdateBy = gconv.Uint(user_service.GetLoginID(r))
+		err := model_service.EditCategory(req)
+		if err != nil {
+			response.FailJson(true, r, err.Error())
+		}
+		cache_service.New().RemoveByTag(cache_service.AdminModelTag)
+		response.SusJson(true, r, "修改成功")
+	}
+	id := r.GetInt64("id")
+	if id == 0 {
+		response.FailJson(true, r, "参数错误")
+	}
+	cate, err := model_service.GetCategoryById(id)
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	response.SusJson(true, r, "ok", cate)
+}
+
+// @Summary 分类排序
+// @Description 分类排序
+// @Tags 模型分类
+// @Param sorts body string  true "sorts"
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/model/category/sort [post]
+// @Security
+func (c *ModelCategory) Sort(r *ghttp.Request) {
+	sorts := r.Get("sorts")
+	s := gconv.Map(sorts)
+	if s == nil {
+		response.FailJson(true, r, "排序失败")
+	}
+	for k, v := range s {
+		model_category.Model.Where(model_category.Columns.CId+"=?", k).Data(model_category.Columns.CSort, v).Update()
+	}
+	cache_service.New().RemoveByTag(cache_service.AdminModelTag)
+	response.SusJson(true, r, "排序成功")
+}
+
+// @Summary 删除分类
+// @Description 删除分类
+// @Tags 模型分类
+// @Param ids body integer true "ids"
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/model/category/delete [delete]
+// @Security
+func (c *ModelCategory) Delete(r *ghttp.Request) {
+	ids := r.GetInts("ids")
+	if len(ids) == 0 {
+		response.FailJson(true, r, "删除失败")
+	}
+	err := model_service.DeleteCategoryByIds(ids)
+	if err != nil {
+		response.FailJson(true, r, "删除失败")
+	}
+	cache_service.New().RemoveByTag(cache_service.AdminModelTag)
+	response.SusJson(true, r, "删除成功", ids)
+}

+ 106 - 0
app/controller/admin/model_fields.go

@@ -0,0 +1,106 @@
+package admin
+
+import (
+	"gfast/app/model/admin/model_fields"
+	"gfast/app/service/admin/model_service"
+	"gfast/app/service/admin/user_service"
+	"gfast/library/response"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/net/ghttp"
+	"github.com/gogf/gf/util/gconv"
+	"github.com/gogf/gf/util/gvalid"
+)
+
+//控制器
+type ModelFields struct{}
+
+//列表页
+func (c *ModelFields) List(r *ghttp.Request) {
+	// 定义一个结构体存储请求参数
+	var req *model_fields.SelectPageReq
+	// 获取参数
+	err := r.Parse(&req)
+	if err != nil {
+		response.FailJson(true, r, err.(*gvalid.Error).FirstString())
+	}
+	list, err := model_service.SelectFieldsAll(req)
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	result := g.Map{
+		"list": list,
+	}
+	response.SusJson(true, r, "获取列表数据成功", result)
+}
+
+// 新增
+func (c *ModelFields) Add(r *ghttp.Request) {
+	if r.Method == "POST" {
+		var req *model_fields.AddReq
+		// 通过Parse方法解析获取参数
+		err := r.Parse(&req)
+		if err != nil {
+			response.FailJson(true, r, err.(*gvalid.Error).FirstString())
+		}
+		req.CreateBy = gconv.Uint64(user_service.GetLoginID(r))
+		// 调用service中的添加函数添加
+		err = model_service.AddFieldsSave(req)
+		if err != nil {
+			response.FailJson(true, r, err.Error())
+		}
+		response.SusJson(true, r, "添加成功")
+	}
+}
+
+// 修改
+func (c *ModelFields) Edit(r *ghttp.Request) {
+	// 如果是post提交的请求就执行修改操作
+	if r.Method == "POST" {
+		var editReq *model_fields.EditReq
+		// 通过Parse方法解析获取参数
+		err := r.Parse(&editReq)
+		if err != nil {
+			response.FailJson(true, r, err.(*gvalid.Error).FirstString())
+		}
+		editReq.UpdateBy = gconv.Uint64(user_service.GetLoginID(r))
+		err = model_service.EditFieldsSave(editReq)
+		if err != nil {
+			response.FailJson(true, r, err.Error())
+		}
+		response.SusJson(true, r, "修改参数成功")
+	}
+	// 不是post提交的请求就到修改页面后查询出要修改的记录
+	id := r.GetInt("id")
+	params, err := model_fields.GetByID(int64(id))
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	response.SusJson(true, r, "ok", params)
+}
+
+// 删除
+func (c *ModelFields) Delete(r *ghttp.Request) {
+	var req *model_fields.RemoveReq
+	//获取参数
+	if err := r.Parse(&req); err != nil {
+		response.FailJson(true, r, err.(*gvalid.Error).FirstString())
+	}
+	err := model_service.DeleteFieldsByIds(req.Ids)
+	if err != nil {
+		response.FailJson(true, r, "删除失败")
+	}
+	response.SusJson(true, r, "删除成功")
+}
+
+//修改字段属性
+func (c *ModelFields) SetFieldsAttr(r *ghttp.Request) {
+	var req *model_fields.SetFieldsAttrReq
+	//获取参数
+	if err := r.Parse(&req); err != nil {
+		response.FailJson(true, r, err.(*gvalid.Error).FirstString())
+	}
+	if err := model_service.SetFieldsAttr(req); err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	response.SusJson(true, r, "修改成功", req)
+}

+ 170 - 0
app/controller/admin/model_info.go

@@ -0,0 +1,170 @@
+package admin
+
+import (
+	"gfast/app/model/admin/model_info"
+	"gfast/app/service/admin/model_service"
+	"gfast/app/service/admin/user_service"
+	"gfast/library/response"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/net/ghttp"
+	"github.com/gogf/gf/util/gvalid"
+)
+
+//控制器
+type ModelInfo struct{}
+
+// @Summary 列表页
+// @Description 列表页
+// @Tags 模型列表
+// @Param data body model_info.SelectPageReq true "data"
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/model/info/list [post]
+// @Security
+func (c *ModelInfo) List(r *ghttp.Request) {
+	// 定义一个结构体存储请求参数
+	var req *model_info.SelectPageReq
+	// 获取参数
+	err := r.Parse(&req)
+	if err != nil {
+		response.FailJson(true, r, err.(*gvalid.Error).FirstString())
+	}
+	total, page, list, err := model_service.SelectListByPage(req)
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	result := g.Map{
+		"currentPage": page,
+		"total":       total,
+		"list":        list,
+	}
+	response.SusJson(true, r, "获取列表数据成功", result)
+}
+
+// @Summary 新增
+// @Description 新增
+// @Tags 模型列表
+// @Param data body model_info.AddReq true "data"
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/model/info/add [post]
+// @Security
+func (c *ModelInfo) Add(r *ghttp.Request) {
+	if r.Method == "POST" {
+		var req *model_info.AddReq
+		// 通过Parse方法解析获取参数
+		err := r.Parse(&req)
+		if err != nil {
+			response.FailJson(true, r, err.(*gvalid.Error).FirstString())
+		}
+		req.CreateBy = user_service.GetLoginID(r)
+		// 调用service中的添加函数添加
+		err = model_service.AddSave(req)
+		if err != nil {
+			response.FailJson(true, r, err.Error())
+		}
+		response.SusJson(true, r, "添加模型成功")
+	}
+}
+
+// @Summary 修改
+// @Description 修改
+// @Tags 模型列表
+// @Param data body model_info.EditReq true "data"
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/model/info/edit [post]
+// @Security
+func (c *ModelInfo) Edit(r *ghttp.Request) {
+	// 如果是post提交的请求就执行修改操作
+	if r.Method == "POST" {
+		var editReq *model_info.EditReq
+		// 通过Parse方法解析获取参数
+		err := r.Parse(&editReq)
+		if err != nil {
+			response.FailJson(true, r, err.(*gvalid.Error).FirstString())
+		}
+		editReq.UpdateBy = user_service.GetLoginID(r)
+		err = model_service.EditSave(editReq)
+		if err != nil {
+			response.FailJson(true, r, err.Error())
+		}
+		response.SusJson(true, r, "修改参数成功")
+	}
+	// 不是post提交的请求就到修改页面后查询出要修改的记录
+	id := r.GetInt64("id")
+	params, err := model_service.GetByID(id)
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	response.SusJson(true, r, "ok", params)
+}
+
+// @Summary 删除
+// @Description 删除
+// @Tags 模型列表
+// @Param data body model_info.RemoveReq true "data"
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/model/info/delete [post]
+// @Security
+func (c *ModelInfo) Delete(r *ghttp.Request) {
+	var req *model_info.RemoveReq
+	//获取参数
+	if err := r.Parse(&req); err != nil {
+		response.FailJson(true, r, err.(*gvalid.Error).FirstString())
+	}
+	err := model_service.DeleteByIds(req.Ids)
+	if err != nil {
+		response.FailJson(true, r, "删除失败")
+	}
+	response.SusJson(true, r, "删除成功")
+}
+
+// @Summary 设置模型状态
+// @Description 设置模型状态
+// @Tags 模型列表
+// @Param data body model_info.StatusSetReq true "data"
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/model/info/setStatus [post]
+// @Security
+func (c *ModelInfo) SetStatus(r *ghttp.Request) {
+	var req *model_info.StatusSetReq
+	//获取参数
+	if err := r.Parse(&req); err != nil {
+		response.FailJson(true, r, err.(*gvalid.Error).FirstString())
+	}
+	err := model_service.SetInfoStatus(req)
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	response.SusJson(true, r, "状态设置成功")
+}
+
+// @Summary 模型生成
+// @Description 模型生成
+// @Tags 模型列表
+// @Param modelId path int true "int valid"
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/model/info/createModel [post]
+// @Security
+func (c *ModelInfo) CreateModel(r *ghttp.Request) {
+	modelId := r.GetInt64("modelId")
+	err := model_service.CreateModel(modelId)
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	response.SusJson(true, r, "模型生成成功")
+}
+
+// @Summary 复制模型
+// @Description 复制模型
+// @Tags 模型列表
+// @Param modelId path int true "int valid"
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/model/info/copyModel [post]
+// @Security
+func (c *ModelInfo) CopyModel(r *ghttp.Request) {
+	modelId := r.GetInt64("modelId")
+	err := model_service.CopyModel(modelId)
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	response.SusJson(true, r, "模型复制成功")
+}

+ 138 - 0
app/controller/admin/plug_ad.go

@@ -0,0 +1,138 @@
+package admin
+
+import (
+	"gfast/app/model/admin/plug_ad"
+	"gfast/app/service/admin/plug_service"
+	"gfast/library/response"
+	"github.com/gogf/gf/util/gconv"
+	"github.com/gogf/gf/util/gvalid"
+
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/net/ghttp"
+)
+
+type PlugAd struct{}
+
+// @Summary 添加广告
+// @Description 添加广告
+// @Tags 广告列表
+// @Param data body plug_ad.AddReq true "data"
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/plug/ad/info/add [post]
+// @Security
+func (c *PlugAd) Add(r *ghttp.Request) {
+	if r.Method == "POST" {
+		var req *plug_ad.AddReq
+		// 通过Parse方法解析获取参数
+		err := r.Parse(&req)
+		if err != nil {
+			response.FailJson(true, r, err.(*gvalid.Error).FirstString())
+		}
+		// 调用service中的添加函数添加广告
+		err = plug_service.AddSaveAd(req)
+		if err != nil {
+			response.FailJson(true, r, err.Error())
+		}
+		response.SusJson(true, r, "添加成功!")
+	}
+}
+
+// @Summary 删除广告
+// @Description 删除广告
+// @Tags 广告列表
+// @Param data body integer  true "ids"
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/plug/ad/info/delete [delete]
+// @Security
+func (c *PlugAd) Delete(r *ghttp.Request) {
+	ids := r.GetInts("plugAdID")
+	if len(ids) == 0 {
+		response.FailJson(true, r, "ID获取失败,删除失败")
+	}
+	err := plug_service.DeleteByIDs(ids)
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	response.SusJson(true, r, "删除成功")
+}
+
+// @Summary 修改广告
+// @Description 修改广告
+// @Tags 广告列表
+// @Param data body plug_ad.EditReq true "data"
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/plug/ad/info/edit [post]
+// @Security
+func (c *PlugAd) Edit(r *ghttp.Request) {
+	// 如果是post提交的请求就执行修改操作
+	if r.Method == "POST" {
+		var editReq *plug_ad.EditReq
+		// 通过Parse方法解析获取参数
+		err := r.Parse(&editReq)
+		if err != nil {
+			response.FailJson(true, r, err.(*gvalid.Error).FirstString())
+		}
+		err = plug_service.EditPlugAdSave(editReq)
+		if err != nil {
+			response.FailJson(true, r, err.Error())
+		}
+		response.SusJson(true, r, "修改参数成功")
+	}
+	// 不是post提交的请求就到修改页面后查询出要修改的记录
+	id := r.GetInt("plugAdID")
+	params, err := plug_service.GetPlugAdByID(int64(id))
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	response.SusJson(true, r, "ok", params)
+}
+
+// @Summary 广告列表
+// @Description 广告列表
+// @Tags 广告列表
+// @Param data body plug_ad.SelectPageReq true "data"
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/plug/ad/info/list [get]
+// @Security
+func (c *PlugAd) List(r *ghttp.Request) {
+	// 定义一个结构体存储请求参数
+	var req *plug_ad.SelectPageReq
+	// 获取参数
+	err := r.Parse(&req)
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	total, page, list, err := plug_service.SelectPlugAdListByPage(req)
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	result := g.Map{
+		"currentPage": page,
+		"total":       total,
+		"list":        list,
+	}
+	response.SusJson(true, r, "广告列表", result)
+}
+
+// @Summary 栏目排序
+// @Description 栏目排序
+// @Tags 广告列表
+// @Param sorts body integer  true "sorts[]"
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/plug/ad/info/sort [post]
+// @Security
+func (c *PlugAd) Sort(r *ghttp.Request) {
+	sorts := r.Get("sorts")
+	s := gconv.Map(sorts)
+	if s == nil {
+		response.FailJson(true, r, "获取记录id、序号失败")
+	}
+	var err error
+	for k, v := range s {
+		_, err = plug_ad.Model.Where("ad_id=?", k).Data("ad_sort", v).Update()
+		if err != nil {
+			response.FailJson(true, r, err.Error())
+		}
+	}
+	response.SusJson(true, r, "排序成功")
+}

+ 151 - 0
app/controller/admin/plug_ad_type.go

@@ -0,0 +1,151 @@
+package admin
+
+import (
+	"gfast/app/model/admin/plug_adtype"
+	"gfast/app/service/admin/plug_service"
+	"gfast/library/response"
+	"github.com/gogf/gf/errors/gerror"
+	"github.com/gogf/gf/util/gconv"
+
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/net/ghttp"
+	"github.com/gogf/gf/util/gvalid"
+)
+
+type AdType struct{}
+
+// @Summary 添加广告位
+// @Description 添加广告位
+// @Tags 广告位管理
+// @Param data body plug_adtype.AddReq true "data"
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/plug/ad/type/add [post]
+// @Security
+func (c *AdType) Add(r *ghttp.Request) {
+	if r.Method == "POST" {
+		var req *plug_adtype.AddReq
+		// 通过Parse方法解析获取参数
+		err := r.Parse(&req)
+		if err != nil {
+			response.FailJson(true, r, err.(*gvalid.Error).FirstString())
+		}
+		// 调用service中的添加函数添加广告位
+		err = plug_service.AddSave(req)
+		if err != nil {
+			response.FailJson(true, r, err.Error())
+		}
+		response.SusJson(true, r, "添加成功!")
+	}
+}
+
+// @Summary 删除广告位
+// @Description 删除广告位
+// @Tags 广告位管理
+// @Param adtypeID body integer  true "adtypeID[1,2,3...]"
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/plug/ad/type/delete [delete]
+// @Security
+func (c *AdType) Delete(r *ghttp.Request) {
+	// 从页面获取要删除记录的 ID int切片
+	id := r.GetInts("adTypeID")
+	if len(id) == 0 {
+		response.FailJson(true, r, "ID获取失败,删除失败")
+	}
+	err := plug_service.DeleteAdTypeByID(id)
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	response.SusJson(true, r, "删除成功")
+}
+
+// @Summary 修改广告位信息
+// @Description 修改广告位信息
+// @Tags 广告位管理
+// @Param data body plug_adtype.EditReq true "data"
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/plug/ad/type/edit [post]
+// @Security
+func (c *AdType) Edit(r *ghttp.Request) {
+	// 如果是post提交的请求就执行修改操作
+	if r.Method == "POST" {
+		var editReq *plug_adtype.EditReq
+		// 通过Parse方法解析获取参数
+		err := r.Parse(&editReq)
+		if err != nil {
+			response.FailJson(true, r, err.(*gvalid.Error).FirstString())
+		}
+		err = plug_service.EditSave(editReq)
+		if err != nil {
+			response.FailJson(true, r, err.Error())
+		}
+		response.SusJson(true, r, "修改参数成功")
+	}
+	// 不是post提交的请求就到修改页面后查询出要修改的记录
+	id := r.GetInt("adTypeID")
+	params, err := plug_service.GetAdtypeByID(int64(id))
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	response.SusJson(true, r, "ok", params)
+}
+
+// @Summary 广告位列表
+// @Description 广告位列表
+// @Tags 广告位管理
+// @Param data body plug_adtype.SelectPageReq true "data"
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/plug/ad/type/list [get]
+// @Security
+func (c *AdType) List(r *ghttp.Request) {
+	// 定义一个结构体存储请求参数
+	var req *plug_adtype.SelectPageReq
+	// 获取参数
+	err := r.Parse(&req)
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	total, page, list, err := plug_service.SelectListByPage(req)
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	result := g.Map{
+		"currentPage": page,
+		"total":       total,
+		"list":        list,
+	}
+	response.SusJson(true, r, "广告位列表", result)
+}
+
+// @Summary 栏目排序
+// @Description 栏目排序
+// @Tags 广告位管理
+// @Param sorts body integer  true "sorts"
+// @Success 0 {object} response.Response "{"code": 200, "data": [...]}"
+// @Router /system/plug/ad/type/sort [post]
+// @Security
+func (c *AdType) Sort(r *ghttp.Request) {
+	sorts := r.Get("sorts")
+	s := gconv.Map(sorts)
+	if s == nil {
+		response.FailJson(true, r, "排序失败")
+	}
+	var err error
+	for k, v := range s {
+		_, err = plug_adtype.Model.Where("adtype_id=?", k).Data("adtype_sort", v).Update()
+		if err != nil {
+			response.FailJson(true, r, err.Error())
+		}
+	}
+	response.SusJson(true, r, "排序成功")
+}
+
+// 查询所有广告位名和对应id
+func (c *AdType) Type(r *ghttp.Request) {
+	res, err := plug_adtype.Model.FindAll()
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("查询所有广告位名失败!")
+		response.FailJson(true, r, err.Error())
+	}
+	response.SusJson(true, r, "广告位名列表", res)
+}

+ 108 - 0
app/controller/admin/plug_link.go

@@ -0,0 +1,108 @@
+package admin
+
+import (
+	"gfast/app/model/admin/plug_link"
+	"gfast/app/service/admin/plug_link_service"
+	"gfast/library/response"
+
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/net/ghttp"
+	"github.com/gogf/gf/util/gconv"
+	"github.com/gogf/gf/util/gvalid"
+)
+
+type PlugLink struct{}
+
+// 添加链接
+func (c *PlugLink) Add(r *ghttp.Request) {
+	if r.Method == "POST" {
+		var req *plug_link.AddReq
+		// 通过Parse方法解析获取参数
+		err := r.Parse(&req)
+		if err != nil {
+			response.FailJson(true, r, err.(*gvalid.Error).FirstString())
+		}
+		// 调用service中的添加函数添加链接
+		err = plug_link.AddSave(req)
+		if err != nil {
+			response.FailJson(true, r, err.Error())
+		}
+		response.SusJson(true, r, "添加成功!")
+	}
+}
+
+// 批量删除链接
+func (c *PlugLink) Delete(r *ghttp.Request) {
+	ids := r.GetInts("plugLinkID")
+	if len(ids) == 0 {
+		response.FailJson(true, r, "ID获取失败,删除失败")
+	}
+	err := plug_link_service.DeleteByIDs(ids)
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	response.SusJson(true, r, "删除成功")
+}
+
+// 修改链接
+func (c *PlugLink) Edit(r *ghttp.Request) {
+	// 如果是post提交的请求就执行修改操作
+	if r.Method == "POST" {
+		var editReq *plug_link.EditReq
+		// 通过Parse方法解析获取参数
+		err := r.Parse(&editReq)
+		if err != nil {
+			response.FailJson(true, r, err.(*gvalid.Error).FirstString())
+		}
+		err = plug_link_service.EditPlugLinkSave(editReq)
+		if err != nil {
+			response.FailJson(true, r, err.Error())
+		}
+		response.SusJson(true, r, "修改链接成功")
+	}
+	// 不是post提交的请求就到修改页面后查询出要修改的记录
+	id := r.GetInt("plugLinkID")
+	params, err := plug_link_service.GetPlugLinkByID(int64(id))
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	response.SusJson(true, r, "ok", params)
+}
+
+// 友情链接列表
+func (c *PlugLink) List(r *ghttp.Request) {
+	// 定义一个结构体存储请求参数
+	var req *plug_link.SelectPageReq
+	// 获取参数
+	err := r.Parse(&req)
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	total, page, list, err := plug_link_service.SelectPlugLinkListByPage(req)
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	result := g.Map{
+		"currentPage": page,
+		"total":       total,
+		"list":        list,
+	}
+	response.SusJson(true, r, "链接列表", result)
+}
+
+// 链接排序
+func (c *PlugLink) Sort(r *ghttp.Request) {
+	sorts := r.Get("sorts")
+	s := gconv.Map(sorts)
+	if s == nil {
+		response.FailJson(true, r, "获取记录id、序号失败")
+	}
+	var err error
+	for k, v := range s {
+		_, err = plug_link.Model.Where("link_id=?", k).Data("link_order", v).Update()
+		if err != nil {
+			response.FailJson(true, r, err.Error())
+		}
+	}
+	response.SusJson(true, r, "排序成功")
+}

+ 122 - 0
app/controller/admin/plug_link_type.go

@@ -0,0 +1,122 @@
+package admin
+
+import (
+	"gfast/app/model/admin/plug_linktype"
+	"gfast/app/service/admin/plug_link_service"
+	"gfast/library/response"
+	"github.com/gogf/gf/errors/gerror"
+
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/net/ghttp"
+	"github.com/gogf/gf/util/gconv"
+	"github.com/gogf/gf/util/gvalid"
+)
+
+type LinkType struct{}
+
+// 添加栏目
+func (c *LinkType) Add(r *ghttp.Request) {
+	// 判断提交方式
+	if r.Method == "POST" {
+		// 定义req来保存请求参数
+		var req *plug_linktype.AddReq
+		// 解析req获取参数
+		err := r.Parse(&req)
+		if err != nil {
+			response.FailJson(true, r, err.(*gvalid.Error).FirstString())
+		}
+		// 调用添加的方法添加栏目
+		err = plug_link_service.AddSave(req)
+		if err != nil {
+			response.FailJson(true, r, err.Error())
+		}
+		response.SusJson(true, r, "添加成功!")
+	}
+}
+
+// 删除栏目
+func (c *LinkType) Delete(r *ghttp.Request) {
+	// 获取要删除的栏目ID切片
+	ids := r.GetInts("linkTypeID")
+	if len(ids) == 0 {
+		response.FailJson(true, r, "ID获取失败,删除失败")
+	}
+	err := plug_link_service.DeleteLinkTypeByID(ids)
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	response.SusJson(true, r, "删除成功")
+}
+
+// 修改栏目
+func (c *LinkType) Edit(r *ghttp.Request) {
+	// 如果不是POST提交的请求,则说明是第一次发送修改请求,需到修改页面查出要修改记录信息
+	if r.Method == "POST" {
+		var req *plug_linktype.EditReq
+		err := r.Parse(&req)
+		if err != nil {
+			response.FailJson(true, r, err.(*gvalid.Error).FirstString())
+		}
+		err = plug_link_service.EditSave(req)
+		if err != nil {
+			response.FailJson(true, r, err.Error())
+		}
+		response.SusJson(true, r, "修改栏目成功")
+	}
+	// 不是post提交的请求就到修改页面后查询出要修改的记录
+	id := r.GetInt("linkTypeID")
+	params, err := plug_link_service.GetLinkTypeByID(int64(id))
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	response.SusJson(true, r, "ok", params)
+}
+
+// 友情链接栏目列表
+func (c *LinkType) List(r *ghttp.Request) {
+	// 定义一个结构体存储请求参数
+	var req *plug_linktype.SelectPageReq
+	// 获取参数
+	err := r.Parse(&req)
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	total, page, list, err := plug_link_service.SelectListByPage(req)
+	if err != nil {
+		response.FailJson(true, r, err.Error())
+	}
+	result := g.Map{
+		"currentPage": page,
+		"total":       total,
+		"list":        list,
+	}
+	response.SusJson(true, r, "栏目列表", result)
+}
+
+//栏目排序
+func (c *LinkType) Sort(r *ghttp.Request) {
+	sorts := r.Get("sorts")
+	s := gconv.Map(sorts)
+	if s == nil {
+		response.FailJson(true, r, "排序失败")
+	}
+	var err error
+	for k, v := range s {
+		_, err = plug_linktype.Model.Where("linktype_id=?", k).Data("linktype_order", v).Update()
+		if err != nil {
+			response.FailJson(true, r, err.Error())
+		}
+	}
+	response.SusJson(true, r, "排序成功")
+}
+
+// 查询所有分类名和对应id
+func (c *LinkType) Type(r *ghttp.Request) {
+	res, err := plug_linktype.Model.FindAll()
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("查询所有分类列表失败!")
+		response.FailJson(true, r, err.Error())
+	}
+	response.SusJson(true, r, "分类列表", res)
+}

+ 3 - 0
app/model/admin/cms_category/cms_category.go

@@ -26,6 +26,7 @@ type ReqSearchList struct {
 //添加请求参数
 type ReqAdd struct {
 	ParentId            int64  `p:"parent_id" v:"integer|min:0#父级ID不能为空|父级ID必须为大于等于0的整数"`
+	ModelId             uint   `p:"model_id" v:"integer|min:1#模型ID必须为数字|模型ID不能为空"`
 	Name                string `p:"name" v:"required#栏目名称不能为空"`
 	Alias               string `p:"alias"`
 	CateType            uint   `p:"cate_type" v:"required|in:1,2,3,4#请选择栏目类型|栏目类型只能在1-4之间"`
@@ -69,6 +70,7 @@ func GetList() (list []*Entity, err error) {
 func AddSave(req *ReqAdd) (id int64, err error) {
 	var entity Entity
 	entity.ParentId = req.ParentId
+	entity.ModelId = req.ModelId
 	entity.CateType = req.CateType
 	entity.Status = req.Status
 	entity.Name = req.Name
@@ -128,6 +130,7 @@ func EditSave(req *ReqEdit) (id int64, err error) {
 	}
 	entity.Id = gconv.Uint64(req.Id)
 	entity.ParentId = req.ParentId
+	entity.ModelId = req.ModelId
 	entity.CateType = req.CateType
 	entity.Status = req.Status
 	entity.Name = req.Name

+ 1 - 0
app/model/admin/cms_category/cms_category_entity.go

@@ -13,6 +13,7 @@ import (
 type Entity struct {
 	Id             uint64  `orm:"id,primary"      json:"id"`              // 分类id
 	ParentId       int64   `orm:"parent_id"       json:"parent_id"`       // 分类父id
+	ModelId        uint    `orm:"model_id"       json:"model_id"`         // 模型id
 	Status         uint    `orm:"status"          json:"status"`          // 状态,1:发布,0:不发布
 	DeleteTime     uint    `orm:"delete_time"     json:"delete_time"`     // 删除时间
 	ListOrder      float64 `orm:"list_order"      json:"list_order"`      // 排序

+ 20 - 39
app/model/admin/cms_news/cms_news.go

@@ -20,19 +20,17 @@ import (
 
 //添加文章参数
 type ReqAddParams struct {
-	NewsStatus uint `p:"status"    v:"in:0,1#状态只能为0或1"` // 状态;1:已发布;0:未发布;
-	//IsTop         uint   `p:"IsTop"         v:"in:0,1#置顶只能为0或1"`   // 是否置顶;1:置顶;0:不置顶
-	//Recommended   uint   `p:"recommended"    v:"in:0,1#推荐只能为0或1"`  // 是否推荐;1:推荐;0:不推荐
+	NewsStatus    uint   `p:"status"    v:"in:0,1#状态只能为0或1"`                                // 状态;1:已发布;0:未发布;
 	Attr          []int  `p:attr`                                                           //文章标记 置顶 推荐
 	PublishedTime string `p:"published_time"`                                               // 发布时间
 	NewsTitle     string `p:"title"     v:"required#标题不能为空"`                                // post标题
 	NewsKeywords  string `p:"keywords"`                                                     // seo keywords
 	NewsExcerpt   string `p:"excerpt"`                                                      // post摘要
 	NewsSource    string `p:"source"  `                                                     // 转载文章的来源
-	NewsContent   string `p:"content"   v:"required-if:IsJump,0#文章内容不能为空"`                  // 文章内容
 	Thumbnail     string `p:"thumbnail"    `                                                // 缩略图
 	IsJump        uint   `p:"IsJump"        v:"in:0,1#跳转类型只能为0或1"`                          // 是否跳转地址
 	JumpUrl       string `p:"JumpUrl"      v:"required-if:IsJump,1|url#跳转地址不能为空|跳转地址格式不正确"` // 跳转地址
+	ModelForm     g.Map  `p:"modelForm"`
 }
 
 //文章搜索参数
@@ -51,17 +49,12 @@ type ReqEditParams struct {
 }
 
 //添加文章操作
-func AddNews(req *ReqAddParams, cateIds []int, userId int) (insId int64, err error) {
+func AddNews(req *ReqAddParams, cateIds []int, userId int, tx *gdb.TX) (insId int64, err error) {
 	if len(cateIds) == 0 {
 		err = gerror.New("栏目不能为空")
 		return
 	}
-	tx, err := g.DB().Begin()
-	if err != nil {
-		g.Log().Error(err)
-		err = gerror.New("添加失败")
-		return
-	}
+
 	nowTime := gconv.Uint(gtime.Timestamp())
 	entity := &Entity{
 		UserId:        gconv.Uint64(userId),
@@ -73,7 +66,6 @@ func AddNews(req *ReqAddParams, cateIds []int, userId int) (insId int64, err err
 		NewsKeywords:  req.NewsKeywords,
 		NewsExcerpt:   req.NewsExcerpt,
 		NewsSource:    req.NewsSource,
-		NewsContent:   req.NewsContent,
 		Thumbnail:     req.Thumbnail,
 		IsJump:        req.IsJump,
 		JumpUrl:       req.JumpUrl,
@@ -85,18 +77,16 @@ func AddNews(req *ReqAddParams, cateIds []int, userId int) (insId int64, err err
 			entity.Recommended = 1
 		}
 	}
-	res, e := entity.Save()
+	res, e := Model.TX(tx).Insert(entity)
 	if e != nil {
 		g.Log().Error(e)
-		err = gerror.New("添加文章失败")
-		tx.Rollback()
+		err = gerror.New("保存文章失败")
 		return
 	}
 	insId, err = res.LastInsertId()
 	if err != nil {
 		g.Log().Error(err)
 		err = gerror.New("添加文章失败")
-		tx.Rollback()
 		return
 	}
 	//保存栏目与文章关联信息
@@ -105,29 +95,21 @@ func AddNews(req *ReqAddParams, cateIds []int, userId int) (insId int64, err err
 		catNewsEntity[k].CategoryId = gconv.Uint64(cateId)
 		catNewsEntity[k].NewsId = gconv.Uint64(insId)
 	}
-	_, err = cms_category_news.Model.Data(catNewsEntity).Insert()
+	_, err = cms_category_news.Model.TX(tx).Data(catNewsEntity).Insert()
 	if err != nil {
 		g.Log().Error(err)
-		err = gerror.New("添加文章失败")
-		tx.Rollback()
+		err = gerror.New("设置文章栏目失败")
 		return
 	}
-	tx.Commit()
 	return
 }
 
 //修改文章操作
-func EditNews(req *ReqEditParams, cateIds []int) (err error) {
+func EditNews(req *ReqEditParams, cateIds []int, tx *gdb.TX) (err error) {
 	if len(cateIds) == 0 {
 		err = gerror.New("栏目不能为空")
 		return
 	}
-	tx, err := g.DB().Begin()
-	if err != nil {
-		g.Log().Error(err)
-		err = gerror.New("添加失败")
-		return
-	}
 	entity, err := Model.FindOne("id", req.Id)
 	if err != nil {
 		g.Log().Error(err)
@@ -143,7 +125,6 @@ func EditNews(req *ReqEditParams, cateIds []int) (err error) {
 	entity.NewsKeywords = req.NewsKeywords
 	entity.NewsExcerpt = req.NewsExcerpt
 	entity.NewsSource = req.NewsSource
-	entity.NewsContent = req.NewsContent
 	entity.Thumbnail = req.Thumbnail
 	entity.IsJump = req.IsJump
 	entity.JumpUrl = req.JumpUrl
@@ -154,11 +135,10 @@ func EditNews(req *ReqEditParams, cateIds []int) (err error) {
 			entity.Recommended = 1
 		}
 	}
-	_, err = entity.Update()
+	_, err = Model.TX(tx).Replace(entity)
 	if err != nil {
 		g.Log().Error(err)
 		err = gerror.New("修改文章失败")
-		tx.Rollback()
 		return
 	}
 	//删除旧的栏目文章关联信息
@@ -167,11 +147,10 @@ func EditNews(req *ReqEditParams, cateIds []int) (err error) {
 		return
 	}
 	for _, cn := range cnList {
-		_, err = cn.Delete()
+		_, err = cms_category_news.Model.TX(tx).Delete("news_id", cn.NewsId)
 		if err != nil {
 			g.Log().Error(err)
 			err = gerror.New("更新文章栏目所属信息失败")
-			tx.Rollback()
 			return
 		}
 	}
@@ -181,14 +160,12 @@ func EditNews(req *ReqEditParams, cateIds []int) (err error) {
 		catNewsEntity[k].CategoryId = gconv.Uint64(cateId)
 		catNewsEntity[k].NewsId = gconv.Uint64(req.Id)
 	}
-	_, err = cms_category_news.Model.Data(catNewsEntity).Insert()
+	_, err = cms_category_news.Model.TX(tx).Data(catNewsEntity).Insert()
 	if err != nil {
 		g.Log().Error(err)
 		err = gerror.New("更新文章栏目所属信息失败")
-		tx.Rollback()
 		return
 	}
-	tx.Commit()
 	return
 }
 
@@ -247,12 +224,16 @@ func GetById(id int) (news *Entity, err error) {
 	return
 }
 
-func DeleteByIds(ids []int) error {
-	_, err := Model.Delete("id in (?)", ids)
+func DeleteByIds(ids []int, tx *gdb.TX) error {
+	_, err := Model.TX(tx).Delete("id in (?)", ids)
+	if err != nil {
+		g.Log().Error(err)
+		return gerror.New("删除文章失败")
+	}
+	_, err = cms_category_news.Model.TX(tx).Delete("news_id in (?)", ids)
 	if err != nil {
 		g.Log().Error(err)
-		return gerror.New("删除失败")
+		return gerror.New("删除文章栏目关联信息失败")
 	}
-	cms_category_news.Delete("news_id in (?)", ids)
 	return nil
 }

+ 0 - 3
app/model/admin/cms_news/cms_news_entity.go

@@ -12,7 +12,6 @@ import (
 // Entity is the golang structure for table cms_news.
 type Entity struct {
 	Id            uint64 `orm:"id,primary"     json:"id"`             //
-	NewsFormat    uint   `orm:"news_format"    json:"news_format"`    // 内容格式;1:html;2:md
 	UserId        uint64 `orm:"user_id"        json:"user_id"`        // 发表者用户id
 	NewsStatus    uint   `orm:"news_status"    json:"news_status"`    // 状态;1:已发布;0:未发布;
 	IsTop         uint   `orm:"is_top"         json:"is_top"`         // 是否置顶;1:置顶;0:不置顶
@@ -27,11 +26,9 @@ type Entity struct {
 	NewsKeywords  string `orm:"news_keywords"  json:"news_keywords"`  // seo keywords
 	NewsExcerpt   string `orm:"news_excerpt"   json:"news_excerpt"`   // post摘要
 	NewsSource    string `orm:"news_source"    json:"news_source"`    // 转载文章的来源
-	NewsContent   string `orm:"news_content"   json:"news_content"`   // 文章内容
 	Thumbnail     string `orm:"thumbnail"      json:"thumbnail"`      // 缩略图
 	IsJump        uint   `orm:"is_jump"        json:"is_jump"`        // 是否跳转地址
 	JumpUrl       string `orm:"jump_url"       json:"jump_url"`       // 跳转地址
-	IsPushed      uint   `orm:"is_pushed"      json:"is_pushed"`      // 是否已推送
 }
 
 // OmitEmpty sets OPTION_OMITEMPTY option for the model, which automatically filers

+ 0 - 6
app/model/admin/cms_news/cms_news_model.go

@@ -25,7 +25,6 @@ var (
 	// Columns defines and stores column names for table cms_news.
 	Columns = struct {
 		Id            string //
-		NewsFormat    string // 内容格式;1:html;2:md
 		UserId        string // 发表者用户id
 		NewsStatus    string // 状态;1:已发布;0:未发布;
 		IsTop         string // 是否置顶;1:置顶;0:不置顶
@@ -40,14 +39,11 @@ var (
 		NewsKeywords  string // seo keywords
 		NewsExcerpt   string // post摘要
 		NewsSource    string // 转载文章的来源
-		NewsContent   string // 文章内容
 		Thumbnail     string // 缩略图
 		IsJump        string // 是否跳转地址
 		JumpUrl       string // 跳转地址
-		IsPushed      string // 是否已推送
 	}{
 		Id:            "id",
-		NewsFormat:    "news_format",
 		UserId:        "user_id",
 		NewsStatus:    "news_status",
 		IsTop:         "is_top",
@@ -62,11 +58,9 @@ var (
 		NewsKeywords:  "news_keywords",
 		NewsExcerpt:   "news_excerpt",
 		NewsSource:    "news_source",
-		NewsContent:   "news_content",
 		Thumbnail:     "thumbnail",
 		IsJump:        "is_jump",
 		JumpUrl:       "jump_url",
-		IsPushed:      "is_pushed",
 	}
 )
 

+ 10 - 0
app/model/admin/model_category/model_category.go

@@ -124,3 +124,13 @@ func DeleteByIds(ids []int) error {
 	}
 	return nil
 }
+
+func GetCategoryAll() (entity []*Entity, err error) {
+	entity, err = Model.Where(Columns.CStatus, 1).Order(Columns.CSort + " ASC," + Columns.CId + " ASC").All()
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("获取模型分类数据失败")
+		return
+	}
+	return
+}

+ 222 - 0
app/model/admin/model_fields/model_fields.go

@@ -0,0 +1,222 @@
+// ============================================================================
+// This is auto-generated by gf cli tool only once. Fill this file as you wish.
+// ============================================================================
+
+package model_fields
+
+import (
+	"github.com/gogf/gf/database/gdb"
+	"github.com/gogf/gf/errors/gerror"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/os/gtime"
+	"github.com/gogf/gf/text/gstr"
+	"github.com/gogf/gf/util/gconv"
+)
+
+// AddReq 用于存储新增请求的请求参数
+type AddReq struct {
+	ModelId      uint     `p:"modelId" `
+	FieldName    string   `p:"fieldName" v:"required#字段名称不能为空"`
+	FieldTitle   string   `p:"fieldTitle" `
+	FieldType    string   `p:"fieldType" `
+	FieldLength  string   `p:"fieldLength" `
+	FieldDefault string   `p:"fieldDefault" `
+	FieldData    string   `p:"fieldData" `
+	FieldDesc    string   `p:"fieldDesc" `
+	FieldRules   []string `p:"fieldRules" `
+	CreateBy     uint64
+	FieldWidth   string `p:"fieldWidth"`
+	fieldAlign   string `p:"fieldAlign"`
+}
+
+// EditReq 用于存储修改请求参数
+type EditReq struct {
+	FieldId      int64    `p:"fieldId" v:"required#主键ID不能为空"`
+	ModelId      uint     `p:"modelId" `
+	FieldName    string   `p:"fieldName" v:"required#字段名称不能为空"`
+	FieldTitle   string   `p:"fieldTitle" `
+	FieldType    string   `p:"fieldType" `
+	FieldLength  string   `p:"fieldLength" `
+	FieldDefault string   `p:"fieldDefault" `
+	FieldData    string   `p:"fieldData" `
+	FieldDesc    string   `p:"fieldDesc" `
+	FieldRules   []string `p:"fieldRules" `
+	UpdateBy     uint64
+	FieldWidth   string `p:"fieldWidth"`
+	fieldAlign   string `p:"fieldAlign"`
+}
+
+type RemoveReq struct {
+	Ids []int `p:"ids"` //删除id
+}
+
+type FieldInfo struct {
+	FieldId      uint64 `p:"field_id,primary" json:"field_id"`      // 模型字段ID
+	ModelId      uint   `p:"model_id"         json:"model_id"`      // 模型ID
+	FieldName    string `p:"field_name"       json:"field_name"`    // 字段名称
+	FieldTitle   string `p:"field_title"      json:"field_title"`   // 字段标题
+	FieldType    string `p:"field_type"       json:"field_type"`    // 字段类型
+	FieldLength  string `p:"field_length"     json:"field_length"`  // 字段长度
+	FieldDefault string `p:"field_default"    json:"field_default"` // 字段默认值
+	FieldData    string `p:"field_data"       json:"field_data"`    // 字段数据
+	FieldDesc    string `p:"field_desc"       json:"field_desc"`    // 字段描述
+	FieldRules   string `p:"field_rules"      json:"field_rules"`   // 字段规则
+	FieldSort    int64  `p:"field_sort"       json:"field_sort"`    // 字段排序
+	CreateBy     uint64 `p:"create_by"        json:"create_by"`     // 创建人
+	UpdateBy     uint64 `p:"update_by"        json:"update_by"`     // 修改人
+	CreateTime   uint64 `p:"create_time"      json:"create_time"`   // 创建时间
+	UpdateTime   uint64 `p:"update_time"      json:"update_time"`   // 修改时间
+	ModelEdit    string `p:"model_edit" json:"model_edit"`
+	ModelIndexes string `p:"model_indexes" json:"model_indexes"`
+	ModelList    string `p:"model_list" json:"model_list"`
+	ModelOrder   string `p:"model_order" json:"model_order"`
+	ModelPk      string `p:"model_pk" json:"model_pk"`
+	ModelSort    string `p:"model_sort" json:"model_sort"`
+	SearchList   string `p:"search_list" json:"search_list"`
+}
+
+type SetFieldsAttrReq struct {
+	ModelId    int    `p:"modelId" json:"model_id"`
+	PkId       uint64 `p:"pkId" json:"pk_id"`
+	FieldsList []FieldInfo
+}
+
+// SelectPageReq 用于存储分页查询的请求参数
+type SelectPageReq struct {
+	ModelId   int64  `p:"modelId"`   //模型ID
+	BeginTime string `p:"beginTime"` //开始时间
+	EndTime   string `p:"endTime"`   //结束时间
+	PageNum   int64  `p:"pageNum"`   //当前页码
+	PageSize  int    `p:"pageSize"`  //每页数
+}
+
+// 根据ID查询记录
+func GetByID(id int64) (*Entity, error) {
+	entity, err := Model.FindOne(id)
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("根据ID查询记录出错")
+	}
+	if entity == nil {
+		err = gerror.New("没有查询到对应模型的字段")
+	}
+	return entity, nil
+}
+
+// AddSave 添加
+func AddSave(req *AddReq) error {
+	if err := Exists(req.FieldName, req.ModelId, 0); err != nil {
+		return err
+	}
+	entity := new(Entity)
+	entity.ModelId = req.ModelId
+	entity.FieldName = req.FieldName
+	entity.FieldTitle = req.FieldTitle
+	entity.FieldType = req.FieldType
+	entity.FieldLength = req.FieldLength
+	entity.FieldDefault = req.FieldDefault
+	entity.FieldData = req.FieldData
+	entity.FieldDesc = req.FieldDesc
+	entity.FieldRules = gstr.Join(req.FieldRules, ",")
+	entity.CreateBy = req.CreateBy
+	entity.FieldSort = 1000
+	entity.FieldWidth = req.FieldWidth
+	entity.FieldAlign = req.fieldAlign
+	time := gconv.Uint64(gtime.Timestamp())
+	entity.CreateTime = time
+	entity.UpdateTime = time
+	result, err := entity.Insert()
+	if err != nil {
+		return err
+	}
+	_, err = result.LastInsertId()
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+//判断字段是否存在
+func Exists(name string, modelId, fieldsId uint) error {
+	model := Model.Where(Columns.FieldName, name).And(Columns.ModelId, modelId)
+	if fieldsId != 0 {
+		model = model.And(Columns.FieldId, fieldsId)
+	}
+	entity, err := model.FindOne()
+	if err != nil {
+		g.Log().Error(err)
+		return gerror.New("判断数据重复时出错")
+	}
+	if entity != nil {
+		return gerror.New("已存在相同名称的字段")
+	}
+	return nil
+}
+
+// 删除
+func DeleteByIds(Ids []int) error {
+	_, err := Model.Delete("field_id in(?)", Ids)
+	if err != nil {
+		g.Log().Error(err)
+		return gerror.New("删除失败")
+	}
+	return nil
+}
+
+// 根据ID来修改信息
+func EditSave(req *EditReq) error {
+	// 先根据ID来查询要修改的记录
+	entity, err := GetByID(req.FieldId)
+	if err != nil {
+		return err
+	}
+
+	// 修改实体
+	entity.ModelId = req.ModelId
+	entity.FieldName = req.FieldName
+	entity.FieldTitle = req.FieldTitle
+	entity.FieldType = req.FieldType
+	entity.FieldLength = req.FieldLength
+	entity.FieldDefault = req.FieldDefault
+	entity.FieldData = req.FieldData
+	entity.FieldDesc = req.FieldDesc
+	entity.FieldRules = gstr.Join(req.FieldRules, ",")
+	entity.UpdateBy = req.UpdateBy
+	entity.UpdateTime = gconv.Uint64(gtime.Timestamp())
+	entity.FieldWidth = req.FieldWidth
+	entity.FieldAlign = req.fieldAlign
+	_, err = entity.Update()
+	if err != nil {
+		g.Log().Error(err)
+		return gerror.New("修改失败")
+	}
+	return nil
+}
+
+// 获取所有数据
+func SelectListAll(req *SelectPageReq) (list []*Entity, err error) {
+	model := Model
+	if req != nil {
+		if req.ModelId != 0 {
+			model = model.Where(Columns.ModelId, req.ModelId)
+		}
+	}
+	// 查询
+	list, err = model.Order("field_sort asc,field_id asc").All()
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("查询失败")
+		return
+	}
+	return
+}
+
+//通过模型ID删除对应模型字段
+func DeleteByModelIds(modelIds []int, tx *gdb.TX) error {
+	_, err := Model.TX(tx).Delete(Columns.ModelId+" in(?)", modelIds)
+	if err != nil {
+		g.Log().Error(err)
+		return gerror.New("删除字段信息失败")
+	}
+	return nil
+}

+ 73 - 0
app/model/admin/model_fields/model_fields_entity.go

@@ -0,0 +1,73 @@
+// ==========================================================================
+// This is auto-generated by gf cli tool. You may not really want to edit it.
+// ==========================================================================
+
+package model_fields
+
+import (
+	"database/sql"
+	"github.com/gogf/gf/database/gdb"
+)
+
+// Entity is the golang structure for table model_fields.
+type Entity struct {
+	FieldId      uint64 `orm:"field_id,primary" json:"field_id"`      // 模型字段ID
+	ModelId      uint   `orm:"model_id"         json:"model_id"`      // 模型ID
+	FieldName    string `orm:"field_name"       json:"field_name"`    // 字段名称
+	FieldTitle   string `orm:"field_title"      json:"field_title"`   // 字段标题
+	FieldType    string `orm:"field_type"       json:"field_type"`    // 字段类型
+	FieldLength  string `orm:"field_length"     json:"field_length"`  // 字段长度
+	FieldDefault string `orm:"field_default"    json:"field_default"` // 字段默认值
+	FieldData    string `orm:"field_data"       json:"field_data"`    // 字段数据
+	FieldDesc    string `orm:"field_desc"       json:"field_desc"`    // 字段描述
+	FieldRules   string `orm:"field_rules"      json:"field_rules"`   // 字段规则
+	FieldSort    int64  `orm:"field_sort"       json:"field_sort"`    // 字段排序
+	FieldWidth   string `orm:"field_width"       json:"field_width"`  // 字段列表显示宽度
+	FieldAlign   string `orm:"field_align"       json:"field_align"`  // 字段列表对齐
+	CreateBy     uint64 `orm:"create_by"        json:"create_by"`     // 创建人
+	UpdateBy     uint64 `orm:"update_by"        json:"update_by"`     // 修改人
+	CreateTime   uint64 `orm:"create_time"      json:"create_time"`   // 创建时间
+	UpdateTime   uint64 `orm:"update_time"      json:"update_time"`   // 修改时间
+}
+
+// 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()
+}

+ 375 - 0
app/model/admin/model_fields/model_fields_model.go

@@ -0,0 +1,375 @@
+// ==========================================================================
+// This is auto-generated by gf cli tool. You may not really want to edit it.
+// ==========================================================================
+
+package model_fields
+
+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 model_fields operations.
+type arModel struct {
+	gmvc.M
+}
+
+var (
+	// Table is the table name of model_fields.
+	Table = "model_fields"
+	// Model is the model object of model_fields.
+	Model = &arModel{g.DB("default").Table(Table).Safe()}
+	// Columns defines and stores column names for table model_fields.
+	Columns = struct {
+		FieldId      string // 模型字段ID
+		ModelId      string // 模型ID
+		FieldName    string // 字段名称
+		FieldTitle   string // 字段标题
+		FieldType    string // 字段类型
+		FieldLength  string // 字段长度
+		FieldDefault string // 字段默认值
+		FieldData    string // 字段数据
+		FieldDesc    string // 字段描述
+		FieldRules   string // 字段规则
+		FieldSort    string // 字段排序
+		FieldWidth   string //字段列表显示宽度
+		fieldAlign   string //字段列表对齐
+		CreateBy     string // 创建人
+		UpdateBy     string // 修改人
+		CreateTime   string // 创建时间
+		UpdateTime   string // 修改时间
+	}{
+		FieldId:      "field_id",
+		ModelId:      "model_id",
+		FieldName:    "field_name",
+		FieldTitle:   "field_title",
+		FieldType:    "field_type",
+		FieldLength:  "field_length",
+		FieldDefault: "field_default",
+		FieldData:    "field_data",
+		FieldDesc:    "field_desc",
+		FieldRules:   "field_rules",
+		FieldSort:    "field_sort",
+		FieldWidth:   "field_width",
+		fieldAlign:   "field_align",
+		CreateBy:     "create_by",
+		UpdateBy:     "update_by",
+		CreateTime:   "create_time",
+		UpdateTime:   "update_time",
+	}
+)
+
+// 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 <table> 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 <table> 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 <table> 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 <where> 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 <limit> 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 <page> 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 <duration> < 0, which means it clear the cache with given <name>.
+// If the parameter <duration> = 0, which means it never expires.
+// If the parameter <duration> > 0, which means it expires after <duration>.
+//
+// The optional parameter <name> is used to bind a name to the cache, which means you can later
+// control the cache like changing the <duration> or clearing the cache with specified <name>.
+//
+// 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 <data> 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 <where> 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 <where> 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()}
+}

+ 24 - 0
app/model/admin/model_fields/model_rules.go

@@ -0,0 +1,24 @@
+package model_fields
+
+import "github.com/gogf/gf/frame/g"
+
+//模型字段规则
+type FieldRule struct {
+	FType     string      `json:"type"`
+	FTitle    string      `json:"title"`
+	FField    string      `json:"field"`
+	FValue    interface{} `json:"value"`
+	FOptions  g.List      `json:"options"`
+	FAttr     g.Map       `json:"attr"` //设置组件普通的 HTML 特性
+	FProps    g.Map       `json:"props"`
+	FValidate []Validate  `json:"validate"`
+}
+
+//字段验证规则
+type Validate struct {
+	VType     string `json:"type"`
+	VRequired bool   `json:"required"`
+	VMessage  string `json:"message"`
+	VMin      int64  `json:"min"`
+	VTrigger  string `json:"trigger"`
+}

+ 264 - 0
app/model/admin/model_info/model_info.go

@@ -0,0 +1,264 @@
+// ============================================================================
+// This is auto-generated by gf cli tool only once. Fill this file as you wish.
+// ============================================================================
+
+package model_info
+
+import (
+	"fmt"
+	"gfast/app/model/admin/model_category"
+	"github.com/gogf/gf/database/gdb"
+	"github.com/gogf/gf/errors/gerror"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/os/gtime"
+	"github.com/gogf/gf/util/gconv"
+)
+
+// AddReq 用于存储新增请求的请求参数
+type AddReq struct {
+	ModelCategoryId uint   `p:"modelCategoryId" v:"required|min:1#模型分类id不能为空|模型分类id不能为空"`
+	ModelName       string `p:"modelName" v:"required#模型标识不能为空"`
+	ModelTitle      string `p:"modelTitle"  v:"required#模型名称不能为空"`
+	ModelStatus     uint   `p:"modelStatus" v:"required#状态不能为空"`
+	ModelEngine     string `p:"modelEngine" `
+	CreateBy        int    //添加人
+}
+
+// EditReq 用于存储修改请求参数
+type EditReq struct {
+	ModelId         int    `p:"modelId" v:"required#主键ID不能为空"`
+	ModelCategoryId int    `p:"modelCategoryId" `
+	ModelName       string `p:"modelName" v:"required#模型标识不能为空"`
+	ModelTitle      string `p:"modelTitle" `
+	ModelStatus     int    `p:"modelStatus" v:"required#状态不能为空"`
+	ModelEngine     string `p:"modelEngine" `
+	UpdateBy        int
+}
+
+//模型字段属性修改请求参数
+type FieldsAttrReq struct {
+	ModelId      int    `p:"modelId" v:"required#主键ID不能为空"`
+	ModelPk      string `p:"modelPk" `
+	ModelOrder   string `p:"modelOrder" `
+	ModelSort    string `p:"modelSort" `
+	ModelList    string `p:"modelList" `
+	ModelEdit    string `p:"modelEdit" `
+	ModelIndexes string `p:"modelIndexes" `
+	SearchList   string `p:"searchList" `
+}
+
+type ListEntity struct {
+	Entity
+	CName string `orm:"c_name"      json:"c_name" `
+}
+
+type StatusSetReq struct {
+	ModelId     uint `p:"modelId" v:"required#模型ID不能为空"`
+	ModelStatus int  `p:"modelStatus" v:"required#状态不能为空"`
+}
+
+// SelectPageReq 用于存储分页查询的请求参数
+type SelectPageReq struct {
+	ModelName       string `p:"modelName"`      //模型标识
+	ModelTitle      string `p:"modelTitle"`     //模型名称
+	ModelStatus     string `p"modelStatus"`     //模型状态
+	ModelCategoryId string `p"modelCategoryId"` //模型分类
+	BeginTime       string `p:"beginTime"`      //开始时间
+	EndTime         string `p:"endTime"`        //结束时间
+	PageNum         int    `p:"pageNum"`        //当前页码
+	PageSize        int    `p:"pageSize"`       //每页数
+}
+
+type RemoveReq struct {
+	Ids []int `p:"ids"` //删除id
+}
+
+// GetPlugAdByID 根据ID查询记录
+func GetByID(id int64) (*Entity, error) {
+	entity, err := Model.FindOne(id)
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("根据ID查询记录出错")
+	}
+	if entity == nil {
+		err = gerror.New("根据ID未能查询到记录")
+	}
+	return entity, nil
+}
+
+// AddSave 添加
+func AddSave(req *AddReq) error {
+	if err := ModelExists(req.ModelName, 0); err != nil {
+		return err
+	}
+	entity := new(Entity)
+	entity.ModelCategoryId = req.ModelCategoryId
+	entity.ModelName = req.ModelName
+	entity.ModelTitle = req.ModelTitle
+	entity.ModelStatus = req.ModelStatus
+	entity.ModelEngine = req.ModelEngine
+	entity.CreateBy = gconv.Uint64(req.CreateBy)
+	time := gconv.Uint64(gtime.Timestamp())
+	entity.CreateTime = time
+	entity.UpdateTime = time
+
+	result, err := entity.Insert()
+	if err != nil {
+		return err
+	}
+	_, err = result.LastInsertId()
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+//判断模型是否存在
+func ModelExists(name string, id int) error {
+	var res *Entity
+	if id != 0 {
+		res, _ = Model.Where(Columns.ModelName, name).And(Columns.ModelId+" != ", id).One()
+	} else {
+		res, _ = Model.Where(Columns.ModelName, name).One()
+	}
+	if res != nil {
+		return gerror.New("模型标识已经存在")
+	}
+	return nil
+}
+
+// 删除模型
+func DeleteByIds(Ids []int, tx *gdb.TX) error {
+	_, err := Model.TX(tx).Delete("model_id in(?)", Ids)
+	if err != nil {
+		g.Log().Error(err)
+		return gerror.New("删除失败")
+	}
+	return nil
+}
+
+// 根据ID来修改信息
+func EditSave(req *EditReq) error {
+	if err := ModelExists(req.ModelName, req.ModelId); err != nil {
+		return err
+	}
+	// 先根据ID来查询要修改的记录
+	entity, err := GetByID(gconv.Int64(req.ModelId))
+	if err != nil {
+		return err
+	}
+	// 修改实体
+	entity.ModelCategoryId = gconv.Uint(req.ModelCategoryId)
+	entity.ModelName = req.ModelName
+	entity.ModelTitle = req.ModelTitle
+	entity.ModelStatus = gconv.Uint(req.ModelStatus)
+	entity.ModelEngine = req.ModelEngine
+	entity.UpdateBy = gconv.Uint64(req.UpdateBy)
+	entity.UpdateTime = gconv.Uint64(gtime.Timestamp())
+	_, err = entity.Update()
+	if err != nil {
+		g.Log().Error(err)
+		return gerror.New("修改失败")
+	}
+	return nil
+}
+
+// 分页查询,返回值total总记录数,page当前页
+func SelectListByPage(req *SelectPageReq) (total int, page int, list []*ListEntity, err error) {
+	model := g.DB().Table(Table + " info")
+	if req != nil {
+		if req.ModelName != "" {
+			model.Where("info.model_name like ?", "%"+req.ModelName+"%")
+		}
+		if req.ModelTitle != "" {
+			model.Where("info.model_title like ?", "%"+req.ModelTitle+"%")
+		}
+		if req.ModelStatus != "" {
+			model.Where("info.model_status", gconv.Uint(req.ModelStatus))
+		}
+		if req.ModelCategoryId != "" {
+			model.Where("info.model_category_id", gconv.Uint(req.ModelCategoryId))
+		}
+	}
+	model = model.LeftJoin(model_category.Table+" cate", "cate.c_id=info.model_category_id")
+	// 查询总记录数(总行数)
+	total, err = model.Count()
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("获取总记录数失败")
+		return
+	}
+	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("info.*,cate.c_name").Page(page, req.PageSize).Order("info.model_id asc").All()
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("查询失败")
+		return
+	}
+	err = res.Structs(&list)
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("查询失败")
+		return
+	}
+	return
+}
+
+// 获取所有数据
+func SelectListAll(req *SelectPageReq) (list []*Entity, err error) {
+	model := Model
+	if req != nil {
+		if req.ModelName != "" {
+			model.Where("model_name like ?", "%"+req.ModelName+"%")
+		}
+		if req.ModelTitle != "" {
+			model.Where("model_title = ?", req.ModelTitle)
+		}
+	}
+	// 查询
+	list, err = model.Order("model_id asc").All()
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("查询失败")
+		return
+	}
+	return
+}
+
+//设置模型状态
+func SetStatus(req *StatusSetReq) error {
+	if req != nil {
+		entity, err := Model.Where(Columns.ModelId, req.ModelId).One()
+		if err != nil {
+			g.Log().Error(err)
+			return gerror.New("获取模型信息失败")
+		}
+		entity.ModelStatus = gconv.Uint(req.ModelStatus)
+		_, err = entity.Update()
+		if err != nil {
+			g.Log().Error(err)
+			return gerror.New("设置状态失败")
+		}
+	}
+	return nil
+}
+
+//通过模型分类ID获取模型
+func GetModelsByCateIds(cateIds []int) (models []*Entity, err error) {
+	models, err = Model.Fields(fmt.Sprintf("%s,%s,%s", Columns.ModelId, Columns.ModelName, Columns.ModelTitle)).
+		Where(Columns.ModelCategoryId+" in(?)", cateIds).
+		Order(Columns.ModelId + " ASC ").FindAll()
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("获取模型信息失败")
+	}
+	return
+}

+ 73 - 0
app/model/admin/model_info/model_info_entity.go

@@ -0,0 +1,73 @@
+// ==========================================================================
+// This is auto-generated by gf cli tool. You may not really want to edit it.
+// ==========================================================================
+
+package model_info
+
+import (
+	"database/sql"
+	"github.com/gogf/gf/database/gdb"
+)
+
+// Entity is the golang structure for table model_info.
+type Entity struct {
+	ModelId         uint   `orm:"model_id,primary"  json:"model_id"`          // 模型ID
+	ModelCategoryId uint   `orm:"model_category_id" json:"model_category_id"` // 模板分类id
+	ModelName       string `orm:"model_name"        json:"model_name"`        // 模型标识
+	ModelTitle      string `orm:"model_title"       json:"model_title"`       // 模型名称
+	ModelPk         string `orm:"model_pk"          json:"model_pk"`          // 主键字段
+	ModelOrder      string `orm:"model_order"       json:"model_order"`       // 默认排序字段
+	ModelSort       string `orm:"model_sort"        json:"model_sort"`        // 表单字段排序
+	ModelList       string `orm:"model_list"        json:"model_list"`        // 列表显示字段,为空显示全部
+	ModelEdit       string `orm:"model_edit"        json:"model_edit"`        // 可编辑字段,为空则除主键外均可以编辑
+	ModelIndexes    string `orm:"model_indexes"     json:"model_indexes"`     // 索引字段
+	SearchList      string `orm:"search_list"       json:"search_list"`       // 高级搜索的字段
+	CreateTime      uint64 `orm:"create_time"       json:"create_time"`       // 创建时间
+	UpdateTime      uint64 `orm:"update_time"       json:"update_time"`       // 更新时间
+	ModelStatus     uint   `orm:"model_status"      json:"model_status"`      // 状态
+	ModelEngine     string `orm:"model_engine"      json:"model_engine"`      // 数据库引擎
+	CreateBy        uint64 `orm:"create_by"         json:"create_by"`         // 创建人
+	UpdateBy        uint64 `orm:"update_by"         json:"update_by"`         // 修改人
+}
+
+// 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()
+}

+ 375 - 0
app/model/admin/model_info/model_info_model.go

@@ -0,0 +1,375 @@
+// ==========================================================================
+// This is auto-generated by gf cli tool. You may not really want to edit it.
+// ==========================================================================
+
+package model_info
+
+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 model_info operations.
+type arModel struct {
+	gmvc.M
+}
+
+var (
+	// Table is the table name of model_info.
+	Table = "model_info"
+	// Model is the model object of model_info.
+	Model = &arModel{g.DB("default").Table(Table).Safe()}
+	// Columns defines and stores column names for table model_info.
+	Columns = struct {
+		ModelId         string // 模型ID
+		ModelCategoryId string // 模板分类id
+		ModelName       string // 模型标识
+		ModelTitle      string // 模型名称
+		ModelPk         string // 主键字段
+		ModelOrder      string // 默认排序字段
+		ModelSort       string // 表单字段排序
+		ModelList       string // 列表显示字段,为空显示全部
+		ModelEdit       string // 可编辑字段,为空则除主键外均可以编辑
+		ModelIndexes    string // 索引字段
+		SearchList      string // 高级搜索的字段
+		CreateTime      string // 创建时间
+		UpdateTime      string // 更新时间
+		ModelStatus     string // 状态
+		ModelEngine     string // 数据库引擎
+		CreateBy        string // 创建人
+		UpdateBy        string // 修改人
+	}{
+		ModelId:         "model_id",
+		ModelCategoryId: "model_category_id",
+		ModelName:       "model_name",
+		ModelTitle:      "model_title",
+		ModelPk:         "model_pk",
+		ModelOrder:      "model_order",
+		ModelSort:       "model_sort",
+		ModelList:       "model_list",
+		ModelEdit:       "model_edit",
+		ModelIndexes:    "model_indexes",
+		SearchList:      "search_list",
+		CreateTime:      "create_time",
+		UpdateTime:      "update_time",
+		ModelStatus:     "model_status",
+		ModelEngine:     "model_engine",
+		CreateBy:        "create_by",
+		UpdateBy:        "update_by",
+	}
+)
+
+// 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 <table> 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 <table> 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 <table> 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 <where> 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 <limit> 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 <page> 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 <duration> < 0, which means it clear the cache with given <name>.
+// If the parameter <duration> = 0, which means it never expires.
+// If the parameter <duration> > 0, which means it expires after <duration>.
+//
+// The optional parameter <name> is used to bind a name to the cache, which means you can later
+// control the cache like changing the <duration> or clearing the cache with specified <name>.
+//
+// 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 <data> 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 <where> 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 <where> 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()}
+}

+ 41 - 25
app/model/admin/plug_ad/plug_ad.go

@@ -1,6 +1,8 @@
 package plug_ad
 
 import (
+	"gfast/app/model/admin/plug_adtype"
+	"github.com/gogf/gf/database/gdb"
 	"github.com/gogf/gf/errors/gerror"
 	"github.com/gogf/gf/frame/g"
 	"github.com/gogf/gf/os/gtime"
@@ -8,39 +10,45 @@ import (
 
 // AddReq 用于存储新增广告请求的请求参数
 type AddReq struct {
-	AdName     string `p:"adName"`     // 广告名称
-	AdAdtypeid int    `p:"adAdtypeid"` // 所属位置
-	AdCheckid  int    `p:"adCheckid"`  // 1=图片 2=JS
-	AdJs       string `p:"adJs"`       // JS代码
-	AdPic      string `p:"adPic"`      // 广告图片URL
-	AdUrl      string `p:"adUrl"`      // 广告链接
-	AdContent  string `p:"adContent"`  // 广告文字内容
-	AdSort     int    `p:"adSort"`     // 排序
-	AdOpen     int    `p:"adOpen"`     // 1=审核  0=未审核
+	AdName     string `p:"adName" v:"required#名称不能为空"` // 广告名称
+	AdAdtypeid int    `p:"adAdtypeid"`                 // 所属位置
+	AdCheckid  int    `p:"adCheckid"`                  // 1=图片 2=JS
+	AdJs       string `p:"adJs"`                       // JS代码
+	AdPic      string `p:"adPic"`                      // 广告图片URL
+	AdUrl      string `p:"adUrl"`                      // 广告链接
+	AdContent  string `p:"adContent"`                  // 广告文字内容
+	AdSort     int    `p:"adSort"`                     // 排序
+	AdOpen     int    `p:"adOpen"`                     // 1=审核  0=未审核
 }
 
-// EditReq 用于存储修改广告请求参数
+// EditReq 用于存储修改广告请求参数
 type EditReq struct {
-	PlugAdID int64 `p:"plugAdID"`
+	PlugAdID int64 `p:"plugAdID" v:"required|min:1#广告id不能为空|广告id参数错误"`
 	AddReq
 }
 
 // SelectPageReq 用于存储分页查询广告的请求参数
 type SelectPageReq struct {
 	AdName   string `p:"adName"`   // 广告名称
-	PageNo   int64  `p:"pageNo"`   // 当前页
+	PageNo   int64  `p:"pageNum"`  // 当前页
 	PageSize int64  `p:"pageSize"` // 每页显示记录数
 }
 
+// 用于存储分页查询的数据
+type ListEntity struct {
+	Entity
+	AdTypeName string `orm:"adtype_name"      json:"adtype_name" ` // 广告所属位置
+}
+
 // GetPlugAdByID 根据ID查询广告记录
 func GetPlugAdByID(id int64) (*Entity, error) {
 	entity, err := Model.FindOne("ad_id", id)
 	if err != nil {
 		g.Log().Error(err)
-		err = gerror.New("根据ID查询广告记录出错")
+		return nil, gerror.New("根据ID查询广告记录出错")
 	}
 	if entity == nil {
-		err = gerror.New("根据ID未能查询到广告记录")
+		return nil, gerror.New("根据ID未能查询到广告记录")
 	}
 	return entity, nil
 }
@@ -62,15 +70,14 @@ func AddSave(req *AddReq) error {
 	_, err := entity.Insert()
 	if err != nil {
 		g.Log().Error(err)
-		err = gerror.New("保存失败")
-		return err
+		return gerror.New("添加广告记录入库失败")
 	}
 	return nil
 }
 
-// 删除广告
-func DeleteByIDs(Ids []int) error {
-	_, err := Model.Delete("ad_id in(?)", Ids)
+// 批量删除广告记录
+func DeleteByIDs(ids []int) error {
+	_, err := Model.Delete("ad_id in(?)", ids)
 	if err != nil {
 		g.Log().Error(err)
 		return gerror.New("删除广告失败")
@@ -78,7 +85,7 @@ func DeleteByIDs(Ids []int) error {
 	return nil
 }
 
-// 根据广告ID来修改广告信息
+// 根据广告ID来修改广告信息
 func EditSave(editReq *EditReq) error {
 	// 先根据ID来查询要修改的广告记录
 	entity, err := GetPlugAdByID(editReq.PlugAdID)
@@ -98,19 +105,20 @@ func EditSave(editReq *EditReq) error {
 	_, err = entity.Update()
 	if err != nil {
 		g.Log().Error(err)
-		return gerror.New("修改广告失败")
+		return gerror.New("修改广告失败")
 	}
 	return nil
 }
 
 // 分页查询,返回值total总记录数,page当前页
-func SelectListByPage(req *SelectPageReq) (total int, page int64, list []*Entity, err error) {
-	model := Model
+func SelectListByPage(req *SelectPageReq) (total int, page int64, list []*ListEntity, err error) {
+	model := g.DB().Table(Table + " ad")
 	if req != nil {
 		if req.AdName != "" {
-			model = model.Where("ad_name like ?", "%"+req.AdName+"%")
+			model.Where("ad.ad_name like ?", "%"+req.AdName+"%")
 		}
 	}
+	model = model.LeftJoin(plug_adtype.Table+" type", "type.adtype_id=ad.ad_adtypeid")
 	// 查询广告位总记录数(总行数)
 	total, err = model.Count()
 	if err != nil {
@@ -126,7 +134,15 @@ func SelectListByPage(req *SelectPageReq) (total int, page int64, list []*Entity
 		req.PageSize = 10
 	}
 	// 分页排序查询
-	list, err = model.Page(int(page), int(req.PageSize)).Order("ad_sort asc,ad_id asc").All()
+	var res gdb.Result
+	res, err = model.Fields("ad.*,type.adtype_name").Page(int(page), int(req.PageSize)).Order("ad.ad_sort asc,ad.ad_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("分页查询广告失败")

+ 11 - 10
app/model/admin/plug_adtype/plug_adtype.go

@@ -17,14 +17,14 @@ type AddReq struct {
 
 // EditReq 用于存储修改广告位请求参数
 type EditReq struct {
-	AdtypeID int64 `p:"adtypeID" v:"required|min:1#主键ID不能为空|主键ID值错误"`
+	AdtypeID int64 `p:"adTypeID" v:"required|min:1#主键ID不能为空|主键ID值错误"`
 	AddReq
 }
 
 // SelectPageReq 用于存储分页查询广告位的请求参数
 type SelectPageReq struct {
 	AdtypeName string `p:"adTypeName"` // 广告位名称
-	PageNo     int64  `p:"pageNo"`     // 当前页
+	PageNo     int64  `p:"pageNum"`    // 当前页
 	PageSize   int64  `p:"pageSize"`   // 每页显示记录数
 }
 
@@ -33,24 +33,24 @@ func GetAdtypeByID(id int64) (*Entity, error) {
 	entity, err := Model.FindOne(id)
 	if err != nil {
 		g.Log().Error(err)
-		err = gerror.New("根据ID查询广告位记录出错")
+		return nil, gerror.New("根据ID查询广告位记录出错")
 	}
 	if entity == nil {
-		err = gerror.New("根据ID未能查询到广告位记录")
+		return nil, gerror.New("根据ID未能查询到广告位记录")
 	}
 	return entity, nil
 }
 
-// 根据广告位的名称来判断是否已存在相同名称的广告位
-func CheakAdtypeNameUnique(adtypeName string, adTypeId int64) error {
+// 根据广告位的名称和ID来判断是否已存在相同名称的广告位
+func CheakAdtypeNameUnique(adTypeName string, adTypeId int64) error {
 	var (
 		entity *Entity
 		err    error
 	)
 	if adTypeId == 0 {
-		entity, err = Model.FindOne(Columns.AdtypeName, adtypeName)
+		entity, err = Model.FindOne(Columns.AdtypeName, adTypeName)
 	} else {
-		entity, err = Model.Where(Columns.AdtypeName, adtypeName).And(Columns.AdtypeId+"!=?", adTypeId).FindOne()
+		entity, err = Model.Where(Columns.AdtypeName, adTypeName).And(Columns.AdtypeId+"!=?", adTypeId).FindOne()
 	}
 	if err != nil {
 		g.Log().Error(err)
@@ -70,7 +70,7 @@ func AddSave(req *AddReq) error {
 	_, err := entity.Insert()
 	if err != nil {
 		g.Log().Error(err)
-		err = gerror.New("保存广告位失败")
+		return gerror.New("保存广告位失败")
 	}
 	return nil
 }
@@ -90,7 +90,8 @@ func EditSave(editReq *EditReq) error {
 	// 先根据ID来查询要修改的广告位记录
 	entity, err := GetAdtypeByID(editReq.AdtypeID)
 	if err != nil {
-		return err
+		g.Log().Error(err)
+		return gerror.New("查询要修改的记录时出错")
 	}
 	// 修改实体
 	entity.AdtypeName = editReq.AdtypeName

+ 149 - 0
app/model/admin/plug_link/plug_link.go

@@ -0,0 +1,149 @@
+// ============================================================================
+// This is auto-generated by gf cli tool only once. Fill this file as you wish.
+// ============================================================================
+
+package plug_link
+
+import (
+	"gfast/app/model/admin/plug_linktype"
+	"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 {
+	LinkName   string `p:"linkName" v:"required#名称不能为空"` // 链接名称
+	LinkUrl    string `p:"linkUrl"`                      // 链接URL
+	LinkTarget string `p:"linkTarget"`                   // 打开方式
+	LinkTypeID int    `p:"linkTypeID"`                   // 所属栏目ID
+	LinkQQ     string `p:"linkQQ"`                       // 联系QQ
+	LinkOrder  int64  `p:"linkOrder"`                    // 排序
+	LinkOpen   int    `p:"linkOpen"`                     // 0禁用1启用(是否审核)
+}
+
+// EditReq 用于存储修改广告位请求参数
+type EditReq struct {
+	PlugLinkID int64 `p:"plugLinkID" v:"required|min:1#广告id不能为空|广告id参数错误"`
+	AddReq
+}
+
+// SelectPageReq 用于存储分页查询广告的请求参数
+type SelectPageReq struct {
+	LinkName string `p:"linkName"` // 广告名称
+	PageNo   int64  `p:"pageNum"`  // 当前页
+	PageSize int64  `p:"pageSize"` // 每页显示记录数
+}
+
+// 用于存储分页查询的数据
+type ListEntity struct {
+	Entity
+	LinkTypeName string `orm:"linktype_name"      json:"linktype_name" ` // 友情链接所属分类
+}
+
+// GetPlugLinkByID 根据ID查询链接记录
+func GetPlugLinkByID(id int64) (*Entity, error) {
+	entity, err := Model.FindOne("link_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
+}
+
+// AddSave 添加友情链接
+func AddSave(req *AddReq) error {
+	var entity Entity
+	entity.LinkName = req.LinkName
+	entity.LinkUrl = req.LinkUrl
+	entity.LinkTarget = req.LinkTarget
+	entity.LinkTypeid = req.LinkTypeID
+	entity.LinkQq = req.LinkQQ
+	entity.LinkAddtime = int(gtime.Timestamp()) // 添加时间
+	entity.LinkOrder = req.LinkOrder
+	entity.LinkOpen = req.LinkOpen
+	// 保存实体
+	_, err := entity.Insert()
+	if err != nil {
+		g.Log().Error(err)
+		return gerror.New("保存失败")
+	}
+	return nil
+}
+
+// 根据ID批量删除链接
+func DeleteByIDs(ids []int) error {
+	_, err := Model.Delete("link_id in(?)", ids)
+	if err != nil {
+		g.Log().Error(err)
+		return gerror.New("删除链接失败")
+	}
+	return nil
+}
+
+// 根据ID来修改链接信息
+func EditSave(editReq *EditReq) error {
+	// 先根据ID来查询要修改的链接记录
+	entity, err := GetPlugLinkByID(editReq.PlugLinkID)
+	if err != nil {
+		return err
+	}
+	// 修改实体
+	entity.LinkName = editReq.LinkName
+	entity.LinkUrl = editReq.LinkUrl
+	entity.LinkTarget = editReq.LinkTarget
+	entity.LinkTypeid = editReq.LinkTypeID
+	entity.LinkQq = editReq.LinkQQ
+	entity.LinkOrder = editReq.LinkOrder
+	entity.LinkOpen = editReq.LinkOpen
+	_, err = entity.Update()
+	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 + " link")
+	if req != nil {
+		if req.LinkName != "" {
+			model.Where("link.link_name like ?", "%"+req.LinkName+"%")
+		}
+	}
+	model = model.LeftJoin(plug_linktype.Table+" type", "type.linktype_id=link.link_typeid")
+	// 查询友情链接总记录数(总行数)
+	total, err = model.Count()
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("获取总记录数失败")
+		return 0, 0, nil, err
+	}
+	if req.PageNo == 0 {
+		req.PageNo = 1
+	}
+	page = req.PageNo
+	if req.PageSize == 0 {
+		req.PageSize = 10
+	}
+	// 分页排序查询
+	var res gdb.Result
+	res, err = model.Fields("link.*,type.linktype_name").Page(int(page), int(req.PageSize)).Order("link.link_order asc,link.link_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
+}

+ 65 - 0
app/model/admin/plug_link/plug_link_entity.go

@@ -0,0 +1,65 @@
+// ==========================================================================
+// This is auto-generated by gf cli tool. You may not really want to edit it.
+// ==========================================================================
+
+package plug_link
+
+import (
+	"database/sql"
+	"github.com/gogf/gf/database/gdb"
+)
+
+// Entity is the golang structure for table plug_link.
+type Entity struct {
+	LinkId      int    `orm:"link_id,primary" json:"link_id"`      //
+	LinkName    string `orm:"link_name"       json:"link_name"`    // 链接名称
+	LinkUrl     string `orm:"link_url"        json:"link_url"`     // 链接URL
+	LinkTarget  string `orm:"link_target"     json:"link_target"`  // 打开方式
+	LinkTypeid  int    `orm:"link_typeid"     json:"link_typeid"`  // 所属栏目ID
+	LinkQq      string `orm:"link_qq"         json:"link_qq"`      // 联系QQ
+	LinkOrder   int64  `orm:"link_order"      json:"link_order"`   // 排序
+	LinkAddtime int    `orm:"link_addtime"    json:"link_addtime"` // 添加时间
+	LinkOpen    int    `orm:"link_open"       json:"link_open"`    // 0禁用1启用
+}
+
+// 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()
+}

+ 359 - 0
app/model/admin/plug_link/plug_link_model.go

@@ -0,0 +1,359 @@
+// ==========================================================================
+// This is auto-generated by gf cli tool. You may not really want to edit it.
+// ==========================================================================
+
+package plug_link
+
+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 plug_link operations.
+type arModel struct {
+	gmvc.M
+}
+
+var (
+	// Table is the table name of plug_link.
+	Table = "plug_link"
+	// Model is the model object of plug_link.
+	Model = &arModel{g.DB("default").Table(Table).Safe()}
+	// Columns defines and stores column names for table plug_link.
+	Columns = struct {
+		LinkId      string //
+		LinkName    string // 链接名称
+		LinkUrl     string // 链接URL
+		LinkTarget  string // 打开方式
+		LinkTypeid  string // 所属栏目ID
+		LinkQq      string // 联系QQ
+		LinkOrder   string // 排序
+		LinkAddtime string // 添加时间
+		LinkOpen    string // 0禁用1启用
+	}{
+		LinkId:      "link_id",
+		LinkName:    "link_name",
+		LinkUrl:     "link_url",
+		LinkTarget:  "link_target",
+		LinkTypeid:  "link_typeid",
+		LinkQq:      "link_qq",
+		LinkOrder:   "link_order",
+		LinkAddtime: "link_addtime",
+		LinkOpen:    "link_open",
+	}
+)
+
+// 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 <table> 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 <table> 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 <table> 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 <where> 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 <limit> 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 <page> 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 <duration> < 0, which means it clear the cache with given <name>.
+// If the parameter <duration> = 0, which means it never expires.
+// If the parameter <duration> > 0, which means it expires after <duration>.
+//
+// The optional parameter <name> is used to bind a name to the cache, which means you can later
+// control the cache like changing the <duration> or clearing the cache with specified <name>.
+//
+// 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 <data> 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 <where> 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 <where> 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()}
+}

+ 142 - 0
app/model/admin/plug_linktype/plug_linktype.go

@@ -0,0 +1,142 @@
+// ============================================================================
+// This is auto-generated by gf cli tool only once. Fill this file as you wish.
+// ============================================================================
+
+package plug_linktype
+
+import (
+	"github.com/gogf/gf/errors/gerror"
+	"github.com/gogf/gf/frame/g"
+)
+
+// AddReq 用于获取添加栏目的请求参数
+type AddReq struct {
+	LinktypeName  string `p:"linkTypeName" v:"required#栏目名称不能为空"`  // 栏目名称
+	LinktypeOrder int    `p:"linkTypeOrder" v:"required#栏目排序不能为空"` // 栏目排序
+}
+
+// EditReq 用于存储修改栏目的请求参数
+type EditReq struct {
+	LinktypeId int64 `p:"linkTypeID" v:"required|min:1#主键ID不能为空|主键ID值错误"`
+	AddReq
+}
+
+// SelectPageReq 用于存储分页查询栏目的请求参数
+type SelectPageReq struct {
+	LinktypeName string `p:"linkTypeName"` // 栏目名称,用于根据栏目名来模糊查询
+	PageNo       int64  `p:"pageNum"`      // 当前页
+	PageSize     int64  `p:"pageSize"`     // 每页显示记录数
+}
+
+// GetLinkTypeByID 根据ID查询栏目记录
+func GetLinkTypeByID(id int64) (*Entity, error) {
+	entity, err := Model.FindOne(id)
+	if err != nil {
+		g.Log().Error(err)
+		return nil, gerror.New("根据ID查询栏目记录出错")
+	}
+	if entity == nil {
+		return nil, gerror.New("根据ID未能查询到栏目记录")
+	}
+	return entity, nil
+}
+
+// CheakLinkTypeNameUnique 用于检测栏目名称唯一性
+func CheakLinkTypeNameUnique(linkTypeName string, linkTypeID int64) error {
+	var (
+		entity *Entity // 用于存储查询出的记录
+		err    error
+	)
+	// 此函数用于增加栏目时没有ID传入
+	if linkTypeID == 0 {
+		entity, err = Model.FindOne(Columns.LinktypeName, linkTypeName)
+	} else {
+		// 用于修改时有ID传入
+		entity, err = Model.Where(Columns.LinktypeName, linkTypeName).And(Columns.LinktypeId+"!=?", linkTypeID).FindOne()
+	}
+	if err != nil {
+		g.Log().Error(err)
+		return gerror.New("校验栏目名称唯一性出错")
+	}
+	if entity != nil {
+		return gerror.New("栏目名称已存在")
+	}
+	return nil
+}
+
+// AddSave 用于添加栏目
+func AddSave(req *AddReq) error {
+	// 定义一个实体用于接收req中的信息并将信息添加到数据库
+	var entity Entity
+	entity.LinktypeName = req.LinktypeName
+	entity.LinktypeOrder = uint(req.LinktypeOrder)
+	// 调用实体中的Insert方法将信息添加到数据库中
+	_, err := entity.Insert()
+	if err != nil {
+		// 错误放入错误日志
+		g.Log().Error(err)
+		// New创建并返回从给定文本格式化的错误
+		return gerror.New("信息插入数据库失败")
+	}
+	return nil
+}
+
+// DeleteLinkTypeByID 根据ID批量删除栏目
+func DeleteLinkTypeByID(id []int) error {
+	_, err := Model.Where("linktype_id in(?)", id).Delete()
+	if err != nil {
+		g.Log().Error(err)
+		return gerror.New("批量删除栏目出错")
+	}
+	return nil
+}
+
+// EditSave 修改
+func EditSave(req *EditReq) error {
+	// 先根据ID来查询要修改的广告位记录
+	entity, err := GetLinkTypeByID(req.LinktypeId)
+	if err != nil {
+		return err
+	}
+	// 修改实体
+	entity.LinktypeName = req.LinktypeName
+	entity.LinktypeOrder = uint(req.LinktypeOrder)
+	_, err = entity.Update()
+	if err != nil {
+		g.Log().Error(err)
+		return gerror.New("修改栏目失败")
+	}
+	return nil
+}
+
+// 分页查询,返回值total总记录数,page当前页
+func SelectListByPage(req *SelectPageReq) (total int, page int64, list []*Entity, err error) {
+	model := Model
+	if req != nil {
+		if req.LinktypeName != "" {
+			model = model.Where("linktype_name like ?", "%"+req.LinktypeName+"%")
+		}
+	}
+	// 查询栏目总记录数(总行数)
+	total, err = model.Count()
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("获取总记录数失败")
+		return 0, 0, nil, err
+	}
+	if req.PageNo == 0 {
+		req.PageNo = 1
+	}
+	page = req.PageNo
+	if req.PageSize == 0 {
+		req.PageSize = 10
+	}
+	// 分页排序查询
+	list, err = model.Page(int(page), int(req.PageSize)).Order("linktype_order asc,linktype_id asc").All()
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("分页查询栏目失败")
+		return 0, 0, nil, err
+	}
+	return total, page, list, nil
+}

+ 59 - 0
app/model/admin/plug_linktype/plug_linktype_entity.go

@@ -0,0 +1,59 @@
+// ==========================================================================
+// This is auto-generated by gf cli tool. You may not really want to edit it.
+// ==========================================================================
+
+package plug_linktype
+
+import (
+	"database/sql"
+	"github.com/gogf/gf/database/gdb"
+)
+
+// Entity is the golang structure for table plug_linktype.
+type Entity struct {
+	LinktypeId    int    `orm:"linktype_id,primary" json:"linktype_id"`    //
+	LinktypeName  string `orm:"linktype_name"       json:"linktype_name"`  // 所属栏目名称
+	LinktypeOrder uint   `orm:"linktype_order"      json:"linktype_order"` // 排序
+}
+
+// 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()
+}

+ 347 - 0
app/model/admin/plug_linktype/plug_linktype_model.go

@@ -0,0 +1,347 @@
+// ==========================================================================
+// This is auto-generated by gf cli tool. You may not really want to edit it.
+// ==========================================================================
+
+package plug_linktype
+
+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 plug_linktype operations.
+type arModel struct {
+	gmvc.M
+}
+
+var (
+	// Table is the table name of plug_linktype.
+	Table = "plug_linktype"
+	// Model is the model object of plug_linktype.
+	Model = &arModel{g.DB("default").Table(Table).Safe()}
+	// Columns defines and stores column names for table plug_linktype.
+	Columns = struct {
+		LinktypeId    string //
+		LinktypeName  string // 所属栏目名称
+		LinktypeOrder string // 排序
+	}{
+		LinktypeId:    "linktype_id",
+		LinktypeName:  "linktype_name",
+		LinktypeOrder: "linktype_order",
+	}
+)
+
+// 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 <table> 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 <table> 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 <table> 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 <where> 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 <limit> 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 <page> 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 <duration> < 0, which means it clear the cache with given <name>.
+// If the parameter <duration> = 0, which means it never expires.
+// If the parameter <duration> > 0, which means it expires after <duration>.
+//
+// The optional parameter <name> is used to bind a name to the cache, which means you can later
+// control the cache like changing the <duration> or clearing the cache with specified <name>.
+//
+// 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 <data> 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 <where> 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 <where> 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()}
+}

+ 151 - 0
app/service/admin/cms_service/menu.go

@@ -0,0 +1,151 @@
+package cms_service
+
+import (
+	"gfast/app/model/admin/cms_category"
+	"gfast/app/model/admin/model_fields"
+	"gfast/app/service/common/com_model_service"
+	"gfast/library/utils"
+	"github.com/gogf/gf/container/garray"
+	"github.com/gogf/gf/errors/gerror"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/net/ghttp"
+	"github.com/gogf/gf/util/gconv"
+)
+
+//获取频道列表
+func GetMenuListChannel() (list []*cms_category.Entity, err error) {
+	//获取频道列表
+	listAll, err := GetMenuList()
+	if err != nil {
+		return
+	}
+	list = make([]*cms_category.Entity, 0, len(listAll))
+	for _, v := range listAll {
+		if v.Status == 1 && v.CateType == cms_category.ChannelCateType {
+			list = append(list, v)
+		}
+	}
+	return
+}
+
+//获取可发布文章栏目
+func GetPublishableMenuList(catId ...int) (list []*cms_category.Entity, err error) {
+	menuList, err := GetMenuList()
+	var catIdMap map[int]int
+	if len(catId) > 0 {
+		catIdMap = make(map[int]int, len(catId))
+		for _, v := range catId {
+			catIdMap[v] = v
+		}
+	}
+	if err != nil {
+		return
+	}
+	for _, menu := range menuList {
+		if menu.CateType == cms_category.JumpCateType || menu.CateType == cms_category.SingleCateType {
+			continue
+		}
+		if menu.Status == 1 {
+			list = append(list, menu)
+		}
+	}
+	return
+}
+
+//获取所有菜单列表
+func GetMenuList() (list []*cms_category.Entity, err error) {
+	return cms_category.GetList()
+}
+
+//保存栏目操作
+func AddSave(req *cms_category.ReqAdd) (id int64, err error) {
+	return cms_category.AddSave(req)
+}
+
+//修改栏目操作
+func EditSave(req *cms_category.ReqEdit) (id int64, err error) {
+	return cms_category.EditSave(req)
+}
+
+//获取搜索栏目结果
+func GetMenuListSearch(req *cms_category.ReqSearchList) (menus []*cms_category.Entity, err error) {
+	return cms_category.GetListSearch(req)
+}
+
+//根据栏目ID获取栏目信息
+func GetMenuInfoById(id int) (menu *cms_category.Entity, err error) {
+	return cms_category.GetInfoById(id)
+}
+
+//通过栏目ID获取子级栏目ID
+func GetChildrenIds(ids []int) ([]int, error) {
+	//获取所有栏目
+	menus, err := GetMenuList()
+	if err != nil {
+		return nil, err
+	}
+	menuList := make(g.List, len(menus))
+	for k, menu := range menus {
+		menuList[k] = gconv.Map(menu)
+	}
+	for _, id := range ids {
+		children := utils.FindSonByParentId(menuList, id, "parent_id", "id")
+		for _, cid := range children {
+			ids = append(ids, gconv.Int(cid["id"]))
+		}
+	}
+	return ids, nil
+}
+
+//删除栏目
+func DeleteMenuByIds(ids []int) (err error) {
+	ids, err = GetChildrenIds(ids)
+	if err != nil {
+		return
+	}
+	return cms_category.DeleteByIds(ids)
+}
+
+//获取栏目对应的模型字段规则
+func GetModelFieldsByCateIds(r *ghttp.Request, ids []int, newsId int64) (rules []*model_fields.FieldRule, err error) {
+	var modelId uint
+	modelId, err = GetModelIdByCateIds(ids)
+	if err != nil {
+		return
+	}
+	//获取模型字段信息
+	rules, err = com_model_service.GetModelRuleByModelId(r, gconv.Int64(modelId), newsId)
+	if err != nil {
+		return
+	}
+	return
+}
+
+//通过栏目ids获取模型id
+func GetModelIdByCateIds(ids []int) (modelId uint, err error) {
+	ids, err = GetChildrenIds(ids)
+	if err != nil {
+		return
+	}
+	idsArr := garray.NewIntArrayFrom(ids)
+	menus, err := GetMenuList()
+	if err != nil {
+		return
+	}
+	for _, menu := range menus {
+		if idsArr.Contains(gconv.Int(menu.Id)) {
+			if modelId == 0 {
+				modelId = menu.ModelId
+			} else if modelId != menu.ModelId {
+				err = gerror.New("所选多个栏目必须为同一模型。")
+				return
+			}
+		}
+	}
+	//不存在管理的模型
+	if modelId == 0 {
+		err = gerror.New("未获取到对应模型信息")
+		return
+	}
+	return
+}

+ 224 - 0
app/service/admin/cms_service/news.go

@@ -0,0 +1,224 @@
+package cms_service
+
+import (
+	"gfast/app/model/admin/cms_category"
+	"gfast/app/model/admin/cms_category_news"
+	"gfast/app/model/admin/cms_news"
+	"gfast/app/service/common/com_model_service"
+	"gfast/library/utils"
+	"github.com/gogf/gf/container/gvar"
+	"github.com/gogf/gf/database/gdb"
+	"github.com/gogf/gf/errors/gerror"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/util/gconv"
+)
+
+//添加文章操作
+func AddNews(req *cms_news.ReqAddParams, cateIds []int, userId int) (insId int64, err error) {
+	cateIds, err = getPubCateIds(cateIds)
+	if err != nil {
+		return
+	}
+	tx, err := g.DB().Begin()
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("添加事务开启失败")
+		return
+	}
+	insId, err = cms_news.AddNews(req, cateIds, userId, tx)
+	if err != nil {
+		tx.Rollback()
+		return
+	}
+	//保存文章模型字段数据
+	err = handlePostData(cateIds, insId, req.ModelForm, tx, false)
+	if err != nil {
+		tx.Rollback()
+		return
+	}
+	tx.Commit()
+	return
+}
+
+//修改文章操作
+func EditNews(req *cms_news.ReqEditParams, cateIds []int) (err error) {
+	cateIds, err = getPubCateIds(cateIds)
+	if err != nil {
+		return
+	}
+	tx, err := g.DB().Begin()
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("开启事务失败")
+		return
+	}
+	err = cms_news.EditNews(req, cateIds, tx)
+	if err != nil {
+		tx.Rollback()
+		return
+	}
+	//保存模型字段数据
+	err = handlePostData(cateIds, gconv.Int64(req.Id), req.ModelForm, tx, true)
+	if err != nil {
+		tx.Rollback()
+		return
+	}
+	tx.Commit()
+	return
+}
+
+func getPubCateIds(cateIds []int) ([]int, error) {
+	//获取所有栏目
+	menuList, err := GetMenuList()
+	if err != nil {
+		return nil, err
+	}
+	pubCateIds := make([]int, 0, len(menuList))
+	if len(cateIds) > 0 {
+		//查询可发布栏目id
+		menuListSlice := gconv.SliceMap(menuList)
+		for _, cid := range cateIds {
+			for _, entity := range menuList {
+				if gconv.Int(entity.Id) == cid && entity.CateType == cms_category.PublishCateType {
+					pubCateIds = append(pubCateIds, cid)
+				}
+			}
+			mList := make([]*cms_category.Entity, 0)
+			sonList := utils.FindSonByParentId(menuListSlice, cid, "parent_id", "id")
+			gconv.Structs(sonList, &mList)
+			for _, v := range mList {
+				if v.CateType == cms_category.PublishCateType {
+					pubCateIds = append(pubCateIds, gconv.Int(v.Id))
+				}
+			}
+		}
+		if len(pubCateIds) == 0 {
+			return nil, gerror.New("所选栏目不存在可发布文章的栏目")
+		}
+		return pubCateIds, nil
+	} else {
+		return nil, gerror.New("所属栏目不能为空")
+	}
+}
+
+//文章列表查询
+func NewsListByPage(req *cms_news.ReqListSearchParams) (total, page int, list gdb.Result, err error) {
+	var menuList []*cms_category.Entity
+	//获取所有栏目
+	menuList, err = GetMenuList()
+	if err != nil {
+		return
+	}
+	if len(req.CateId) > 0 {
+		//查询可发布栏目id
+		menuListSlice := gconv.SliceMap(menuList)
+		cateIds := req.CateId
+		for _, cid := range cateIds {
+			mList := make([]*cms_category.Entity, 0)
+			sonList := utils.FindSonByParentId(menuListSlice, cid, "parent_id", "id")
+			gconv.Structs(sonList, &mList)
+			for _, v := range mList {
+				if v.CateType == cms_category.PublishCateType {
+					req.CateId = append(req.CateId, gconv.Int(v.Id))
+				}
+			}
+		}
+	}
+	total, page, list, err = cms_news.ListByPage(req)
+	if err != nil || len(list) == 0 {
+		return
+	}
+	//匹配文章所属栏目
+	var cateIds []int
+	for _, v := range list {
+		cateIds, err = GetCheckedCategoryIdByNewsId(gconv.Uint64(v["id"]))
+		if err != nil {
+			return
+		}
+		cateNameList := make(map[int]string)
+		for _, menu := range menuList {
+			for _, cateId := range cateIds {
+				if menu.Id == gconv.Uint64(cateId) {
+					cateNameList[cateId] = menu.Name
+				}
+			}
+		}
+		cateVal := new(gvar.Var)
+		if len(cateNameList) > 0 {
+			cateVal.Set(cateNameList)
+		}
+		v["cateList"] = cateVal
+	}
+	return
+}
+
+//通过id获取文章信息
+func GetNewsById(id int) (news *cms_news.Entity, err error) {
+	return cms_news.GetById(id)
+}
+
+//通过文章id获取关联的栏目id
+func GetCheckedCategoryIdByNewsId(newsId uint64) (catIds []int, err error) {
+	categories, err := GetCategoriesByNewsId(newsId)
+	if err != nil {
+		return
+	}
+	catIds = make([]int, len(categories))
+	for k, v := range categories {
+		catIds[k] = gconv.Int(v.CategoryId)
+	}
+	return
+}
+
+//通过文章id获取关联栏目信息
+func GetCategoriesByNewsId(newsId uint64) (categories []*cms_category_news.Entity, err error) {
+	return cms_category_news.GetCategoriesByNewsId(newsId)
+}
+
+//删除文章数据
+func DeleteCmsByIds(ids []int) (err error) {
+	var tx *gdb.TX
+	tx, err = g.DB().Begin()
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("开启事务失败")
+		return
+	}
+	//获取对应文章模型ID
+	for _, id := range ids {
+		var cateNews []*cms_category_news.Entity
+		cateNews, err = GetCategoriesByNewsId(gconv.Uint64(id))
+		if err != nil {
+			return
+		}
+		for _, cn := range cateNews {
+			var cateInfo *cms_category.Entity
+			cateInfo, err = GetMenuInfoById(gconv.Int(cn.CategoryId))
+			if err != nil {
+				return
+			}
+			//删除模型字段数据
+			err = com_model_service.DeleteModelFieldData(gconv.Int64(cateInfo.ModelId), gconv.Int64(id), tx)
+			if err != nil {
+				tx.Rollback()
+				return
+			}
+		}
+	}
+	err = cms_news.DeleteByIds(ids, tx)
+	if err != nil {
+		tx.Rollback()
+	}
+	tx.Commit()
+	return
+}
+
+//处理模型数据
+func handlePostData(cateIds []int, insId int64, modelForm g.Map, tx *gdb.TX, isUpdate bool) error {
+	modelId, err := GetModelIdByCateIds(cateIds)
+	if err != nil {
+		return err
+	}
+	//保存模型数据
+	return com_model_service.HandlePostData(gconv.Int64(modelId), modelForm, insId, tx, isUpdate)
+}

+ 44 - 0
app/service/admin/model_service/category.go

@@ -0,0 +1,44 @@
+package model_service
+
+import (
+	"gfast/app/model/admin/model_category"
+	"gfast/app/service/cache_service"
+)
+
+func GetCategoryList(req *model_category.SearchReq) (total int, list []*model_category.Entity, err error) {
+	return model_category.GetList(req)
+}
+
+func AddCategory(req *model_category.AddReq) error {
+	return model_category.Add(req)
+}
+
+func EditCategory(req *model_category.EditReq) error {
+	return model_category.Edit(req)
+}
+
+func GetCategoryById(id int64) (entity *model_category.Entity, err error) {
+	return model_category.GetById(id)
+}
+
+func DeleteCategoryByIds(ids []int) error {
+	return model_category.DeleteByIds(ids)
+}
+
+//获取所有状态正常的模型分类
+func GetCategoryAll() (entity []*model_category.Entity, err error) {
+	cache := cache_service.New()
+	ch := cache.Get("model_category_all")
+	if ch != nil {
+		entity = ch.([]*model_category.Entity)
+		return
+	}
+	entity, err = model_category.GetCategoryAll()
+	if err != nil {
+		return
+	}
+	if entity != nil {
+		cache.Set("model_category_all", entity, 0, cache_service.AdminModelTag)
+	}
+	return
+}

+ 165 - 0
app/service/admin/model_service/field.go

@@ -0,0 +1,165 @@
+package model_service
+
+import (
+	"fmt"
+	"gfast/app/model/admin/model_fields"
+	"gfast/app/model/admin/model_info"
+	"github.com/gogf/gf/errors/gerror"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/text/gstr"
+	"github.com/gogf/gf/util/gconv"
+)
+
+// 添加
+func AddFieldsSave(req *model_fields.AddReq) error {
+	return model_fields.AddSave(req)
+}
+
+// 删除
+func DeleteFieldsByIds(Ids []int) error {
+	return model_fields.DeleteByIds(Ids)
+}
+
+//修改
+func EditFieldsSave(editReq *model_fields.EditReq) error {
+	return model_fields.EditSave(editReq)
+}
+
+// 根据ID查询
+func GetFieldsByID(id int64) (*model_fields.Entity, error) {
+	return model_fields.GetByID(id)
+}
+
+// 分页查询
+func SelectFieldsAll(req *model_fields.SelectPageReq) (list []*model_fields.FieldInfo, err error) {
+	var fieldEntities []*model_fields.Entity
+	fieldEntities, err = model_fields.SelectListAll(req)
+	if err != nil {
+		return
+	}
+	//获取模型信息
+	var modelInfo *model_info.Entity
+	modelInfo, err = model_info.GetByID(req.ModelId)
+	if err != nil {
+		return
+	}
+	if modelInfo == nil || fieldEntities == nil {
+		return
+	}
+	list = make([]*model_fields.FieldInfo, len(fieldEntities))
+	for key, field := range fieldEntities {
+		fInfo := new(model_fields.FieldInfo)
+		fInfo.ModelId = field.ModelId
+		fInfo.FieldId = field.FieldId
+		fInfo.FieldName = field.FieldName
+		fInfo.FieldTitle = field.FieldTitle
+		fInfo.FieldType = field.FieldType
+		fInfo.FieldRules = field.FieldRules
+		if gstr.ContainsI(modelInfo.SearchList, field.FieldName) {
+			//列表查询
+			fInfo.SearchList = "1"
+		}
+		if gstr.ContainsI(modelInfo.ModelSort, field.FieldName) {
+			//列表排序
+			fInfo.ModelSort = "1"
+		}
+		if gstr.ContainsI(modelInfo.ModelList, field.FieldName) {
+			//列表显示
+			fInfo.ModelList = "1"
+		}
+		if gstr.ContainsI(modelInfo.ModelEdit, field.FieldName) {
+			//可编辑
+			fInfo.ModelEdit = "1"
+		}
+		if gstr.ContainsI(modelInfo.ModelIndexes, field.FieldName) {
+			//索引字段
+			fInfo.ModelIndexes = "1"
+		}
+		if modelInfo.ModelPk == field.FieldName {
+			//主键
+			fInfo.ModelPk = "1"
+		}
+		list[key] = fInfo
+	}
+	return
+}
+
+//设置字段属性
+func SetFieldsAttr(req *model_fields.SetFieldsAttrReq) error {
+	infoReq := new(model_info.FieldsAttrReq)
+	infoReq.ModelId = req.ModelId
+	tx, err := g.DB().Begin()
+	if err != nil {
+		g.Log().Error(err)
+		return gerror.New("开启事务处理失败")
+	}
+
+	for key, field := range req.FieldsList {
+		field.FieldSort = gconv.Int64(key)
+		if field.FieldId == req.PkId {
+			infoReq.ModelPk = field.FieldName
+		}
+		if field.ModelEdit == "1" {
+			//可编辑字段
+			infoReq.ModelEdit += fmt.Sprintf(",%s", field.FieldName)
+		}
+		if field.ModelIndexes == "1" {
+			//索引字段
+			infoReq.ModelIndexes += fmt.Sprintf(",%s", field.FieldName)
+		}
+		if field.ModelList == "1" {
+			//列表显示字段
+			infoReq.ModelList += fmt.Sprintf(",%s", field.FieldName)
+		}
+		if field.ModelSort == "1" {
+			//列表排序字段
+			infoReq.ModelSort += fmt.Sprintf(",%s", field.FieldName)
+		}
+		if field.SearchList == "1" {
+			//列表查询字段
+			infoReq.SearchList += fmt.Sprintf(",%s", field.FieldName)
+		}
+
+		//修改字段排序
+		entity := new(model_fields.Entity)
+		err = tx.Table(model_fields.Table).Struct(entity, g.Map{"field_id": field.FieldId})
+		if err != nil || entity == nil {
+			g.Log().Error(err)
+			tx.Rollback()
+			return gerror.New("设置字段排序失败")
+		}
+		entity.FieldSort = field.FieldSort
+		_, err = entity.Save()
+		if err != nil {
+			g.Log().Error(err)
+			tx.Rollback()
+			return gerror.New("保存字段排序失败")
+		}
+	}
+	infoReq.ModelEdit = gstr.TrimLeftStr(infoReq.ModelEdit, ",")
+	infoReq.ModelIndexes = gstr.TrimLeftStr(infoReq.ModelIndexes, ",")
+	infoReq.ModelList = gstr.TrimLeftStr(infoReq.ModelList, ",")
+	infoReq.ModelSort = gstr.TrimLeftStr(infoReq.ModelSort, ",")
+	infoReq.SearchList = gstr.TrimLeftStr(infoReq.SearchList, ",")
+	modelInfo := new(model_info.Entity)
+	err = tx.Table(model_info.Table).Struct(modelInfo, g.Map{"model_id": req.ModelId})
+	if err != nil || modelInfo == nil {
+		g.Log().Error(err)
+		tx.Rollback()
+		return gerror.New("获取模型信息失败")
+	}
+	modelInfo.ModelPk = infoReq.ModelPk
+	modelInfo.ModelEdit = infoReq.ModelEdit
+	modelInfo.ModelIndexes = infoReq.ModelIndexes
+	modelInfo.ModelList = infoReq.ModelList
+	modelInfo.ModelSort = infoReq.ModelSort
+	modelInfo.SearchList = infoReq.SearchList
+	_, err = modelInfo.Save()
+	if err != nil {
+		g.Log().Error(err)
+		tx.Rollback()
+		return gerror.New("设置模型字段属性失败")
+	}
+	tx.Commit()
+	return nil
+}

+ 373 - 0
app/service/admin/model_service/info.go

@@ -0,0 +1,373 @@
+package model_service
+
+import (
+	"fmt"
+	"gfast/app/model/admin/model_fields"
+	"gfast/app/model/admin/model_info"
+	"github.com/gogf/gf/container/garray"
+	"github.com/gogf/gf/errors/gerror"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/os/gfile"
+	"github.com/gogf/gf/os/gtime"
+	"github.com/gogf/gf/text/gstr"
+	"github.com/gogf/gf/util/gconv"
+	"os"
+	"path/filepath"
+	"strconv"
+)
+
+// 添加
+func AddSave(req *model_info.AddReq) error {
+	return model_info.AddSave(req)
+}
+
+// 删除
+func DeleteByIds(Ids []int) error {
+	tx, err := g.DB().Begin()
+	if err != nil {
+		g.Log().Error(err)
+		return gerror.New("事务开启失败")
+	}
+	//删除模型信息
+	err = model_info.DeleteByIds(Ids, tx)
+	if err != nil {
+		tx.Rollback()
+		return err
+	}
+	//删除模型字段信息
+	err = model_fields.DeleteByModelIds(Ids, tx)
+	if err != nil {
+		tx.Rollback()
+		return err
+	}
+	tx.Commit()
+	return nil
+}
+
+//修改
+func EditSave(editReq *model_info.EditReq) error {
+	return model_info.EditSave(editReq)
+}
+
+// 根据ID查询
+func GetByID(id int64) (*model_info.Entity, error) {
+	return model_info.GetByID(id)
+}
+
+// 分页查询
+func SelectListByPage(req *model_info.SelectPageReq) (total int, page int, list []*model_info.ListEntity, err error) {
+	return model_info.SelectListByPage(req)
+}
+
+func SetInfoStatus(req *model_info.StatusSetReq) error {
+	return model_info.SetStatus(req)
+}
+
+//模型生成操作
+func CreateModel(modelId int64) error {
+	//表前缀
+	dbPrefix := g.DB().GetPrefix()
+	//模型信息
+	modelInfo, err := model_info.GetByID(modelId)
+	if err != nil {
+		return err
+	}
+	//字段信息
+	args := &model_fields.SelectPageReq{
+		ModelId: modelId,
+	}
+	modelFields, err := model_fields.SelectListAll(args)
+	if err != nil {
+		return err
+	}
+	if modelFields == nil {
+		return gerror.New("请设置模型字段")
+	}
+	if modelInfo.ModelPk == "" {
+		return gerror.New("请设置主键")
+	}
+	if modelInfo.ModelEngine == "" {
+		modelInfo.ModelEngine = "MyISAM"
+	}
+	//备份旧模型表数据
+	err = buildTableExists(dbPrefix, modelInfo.ModelName)
+	if err != nil {
+		return err
+	}
+	//重建表操作
+	//1、删除旧表
+	sql := fmt.Sprintf("DROP TABLE IF EXISTS `%s%s`;", dbPrefix, modelInfo.ModelName)
+	_, err = g.DB().Exec(sql)
+	if err != nil {
+		g.Log().Error(err)
+		return gerror.New("删除旧模型数据失败")
+	}
+
+	//重建表格
+	switch g.Cfg().GetString("database.type") {
+	case "mysql":
+		sql := "CREATE TABLE `" + dbPrefix + modelInfo.ModelName + "` (" +
+			"`" + modelInfo.ModelPk + "` INT UNSIGNED NOT NULL AUTO_INCREMENT," +
+			"%FIELDS_SQL%" +
+			"%PRIMARY_KEY_SQL%" +
+			"%UNIQUE_KEY_SQL%" +
+			"%KEY_SQL%" +
+			")ENGINE=" + modelInfo.ModelEngine + " AUTO_INCREMENT=1 DEFAULT  CHARACTER SET utf8mb4 COLLATE = utf8mb4_general_ci;"
+
+		SqlFields := garray.NewStrArray()
+		SqlPrimaryKey := "PRIMARY KEY (`" + modelInfo.ModelPk + "`)"
+		SqlUniqueKey := garray.NewStrArray()
+		SqlKey := garray.NewStrArray()
+		for _, fi := range modelFields {
+			if fi.FieldName == modelInfo.ModelPk {
+				continue
+			}
+			rules := garray.NewStrArrayFrom(gstr.Split(gstr.Replace(fi.FieldRules, " ", ""), ","))
+			switch fi.FieldType {
+			//百度地图字段,双精度型
+			case "baidu_map":
+				defal := gstr.Split(fi.FieldDefault, ",")
+				if len(defal) == 2 {
+					SqlFields.Append(fmt.Sprintf("`%s_lng` DOUBLE NOT NULL DEFAULT %s COMMENT %s", fi.FieldName, defal[0], fi.FieldTitle))
+					SqlFields.Append(fmt.Sprintf("`%s_lat` DOUBLE NOT NULL DEFAULT %s COMMENT %s", fi.FieldName, defal[1], fi.FieldTitle))
+				} else {
+					SqlFields.Append(fmt.Sprintf("`%s_lng` DOUBLE NOT NULL DEFAULT 0 COMMENT %s", fi.FieldName, fi.FieldTitle))
+					SqlFields.Append(fmt.Sprintf("`%s_lat` DOUBLE NOT NULL DEFAULT 0 COMMENT %s", fi.FieldName, fi.FieldTitle))
+				}
+			//变长或固定字符串型
+			case "text", "imagefile", "file", "selecttext", "checkbox":
+				if fi.FieldLength == "" {
+					fi.FieldLength = "200"
+				}
+				fType := "VARCHAR"
+				//固定长度
+				if rules.Contains("lengthfixed") {
+					fType = "CHAR"
+				}
+				fNull := ""
+				//非空
+				if rules.Contains("required") {
+					fNull = "NOT NULL"
+				}
+				SqlFields.Append(fmt.Sprintf("`%s` %s(%s) %s DEFAULT '%s' COMMENT '%s'", fi.FieldName, fType, fi.FieldLength, fNull, fi.FieldDefault, fi.FieldTitle))
+			//bigint型
+			case "currency", "large_number", "datetime", "date":
+				fUnsigned := ""
+				//非负数
+				if rules.Contains("unsigned") || fi.FieldType == "date" || fi.FieldType == "datetime" {
+					fUnsigned = "UNSIGNED"
+				}
+
+				fNull := "NOT NULL"
+				if fi.FieldDefault == "" {
+					fi.FieldDefault = "0"
+				}
+
+				SqlFields.Append(fmt.Sprintf("`%s` BIGINT %s %s DEFAULT %s COMMENT '%s' ", fi.FieldName, fUnsigned, fNull, fi.FieldDefault, fi.FieldTitle))
+			//整数型
+			case "number", "selectnumber":
+				fUnsigned := ""
+				//非负数
+				if rules.Contains("unsigned") {
+					fUnsigned = "UNSIGNED"
+				}
+
+				fNull := "NOT NULL"
+				if fi.FieldDefault == "" {
+					fi.FieldDefault = "0"
+				}
+
+				SqlFields.Append(fmt.Sprintf("`%s` INT %s %s DEFAULT %s COMMENT '%s' ", fi.FieldName, fUnsigned, fNull, fi.FieldDefault, fi.FieldTitle))
+			//text型
+			case "richtext", "bigtext", "images", "files":
+				SqlFields.Append(fmt.Sprintf("`%s` TEXT COMMENT '%s'", fi.FieldName, fi.FieldTitle))
+			//TINYINT型
+			case "switch":
+				if fi.FieldDefault == "" {
+					fi.FieldDefault = "0"
+				}
+				SqlFields.Append(fmt.Sprintf("`%s` TINYINT UNSIGNED NOT NULL DEFAULT %s COMMENT '%s'",
+					fi.FieldName, fi.FieldDefault, fi.FieldTitle))
+			default:
+				return gerror.New("不能识别字段类型")
+			}
+			typeUnique := garray.NewStrArrayFrom([]string{"switch", "text", "number", "datetime", "date", "selecttext", "selectnumber", "checkbox"})
+			if typeUnique.Contains(fi.FieldType) && rules.Contains("unique") {
+				SqlUniqueKey.Append(fmt.Sprintf("UNIQUE KEY %s (%s)", fi.FieldName, fi.FieldName))
+			}
+		}
+		//普通索引
+		if modelInfo.ModelIndexes != "" {
+			modelIndexes := gstr.Split(modelInfo.ModelIndexes, ",")
+			for _, ind := range modelIndexes {
+				SqlKey.Append(fmt.Sprintf("INDEX IX_%s (%s)", ind, ind))
+			}
+		}
+		//替换sql占位符
+		sqlFieldTag := ""
+		if SqlPrimaryKey != "" || SqlUniqueKey.Len() != 0 || SqlKey.Len() != 0 {
+			sqlFieldTag = ",\n"
+		}
+		primaryTag := ""
+		if SqlPrimaryKey != "" && (SqlUniqueKey.Len() != 0 || SqlKey.Len() != 0) {
+			primaryTag = ",\n"
+		}
+		uniqueTag := ""
+		if SqlUniqueKey.Len() != 0 && SqlKey.Len() != 0 {
+			uniqueTag = ",\n"
+		}
+		sql = gstr.ReplaceByArray(sql, []string{
+			"%FIELDS_SQL%",
+			SqlFields.Join(",") + sqlFieldTag,
+			"%PRIMARY_KEY_SQL%",
+			SqlPrimaryKey + primaryTag,
+			"%UNIQUE_KEY_SQL%",
+			SqlUniqueKey.Join(",") + uniqueTag,
+			"%KEY_SQL%",
+			SqlKey.Join(","),
+		})
+		_, err = g.DB().Exec(sql)
+		if err != nil {
+			g.Log().Error(err)
+			return gerror.New("建表发生错误")
+		}
+	}
+	return nil
+}
+
+//备份已存在的模型表
+func buildTableExists(dbPrefix, modelName string) error {
+	//数据库名称
+	dbName := g.Cfg().GetString("database.name")
+	res, err := g.DB().GetAll("SHOW TABLE STATUS FROM " + dbName)
+	if err != nil {
+		g.Log().Error(err)
+		return gerror.New("数据库信息获取失败")
+	}
+	tables := garray.New()
+	for _, tbInfo := range res {
+		tabName := gconv.String(tbInfo["Name"])
+		if dbPrefix != "" && gstr.PosI(tabName, dbPrefix) == 0 {
+			tables.Append(gstr.ToLower(gstr.SubStr(tabName, 0, len(dbPrefix))))
+		}
+		tables.Append(gstr.ToLower(tabName))
+	}
+	if tables.Contains(modelName) {
+		//表格若已经存在则备份
+		pathDir := g.Cfg().GetString("adminInfo.dataDir") + "/dataBak"
+		pathDir, err := filepath.Abs(pathDir)
+		if err != nil {
+			g.Log().Error(err)
+			return gerror.New("获取数据表备份路径失败")
+		}
+		//路径不存在则创建之
+		err = os.MkdirAll(pathDir, os.ModeDir)
+		if err != nil {
+			g.Log().Error(err)
+			return gerror.New("创建备份目录失败")
+		}
+		err = backupTable(pathDir, modelName, dbPrefix)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+//备份数据表操作
+func backupTable(pathDir, tableName, prefix string) error {
+	switch g.Cfg().GetString("database.type") {
+	case "mysql":
+		rec, err := g.DB().GetOne(fmt.Sprintf("SHOW CREATE TABLE %s%s ", prefix, tableName))
+		if err != nil {
+			g.Log().Error(err)
+			return gerror.New("获取备份表信息失败")
+		}
+		tableCreateSql := rec["Create Table"].String()
+		res, err := g.DB().GetAll(fmt.Sprintf("SELECT * FROM %s%s", prefix, tableName))
+		if err != nil {
+			g.Log().Error(err)
+			return gerror.New("获取备份表数据失败")
+		}
+		dataValues := garray.NewStrArray()
+		keyValues := garray.NewStrArray()
+		for _, v := range res {
+			var str string
+			vKey := garray.NewStrArray()
+			vValues := garray.NewStrArray()
+			for kk, vv := range v {
+				str = strconv.Quote(gconv.String(vv))
+				vValues.Append(str)
+				vKey.Append(kk)
+			}
+			dataValues.Append("(" + vValues.Join(",") + ")")
+			keyValues.Append("(" + vKey.Join(",") + ")")
+		}
+		insertDataSql := ""
+		keyValues.Iterator(func(kk int, kv string) bool {
+			vv, _ := dataValues.Get(kk)
+			insertDataSql += fmt.Sprintf("INSERT INTO `%s%s` %s VALUES %s;\n", prefix, tableName, kv, vv)
+			return true
+		})
+		tableCreateSql = tableCreateSql + ";\n" + insertDataSql + ";"
+		//保存到文件中
+		err = gfile.PutContents(fmt.Sprintf(pathDir+"/%s%s_%d.sql", prefix, tableName, gtime.Timestamp()), tableCreateSql)
+		if err != nil {
+			g.Log().Error(err)
+			return gerror.New("创建备份文件失败")
+		}
+	}
+	return nil
+}
+
+//复制模型
+func CopyModel(modelId int64) error {
+	//获取要复制的模型信息
+	modelInfo, err := GetByID(modelId)
+	if err != nil {
+		return err
+	}
+	req := &model_fields.SelectPageReq{ModelId: modelId}
+	modelFields, err := model_fields.SelectListAll(req)
+	if err != nil {
+		return err
+	}
+	tx, err := g.DB().Begin()
+	if err != nil {
+		g.Log().Error(err)
+		return gerror.New("开启事务失败")
+	}
+	modelInfo.ModelId = 0
+	modelInfo.ModelName = modelInfo.ModelName + "_copy"
+	res, err := tx.Model(model_info.Table).Insert(modelInfo)
+	if err != nil {
+		g.Log().Error(err)
+		tx.Rollback()
+		return gerror.New("复制模型信息失败")
+	}
+	insId, err := res.LastInsertId()
+	if err != nil {
+		g.Log().Error(err)
+		tx.Rollback()
+		return gerror.New("获取复制模型信Id失败")
+	}
+	newModelId := gconv.Uint(insId)
+	for _, field := range modelFields {
+		field.FieldId = 0
+		field.ModelId = newModelId
+	}
+	if modelFields != nil {
+		_, err = tx.BatchInsert(model_fields.Table, modelFields)
+		if err != nil {
+			g.Log().Error(err)
+			tx.Rollback()
+			return gerror.New("复制模型字段信息失败")
+		}
+	}
+	tx.Commit()
+	return nil
+}
+
+func GetModelsByCateIds(cateIds []int) (models []*model_info.Entity, err error) {
+	return model_info.GetModelsByCateIds(cateIds)
+}

+ 30 - 0
app/service/admin/plug_link_service/plugLink.go

@@ -0,0 +1,30 @@
+package plug_link_service
+
+import (
+	"gfast/app/model/admin/plug_link"
+)
+
+// 添加
+func AddSavePlugLink(req *plug_link.AddReq) error {
+	return plug_link.AddSave(req)
+}
+
+// 删除
+func DeleteByIDs(Ids []int) error {
+	return plug_link.DeleteByIDs(Ids)
+}
+
+//修改
+func EditPlugLinkSave(editReq *plug_link.EditReq) error {
+	return plug_link.EditSave(editReq)
+}
+
+// 根据ID查询
+func GetPlugLinkByID(id int64) (*plug_link.Entity, error) {
+	return plug_link.GetPlugLinkByID(id)
+}
+
+// 分页查询,返回值total总记录数,page当前页
+func SelectPlugLinkListByPage(req *plug_link.SelectPageReq) (total int, page int64, list []*plug_link.ListEntity, err error) {
+	return plug_link.SelectListByPage(req)
+}

+ 48 - 0
app/service/admin/plug_link_service/plugLinkType.go

@@ -0,0 +1,48 @@
+package plug_link_service
+
+import (
+	"gfast/app/model/admin/plug_linktype"
+)
+
+// 添加
+func AddSave(req *plug_linktype.AddReq) error {
+	// 验证栏目名称的唯一性
+	err := plug_linktype.CheakLinkTypeNameUnique(req.LinktypeName, 0)
+	if err != nil {
+		return err
+	}
+	err = plug_linktype.AddSave(req)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+// 删除
+func DeleteLinkTypeByID(id []int) error {
+	return plug_linktype.DeleteLinkTypeByID(id)
+}
+
+// 修改栏目
+func EditSave(editReq *plug_linktype.EditReq) error {
+	// 判断修改后的栏目名称的唯一性
+	err := plug_linktype.CheakLinkTypeNameUnique(editReq.LinktypeName, editReq.LinktypeId)
+	if err != nil {
+		return err
+	}
+	err = plug_linktype.EditSave(editReq)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+// 根据ID查询栏目
+func GetLinkTypeByID(id int64) (*plug_linktype.Entity, error) {
+	return plug_linktype.GetLinkTypeByID(id)
+}
+
+// 分页查询栏目
+func SelectListByPage(req *plug_linktype.SelectPageReq) (total int, page int64, list []*plug_linktype.Entity, err error) {
+	return plug_linktype.SelectListByPage(req)
+}

+ 47 - 0
app/service/admin/plug_service/plug.go

@@ -0,0 +1,47 @@
+package plug_service
+
+import "gfast/app/model/admin/plug_adtype"
+
+// 添加广告位
+func AddSave(req *plug_adtype.AddReq) (err error) {
+	// 判断广告位名称是否已存在
+	err = plug_adtype.CheakAdtypeNameUnique(req.AdtypeName, 0)
+	if err != nil {
+		return err
+	}
+	// 不存在则调用plug_adtype中的AddSave()函数添加广告位信息
+	err = plug_adtype.AddSave(req)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+// 删除广告位
+func DeleteAdTypeByID(id []int) error {
+	return plug_adtype.DeleteAdTypeByID(id)
+}
+
+// 修改广告位
+func EditSave(editReq *plug_adtype.EditReq) error {
+	// 判断修改后的广告位名称的唯一性
+	err := plug_adtype.CheakAdtypeNameUnique(editReq.AdtypeName, editReq.AdtypeID)
+	if err != nil {
+		return err
+	}
+	err = plug_adtype.EditSave(editReq)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+// 根据ID查询广告位
+func GetAdtypeByID(id int64) (*plug_adtype.Entity, error) {
+	return plug_adtype.GetAdtypeByID(id)
+}
+
+// 分页查询广告位
+func SelectListByPage(req *plug_adtype.SelectPageReq) (total int, page int64, list []*plug_adtype.Entity, err error) {
+	return plug_adtype.SelectListByPage(req)
+}

+ 30 - 0
app/service/admin/plug_service/plugAd.go

@@ -0,0 +1,30 @@
+package plug_service
+
+import (
+	"gfast/app/model/admin/plug_ad"
+)
+
+// 添加广告
+func AddSaveAd(req *plug_ad.AddReq) error {
+	return plug_ad.AddSave(req)
+}
+
+// 删除广告
+func DeleteByIDs(Ids []int) error {
+	return plug_ad.DeleteByIDs(Ids)
+}
+
+//修改广告
+func EditPlugAdSave(editReq *plug_ad.EditReq) error {
+	return plug_ad.EditSave(editReq)
+}
+
+// 根据ID查询广告
+func GetPlugAdByID(id int64) (*plug_ad.Entity, error) {
+	return plug_ad.GetPlugAdByID(id)
+}
+
+// 分页查询广告
+func SelectPlugAdListByPage(req *plug_ad.SelectPageReq) (total int, page int64, list []*plug_ad.ListEntity, err error) {
+	return plug_ad.SelectListByPage(req)
+}

+ 4 - 0
app/service/cache_service/cache_values.go

@@ -6,6 +6,7 @@ const (
 	AdminAuthRole
 	AdminCmsMenu
 	AdminConfigDict
+	GovProject
 )
 
 //缓存TAG标签
@@ -13,4 +14,7 @@ const (
 	AdminAuthTag = iota
 	AdminCmsTag
 	AdminSysConfigTag
+	AdminModelTag
+	AdminDeptUserTag
+	GovProjectCateTag
 )

+ 619 - 0
app/service/common/com_model_service/model.go

@@ -0,0 +1,619 @@
+package com_model_service
+
+import (
+	"encoding/json"
+	"gfast/app/model/admin/model_fields"
+	"gfast/app/model/admin/model_info"
+	"gfast/app/model/admin/sys_dict_type"
+	"gfast/library/utils"
+	"github.com/gogf/gf/container/garray"
+	"github.com/gogf/gf/database/gdb"
+	"github.com/gogf/gf/encoding/gjson"
+	"github.com/gogf/gf/errors/gerror"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/net/ghttp"
+	"github.com/gogf/gf/os/gtime"
+	"github.com/gogf/gf/text/gstr"
+	"github.com/gogf/gf/util/gconv"
+)
+
+//通过模型ID获取模型字段规则
+func GetModelRuleByModelId(r *ghttp.Request, modelId int64, masterId int64) (rules []*model_fields.FieldRule, err error) {
+	//1.获取模型信息
+	modelInfo, err := model_info.GetByID(modelId)
+	if err != nil {
+		return
+	}
+	//获取模型字段信息
+	args := &model_fields.SelectPageReq{
+		ModelId: modelId,
+	}
+	//2.字段信息
+	modelFields, err := model_fields.SelectListAll(args)
+	if err != nil {
+		return
+	}
+	//3.获取字段数据
+	//表前缀
+	dbPrefix := g.DB().GetPrefix()
+	var fieldData gdb.Record
+	if masterId != 0 {
+		fieldData, err = g.DB().Table(dbPrefix+modelInfo.ModelName).Where(modelInfo.ModelPk, masterId).FindOne()
+		if err != nil {
+			g.Log().Error(err)
+			err = gerror.New("获取模型字段数据失败")
+			return
+		}
+	}
+	//获取可编辑字段
+	fieldEdit := garray.NewStrArrayFrom(gstr.Split(modelInfo.ModelEdit, ","))
+	for _, field := range modelFields {
+		rule := new(model_fields.FieldRule)
+		fAttr := g.Map{}
+		//主键跳过不在表单显示
+		if field.FieldName == modelInfo.ModelPk {
+			continue
+		}
+		//过滤不可编辑字段
+		if !fieldEditAble(field.FieldName, fieldEdit) {
+			continue
+		}
+		//字段规则
+		fieldRules := garray.NewStrArrayFrom(gstr.Split(field.FieldRules, ","))
+		//隐藏字段处理
+		if fieldRules.Contains("hidden") {
+			continue
+		}
+		rule.FField = field.FieldName
+		rule.FTitle = field.FieldTitle
+		rule.FValue = field.FieldDefault
+		//必填验证
+		if fieldRules.Contains("required") {
+			validate := model_fields.Validate{
+				VType:     "string",
+				VRequired: true,
+				VMessage:  rule.FTitle + "不能为空",
+				VTrigger:  "blur",
+			}
+			rule.FValidate = append(rule.FValidate, validate)
+		}
+		//只读规则
+		if fieldRules.Contains("fAttr") {
+			fAttr["readonly"] = true
+		}
+
+		switch field.FieldType {
+		case "baidu_map":
+		case "text":
+			rule.FType = "input"
+			if fieldData != nil {
+				rule.FValue = fieldData[field.FieldName]
+			}
+		case "number", "large_number", "currency":
+			rule.FType = "inputNumber"
+			//非负
+			if fieldRules.Contains("unsigned") {
+				validate := model_fields.Validate{
+					VType:     "number",
+					VRequired: true,
+					VMin:      0,
+					VMessage:  rule.FTitle + "不能为负数",
+				}
+				rule.FValidate = append(rule.FValidate, validate)
+			}
+			if fieldData != nil {
+				rule.FValue = fieldData[field.FieldName]
+			}
+		case "datetime":
+			rule.FType = "DatePicker"
+			rule.FProps = g.Map{
+				"type":        "datetime",
+				"format":      "yyyy-MM-dd HH:mm:ss",
+				"placeholder": "请选择" + field.FieldTitle,
+			}
+			if fieldData != nil {
+				rule.FValue = utils.TimeStampToDateTime(gconv.Int64(fieldData[field.FieldName]))
+			} else if field.FieldDefault == "" {
+				rule.FValue = gtime.Now().Format("Y-m-d H:i:s")
+			}
+		case "date":
+			rule.FType = "DatePicker"
+			rule.FProps = g.Map{
+				"format":      "yyyy-MM-dd",
+				"placeholder": "请选择" + field.FieldTitle,
+			}
+			if fieldData != nil {
+				rule.FValue = utils.TimeStampToDate(gconv.Int64(fieldData[field.FieldName]))
+			} else if field.FieldDefault == "" {
+				rule.FValue = gtime.Now().Format("Y-m-d")
+			}
+		case "switch":
+			rule.FType = "switch"
+			rule.FProps = g.Map{
+				"activeValue":   "1",
+				"inactiveValue": "0",
+			}
+			if fieldData != nil {
+				rule.FValue = fieldData[field.FieldName]
+			}
+		case "bigtext":
+			rule.FType = "input"
+			rule.FProps = g.Map{
+				"type":        "textarea",
+				"placeholder": "请输入" + field.FieldTitle,
+				"rows":        5,
+			}
+			if fieldData != nil {
+				rule.FValue = fieldData[field.FieldName]
+			}
+		case "richtext":
+			rule.FType = "Editor"
+			if fieldData != nil {
+				rule.FValue = fieldData[field.FieldName]
+			}
+		case "selectnumber", "selecttext":
+			rule.FType = "select"
+			rule.FOptions, err = fieldOptionConv(field.FieldData)
+			if err != nil {
+				return
+			}
+			for k, _ := range rule.FValidate {
+				rule.FValidate[k].VTrigger = "change"
+			}
+			if fieldData != nil {
+				rule.FValue = gconv.String(fieldData[field.FieldName])
+			}
+		case "checkbox":
+			rule.FType = "checkbox"
+			if fieldData != nil {
+				rule.FValue = gstr.Split(gconv.String(fieldData[field.FieldName]), ",")
+			} else {
+				if field.FieldDefault != "" {
+					rule.FValue = gstr.Split(field.FieldDefault, ",")
+				} else {
+					rule.FValue = g.Slice{}
+				}
+			}
+			rule.FOptions, err = fieldOptionConv(field.FieldData)
+			if err != nil {
+				return
+			}
+			for k, _ := range rule.FValidate {
+				rule.FValidate[k].VType = "array"
+				rule.FValidate[k].VTrigger = "change"
+			}
+		case "file", "files":
+			rule.FType = "upFile"
+			if fieldData != nil {
+				type fileMap map[string]interface{}
+				var filesSlice []fileMap
+				filesByte := gconv.Bytes(fieldData[field.FieldName])
+				if len(filesByte) > 0 {
+					err = json.Unmarshal(filesByte, &filesSlice)
+					if err != nil {
+						g.Log().Error(err)
+						err = gerror.New("获取附件信息失败")
+						return
+					}
+				}
+				for k, fm := range filesSlice {
+					filesSlice[k]["url"], err = utils.GetRealFilesUrl(r, gconv.String(fm["url"]))
+					if err != nil {
+						return
+					}
+				}
+				rule.FValue = filesSlice
+			} else {
+				if field.FieldDefault != "" {
+					rule.FValue = gjson.New(field.FieldDefault)
+				} else {
+					rule.FValue = g.Slice{}
+				}
+			}
+			limit := 1
+			multiple := false
+			if field.FieldType == "files" {
+				limit = 6
+				multiple = true
+			}
+			rule.FProps = g.Map{
+				"type":       "select",
+				"uploadType": "file",
+				"action":     "/system/upload/upFile",
+				"name":       "file",
+				"multiple":   multiple,
+				"limit":      limit,
+			}
+		case "imagefile", "images":
+			rule.FType = "upload"
+			limit := 1
+			multiple := false
+			if field.FieldType == "images" {
+				limit = 6
+				multiple = true
+				if fieldData != nil {
+					imgStr := gconv.String(fieldData[field.FieldName])
+					if imgStr != "" {
+						imgs := gstr.Split(imgStr, ",")
+						for k, img := range imgs {
+							imgs[k], err = utils.GetRealFilesUrl(r, img)
+							if err != nil {
+								return
+							}
+						}
+						rule.FValue = imgs
+					} else {
+						rule.FValue = g.Slice{}
+					}
+				} else {
+					if field.FieldDefault != "" {
+						rule.FValue = gstr.Split(field.FieldDefault, ",")
+					} else {
+						rule.FValue = g.Slice{}
+					}
+				}
+			} else {
+				if fieldData != nil {
+					imgStr := gconv.String(fieldData[field.FieldName])
+					if imgStr != "" {
+						rule.FValue, err = utils.GetRealFilesUrl(r, imgStr)
+						if err != nil {
+							return
+						}
+					} else {
+						rule.FValue = ""
+					}
+				} else {
+					if field.FieldDefault != "" {
+						rule.FValue = field.FieldDefault
+					} else {
+						rule.FValue = ""
+					}
+				}
+			}
+			rule.FProps = g.Map{
+				"type":       "select",
+				"uploadType": "image",
+				"action":     "/system/upload/upImg",
+				"name":       "file",
+				"multiple":   multiple,
+				"accept":     "image/*",
+				"limit":      limit,
+			}
+		case "DepartmentSelector":
+			rule.FType = "DepartmentSelector"
+			if fieldData != nil {
+				rule.FValue = gstr.Split(gconv.String(fieldData[field.FieldName]), ",")
+			} else {
+				if field.FieldDefault != "" {
+					rule.FValue = gstr.Split(field.FieldDefault, ",")
+				} else {
+					rule.FValue = g.Slice{}
+				}
+			}
+			rule.FProps = g.Map{
+				"dataListApi": "/api/v1/govWork/options/getDepartmentSelector",
+			}
+		default:
+			err = gerror.New("未知字段" + field.FieldName + ",类型:" + field.FieldType)
+			return
+		}
+		rule.FAttr = fAttr
+		rules = append(rules, rule)
+	}
+
+	return
+}
+
+//保存模型字段数据
+//modelId  模型ID
+//data  字段数据
+//masterId 主表主键ID
+func HandlePostData(modelId int64, data g.Map, masterId int64, tx *gdb.TX, isUpdate bool) (err error) {
+	//表前缀
+	dbPrefix := g.DB().GetPrefix()
+	//1.获取模型信息
+	modelInfo, err := model_info.GetByID(modelId)
+	if err != nil {
+		return
+	}
+	//获取模型字段信息
+	//字段信息
+	args := &model_fields.SelectPageReq{
+		ModelId: modelId,
+	}
+	modelFields, err := model_fields.SelectListAll(args)
+	if err != nil {
+		return
+	}
+	//获取可编辑字段
+	fieldEdit := garray.NewStrArrayFrom(gstr.Split(modelInfo.ModelEdit, ","))
+	insertData := g.Map{}
+	for _, field := range modelFields {
+		//主键
+		if field.FieldName == modelInfo.ModelPk {
+			insertData[field.FieldName] = masterId
+		}
+		//过滤不可编辑字段
+		if !fieldEditAble(field.FieldName, fieldEdit) {
+			continue
+		}
+		//字段规则
+		fieldRules := garray.NewStrArrayFrom(gstr.Split(field.FieldRules, ","))
+		switch field.FieldType {
+		case "images":
+			//图片上传
+			picArr := gconv.SliceStr(data[field.FieldName])
+			for k, a := range picArr {
+				picArr[k], err = utils.GetFilesPath(a)
+				g.Log().Error(picArr)
+				if err != nil {
+					return
+				}
+			}
+			insertData[field.FieldName] = gstr.Join(picArr, ",")
+		case "files", "file":
+			//文件上传
+			fileArr := gconv.SliceMap(data[field.FieldName])
+			for k, fa := range fileArr {
+				fileArr[k]["url"], err = utils.GetFilesPath(gconv.String(fa["url"]))
+				if err != nil {
+					return
+				}
+			}
+			insertData[field.FieldName] = gconv.String(fileArr)
+		case "imagefile":
+			//单图上传
+			if _, ok := data[field.FieldName].(string); !ok {
+				err = gerror.New("单图片上传字段必须是一个字符串")
+				return
+			}
+			imgStr := gconv.String(data[field.FieldName])
+			if imgStr != "" {
+				imgStr, err = utils.GetFilesPath(imgStr)
+				if err != nil {
+					return
+				}
+			}
+			insertData[field.FieldName] = imgStr
+		case "baidu_map", "text", "bigtext", "switch", "richtext":
+			insertData[field.FieldName] = data[field.FieldName]
+		case "number", "large_number":
+			insertData[field.FieldName] = gconv.Int64(data[field.FieldName])
+		case "currency":
+			insertData[field.FieldName] = currencyLong(data[field.FieldName])
+		case "datetime", "date":
+			insertData[field.FieldName] = utils.StrToTimestamp(gconv.String(data[field.FieldName]))
+		case "selectnumber":
+			insertData[field.FieldName] = gconv.Int64(data[field.FieldName])
+			var b bool
+			//验证字段数据
+			b, err = fieldOptionValid(field.FieldData, insertData[field.FieldName])
+			if err != nil {
+				return
+			}
+			if fieldRules.Contains("required") && !b {
+				err = gerror.New(field.FieldTitle + "数据无效")
+				return
+			}
+		case "selecttext":
+			insertData[field.FieldName] = gstr.Trim(gconv.String(data[field.FieldName]))
+			var b bool
+			//验证字段数据
+			b, err = fieldOptionValid(field.FieldData, insertData[field.FieldName])
+			if err != nil {
+				return
+			}
+			if fieldRules.Contains("required") && !b {
+				err = gerror.New(field.FieldTitle + "数据无效")
+				return
+			}
+		case "checkbox":
+			checkboxData := data[field.FieldName]
+			var b bool
+			//验证字段数据
+			b, err = fieldOptionValid(field.FieldData, checkboxData)
+			if err != nil {
+				return
+			}
+			if fieldRules.Contains("required") && !b {
+				err = gerror.New(field.FieldTitle + "数据无效")
+				return
+			}
+			sliceData := gconv.SliceAny(checkboxData)
+			dataArr := garray.NewFrom(sliceData)
+			insertData[field.FieldName] = dataArr.Join(",")
+		case "DepartmentSelector":
+			data := gconv.SliceStr(data[field.FieldName])
+			insertData[field.FieldName] = gstr.Join(data, ",")
+		default:
+			err = gerror.New("未知字段:" + field.FieldTitle + field.FieldType)
+			return
+		}
+		//处理特殊规则-必须
+		if fieldRules.Contains("required") {
+			if val, ok := insertData[field.FieldName].(string); ok && val == "" {
+				err = gerror.New(field.FieldTitle + "不能为空")
+				return
+			}
+		}
+		//唯一验证
+		if fieldRules.Contains("unique") {
+			var one gdb.Record
+			one, err = g.DB().Table(dbPrefix+modelInfo.ModelName).Where(field.FieldName, insertData[field.FieldName]).FindOne()
+			if err != nil {
+				g.Log().Error(one)
+				err = gerror.New("判断字段" + field.FieldTitle + "唯一性失败")
+				return
+			}
+			if one != nil {
+				if !isUpdate || (isUpdate && gconv.Int64(one[modelInfo.ModelPk]) != gconv.Int64(insertData[modelInfo.ModelPk])) {
+					err = gerror.New(field.FieldTitle + "已存在")
+					return
+				}
+			}
+		}
+	}
+
+	//保存模型字段数据
+	_, err = tx.Table(dbPrefix + modelInfo.ModelName).Replace(insertData)
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("报错模型数据失败")
+		return
+	}
+	return nil
+}
+
+//删除模型字段数据
+func DeleteModelFieldData(modelId int64, masterId int64, tx *gdb.TX) (err error) {
+	//表前缀
+	dbPrefix := g.DB().GetPrefix()
+	//1.获取模型信息
+	modelInfo, err := model_info.GetByID(modelId)
+	if err != nil {
+		return
+	}
+	//删除操作
+	_, err = g.DB().Table(dbPrefix+modelInfo.ModelName).TX(tx).Where(modelInfo.ModelPk, masterId).Delete()
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("删除模型数据失败")
+		return
+	}
+	return
+}
+
+//字段选项数据解析
+func fieldOptionConv(fieldData string) (data g.List, err error) {
+	//直接查库操作
+	if gstr.Pos(fieldData, "|") > -1 {
+		dataInfo := garray.NewStrArrayFrom(gstr.Split(fieldData, "|"))
+		var (
+			table, fieldK, fieldV, sort, where string
+		)
+		var has bool = true
+		if has {
+			table, has = dataInfo.Get(0)
+		}
+		if has {
+			fieldK, has = dataInfo.Get(1)
+		}
+		if has {
+			fieldV, has = dataInfo.Get(2)
+		}
+		if !has {
+			err = gerror.New("数据规则格式不正确,请检查模型字段设置")
+		}
+		model := g.DB().Table(table).Fields(fieldK + "," + fieldV)
+		sort, has = dataInfo.Get(3)
+		if has {
+			model = model.Order(sort)
+		}
+		where, has = dataInfo.Get(4)
+		if has {
+			model = model.Where(where)
+		}
+		var res gdb.Result
+		res, err = model.FindAll()
+		if err != nil {
+			g.Log().Error(err)
+			err = gerror.New("获取模型数据选项失败")
+			return
+		}
+		for _, r := range res {
+			data = append(data, g.Map{
+				"value":   r[fieldK],
+				"label":   r[fieldV],
+				"disable": false,
+			})
+		}
+	} else if gstr.Pos(fieldData, ":") == 0 { //从字典库查询
+		dictType := gstr.SubStr(fieldData, 1)
+		var dict g.Map
+		dict, err = sys_dict_type.GetDictWithDataByType(dictType, "", "")
+		if err != nil {
+			return
+		}
+		values := gconv.SliceMap(dict["values"])
+		for _, val := range values {
+			data = append(data, g.Map{
+				"value":   val["key"],
+				"label":   val["value"],
+				"disable": false,
+			})
+		}
+	} else { //硬编码
+		dataSlice := gstr.Split(fieldData, ",")
+		for _, val := range dataSlice {
+			keyVal := gstr.Split(val, ":")
+			if len(keyVal) != 2 {
+				err = gerror.New("数据规则格式不正确,请检查模型字段设置")
+				return
+			}
+			data = append(data, g.Map{
+				"value":   keyVal[0],
+				"label":   keyVal[1],
+				"disable": false,
+			})
+		}
+	}
+	return
+}
+
+//判断字段是否可编辑
+func fieldEditAble(fieldName string, fieldEdits *garray.StrArray) bool {
+	if fieldEdits.Len() == 0 {
+		return false
+	}
+	return fieldEdits.ContainsI(fieldName)
+}
+
+//货币转化为分
+func currencyLong(currency interface{}) int64 {
+	strArr := gstr.Split(gconv.String(currency), ".")
+	switch len(strArr) {
+	case 1:
+		return gconv.Int64(strArr[0]) * 100
+	case 2:
+		if len(strArr[1]) == 1 {
+			strArr[1] += "0"
+		} else if len(strArr[1]) > 2 {
+			strArr[1] = gstr.SubStr(strArr[1], 0, 2)
+		}
+		return gconv.Int64(strArr[0])*100 + gconv.Int64(strArr[1])
+	}
+	return 0
+}
+
+//检测字段选项是否有效
+func fieldOptionValid(fieldData string, value interface{}) (bool, error) {
+	data, err := fieldOptionConv(fieldData)
+	if err != nil {
+		return false, err
+	}
+	valueArr := garray.New()
+	switch value.(type) {
+	case g.Slice:
+		valueArr = garray.NewFrom(value.(g.Slice))
+	default:
+		valueArr.Append(value)
+	}
+	//去重
+	valueArr = valueArr.Unique()
+	if valueArr.Len() != 0 {
+		r := false //检查提交数据是否在选项数据中
+		valueArr.Iterator(func(k int, v interface{}) bool {
+			r = false
+			for _, d := range data {
+				if gstr.Equal(gconv.String(v), gconv.String(d["value"])) {
+					r = true
+					continue
+				}
+			}
+			return r
+		})
+		return r, nil
+	}
+	return true, nil
+}

Dosya farkı çok büyük olduğundan ihmal edildi
+ 3 - 1
data/db.sql


+ 73 - 5
library/utils/slice_tree.go

@@ -2,6 +2,7 @@ package utils
 
 import (
 	"fmt"
+	"github.com/gogf/gf/container/garray"
 	"github.com/gogf/gf/frame/g"
 	"github.com/gogf/gf/util/gconv"
 	"reflect"
@@ -80,7 +81,7 @@ func PushSonToParent(list g.List, params ...interface{}) g.List {
 		args[k] = v
 	}
 	var (
-		pid         int         //父级id
+		pid         string      //父级id
 		fieldName   string      //父级id键名
 		id          string      //id键名
 		key         string      //子级数组键名
@@ -88,7 +89,7 @@ func PushSonToParent(list g.List, params ...interface{}) g.List {
 		filterVal   interface{} //过滤的值
 		showNoChild bool        //是否显示不存在的子级健
 	)
-	pid = gconv.Int(GetSliceByKey(args, 0, 0))
+	pid = gconv.String(GetSliceByKey(args, 0, 0))
 	fieldName = gconv.String(GetSliceByKey(args, 1, "pid"))
 	id = gconv.String(GetSliceByKey(args, 2, "id"))
 	key = gconv.String(GetSliceByKey(args, 3, "children"))
@@ -97,7 +98,7 @@ func PushSonToParent(list g.List, params ...interface{}) g.List {
 	showNoChild = gconv.Bool(GetSliceByKey(args, 6, true))
 	var returnList g.List
 	for _, v := range list {
-		if gconv.Int(v[fieldName]) == pid {
+		if gconv.String(v[fieldName]) == pid {
 			if filter != "" {
 				if reflect.DeepEqual(v[filter], filterVal) {
 					args[0] = v[id]
@@ -147,8 +148,26 @@ func FindSonByParentId(list g.List, parentId int, parentIndex, idIndex string) g
 	return newList
 }
 
+//获取最顶层 parent Id
+func GetTopPidList(list g.List, parentIndex, idIndex string) *garray.Array {
+	arr := garray.NewArray()
+	for _, v1 := range list {
+		tag := true
+		for _, v2 := range list {
+			if v1[parentIndex] == v2[idIndex] {
+				tag = false
+				break
+			}
+		}
+		if tag {
+			arr.PushRight(v1[parentIndex])
+		}
+	}
+	return arr.Unique()
+}
+
 //有层级关系的数组,通过子级fid查找所有父级数组
-func findParentBySonPid(list g.List, id int, params ...interface{}) g.List {
+func FindParentBySonPid(list g.List, id int, params ...interface{}) g.List {
 	args := make([]interface{}, 4)
 	for k, v := range params {
 		if k == 4 {
@@ -172,9 +191,58 @@ func findParentBySonPid(list g.List, id int, params ...interface{}) g.List {
 			} else {
 				rList = append(rList, v)
 			}
-			r := findParentBySonPid(list, gconv.Int(v[fPid]), filter, fPid, filterValue, fid)
+			r := FindParentBySonPid(list, gconv.Int(v[fPid]), filter, fPid, filterValue, fid)
 			rList = append(rList, r...)
 		}
 	}
 	return rList
 }
+
+/**
+ * 根据id查询最顶层父级信息
+ * @param list 有层级关系的数组
+ * @param id 查找的id
+ * @param string fpid 父级id键名
+ * @param string fid 当前id键名
+ * @return g.Map
+ */
+func FindTopParent(list g.List, id int64, params ...interface{}) g.Map {
+	if len(list) == 0 {
+		return g.Map{}
+	}
+	args := make([]interface{}, 2)
+	for k, v := range params {
+		if k == 2 {
+			break
+		}
+		args[k] = v
+	}
+	var (
+		fPid = gconv.String(GetSliceByKey(args, 0, "pid")) //父级id字段键名
+		fid  = gconv.String(GetSliceByKey(args, 1, "id"))  //id字段键名
+	)
+	hasParent := true
+	top := g.Map{}
+	//找到要查找id值的数组
+	for _, v := range list {
+		if gconv.Int64(v[fid]) == gconv.Int64(id) {
+			top = v
+			break
+		}
+	}
+	for {
+		if !hasParent {
+			break
+		}
+		//查询最顶层
+		for _, v := range list {
+			if gconv.Int64(top[fPid]) == gconv.Int64(v[fid]) {
+				top = v
+				hasParent = true
+				break
+			}
+			hasParent = false
+		}
+	}
+	return top
+}

+ 57 - 2
library/utils/tools.go

@@ -1,13 +1,17 @@
 package utils
 
 import (
+	"fmt"
 	"github.com/gogf/gf/crypto/gaes"
 	"github.com/gogf/gf/encoding/gbase64"
 	"github.com/gogf/gf/encoding/gcharset"
 	"github.com/gogf/gf/encoding/gjson"
+	"github.com/gogf/gf/encoding/gurl"
+	"github.com/gogf/gf/errors/gerror"
 	"github.com/gogf/gf/frame/g"
 	"github.com/gogf/gf/net/ghttp"
 	"github.com/gogf/gf/os/gtime"
+	"github.com/gogf/gf/text/gstr"
 	"net"
 	"time"
 )
@@ -96,16 +100,26 @@ func StrToTimestamp(dateStr string) int64 {
 	return tm.Timestamp()
 }
 
+//时间戳转 yyyy-MM-dd HH:mm:ss
+func TimeStampToDateTime(timeStamp int64) string {
+	tm := gtime.NewFromTimeStamp(timeStamp)
+	return tm.Format("Y-m-d H:i:s")
+}
+
+//时间戳转 yyyy-MM-dd
+func TimeStampToDate(timeStamp int64) string {
+	tm := gtime.NewFromTimeStamp(timeStamp)
+	return tm.Format("Y-m-d")
+}
+
 //获取ip所属城市
 func GetCityByIp(ip string) string {
 	if ip == "" {
 		return ""
 	}
-
 	if ip == "[::1]" || ip == "127.0.0.1" {
 		return "内网IP"
 	}
-
 	url := "http://whois.pconline.com.cn/ipJson.jsp?json=true&ip=" + ip
 	bytes := ghttp.GetBytes(url)
 	src := string(bytes)
@@ -122,3 +136,44 @@ func GetCityByIp(ip string) string {
 		return ""
 	}
 }
+
+//获取附件真实路径
+func GetRealFilesUrl(r *ghttp.Request, path string) (realPath string, err error) {
+	if gstr.ContainsI(path, "http") {
+		realPath = path
+		return
+	}
+	realPath, err = GetDomain(r)
+	if err != nil {
+		return
+	}
+	realPath = realPath + path
+	return
+}
+
+//获取当前请求接口域名
+func GetDomain(r *ghttp.Request) (string, error) {
+	pathInfo, err := gurl.ParseURL(r.GetUrl(), -1)
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("解析附件路径失败")
+		return "", err
+	}
+	return fmt.Sprintf("%s://%s:%s/", pathInfo["scheme"], pathInfo["host"], pathInfo["port"]), nil
+}
+
+//获取附件相对路径
+func GetFilesPath(fileUrl string) (path string, err error) {
+	if !gstr.ContainsI(fileUrl, "http") {
+		path = fileUrl
+		return
+	}
+	pathInfo, err := gurl.ParseURL(fileUrl, 32)
+	if err != nil {
+		g.Log().Error(err)
+		err = gerror.New("解析附件路径失败")
+		return
+	}
+	path = gstr.TrimLeft(pathInfo["path"], "/")
+	return
+}

+ 26 - 0
router/routerAdmin.go

@@ -39,6 +39,12 @@ func init() {
 		//岗位管理
 		group.ALL("/post", new(admin.Post))
 
+		//cms管理
+		group.Group("/cms", func(group *ghttp.RouterGroup) {
+			group.ALL("/menu", new(admin.CmsMenu))
+			group.ALL("/news", new(admin.CmsNews))
+		})
+
 		//配置管理
 		group.Group("/config", func(group *ghttp.RouterGroup) {
 			group.ALL("/dict", new(admin.Dict))
@@ -53,9 +59,29 @@ func init() {
 			group.ALL("/operlog", new(admin.MonitorOperationLog))
 			group.ALL("/loginlog", new(admin.MonitorLoginLog))
 		})
+
+		//模型管理
+		group.Group("/model", func(group *ghttp.RouterGroup) {
+			group.ALL("/category", new(admin.ModelCategory))
+			group.ALL("/info", new(admin.ModelInfo))
+			group.ALL("/fields", new(admin.ModelFields))
+		})
+
 		//代码生成
 		group.Group("/tools", func(group *ghttp.RouterGroup) {
 			group.ALL("/gen", new(admin.Gen))
 		})
+
+		//扩展管理
+		group.Group("/plug", func(group *ghttp.RouterGroup) {
+			group.Group("/ad", func(group *ghttp.RouterGroup) {
+				group.ALL("/type", new(admin.AdType))
+				group.ALL("/info", new(admin.PlugAd))
+			})
+			group.Group("/link", func(group *ghttp.RouterGroup) {
+				group.ALL("/type", new(admin.LinkType))
+				group.ALL("/info", new(admin.PlugLink))
+			})
+		})
 	})
 }

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor