{"id":21900824,"url":"https://github.com/smokecat/gfdemo","last_synced_at":"2025-03-22T06:12:58.454Z","repository":{"id":230716548,"uuid":"432352402","full_name":"smokecat/gfdemo","owner":"smokecat","description":"A demo app for goframe","archived":false,"fork":false,"pushed_at":"2021-11-28T13:04:45.000Z","size":21,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-01-27T06:32:11.678Z","etag":null,"topics":["demo","gf","goframe"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/smokecat.png","metadata":{"files":{"readme":"README.MD","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2021-11-27T02:50:13.000Z","updated_at":"2021-11-28T13:04:47.000Z","dependencies_parsed_at":"2024-03-31T12:49:02.055Z","dependency_job_id":null,"html_url":"https://github.com/smokecat/gfdemo","commit_stats":null,"previous_names":["smokecat/gfdemo"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smokecat%2Fgfdemo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smokecat%2Fgfdemo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smokecat%2Fgfdemo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smokecat%2Fgfdemo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/smokecat","download_url":"https://codeload.github.com/smokecat/gfdemo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244913330,"owners_count":20530817,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["demo","gf","goframe"],"created_at":"2024-11-28T15:10:42.667Z","updated_at":"2025-03-22T06:12:58.436Z","avatar_url":"https://github.com/smokecat.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# A GoFrame Project Demo\n\n\u003e [GoFrame](https://goframe.org)\n\n## package sorm\n\n数据库相关功能实现\n\n### paging 分页\n\n自定义了一个包含分页参数的结构体，并添加了gvalid校验规则\n\n```go\npackage sorm\n\ntype Paging struct {\n  Page int `json:\"page\" v:\"required|integer|min:1#||\"`\n  Size int `json:\"size\" v:\"required|integer|min:1#||\"`\n}\n```\n\n在定义请求接口时，可添加Paging到结构体，作为匿名属性，Page和Size字段可提升为接口结构体的字段。\n\n```go\npackage model\n\nimport (\n  \"github.com/smokecat/gfdemo/library/sorm\"\n)\n\ntype UserApiListReq struct {\n  sorm.Paging\n}\n```\n\n接口请求须携带page和size参数。\n\n```http request\nGET /api/user/list?page=1\u0026size=10\n```\n\n可正常对参数进行校验\n\n```go\npackage api\n\nimport (\n  \"github.com/gogf/gf/net/ghttp\"\n)\n\nfunc List(r *ghttp.Request) {\n  var (\n    apiReq UserApiListReq\n  )\n\n  // apiReq = UserAoiListReq{Page: 1, Size: 10}\n  if err := r.ParseQuery(\u0026apiReq); err != nil {\n    response.JsonExit(r, 1, err.Error())\n  }\n}\n```\n\n提供转换为Limit方法参数的方法: `func (p Paging) ToLimitParam() (offset int, limit int)`\n\n```go\npackage service\n\nimport (\n  \"context\"\n\n  \"github.com/smokecat/gfdemo/app/dao\"\n  \"github.com/smokecat/gfdemo/app/model\"\n)\n\nfunc List(ctx context.Context, in *model.UserServiceListInput) (*model.UserServiceListOutput, error) {\n  var users []*model.User\n\n  if err := dao.User.Ctx(ctx).Limit(in.ToLimitParam()).Scan(\u0026users); err != nil {\n    return nil, err\n  }\n  return \u0026model.UserServiceListOutput{Users: users}, nil\n}\n```\n\n### order by 排序\n\n自定义了一个包含排序参数的结构体，并自定义了一个用户gvalid校验的结构体规则。\n\n```go\npackage sorm\n\ntype OrderBy struct {\n  // OrderColumns should conform to `model,col1,col2` and case-sensitive.\n  OrderColumns string `json:\"orderColumns\" dc:\"逗号分割的model字段字符串,不能有空格\"`\n\n  // OrderPositions is a comma-separated string of asc or desc(d), which corresponds to the column name one by one.\n  // If it is not equal to desc(d) or is an empty string, it will be set to asc by default.\n  OrderPositions string `json:\"orderPosition\" dc:\"逗号分割，对应column的排序\"`\n}\n```\n\n在定义请求接口时，可添加OrderBy到结构体，作为匿名属性，OrderColumns和OrderPositions可提升为接口结构体的字段。\n\n```go\npackage model\n\nimport (\n  \"github.com/smokecat/gfdemo/library/sorm\"\n)\n\ntype UserApiListReq struct {\n  sorm.OrderBy `v:\"order-by:user,id,age,headImg#\"`\n}\n```\n\n接口请求可携带orderColumns和orderPositions参数（可选）。\n\n```http request\nGET /api/user/list?orderColumns=id,age\u0026orderPositions=asc,desc\n```\n\n校验OrderBy参数: \u003ca href=\"#order-by\"\u003e详见order-by规则\u003c/a\u003e\n\n提供转换为OrderBy方法参数的方法: `func (o OrderBy) ToOrderByParam() string`\n\n```go\npackage service\n\nfunc (u *userService) List(ctx context.Context, in *model.UserServiceListInput) (*model.UserServiceListOutput, error) {\n  var users []*model.User\n\n  // Order(\"id asc, age desc\")\n  if err := dao.User.Ctx(ctx).Order(in.ToOrderByParam()).Scan(\u0026users); err != nil {\n    return nil, err\n  }\n  return \u0026model.UserServiceListOutput{Users: users}, nil\n}\n```\n\nToOrderByParam方法转换规则\n- OrderPostions逗号分割的排序规则与OrderColumns一一对应。\n- 如果对应的排序方向不等于\"desc\"、\"d\"或者长度不足，那么将转换为\"asc\"。\n\n#### \u003ca id=\"order-by\"\u003e\u003c/a\u003e OrderBy rule\n\n为了更加通用，OrderBy字段参数的校验较为复杂。推荐按如下步骤进行。\n\n1. 在`boot`包的init方法中注册数据库model。并且全局注册order-by校验规则。\n\n```go\npackage boot\n\nfunc init() {\n  // Register sorm model columns map\n  sorm.RegisterModelColumnsMap(map[string]interface{}{\"user\": model.User{}})\n\n  // Register order-by rule\n  if err := gvalid.RegisterRule(sorm.OrderByRuleName, sorm.RuleOrderBy); err != nil {\n    g.Log().Panicf(\"register rule `%s` failed\", sorm.OrderByRuleName)\n  }\n\n}\n```\n\n2. 在OrderBy匿名字段后添加gvalid校验标签`order-by`。\n```go\npackage model\n\ntype UserApiListReq struct {\n  sorm.OrderBy `v:\"order-by:user,id,age,headImg#\"`\n}\n```\n\n标签校验规则如下：\n  - 形如`order-by:\u003cmodel\u003e,\u003ccol1\u003e,\u003ccol2\u003e...`。\n  - 规则将逗号分割的第一个元素作为model，后续元素作为列名，并且都是可选的。\n  - 如果未指定model，即只有规则名`order-by`，那么默认允许所有参数。\n  - 如果仅包含model，即`order-by:model`，那么有以下两种情况。\n    - 该model在步骤1中已注册，那么值允许参数包含注册结构体(model.User)的所有列(包含列名的小驼峰、大驼峰、下划线格式)。\n    - 若为注册，那么默认允许所有参数。\n  - 如果包含cols，那么无论指定的model是否注册，只允许参数包含cols中的列名(大小写敏感)。\n\n3. 使用gvalid进行参数校验\n```go\npackage api\n\nfunc List(r *ghttp.Request) {\n  var (\n    apiReq UserApiListReq\n  )\n\n  // apiReq = UserAoiListReq{OrderColumns: \"id,age\", OrderPositions: \"asc,desc\"}\n  if err := r.ParseQuery(\u0026apiReq); err != nil {\n    response.JsonExit(r, 1, err.Error())\n  }\n}\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmokecat%2Fgfdemo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsmokecat%2Fgfdemo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmokecat%2Fgfdemo/lists"}