浏览代码

openbilibili

tangs 6 年之前
父节点
当前提交
b030ce89ca
共有 100 个文件被更改,包括 4999 次插入1 次删除
  1. 34 0
      .bazelrc
  2. 33 0
      .generated_files
  3. 44 0
      .gitignore
  4. 66 0
      .gitlab-ci.yml
  5. 27 0
      .gitlab/issue_templates/Bug.md
  6. 19 0
      .gitlab/issue_templates/Doc.md
  7. 20 0
      .gitlab/issue_templates/Feature.md
  8. 20 0
      .gitlab/issue_templates/Refactor.md
  9. 22 0
      .gitlab/issue_templates/Suggestion.md
  10. 18 0
      .gitlab/merge_request_templates/Common.md
  11. 20 0
      .gometalinter.json
  12. 1 0
      .kazelcfg.json
  13. 19 0
      .rider/.gometalinter.json
  14. 92 0
      .rider/CHANGELOG.md
  15. 20 0
      .rider/CONTRIBUTORS.md
  16. 20 0
      .rider/OWNERS
  17. 48 0
      .rider/bazel_build.sh
  18. 21 0
      .rider/changefiles.sh
  19. 28 0
      .rider/changepkgs.sh
  20. 39 0
      .rider/lint.sh
  21. 24 0
      .rider/make_update.sh
  22. 28 0
      .rider/sagacheck.sh
  23. 267 0
      .rider/unit_test.sh
  24. 1 0
      BUILD.bazel
  25. 481 0
      CHANGELOG.md
  26. 9 0
      CONTRIBUTORS.md
  27. 40 0
      HERO.md
  28. 1 0
      Makefile
  29. 5 0
      OWNERS
  30. 525 1
      README.md
  31. 1 0
      WORKSPACE
  32. 23 0
      app/BUILD
  33. 10 0
      app/CONTRIBUTORS.md
  34. 6 0
      app/OWNERS
  35. 13 0
      app/README.md
  36. 21 0
      app/admin/BUILD
  37. 5 0
      app/admin/OWNERS
  38. 3 0
      app/admin/README.md
  39. 16 0
      app/admin/bbq/BUILD
  40. 9 0
      app/admin/bbq/OWNERS
  41. 20 0
      app/admin/bbq/comment/BUILD
  42. 2 0
      app/admin/bbq/comment/CHANGELOG.md
  43. 6 0
      app/admin/bbq/comment/CONTRIBUTORS.md
  44. 10 0
      app/admin/bbq/comment/OWNERS
  45. 12 0
      app/admin/bbq/comment/README.md
  46. 42 0
      app/admin/bbq/comment/cmd/BUILD
  47. 48 0
      app/admin/bbq/comment/cmd/main.go
  48. 2 0
      app/admin/bbq/comment/configs/application.toml
  49. 0 0
      app/admin/bbq/comment/configs/grpc.toml
  50. 4 0
      app/admin/bbq/comment/configs/http.toml
  51. 13 0
      app/admin/bbq/comment/configs/memcache.toml
  52. 11 0
      app/admin/bbq/comment/configs/mysql.toml
  53. 13 0
      app/admin/bbq/comment/configs/redis.toml
  54. 36 0
      app/admin/bbq/comment/internal/dao/BUILD
  55. 95 0
      app/admin/bbq/comment/internal/dao/dao.go
  56. 28 0
      app/admin/bbq/comment/internal/model/BUILD
  57. 1 0
      app/admin/bbq/comment/internal/model/model.go
  58. 35 0
      app/admin/bbq/comment/internal/server/http/BUILD
  59. 61 0
      app/admin/bbq/comment/internal/server/http/http.go
  60. 32 0
      app/admin/bbq/comment/internal/service/BUILD
  61. 37 0
      app/admin/bbq/comment/internal/service/service.go
  62. 21 0
      app/admin/ep/BUILD
  63. 6 0
      app/admin/ep/OWNERS
  64. 22 0
      app/admin/ep/marthe/BUILD
  65. 4 0
      app/admin/ep/marthe/CHANGELOG.md
  66. 14 0
      app/admin/ep/marthe/CONTRIBUTORS.md
  67. 19 0
      app/admin/ep/marthe/OWNERS
  68. 19 0
      app/admin/ep/marthe/README.md
  69. 44 0
      app/admin/ep/marthe/cmd/BUILD
  70. 130 0
      app/admin/ep/marthe/cmd/admin.toml
  71. 106 0
      app/admin/ep/marthe/cmd/convey-test.toml
  72. 46 0
      app/admin/ep/marthe/cmd/main.go
  73. 39 0
      app/admin/ep/marthe/conf/BUILD
  74. 162 0
      app/admin/ep/marthe/conf/conf.go
  75. 87 0
      app/admin/ep/marthe/dao/BUILD
  76. 192 0
      app/admin/ep/marthe/dao/bugly.go
  77. 100 0
      app/admin/ep/marthe/dao/dao.go
  78. 30 0
      app/admin/ep/marthe/dao/dao_test.go
  79. 35 0
      app/admin/ep/marthe/dao/mail.go
  80. 92 0
      app/admin/ep/marthe/dao/mysql_bugly_batch_run.go
  81. 58 0
      app/admin/ep/marthe/dao/mysql_bugly_batch_run_test.go
  82. 67 0
      app/admin/ep/marthe/dao/mysql_bugly_cookie.go
  83. 51 0
      app/admin/ep/marthe/dao/mysql_bugly_cookie_test.go
  84. 104 0
      app/admin/ep/marthe/dao/mysql_bugly_issue.go
  85. 78 0
      app/admin/ep/marthe/dao/mysql_bugly_issue_test.go
  86. 89 0
      app/admin/ep/marthe/dao/mysql_bugly_project.go
  87. 139 0
      app/admin/ep/marthe/dao/mysql_bugly_version.go
  88. 49 0
      app/admin/ep/marthe/dao/mysql_bugly_version_test.go
  89. 50 0
      app/admin/ep/marthe/dao/mysql_contact_info.go
  90. 17 0
      app/admin/ep/marthe/dao/mysql_schedule_task.go
  91. 33 0
      app/admin/ep/marthe/dao/mysql_schedule_task_test.go
  92. 47 0
      app/admin/ep/marthe/dao/mysql_tapd_bug_priority_conf.go
  93. 52 0
      app/admin/ep/marthe/dao/mysql_tapd_bug_priority_confg_test.go
  94. 57 0
      app/admin/ep/marthe/dao/mysql_tapd_bug_record.go
  95. 42 0
      app/admin/ep/marthe/dao/mysql_tapd_bug_record_test.go
  96. 66 0
      app/admin/ep/marthe/dao/mysql_tapd_bug_template.go
  97. 75 0
      app/admin/ep/marthe/dao/mysql_tapd_bug_template_test.go
  98. 68 0
      app/admin/ep/marthe/dao/mysql_tapd_bug_version_template.go
  99. 82 0
      app/admin/ep/marthe/dao/mysql_tapd_bug_version_template_test.go
  100. 0 0
      app/admin/ep/marthe/dao/mysql_user.go

+ 34 - 0
.bazelrc

@@ -0,0 +1,34 @@
+startup --expand_configs_in_place
+startup --max_idle_secs=10800 --connect_timeout_secs=5
+startup --host_jvm_args=-XX:+UseParallelGC --host_jvm_args=-Xmx6g --host_jvm_args=-Xms1g
+startup --idle_server_tasks --noexperimental_oom_more_eagerly --experimental_oom_more_eagerly_threshold=100
+# Show us information about failures.
+# build --watchfs
+build:ci -j 4 
+build --experimental_repository_cache_hardlinks
+build --experimental_guard_against_concurrent_changes
+build --experimental_strict_action_env
+build --announce_rc
+build --remote_http_cache=http://bazel-cache.bilibili.co/go-common  --remote_local_fallback 
+build --remote_max_connections=0
+build:ci --genrule_strategy=standalone --spawn_strategy=standalone
+build:office --genrule_strategy=sandboxed --spawn_strategy=sandboxed
+build --verbose_failures
+build:unit --features=race
+
+test --test_output=errors
+test --test_arg=-test.v
+test:unit --features=race
+test --test_summary=detailed
+# Include git version info
+build --workspace_status_command build/print-workspace-status.sh
+
+# Make /tmp hermetic
+#build --sandbox_tmpfs_path=/tmp
+build  --experimental_multi_threaded_digest 
+build --remote_timeout=5 
+# This flag requires Bazel 0.5.0+
+#build --sandbox_fake_username
+
+# Enable go race detection.
+test:unit --features=race

+ 33 - 0
.generated_files

@@ -0,0 +1,33 @@
+# Files that should be ignored by tools which do not want to consider generated
+# code.
+#
+# https://github.com/kubernetes/contrib/blob/master/mungegithub/mungers/size.go
+#
+# This file is a series of lines, each of the form:
+#     <type> <name>
+#
+# Type can be:
+#    path - an exact path to a single file
+#    file-name - an exact leaf filename, regardless of path
+#    path-prefix - a prefix match on the file path
+#    file-prefix - a prefix match of the leaf filename (no path)
+#    paths-from-repo - read a file from the repo and load file paths
+#
+
+file-prefix	zz_generated.
+
+file-name	BUILD.bazel
+file-name	BUILD
+file-name	types.generated.go
+file-name	generated.pb.go
+file-name	generated.proto
+file-name	types_swagger_doc_generated.go
+
+path-prefix	Godeps/
+path-prefix	vendor/
+path-prefix	api/swagger-spec/
+path-prefix	pkg/generated/
+
+paths-from-repo docs/.generated_docs
+
+file-suffix pb.go

+ 44 - 0
.gitignore

@@ -0,0 +1,44 @@
+# idea ignore
+.idea/
+*.ipr
+*.iml
+*.iws
+
+biligo/
+.vscode/
+
+*.swp
+
+# temp ignore
+*.log
+*.cache
+*.diff
+*.exe
+*.exe~
+*.patch
+*.tmp
+*debug.test
+
+# system ignore
+.DS_Store
+Thumbs.db
+
+# project
+*.cert
+*.key
+.test
+iprepo.txt
+
+app/*/*/cmd/cmd
+app/*/*/*/cmd/cmd
+app/*/*/cmd/debug
+app/*/*/*/cmd/debug
+app/tool/kratos/kratos
+
+bazel-bin
+bazel-genfiles
+bazel-go-common
+bazel-out
+bazel-testlogs
+bazel-test
+_output

+ 66 - 0
.gitlab-ci.yml

@@ -0,0 +1,66 @@
+variables:
+  CI_SERVER_URL: http://git.bilibili.co
+#  CI_DEBUG_TRACE: "true"
+
+stages:
+  - COMPILE
+  - LINT
+  - TEST
+
+compile part:
+  stage: COMPILE
+  script:
+    - bash .rider/make_update.sh
+    - bazel info
+    - pwd
+    - bash .rider/bazel_build.sh "part"
+  only:
+    - branches
+  retry: 1
+
+compile(merge master):
+  stage: COMPILE
+  script:
+    - git pull origin master
+    - bash .rider/make_update.sh
+    - bazel info
+    - pwd
+    - bash .rider/bazel_build.sh
+  only:
+    - branches
+  retry: 1
+
+gometalinter:
+  stage: LINT
+  allow_failure: true
+  script:
+    - echo "gometalinter"
+    - pwd
+    - bash .rider/lint.sh
+  only:
+    - branches
+
+saga check:
+  stage: LINT
+  allow_failure: false
+  script:
+    - echo "saga check"
+    - pwd
+    - bash .rider/sagacheck.sh
+  except:
+    - master
+
+
+unit test:
+  stage: TEST
+#  allow_failure: true
+  script:
+    - echo "go unit test run"
+    - pwd
+    - bash .rider/unit_test.sh
+  only:
+    - branches
+  tags:
+    - uat-unittest
+
+

+ 27 - 0
.gitlab/issue_templates/Bug.md

@@ -0,0 +1,27 @@
+<!-- Title规范 : <基础库|服务名|doc> : <标题> 
+栗子:
+    1. library/log : xxxxxxxx
+    2. account-service : xxxxxxxx
+    3. doc : xxxxxxxx
+-->
+### Version
+<!-- 发生bug的版本号(retag) : v2.1.0 -->
+
+### Range of affected
+<!-- 影响范围:服务、用户等 -->
+
+### Bug behavior
+
+### Correct behavior
+
+### Logs / Screenshots
+```
+一些log、截图等,不需要可以删除
+```
+
+### How to fix
+
+
+<!-- /cc @xxx @yyy 抄送某人 -->
+
+/label ~bug

+ 19 - 0
.gitlab/issue_templates/Doc.md

@@ -0,0 +1,19 @@
+<!-- Title规范 : <基础库|服务名|doc> : <标题> 
+栗子:
+    1. library/log : xxxxxxxx
+    2. account-service : xxxxxxxx
+    3. doc : xxxxxxxx
+-->
+### Desc
+<!-- 描述 -->
+
+### Doc list
+- [ ] + README.md
+- [ ] m app/service/CONTRIBUTORS.md
+- [ ] - app/service/*/CHANGELOG.md
+
+### Links / Refs
+* <ref>
+* <ref>
+
+/lable ~documentation

+ 20 - 0
.gitlab/issue_templates/Feature.md

@@ -0,0 +1,20 @@
+<!-- Title规范 : <基础库|服务名|doc> : <标题> 
+栗子:
+    1. library/log : xxxxxxxx
+    2. account-service : xxxxxxxx
+    3. doc : xxxxxxxx
+-->
+### Desc
+<!-- 描述 -->
+
+### Roadmap
+* <step>
+* <step>
+
+### Links / Refs
+* <ref>
+* <ref>
+
+<!-- /cc @xxx @yyy 抄送某人 -->
+
+/label ~feature

+ 20 - 0
.gitlab/issue_templates/Refactor.md

@@ -0,0 +1,20 @@
+<!-- Title规范 : <基础库|服务名|doc> : <标题> 
+栗子:
+    1. library/log : xxxxxxxx
+    2. account-service : xxxxxxxx
+    3. doc : xxxxxxxx
+-->
+### Desc
+<!-- 描述 -->
+
+### Goals
+* <goal>
+* <goal>
+
+### Links / Refs
+* <ref>
+* <ref>
+
+<!-- /cc @xxx @yyy 抄送某人 -->
+
+/label ~refactor

+ 22 - 0
.gitlab/issue_templates/Suggestion.md

@@ -0,0 +1,22 @@
+<!-- Title规范 : <基础库|服务名|doc> : <标题> 
+栗子:
+    1. library/log : xxxxxxxx
+    2. account-service : xxxxxxxx
+    3. doc : xxxxxxxx
+-->
+### Background
+<!-- 背景介绍 -->
+
+### Suggestion
+<!-- 详细的建议内容 -->
+
+### Expected behaviour
+<!-- 预期表现 -->
+
+### Links / Refs
+* <ref>
+* <ref>
+
+<!-- /cc @xxx @yyy 抄送某人 -->
+
+/lable ~suggestion

+ 18 - 0
.gitlab/merge_request_templates/Common.md

@@ -0,0 +1,18 @@
+<!-- Title规范 : <基础库|服务名|doc|其他> : <标题> 
+栗子:
+    1. library/log : xxxxxxxx
+    2. account-service : xxxxxxxx
+    3. doc : xxxxxxxx
+    4. 搞个大新闻 : xxxxxxxx
+-->
+### Version
+* v1.0.0 : library/log
+* v2.1.0 : account-service
+
+### Related issue
+fix #<issue number>
+
+### Review tips
+<!-- 写给 reviewer 的 tips -->
+* <tip>
+* <tip>

+ 20 - 0
.gometalinter.json

@@ -0,0 +1,20 @@
+{
+  "Linters": {},
+  "Vendor": true,
+  "Deadline": "5m",
+  "Cyclo": 50,
+  "Enable": [
+    "gofmt",
+    "vet",
+    "golint",
+    "vetshadow",
+    "gocyclo",
+    "staticcheck",
+    "deadcode"
+  ],
+  "Exclude": [
+    ".+\\.pb\\.go",
+    ".+\\.pb\\.gw\\.go"
+  ],
+  "MinConfidence": 0.81
+}

+ 1 - 0
.kazelcfg.json

@@ -0,0 +1 @@
+build/root/.kazelcfg.json

+ 19 - 0
.rider/.gometalinter.json

@@ -0,0 +1,19 @@
+{
+  "Linters": {},
+  "Vendor": true,
+  "Deadline": "5m",
+  "Cyclo": 50,
+  "Enable": [
+    "gofmt",
+    "vet",
+    "golint",
+    "vetshadow",
+    "gocyclo",
+    "deadcode"
+  ],
+  "Exclude": [
+    ".+\\.pb\\.go",
+    ".+\\.pb\\.gw\\.go"
+  ],
+  "MinConfidence": 0.81
+}

+ 92 - 0
.rider/CHANGELOG.md

@@ -0,0 +1,92 @@
+## .rider
+
+###Version 2.20
+1. 去除megacheck, unused, gosimple三项
+
+###Version 2.19
+1. ut开放library目录执行
+
+###Version 2.18
+1. 更新unit_test.sh的ReadDir()逻辑
+
+###Version 2.17
+1. sagacheck 添加bgr工具作为规则引擎
+
+###Version 2.16
+1. bazel coverage --config=ci 
+
+###Version 2.15
+1. ut增加自定义lint检查
+2. ut 覆盖率文件过滤monkey.go文件
+3. ut magic流程增加评论用户校验
+
+###Version 2.14
+1. ut执行增加 convey json.
+2. ut执行增加过滤器
+
+###Version 2.13
+1. ut脚本增加覆盖率原始文件上传
+
+### Version 2.12
+1. ut脚本去除包含多重子目录测试结果冗余问题
+ 
+### Version 2.11
+1. ut脚本修复未建mr跳过测试
+
+### Version 2.10
+1. ut脚本magic方法bug修复
+
+### Version 2.9
+1. ut脚本bazel test增加超时及测试报告文件检查
+2. ut脚本start方法增加请求commit statues获取实际commit author
+3. ut脚本upload方法增加入参校验
+
+### Version 2.8
+1. ut脚本加入app/(admin|interface)/main的测试
+2. ut脚本加入结束执行后saga评论报告
+
+### Version 2.7
+1. 单元测试改为bazel test
+2. 去除部分debug打印信息
+
+### Version 2.6
+1. 添加跳过check检测开关
+
+### Version 2.5.3
+1. upload接口异常打印日志
+
+### Version 2.5.2
+1. 加入bazel test相关处理
+
+### Version 2.5.1
+1. 优化ut脚本流程控制
+
+### Version 2.5
+1. ut脚本只执行service/main下dao层和library下的单测
+2. 增加达标标准判断
+    2.1 通过率=100% && 覆盖率>=30% && 当前pkg同比上次执行的单测覆盖增长率>=0
+3. 输出当前执行概况日志
+
+### Version 2.4
+1. 删除编译失败的时候打包变化文件的机制
+2. 测试权限
+
+### Version 2.3
+1.修正检测changelog大小写的问题
+2.修改全量编译的job名称
+
+### Version 2.2
+1. 重构pipeline代码
+2. 修改ut为不允许失败
+
+### Version 2.1
+1. cyclo函数复杂度调整到50
+2. gometalinter过滤掉vendor包
+
+### Version 2.0
+1. 合并优化代码到lint.sh
+2. 修改匹配变化文件的逻辑
+
+### Version 1.0
+1. 将compile重试次数改为1次
+2. 修正lint_if_changed.sh匹配文件的bug

+ 20 - 0
.rider/CONTRIBUTORS.md

@@ -0,0 +1,20 @@
+# Owner
+fangrongchang
+tangyongqiang
+chenjianrong
+zhaobingqing
+chengxing
+hedan
+
+# Author 
+muyang
+fangrongchang
+tangyongqiang
+chenjianrong
+zhaobingqing
+chengxing
+fengshanshan
+hedan
+
+# Reviewer
+all 

+ 20 - 0
.rider/OWNERS

@@ -0,0 +1,20 @@
+# See the OWNERS docs at https://go.k8s.io/owners
+
+approvers:
+- chengxing
+- chenjianrong
+- fangrongchang
+- fengshanshan
+- hedan
+- muyang
+- tangyongqiang
+- zhaobingqing
+reviewers:
+- chengxing
+- chenjianrong
+- fangrongchang
+- fengshanshan
+- hedan
+- muyang
+- tangyongqiang
+- zhaobingqing

+ 48 - 0
.rider/bazel_build.sh

@@ -0,0 +1,48 @@
+#!/bin/bash
+
+type=${1}
+
+echo "$(date) build..." >> /data/gitlab-runner/build.log
+
+function compileall()
+{
+    make build-keep-going
+}
+
+function compilepart()
+{
+    pkgs=`.rider/changepkgs.sh`
+    exitCode=$?
+    if [[ ${exitCode} -ne 0 ]]; then
+        echo ".rider/changepkgs.sh fail"
+        exit ${exitCode}
+    fi
+    if [[ "${pkgs}" = "" ]]; then
+        echo "no changepkgs"
+        exit 0
+    fi
+
+    echo -e "change packages:\n${pkgs}\n"
+
+    paths=""
+    for pkg in ${pkgs}
+    do
+        if [[ "${paths}" != "" ]]; then
+            paths="${paths} union allpaths(//app/..., //${pkg}:all)"
+        else
+            paths="allpaths(//app/..., //${pkg}:all)"
+        fi
+    done
+
+    echo "bazel build..."
+    query=`bazel query "${paths}"`
+    echo -e "${query}\n"
+    bazel build --config=ci --watchfs -k $(echo -e "${query}" | grep -v all-srcs | grep -v package-srcs | grep -v _proto)
+    #bazel build $(bazel query "${paths}" |grep -v all-srcs  |grep -v package-srcs)
+}
+
+if [[ "${type}" = "part" ]]; then
+    compilepart
+else
+    compileall
+fi

+ 21 - 0
.rider/changefiles.sh

@@ -0,0 +1,21 @@
+#!/bin/bash
+
+#set -x
+
+suffix=${1}
+
+url="${CI_SERVER_URL}/api/v4/projects/${CI_PROJECT_ID}/repository/commits/${CI_COMMIT_SHA}/merge_requests?private_token=${CI_PRIVATE_TOKEN}"
+json=$(curl -s ${url})
+if [[ "$json" != "[]" && "$json" != "" ]]; then
+    target_branch=$(echo ${json} | jq -r '.[0].target_branch')
+    source_branch=$(echo ${json} | jq -r '.[0].source_branch')
+    files=$(git diff origin/${target_branch}...origin/${source_branch} --name-only  --diff-filter=ACM | grep -E -i "${suffix}")
+else
+#  url="${CI_SERVER_URL}/api/v4/projects/${CI_PROJECT_ID}/pipelines?private_token=${CI_PRIVATE_TOKEN}&status=success&ref=${CI_COMMIT_REF_NAME}"
+#  commit=$(curl -s ${url} | jq -r 'first(.[] | .sha)')
+##  echo "Last green commit is '${commit}'."
+#  files=$(git diff ${commit} --name-only  --diff-filter=ACM | grep -E "${suffix}")
+    files=$(git diff origin/master...origin/${CI_COMMIT_REF_NAME} --name-only  --diff-filter=ACM | grep -E -i "${suffix}")
+fi
+
+echo -e "${files}"

+ 28 - 0
.rider/changepkgs.sh

@@ -0,0 +1,28 @@
+#!/bin/bash
+
+#set -x
+
+files=`.rider/changefiles.sh "*\.go$"`
+
+exitCode=$?
+if [[ ${exitCode} -ne 0 ]]; then
+    echo ".rider/changefiles.sh fail"
+    exit ${exitCode}
+fi
+
+pkgs=""
+for file in ${files}
+do
+    pkg="${file%/*}"
+    if [ $? -eq 0 ]; then
+        if [[ "${pkgs}" = "" ]]; then
+            pkgs="${pkg}"
+        else
+            pkgs="${pkgs}\n${pkg}"
+        fi
+    fi
+done
+echo -e "${pkgs}" > .rider/.pkgs
+pkgs=`sort .rider/.pkgs|uniq`
+
+echo -e "${pkgs}"

+ 39 - 0
.rider/lint.sh

@@ -0,0 +1,39 @@
+#!/bin/bash
+
+#set -x
+
+pkgs=`.rider/changepkgs.sh|grep -v ^vendor/`
+
+exitCode=$?
+if [[ ${exitCode} -ne 0 ]]; then
+    echo ".rider/changepkgs.sh fail"
+    exit ${exitCode}
+fi
+
+if [[ "${pkgs}" = "" ]]; then
+    echo "no changepkgs"
+    exit 0
+fi
+
+echo -e "change packages:\n${pkgs}\n"
+
+if [ ! -d "${CI_PROJECT_DIR}/../src" ];then
+    mkdir ${CI_PROJECT_DIR}/../src
+fi
+ln -fs ${CI_PROJECT_DIR} ${CI_PROJECT_DIR}/../src
+export GOPATH=${CI_PROJECT_DIR}/..
+echo "GOPATH: $GOPATH"
+cd $GOPATH/src/go-common
+
+exitCode=0
+echo -e "\ngometalinter:"
+
+output=`gometalinter --config=.rider/.gometalinter.json ${pkgs}`
+exitCode=$?
+if [[ "${output}" != "" ]]; then
+    exitCode=1
+    echo -e "${output}"
+fi
+
+exit ${exitCode}
+

+ 24 - 0
.rider/make_update.sh

@@ -0,0 +1,24 @@
+#!/bin/bash
+
+#set -x
+
+if [ ! -d "${CI_PROJECT_DIR}/../src" ];then
+  mkdir ${CI_PROJECT_DIR}/../src
+fi
+ln -fs ${CI_PROJECT_DIR} ${CI_PROJECT_DIR}/../src
+export GOPATH=${CI_PROJECT_DIR}/..
+echo "GOPATH: $GOPATH"
+
+vendor=${CI_PROJECT_DIR}/vendor
+for dir in $(ls $vendor)
+do
+  if [ -d $vendor/$dir ]; then
+    ln -fs $vendor/$dir $GOPATH/src
+  fi
+done
+
+sleep $[ ( $RANDOM % 60 )  + 1 ]s
+
+cd ${CI_PROJECT_DIR}/../src/go-common
+#mkdir -p .git/hooks/pre-commit
+make bazel-update

+ 28 - 0
.rider/sagacheck.sh

@@ -0,0 +1,28 @@
+#!/bin/bash
+
+if [ ! -d "${CI_PROJECT_DIR}/../src" ];then
+    mkdir ${CI_PROJECT_DIR}/../src
+fi
+ln -fs ${CI_PROJECT_DIR} ${CI_PROJECT_DIR}/../src
+export GOPATH=${CI_PROJECT_DIR}/..
+exitCode=0
+
+# CHANGELOG check
+echo "====CHANGELOG check:===="
+files=`.rider/changefiles.sh "CHANGELOG.md"`
+if [[ "${files}" = "" ]]; then
+    echo "未发现CHANGELOG.md文件变更,请'添加'或'修改'CHANGELOG.md"
+    exit 1
+else
+    echo -e "变更如下:\n${files}"
+fi
+
+# BGR rule
+echo -e "\n====Bili golang rule check:===="
+diffFiles=`.rider/changefiles.sh`
+cd $GOPATH/src/go-common
+go build ./app/tool/bgr
+./bgr -script=./app/tool/bgr -hit=main -type=file ${diffFiles}
+exitCode=$?
+
+exit ${exitCode}

+ 267 - 0
.rider/unit_test.sh

@@ -0,0 +1,267 @@
+#!/bin/bash
+
+declare -a dirs=(dao)
+declare -a packages
+declare -a projects
+declare mergeUser
+declare commitUser
+declare mergeID
+
+#init env
+function Init(){
+    if [ ! -d "${CI_PROJECT_DIR}/../src" ];then
+        mkdir ${CI_PROJECT_DIR}/../src
+    fi
+    ln -fs ${CI_PROJECT_DIR} ${CI_PROJECT_DIR}/../src
+    export GOPATH=${CI_PROJECT_DIR}/..
+}
+
+function GetPackages(){
+    reg="library/(.*)/(.*).go"
+    for dir in ${dirs[@]}
+    do
+        reg+="|app/(service|interface|admin|job)/main/(.*)/${dir}/(.*).go"
+    done
+    files=`.rider/changefiles.sh ${suffix} | grep  -E "${reg}"`
+    if [[ "${files}" = "" ]]; then
+        echo "shell.GetPackages: no change files"
+        exit 0
+    fi
+    for file in ${files}
+    do
+        # if [[ "${file}" =~ "library"* || "${file}" =~ "/mock" ]]; then
+        if [[ "${file}" =~ "/mock" ]]; then
+            continue
+        fi
+        package="go-common/$(dirname ${file})"
+        if [[ ${packages} =~ ${package} ]]; then
+            continue
+        fi
+        packages+=${package}" "
+        packageSplit=(${package//\// })
+        project=$(printf "%s/" ${packageSplit[@]:0:6})
+        if [[ ${projects} =~ ${project} || ${project} =~ "/library" ]]; then
+            continue
+        fi
+        projects+=${project%*/}" "
+    done
+    if [[ ${packages} = "" || ${projects} = "" ]]; then
+        echo "shell.GetPackages no change packages"
+        exit 0
+    fi
+}
+# GetUserInfo get userinfo by gitlab result.
+function GetUserInfo(){
+    gitMergeRequestUrl="${CI_SERVER_URL}/api/v4/projects/${CI_PROJECT_ID}/repository/commits/${CI_COMMIT_SHA}/merge_requests?private_token=${CI_PRIVATE_TOKEN}"
+    gitCommitUrl="${CI_SERVER_URL}/api/v4/projects/${CI_PROJECT_ID}/repository/commits/${CI_COMMIT_SHA}/statuses?private_token=${CI_PRIVATE_TOKEN}"
+    mergeJson=$(curl -s ${gitMergeRequestUrl})
+    commitJson=$(curl -s ${gitCommitUrl})
+    if [[ "${mergeJson}" = "[]" ]] || [[ "${commitJson}" = "[]" ]]; then
+        echo "Test not run, maybe you should try create a merge request first!"
+        exit 0       
+    fi
+    mergeID=$(echo ${mergeJson} | jq -r '.[0].iid')
+    mergeUser=$(echo ${mergeJson} | jq -r '.[0].author.username')
+    commitUser=$(echo ${commitJson} | jq -r '.[0].author.username')
+}
+
+# Magic ignore method Check()
+function Magic(){
+    url="http://git.bilibili.co/api/v4/projects/${CI_PROJECT_ID}/merge_requests/${mergeID}/notes?private_token=${CI_PRIVATE_TOKEN}"
+    json=$(curl -s ${url})
+    admin="haoguanwei,chenjianrong,hedan,fengshanshan,zhaobingqing"
+    len=$(echo ${json} | jq '.|length')
+    for i in $(seq 0 $len)
+    do
+        comment=$(echo ${json} | jq -r ".[$i].body")
+        user=$(echo ${json} | jq -r ".[$i].author.username")
+        if [[ ${comment} = "+skiput" && ${admin} =~ ${user} ]]; then
+             exit 0
+        fi
+    done
+}
+
+# Check determine whether the standard is up to standard
+#$1: commit_id
+function Check(){
+    curl -s "${CI_UATSVEN_URL}/x/admin/apm/ut/git/report?project_id=${CI_PROJECT_ID}&merge_id=${mergeID}&commit_id=${CI_COMMIT_SHA}"
+    checkURL="${CI_UATSVEN_URL}/x/admin/apm/ut/check?commit_id=${CI_COMMIT_SHA}"
+    json=$(curl -s ${checkURL})
+    code=$(echo ${json} | jq -r '.code')
+    if [[ ${code} -ne 0 ]]; then
+        echo -e "curl ${checkURL} response(${json})"
+        exit 1
+    fi
+    package=$(echo ${json} | jq -r '.data.package')
+    coverage=$(echo ${json} | jq -r '.data.coverage')
+    passRate=$(echo ${json} | jq -r '.data.pass_rate')
+    standard=$(echo ${json} | jq -r '.data.standard')
+    increase=$(echo ${json} | jq -r '.data.increase')
+    tyrant=$(echo ${json} | jq -r '.data.tyrant')
+    lastCID=$(echo ${json} | jq -r '.data.last_cid')
+    if ${tyrant}; then
+        echo -e "\t续命失败!\n\t大佬,本次执行结果未达标哦(灬ꈍ ꈍ灬),请再次优化ut重新提交🆙"
+        echo -e "\t---------------------------------------------------------------------"
+        printf "\t%-14s %-14s %-14s %-14s\n" "本次覆盖率(%)" "本次通过率(%)" "本次增长量(%)" 执行pkg
+        printf "\t%-13.2f %-13.2f %-13.2f %-12s\n" ${coverage} ${passRate} ${increase} ${package}
+        echo -e "\t(达标标准:覆盖率>=${standard} && 通过率=100% && 同比当前package历史最高覆盖率的增长率>=0)"
+        echo -e "\t---------------------------------------------------------------------"
+        echo -e "本次执行详细结果查询地址请访问:http://sven.bilibili.co/#/ut?merge_id=${mergeID}&&pn=1&ps=20"
+        exit 1
+    else
+        echo -e "\t恭喜你,续命成功,可以请求合并MR了!"
+    fi
+}
+
+function ReadDir(){
+    # get go-common/app all dir path
+    gopath=${GOPATH%..}
+    PathDirs=`find ${gopath}app -maxdepth 3 -type d`
+    value=""
+    for dir in ${PathDirs}
+    do
+        if [[ -d "$dir" ]];then
+            for file in `find ${dir} -maxdepth 1 -type f |grep "OWNERS"`
+            do
+                owner=""
+                substr=${dir#*"go-common"}
+                while read line
+                do
+                    if [[ "${line}" = "#"* ]] || [[ "${line}" = "" ]] || [[ "${line}" = "approvers:" ]];then
+                        continue
+                    elif [[ "${line}" = "labels:"* ]];then
+                        break
+                    else
+                        owner+="${line:1},"
+                    fi
+                done < ${file}
+                value+="{\"path\":\"go-common${substr}\",\"owner\":\"${owner%,}\"},"
+            done
+        fi
+    done
+    # delete "," at the end of value
+    value=${value%,}
+    echo "[${value}]" > path.out
+}
+
+# UTLint check the *_test.go files in the pkg
+# $1: pkg
+function UTLint()
+{   
+    cd $GOPATH
+    declare -i numCase=0
+    declare -i numAssertion=0
+    files=$(ls $1 | grep -E "(.*)_test\.go")
+    if [[ ${#files} -eq 0 ]];then
+        echo "RunPKGUT.UTLint no *_test.go files in pkg($1)"
+        exit 1
+    fi
+    for file in ${files}
+    do
+        numCase+=`grep -c -E "^func Test(.+)\(t \*testing\.T\) \{$" $1/${file}`
+        numAssertion+=`grep -c -E "^(.*)So\((.+)\)$" $1/${file}`
+    done
+    if [[ ${numCase} -eq 0 || ${numAssertion} -eq 0 ]];then
+        echo -e "RunPKGUT.UTLint no test case or assertion in pkg($1)"
+        exit 1
+    fi
+}
+
+# BazelTest execute bazel coverage and go tool
+# $1: pkg
+function BazelTest(){
+    cd $GOPATH/go-common
+    pkg=${1//go-common//}":go_default_test"
+    path=${1//go-common\//}
+
+    bazel coverage --config=ci --instrumentation_filter="//${path}[:],-//${path}/mock[/:]" --test_env=DEPLOY_ENV=uat --test_timeout=60 --test_env=APP_ID=bazel.test --test_output=all --cache_test_results=auto --test_arg=-convey-json ${pkg} > result.out
+    if [[ ! -s result.out ]]; then 
+        echo "==================================WARNING!======================================"
+        echo "No test case found,请完善如下路径测试用例: ${pkg} "
+        exit 1
+    else
+        echo $?
+        cp $GOPATH/go-common/bazel-out/k8-fastbuild/testlogs/${path}/go_default_test/coverage.dat ./
+        go tool cover -html=coverage.dat -o cover.html
+    fi
+}
+
+# BazelTest execute bazel coverage for All files
+# $1: pkg(go-common/app/admin/main/xxx/dao)
+function BazelTestAll(){
+    cd $GOPATH/go-common
+    pkg=${1//go-common//}"/..."
+    path=${1//go-common\//}
+    echo "RunProjUT.BazelTestAll(${1}) pkg(${pkg}) path(${path}) pwd($(pwd))"
+    bazel coverage --config=ci --instrumentation_filter="//${path}[/:],-//${path}/mock[/:]" --test_env=DEPLOY_ENV=uat --test_timeout=60 --test_env=APP_ID=bazel.test --test_output=all --cache_test_results=auto --test_arg=-convey-json ${pkg} > result.out
+    find bazel-out/k8-fastbuild/testlogs/${path} -name "coverage.dat" | xargs cat | sort -nr | rev | uniq -s 1 | rev > coverage.dat
+    coverage=$(cat coverage.dat | awk '{sum += $2;covSum += $2 * $3;} END {print covSum/sum*100}')
+    sed -if "s/coverage: .*%/coverage: ${coverage}%/g" result.out
+    go tool cover -html=coverage.dat -o cover.html
+}
+
+# upload data to apm
+# $1: file result.out path
+function Upload () {
+    if [[ ! -f "result.out" ]] || [[ ! -f "cover.html" ]] || [[ ! -f "coverage.dat" ]]; then
+        echo "==================================WARNING!======================================"
+        echo "No test found!~ 请完善如下路径测试用例: ${1} "
+        exit 1
+    fi
+    url="${CI_UATSVEN_URL}/x/admin/apm/ut/upload?merge_id=${mergeID}&username=${mergeUser}&author=${commitUser}&commit_id=${CI_COMMIT_SHA}&pkg=${1}"
+    json=$(curl -s ${url} -H "Content-type: multipart/form-data" -F "html_file=@cover.html" -F "report_file=@result.out" -F "data_file=@coverage.dat")
+    if [[ "${json}" = "" ]]; then
+        echo "RunPKGUT.Upload curl ${url} fail"
+        exit 1
+    fi
+    msg=$(echo ${json} | jq -r '.message')
+    data=$(echo ${json} | jq -r '.data')
+    code=$(echo ${json} | jq -r '.code')
+    if [[ ${code} -ne 0 ]]; then
+        echo "=============================================================================="
+        echo -e "RunPKGUT.Upload Response. message(${msg})"
+        echo -e "RunPKGUT.Upload Response. data(${data})\n\n"
+        echo -e "RunPKGUT.Upload Upload Fail! status(${code})"
+        exit ${code}
+    fi
+}
+
+function RunPKGUT(){
+    for package in ${packages}
+    do
+        echo "RunPKGUT.UTLint Start. pkg(${package})"
+        UTLint ${package}
+        echo "RunPKGUT.BazelTest Start. pkg(${package})"
+        BazelTest ${package}
+        echo "RunPKGUT.Upload Start. pkg(${package})"
+        Upload ${package}
+    done
+    return 0
+}
+
+function RunProjUT(){
+    for project in ${projects}
+    do
+        echo "RunProjUT.BazelTestAll Start. project(${project})"
+        BazelTestAll ${project}
+        echo "RunProjUT.Upload BazelTest Start. project(${project})"
+        Upload ${project%/*}
+    done
+}
+
+function UploadApp(){
+    ReadDir
+    url="${CI_UATSVEN_URL}/x/admin/apm/ut/upload/app" 
+    curl -s ${url} -H "Content-type: multipart/form-data" -F "path_file=@path.out" > /dev/null
+    echo "UploadApp() UpPath has finshed."
+}
+
+# run
+Init
+GetPackages
+GetUserInfo
+Magic
+RunPKGUT
+RunProjUT
+UploadApp
+Check

+ 1 - 0
BUILD.bazel

@@ -0,0 +1 @@
+build/root/BUILD.root

+ 481 - 0
CHANGELOG.md

@@ -0,0 +1,481 @@
+### Go-Common
+### Version 7.19
+1. 更新vendor下github.com/xanzy/go-gitlab包
+
+### Version 7.18
+1. 删除无用的git文件
+
+### Version 7.17
+1. 添加 WithContext 方法, Conn 待实现
+2. 使用 traceConn 实现 trace 埋点
+3. GetMulti 检查 key 列表不为空, 空列表直接返回空 map
+4. legalKey 检查 key 不为空
+
+### Version 7.16.8
+1. 修改saga check无需在master分支跑
+
+### Version 7.16.7
+1. pipeline编译暂时去掉merge master
+
+### Version 7.16.5
+1. pipeline增加bazel局部编译job
+
+### Version 7.16.4
+1. pipeline增加unitest的执行stage
+
+### Version 7.16.3
+1. gometalinter暂时不检查pb
+
+### Version 7.16.3
+1. 修正gometalinter的bug
+2.合并make update到COMPILE里
+
+### Version 7.16.2
+1. 增加saga-admin的功能
+
+### Version 7.16.1
+1. 将编译次数改为一次
+2. 增加gometalinter
+
+### Version 7.15.2
+1. 树状结构展示个业务方权限责任人
+
+### Version 7.15.1
+1. 去掉发邮件的stage
+2. 调整makeupdate为独立的stage
+
+### Version 7.14.3
+1. 由于历史代码太多无法通过lint,所以暂时允许lint失败
+2. 增加将lint不通过的文件列表打印出来
+
+### Version 7.14.2
+1. Runner触发以后,先Merge Master,然后触发Compile
+
+### Version 7.14.1
+1. 为bazel info添加-k参数
+
+### Version 7.14.0 
+1. memcache large value storage
+
+### Version 7.13.2
+1. 修复ecode.OK也打Error日志  
+
+### Version 7.13.1 
+1. 调整cache chan prom数据记录时机   
+
+### Version 7.13.0 
+1. 全链路传递超时时间  
+ 
+  ### Version 7.13.1
+1. HTTP Client 升级,修复此升级导致的identity基础库bug
+
+ ### Version 7.13.0
+1. HTTP Client 升级,通用参数统一配置
+
+### Version 7.12.0
+1. log保留\n,替换\r为空  
+
+### Version 7.11.0
+1. 迁移account interface到Kratos
+
+### Version 7.10.1
+1. 修复hbase依赖btree
+
+### Version 7.10.0
+1. 迁移account service到Kratos
+
+### Version 7.9.0
+1. 增加stack 信息记录,
+2. 修改statHandler为一个方法调用
+
+### Version 7.8.1
+1. 更新ecode的readme和changelog
+
+### Version 7.8.0
+1. 增加pkg/errors 包,用于记录错误信息堆栈信息
+2. 在ecode的example中增加error使用example
+
+### Version 7.7.2
+1. 修复了memcache存入数据, Object & Value 为nil的情况
+
+### Version 7.7.1
+1. 修复redigo
+2. 修复了databus sdk Commit的饥饿问题
+
+### Version 7.7.0
+1. 迁移golang库里的redigo到go-common里的cache/redis
+2. 修复了cache/redis 普罗米修斯耗时、异常上报
+
+### Version 7.6.0
+1. 新增spy service rpc client
+2. 增加history
+
+### Version 7.5.0
+> 1. cache/memcache 升级支持protobuf
+> 2. cache/memcache 破坏性增加了conn.Scan去掉了item.Scan方法
+> 3. business/client/identify 缓存gob改成了protobuf
+> 4. vendor新增了github.com/gogo/protobuf的依赖
+
+
+### Version 7.4.2
+> 1. 更新vendor里golang库到最新版
+
+### Version 7.4.1
+> 1. 更新ecode文档
+
+### Version 7.4.0
+> 1. ecode 获取code message由从数据库全量更新改为通过接口增量更新
+> 2. 升级配置,不兼容老的版本,参考 http://info.bilibili.co/pages/viewpage.action?pageId=3684076
+
+### Version 7.3.0
+> 1.支持vendor
+> 2.继承了location-service
+
+### Version 7.2.0
+> 1.big-repo ,修改business目录  
+
+
+### Version 7.1.0
+> 1.添加secure model  
+> 2.修改location model  
+
+### Version 7.0.0
+> 1.合并go-business
+> 2.合并rouer
+> 3.拆分interceptor
+
+### Version 6.24.5
+> 1.http client的breaker状态变更支持上报prometheus  
+
+### Version 6.24.4
+> 1.rpc server支持recover
+
+### Version 6.24.3
+> 1.修复databus中offset为0不能commit的问题
+
+### Version 6.24.2
+> 1.增加syscall/signal 对 macos(darwin) 的支持
+
+### Version 6.24.1
+> 1.强制要求http和rpc client设置breaker,否则会运行panic
+
+### Version 6.24.0
+> 1.去disconf,使用config-service SDK作为唯一Client
+
+### Version 6.23.0
+> 1.memcache新增序列化和压缩
+
+> 2.新版memcache接口
+
+> 3.net/http新增错误普罗米修斯上报
+
+<b>memcache不再兼容,带有破坏性修改!!!!!!!</b>
+
+### Version 6.22.2
+> 1.去掉vendor
+
+### Version 6.22.1
+1. 修复 syslog 在linux环境下,空指针错误
+
+### Version 6.22.0
+1.新增vendor支持第三方依赖包
+
+### Version 6.21.1
+1.fix mc Stat, 以及增加单元测试
+
+### Version 6.21.0
+兼容了windows,编译:
+1. 增加了Windows上Signal信号处理的Fake方法;
+2. 增加了Syslog兼容的Fake方法;
+
+喜欢windows开发的同学,可以
+
+syslog -> go-common/syslog(syslog日志收集);
+
+os/Signal ->go-common/os/signal,syscall -> go-common/syscall(信号处理);
+
+
+### Version 6.20.0
+> 1.迁移golang库中的gomemcache,交由go-common/cache/memcache维护;
+> 2.优化了net/trace包内私有方法;
+
+### Version 6.19.2
+> 1.修复database/sql Stmt函数漏初始化db变量导致的panic
+
+### Version 6.19.1
+> 1.解决先前版本readme的冲突  
+
+### Version 6.19.0
+> 1.add RESTful httpclient
+
+### Version 6.18.0 
+> 1.修复mysql lifetime,迁移mysql配置  
+
+### Version 6.17.1
+
+> 1.修复log-agent sdk收集日志中有换行符未转义的bug
+
+### Version 6.17.0
+
+> 1.新增log-agent日志收集sdk,以unix socket方式发送日志
+
+### Version 6.16.0
+
+> 1.修改httpconf  
+> 1.改为读写锁读取配置  
+
+### Version 6.15.0
+
+> 1.修改net/netutil熔断器支持全局开关
+
+### Version 6.14.0
+
+> 1.config sdk增加读取appoint参数,用作回退时读取指定配置文件
+### Version 6.14.0
+
+> 1.config sdk增加读取appoint参数,用作回退时读取指定配置文件
+
+### Version 6.13.0
+
+> 1.调整router handler参数,将函数内部join pattern改为外部传入完整pattern
+
+### Version 6.12.1
+
+> 1.修复db 事务初始化的bug  
+
+### Version 6.12.0
+
+> 1.stat支持prometheus功能,实现统计和监控  
+
+### Version 6.11.0
+> 1. 对reids进行了修改,以后不依赖conf包了,配置直接写在redis本包
+ 
+### Version 6.10.0
+> 1. 增加rpc sharding
+
+### Version 6.9.0
+
+> 1.配置中心client 启动参数增加token字段,区分应用和环境
+
+### Version 6.8.0
+
+> 1.依赖zookeeper的rpc client由连接池改为单连接  
+> 2.breaker新增了callback,通知状态变更
+
+### Version 6.7.2
+> 1.修改zookeeper注册参数  
+
+### Version 6.7.1
+
+> 1.配置中心增加获得配置文件路径方法
+
+### Version 6.7.0
+
+1.fix rpc权重为0时,client不创建长连接  
+2.rpc增加配置是否注册zookeeper  
+
+### Version 6.6.4
+
+> 1.fix mc expire max ttl
+
+### Version 6.6.3
+
+> 1. 将配置中心启动参数设置成和disconf的一样
+
+### Version 6.6.2
+
+> 1. 优化了net/http Client的buffer过小导致的syscall过多
+
+### Version 6.6.1
+
+> 1.fix http client超时设置不准确的问题,去掉了读包体和反序列化的时间  
+
+### Version 6.6.0
+> 1.rpc Broadcast 添加reply参数,支持对任意方法进行广播  
+
+### Version 6.5.2
+
+> 1.fix 新版配置中心和老版本init冲突问题
+
+### Version 6.5.1
+
+> 1.fix rpc Boardcast的bug  
+
+### Version 6.5.0
+> 1. 新版本配置中心conf/Client  
+
+### Version 6.4.1
+> 1. 修复remoteip获取  
+
+### Version 6.4.0
+> 1. 去除rpcx  
+
+### Version 6.3.1
+
+> 1.fix配置文件名覆盖的问题  
+
+### Version 6.3.0
+> 1. net/rpc支持了Boardcast广播调用
+
+### Version 6.2.5
+> 1. net/rpc支持了group路由策略
+
+### Version 6.2.4
+> 1. 优化了statsd批量发包
+
+### Version 6.2.3
+> 1. 修复了trace comment 在annocation的bug
+
+### Version 6.2.2
+> 1. 优化了net/rpc反射带来的性能问题
+> 2. net/rpc内置了ping
+
+### Version 6.2.1
+> 1. 临时加回net/rpcx, TODO remove
+> 2. net/trace.Trace2 奔溃和race修复
+
+### Version 6.2.0
+> 1. 去除了net/rpcx
+
+### Version 6.1.3
+> 1. 新增了memcache Get2/Gets
+
+### Version 6.1.2
+> 1. net/rpc使用CPU个数建立连接
+
+### Version 6.1.1
+> 1. 兼容net/rpc server的Client trace传递
+
+### Version 6.1.0
+> 1. 升级databus sdk,注意配置文件有变更
+
+#### Version 6.0.0
+
+> 1. xtime->time, xlog->log perf->net/http/perf
+> 2. rpc支持设置方法级别超时
+> 3. rpc支持breaker熔断
+> 4. database 修复Row和标准库不兼容,使用database Rows替换标准库的Rows使用
+> 5. 新的rpc框架net/rpc
+> 6. net/trace支持Family初始化
+
+#### Version 5.2.2
+
+> 1.Zone结构体加json tag  
+
+#### Version 5.2.0
+
+> 1.更改http包名和路径  
+> 2.增加http单元测试  
+> 3.statd去掉hostname  
+> 4.ip结构体增加isp字段  
+
+#### Version 5.1.2
+
+> 1.xip改为支持对象访问,去掉全局对象和函数  
+
+#### Version 5.1.1
+
+> 1.修复上报trace的位置  
+
+#### Version 5.1.0
+
+> 1.支持熔断  
+> 2.rpc server判断zk是否注册  
+> 3.修复Infoc连接重连  
+> 4.xhttp xrpc xweb改为httpx rpcx webx  
+> 5.修复trace level的bug  
+
+#### Version 5.0.0
+
+> 0.注意一定要使用Go1.7及以上版本  
+> 1.用golang/rpcx替换官方库  
+> 2.使用go1.7的context包  
+> 3.增加traceon业务监控上报  
+> 4.xhttp中ip方法挪到xip包  
+> 5.rpc服务暴露close接口  
+> 6.修复ugc配置中心等待30s的bug  
+> 7.修复rpc client因权重变更导致panic的bug  
+> 8.使用context.WithTimeout替代timer  
+
+#### Version 4.4.1
+
+> 1.日志新增按文件大小rotate  
+
+#### Version 4.4.0
+
+> 1.infoc支持udp和tcp方式  
+> 2.去掉stdout、stderr输出到syslog的逻辑  
+
+#### Version 4.3.2
+
+> 1.fix rpc timeout连接泄露的bug  
+> 2.rpc单连接改为多连接  
+
+#### Version 4.3.1
+
+> 1.支持从环境变量获取配置  
+> 2.syslog支持打印标准输出和错误  
+
+#### Version 4.3.0
+
+> 1.支持配置中心  
+
+#### Version 4.2.0
+
+> 1.修复xredis keys的bug  
+> 2.修复xmemcache批量删除bug  
+> 3.新增 databus v2 客户端   
+
+#### Version 4.1.3
+
+> 1.trace 优化  
+> 2.去掉sp 运营商字段  
+
+#### Version 4.1.2
+
+> 1.trace id改为int64  
+> 2.trace http client增加host  
+> 3.ip新增运营商字段  
+
+#### Version 4.1.1
+
+> 1.fix kafka monitor  
+
+#### Version 4.1.0
+
+> 1.去掉ecode和router  
+
+### Version 4.0.0
+
+> 1.business移到go-business  
+> 2.新增InternalIp()获取本机ip  
+> 3.rpc ping加超时  
+> 4.增加ecode配置  
+> 5.新增支持syslog  
+
+#### Version 3.6.6
+
+> 1.修复xip边界值时死循环问题  
+
+#### Version 3.6.5
+
+> 1.space接口只保留s_img、l_img  
+> 2.archive-service新增viewPage的rpc方法  
+
+#### Version 3.6.4
+
+> 1.VIP相关接口及错误码  
+
+### Version 3.6.3
+
+> 1.修复ip递归查找导致的栈溢出  
+
+#### Version 3.6.2
+
+> 1.account-service profile的http接口、批量获取relation接口  
+> 2.账号新增official_verify字段  
+
+#### Version 3.6.1
+
+> 1.修复degrade中变量名错误  
+> 2.简化redis的auth逻辑,使用option

+ 9 - 0
CONTRIBUTORS.md

@@ -0,0 +1,9 @@
+# Owner
+maojian
+haoguanwei
+
+# Author 
+all
+
+# Reviewer
+all

+ 40 - 0
HERO.md

@@ -0,0 +1,40 @@
+# HERO
+
+## 人类(Human)
+
+- 大法师(Arch mage)-- AM
+- 山丘之王(Mountain King)-- MK
+- 圣骑士(Paladin)-- PAL
+- 血法师(Blood Mage)-- BMG
+
+## 兽族(Orc)
+
+- 先知(Far Seer)-- FS
+- 暗影猎手(Shadow hunter)-- XY
+- 牛头人酋长(Tauren Chieftain)-- NT
+- 剑圣(BladeMaster)-- BM
+
+## 暗夜(Night Elf)
+
+- 守望者(Warden)-- WD
+- 月之女祭师(Priess Of the moon)-- POM
+- 恶魔猎手(Demon Hunter)-- DH
+- 丛林守护者(Keepper of the grove)-- KOG
+
+## 亡灵(Undead)
+
+- 地穴领主(Crypt Lord)-- CL
+- 死亡骑士(Death Knight)-- DK
+- 巫妖(Lich)-- LICH
+- 恐惧魔王(Dread Lord)-- DL
+
+## 中立
+
+- 熊猫酒仙(Pandarenbrewer)-- XM
+- 火焰领主(Flame Lord)-- FL
+- 地精炼金术士(Goblin Alchemist)-- LJ
+- 深渊魔王(Pit Lord)-- PL
+- 黑暗游侠(Dark Ranger)-- BR
+- 驯兽师(BEASTMASTER)-- SW
+- 地精修补匠(Tinker)-- TINKER
+- 娜迦女海巫(Naga Seawitch)-- Naga

+ 1 - 0
Makefile

@@ -0,0 +1 @@
+build/root/Makefile

+ 5 - 0
OWNERS

@@ -0,0 +1,5 @@
+# See the OWNERS docs at https://go.k8s.io/owners
+
+approvers:
+- haoguanwei
+- maojian

+ 525 - 1
README.md

@@ -1,2 +1,526 @@
-# openbilibili
+### Kratos
 
+##### 项目规范
+1,每个目录 需要有独立的README.md  CHANGELOG.md CONTRIBUTORS.md,具体可以参考:
+http://git.bilibili.co/platform/go-common/tree/master/business/service/archive
+
+2,以后每个业务或者基础组件维护自己的版本号,在CHANGELOG.md中,rider 构建以后的tag关联成自己的版本号;
+
+3,整个大仓库不再有tag,只有master 主干分支,所有mr发送前,一定要注意先merge master;
+
+4,使用Rider构建以后(retag),回滚可以基于Rider的retag来回滚,而不是回滚大仓库的代码;
+
+5,提供RPC内部服务放置在business/service中,任务队列放置在business/job中,对外网关服务放置在business/interface,管理后台服务放置在business/admin
+
+6,每个业务自建cmd文件夹,将main.go文件和test配置文件迁移进去
+
+7,构建的时候自定义脚本选择krotos_buil.sh,自定义参数选择自己所在业务的路径 (ps:例如 interface/web-show)
+
+8,大仓库的mr合并方式为,在mr中留言"+merge",鉴权依据服务根目录下 CONTRIBUTORS.md 文件解析,具体可以参考:
+http://info.bilibili.co/pages/viewpage.action?pageId=7539410
+
+## 负责人信息
+<details>
+<summary>展开查看</summary>
+<pre><code>.
+├── Owner: maojian,haoguanwei
+├── app
+│   ├── Owner: maojian,haoguanwei,linmiao
+│   ├── admin
+│   │   ├── ep
+│   │   │   ├── merlin
+│   │   │   │   └── Owner: maojian,yuanmin,fengyifeng,xuneng
+│   │   │   └── saga
+│   │   │       └── Owner: tangyongqiang
+│   │   ├── main
+│   │   │   ├── activity
+│   │   │   │   └── Owner: liweijia,zhapuyu,renwei
+│   │   │   ├── answer
+│   │   │   │   └── Owner: zhaogangtao
+│   │   │   ├── apm
+│   │   │   │   └── Owner: haoguanwei,lintanghui
+│   │   │   ├── app
+│   │   │   │   └── Owner: haoguanwei,peiyifei
+│   │   │   ├── appstatic
+│   │   │   │   └── Owner: liweijia,renwei
+│   │   │   ├── bfs-apm
+│   │   │   │   └── Owner: wangweizhen
+│   │   │   ├── block
+│   │   │   │   └── Owner: zhaogangtao
+│   │   │   ├── cache
+│   │   │   │   └── Owner: lintanghui
+│   │   │   ├── config
+│   │   │   │   └── Owner: haoguanwei,lintanghui
+│   │   │   ├── coupon
+│   │   │   │   └── Owner: yubaihai,zhaogangtao
+│   │   │   ├── creative
+│   │   │   │   └── Owner: shencen,wangzhe01
+│   │   │   ├── credit
+│   │   │   │   └── Owner: zhaogangtao
+│   │   │   ├── dm
+│   │   │   │   └── Owner: liangkai,renwei
+│   │   │   ├── esports
+│   │   │   │   └── Owner: liweijia,renwei
+│   │   │   ├── filter
+│   │   │   │   └── Owner: zhaogangtao,muyang
+│   │   │   ├── growup
+│   │   │   │   └── Owner: gaopeng
+│   │   │   ├── laser
+│   │   │   │   └── Owner: haoguanwei,shencen,wangzhe01
+│   │   │   ├── manager
+│   │   │   │   └── Owner: liweijia,zhapuyu,renwei
+│   │   │   ├── member
+│   │   │   │   └── Owner: linmiao,haoguanwei,zhoujiahui,zhoujixiang,chenjianrong
+│   │   │   ├── point
+│   │   │   │   └── Owner: yubaihai,zhaogangtao
+│   │   │   ├── push
+│   │   │   │   └── Owner: liweijia,zhapuyu,renwei
+│   │   │   ├── relation
+│   │   │   │   └── Owner: linmiao,zhoujiahui
+│   │   │   ├── reply
+│   │   │   │   └── Owner: chenzhihui,lujinhui
+│   │   │   ├── search
+│   │   │   │   └── Owner: liweijia,zhapuyu,renwei,guanhuaxin
+│   │   │   ├── sms
+│   │   │   │   └── Owner: renwei,zhapuyu
+│   │   │   ├── spy
+│   │   │   │   └── Owner: zhaogangtao
+│   │   │   ├── tag
+│   │   │   │   └── Owner: renwei,renyashun
+│   │   │   ├── tv
+│   │   │   │   └── Owner: liweijia,renwei
+│   │   │   ├── up
+│   │   │   │   └── Owner: shencen,wangzhe01
+│   │   │   ├── upload
+│   │   │   │   └── Owner: haoguanwei,zhapuyu
+│   │   │   ├── usersuit
+│   │   │   │   └── Owner: zhaogangtao
+│   │   │   ├── videoup
+│   │   │   │   └── Owner: shencen,wangzhe01
+│   │   │   ├── videoup-task
+│   │   │   │   └── Owner: shencen,wangzhe01
+│   │   │   ├── vip
+│   │   │   │   └── Owner: zhaogangtao
+│   │   │   └── workflow
+│   │   │       └── Owner: haoguanwei,zhapuyu,zhuangzhewei,zhoushuguang
+│   │   └── openplatform
+│   │       └── sug
+│   │           └── Owner: changxuanran,xucheng
+│   ├── common
+│   │   └── openplatform
+│   │       └── Owner: liuzhan,huangshancheng
+│   ├── interface
+│   │   ├── live
+│   │   │   ├── Owner: liuzhen
+│   │   │   └── push-live
+│   │   │       └── Owner: kuangxibin
+│   │   └── main
+│   │       ├── account
+│   │       │   └── Owner: wanghuan01,zhoujiahui,zhaogangtao,chenjianrong,zhoujixiang
+│   │       ├── activity
+│   │       │   └── Owner: liweijia
+│   │       ├── answer
+│   │       │   └── Owner: zhaogangtao
+│   │       ├── app-channel
+│   │       │   └── Owner: peiyifei
+│   │       ├── app-feed
+│   │       │   └── Owner: peiyifei
+│   │       ├── app-interface
+│   │       │   └── Owner: peiyifei
+│   │       ├── app-player
+│   │       │   └── Owner: peiyifei
+│   │       ├── app-resource
+│   │       │   └── Owner: peiyifei
+│   │       ├── app-show
+│   │       │   └── Owner: peiyifei
+│   │       ├── app-tag
+│   │       │   └── Owner: peiyifei
+│   │       ├── app-view
+│   │       │   └── Owner: peiyifei
+│   │       ├── app-wall
+│   │       │   └── Owner: peiyifei
+│   │       ├── article
+│   │       │   └── Owner: changxuanran,lijiadong,qiuliang
+│   │       ├── broadcast
+│   │       │   └── Owner: chenzhihui,caoguoliang,guhao
+│   │       ├── captcha
+│   │       │   └── Owner: chenzhihui
+│   │       ├── creative
+│   │       │   └── Owner: shencen,wangzhe01
+│   │       ├── credit
+│   │       │   └── Owner: zhaogangtao
+│   │       ├── dm
+│   │       │   └── Owner: liangkai,renwei
+│   │       ├── dm2
+│   │       │   └── Owner: liangkai,renwei
+│   │       ├── esports
+│   │       │   └── Owner: liweijia,zhapuyu
+│   │       ├── favorite
+│   │       │   └── Owner: chenzhihui,lujinhui
+│   │       ├── feedback
+│   │       │   └── Owner: peiyifei
+│   │       ├── growup
+│   │       │   └── Owner: gaopeng
+│   │       ├── history
+│   │       │   └── Owner: renwei,wangxu01
+│   │       ├── kvo
+│   │       │   └── Owner: liweijia,zhapuyu
+│   │       ├── laser
+│   │       │   └── Owner: haoguanwei,shencen
+│   │       ├── player
+│   │       │   └── Owner: liweijia,zhapuyu
+│   │       ├── playlist
+│   │       │   └── Owner: liweijia
+│   │       ├── push
+│   │       │   └── Owner: renwei,zhapuyu
+│   │       ├── push-archive
+│   │       │   └── Owner: zhapuyu,shencen,renwei,liweijia,wangzhe01
+│   │       ├── reply
+│   │       │   └── Owner: lujinhui,chenzhihui,caoguoliang
+│   │       ├── report-click
+│   │       │   └── Owner: zhangshengchao,chenzhihui,renyashun
+│   │       ├── shorturl
+│   │       │   └── Owner: peiyifei,zhapuyu
+│   │       ├── space
+│   │       │   └── Owner: liweijia,zhapuyu
+│   │       ├── spread
+│   │       │   └── Owner: zhapuyu,renwei
+│   │       ├── tag
+│   │       │   └── Owner: renwei,renyashun
+│   │       ├── tv
+│   │       │   └── Owner: renwei,liweijia
+│   │       ├── upload
+│   │       │   └── Owner: peiyifei,zhapuyu
+│   │       ├── videoup
+│   │       │   └── Owner: shencen,wangzhe01
+│   │       ├── web
+│   │       │   └── Owner: liweijia,zhapuyu
+│   │       ├── web-feed
+│   │       │   └── Owner: zhapuyu,liweijia,renwei
+│   │       ├── web-goblin
+│   │       │   └── Owner: liweijia,renwei
+│   │       └── web-show
+│   │           └── Owner: liweijia
+│   ├── job
+│   │   ├── live
+│   │   │   ├── Owner: liuzhen
+│   │   │   └── wallet
+│   │   │       └── Owner: lixiang,zhouzhichao
+│   │   ├── main
+│   │   │   ├── account-notify
+│   │   │   │   └── Owner: wanghuan01
+│   │   │   ├── account-summary
+│   │   │   │   └── Owner: zhoujiahui
+│   │   │   ├── activity
+│   │   │   │   └── Owner: liweijia
+│   │   │   ├── answer
+│   │   │   │   └── Owner: zhaogangtao
+│   │   │   ├── app
+│   │   │   │   └── Owner: peiyifei
+│   │   │   ├── app-wall
+│   │   │   │   └── Owner: peiyifei,renwei,haoguanwei
+│   │   │   ├── archive
+│   │   │   │   └── Owner: peiyifei
+│   │   │   ├── archive-kisjd
+│   │   │   │   └── Owner: peiyifei
+│   │   │   ├── article
+│   │   │   │   └── Owner: qiuliang,changxuanran,lijiadong
+│   │   │   ├── block
+│   │   │   │   └── Owner: zhaogangtao
+│   │   │   ├── broadcast
+│   │   │   │   └── Owner: chenzhihui,caoguoliang,guhao
+│   │   │   ├── click
+│   │   │   │   └── Owner: peiyifei
+│   │   │   ├── coin
+│   │   │   │   └── Owner: lintanghui,linmiao,zhapuyu
+│   │   │   ├── coupon
+│   │   │   │   └── Owner: zhaogangtao,yubaihai
+│   │   │   ├── creative
+│   │   │   │   └── Owner: shencen,wangzhe01
+│   │   │   ├── credit
+│   │   │   │   └── Owner: zhaogangtao
+│   │   │   ├── credit-timer
+│   │   │   │   └── Owner: zhaogangtao
+│   │   │   ├── dm
+│   │   │   │   └── Owner: liangkai,renwei
+│   │   │   ├── dm2
+│   │   │   │   └── Owner: liangkai,renwei
+│   │   │   ├── favorite
+│   │   │   │   └── Owner: lujinhui,chenzhihui
+│   │   │   ├── feed
+│   │   │   │   └── Owner: liweijia,zhapuyu,renwei
+│   │   │   ├── figure
+│   │   │   │   └── Owner: zhaogangtao
+│   │   │   ├── figure-timer
+│   │   │   │   └── Owner: zhaogangtao
+│   │   │   ├── growup
+│   │   │   │   └── Owner: gaopeng
+│   │   │   ├── history
+│   │   │   │   └── Owner: renwei,wangxu01
+│   │   │   ├── identify
+│   │   │   │   └── Owner: linmiao,wanghuan01
+│   │   │   ├── member
+│   │   │   │   └── Owner: chenjianrong,zhoujiahui,linmiao,zhoujixiang
+│   │   │   ├── passport
+│   │   │   │   └── Owner: wanghuan01
+│   │   │   ├── passport-auth
+│   │   │   │   └── Owner: wanghuan01
+│   │   │   ├── passport-encrypt
+│   │   │   │   └── Owner: linmiao
+│   │   │   ├── passport-game-cloud
+│   │   │   │   └── Owner: wanghuan01
+│   │   │   ├── passport-game-data
+│   │   │   │   └── Owner: wanghuan01
+│   │   │   ├── passport-game-local
+│   │   │   │   └── Owner: wanghuan01
+│   │   │   ├── playlist
+│   │   │   │   └── Owner: liweijia
+│   │   │   ├── point
+│   │   │   │   └── Owner: yubaihai,zhaogangtao
+│   │   │   ├── push
+│   │   │   │   └── Owner: liweijia,zhapuyu,renwei
+│   │   │   ├── relation
+│   │   │   │   └── Owner: linmiao,zhoujiahui
+│   │   │   ├── reply
+│   │   │   │   └── Owner: chenzhihui,lujinhui,caoguoliang
+│   │   │   ├── search
+│   │   │   │   └── Owner: liweijia,zhapuyu,renwei,guanhuaxin
+│   │   │   ├── sms
+│   │   │   │   └── Owner: renwei,zhapuyu
+│   │   │   ├── spy
+│   │   │   │   └── Owner: zhaogangtao
+│   │   │   ├── stat
+│   │   │   │   └── Owner: peiyifei
+│   │   │   ├── tag
+│   │   │   │   └── Owner: renwei,renyashun
+│   │   │   ├── tv
+│   │   │   │   └── Owner: renwei,liweijia
+│   │   │   ├── upload
+│   │   │   │   └── Owner: zhapuyu,renwei,zhuangzhewei
+│   │   │   ├── usersuit
+│   │   │   │   └── Owner: zhaogangtao
+│   │   │   ├── videoup
+│   │   │   │   └── Owner: shencen,wangzhe01
+│   │   │   ├── videoup-report
+│   │   │   │   └── Owner: shencen,wangzhe01
+│   │   │   ├── vip
+│   │   │   │   └── Owner: zhaogangtao
+│   │   │   ├── web-goblin
+│   │   │   │   └── Owner: liweijia,renwei
+│   │   │   └── workflow
+│   │   │       └── Owner: haoguanwei,zhapuyu
+│   │   └── openplatform
+│   │       └── open-market
+│   │           └── Owner: changxuanran,liuyan02,qiuliang
+│   ├── service
+│   │   ├── ep
+│   │   │   └── saga-agent
+│   │   │       └── Owner: muyang,tangyongqiang,fangrongchang
+│   │   ├── live
+│   │   │   ├── Owner: liuzhen
+│   │   │   ├── userexp
+│   │   │   │   └── Owner: kuangxibing
+│   │   │   └── wallet
+│   │   │       └── Owner: lixiang,zhouzhichao
+│   │   ├── main
+│   │   │   ├── account
+│   │   │   │   └── Owner: wanghuan01,zhoujiahui
+│   │   │   ├── antispam
+│   │   │   │   └── Owner: chenzhihui,lujinhui
+│   │   │   ├── archive
+│   │   │   │   └── Owner: haoguanwei,peiyifei
+│   │   │   ├── assist
+│   │   │   │   └── Owner: shencen,wangzhe01
+│   │   │   ├── block
+│   │   │   │   └── Owner: zhaogangtao
+│   │   │   ├── bns
+│   │   │   │   └── Owner: haoguawnei weicheng
+│   │   │   ├── broadcast
+│   │   │   │   └── Owner: chenzhihui,caoguoliang,guhao
+│   │   │   ├── canal
+│   │   │   │   └── Owner: haoguanwei
+│   │   │   ├── coin
+│   │   │   │   └── Owner: lintanghui,linmiao,zhapuyu
+│   │   │   ├── config
+│   │   │   │   └── Owner: maojian
+│   │   │   ├── coupon
+│   │   │   │   └── Owner: zhaogangtao,yubaihai
+│   │   │   ├── dapper
+│   │   │   │   └── Owner: maojian,haoguanwei
+│   │   │   ├── databus
+│   │   │   │   └── Owner: haoguanwei
+│   │   │   ├── discovery
+│   │   │   │   └── Owner: haoguanwei,lintanghui
+│   │   │   ├── dynamic
+│   │   │   │   └── Owner: liweijia,zhapuyu
+│   │   │   ├── favorite
+│   │   │   │   └── Owner: chenzhihui,lujinhui
+│   │   │   ├── feed
+│   │   │   │   └── Owner: renwei,zhapuyu
+│   │   │   ├── figure
+│   │   │   │   └── Owner: zhaogangtao
+│   │   │   ├── filter
+│   │   │   │   └── Owner: zhaogangtao,muyang
+│   │   │   ├── identify
+│   │   │   │   └── Owner: wanghuan01
+│   │   │   ├── identify-game
+│   │   │   │   └── Owner: wanghuan01
+│   │   │   ├── location
+│   │   │   │   └── Owner: peiyifei,haoguanwei
+│   │   │   ├── member
+│   │   │   │   └── Owner: zhaogangtao,wanghuan01,zhoujiahui,chenjianrong,zhoujixiang
+│   │   │   ├── msm
+│   │   │   │   └── Owner: maojian
+│   │   │   ├── notify
+│   │   │   │   └── Owner: haoguanwei,lintanghui
+│   │   │   ├── passport
+│   │   │   │   └── Owner: wanghuan01
+│   │   │   ├── passport-auth
+│   │   │   │   └── Owner: wanghuan01
+│   │   │   ├── passport-game
+│   │   │   │   └── Owner: wanghuan01
+│   │   │   ├── point
+│   │   │   │   └── Owner: yubaihai,zhaogangtao
+│   │   │   ├── push
+│   │   │   │   └── Owner: renwei,zhapuyu
+│   │   │   ├── push-strategy
+│   │   │   │   └── Owner: renwei,zhapuyu
+│   │   │   ├── relation
+│   │   │   │   └── Owner: linmiao,zhoujiahui
+│   │   │   ├── resource
+│   │   │   │   └── Owner: haoguanwei,peiyifei
+│   │   │   ├── search
+│   │   │   │   └── Owner: liweijia,zhapuyu,renwei,guanhuaxin
+│   │   │   ├── secure
+│   │   │   │   └── Owner: zhaogangtao,lintanghui
+│   │   │   ├── seq-server
+│   │   │   │   └── Owner: peiyifei
+│   │   │   ├── share
+│   │   │   │   └── Owner: peiyifei,haoguanwei
+│   │   │   ├── sms
+│   │   │   │   └── Owner: renwei,zhapuyu
+│   │   │   ├── spy
+│   │   │   │   └── Owner: zhaogangtao
+│   │   │   ├── tag
+│   │   │   │   └── Owner: renwei,renyashun
+│   │   │   ├── thumbup
+│   │   │   │   └── Owner: liweijia,zhapuyu,renwei
+│   │   │   ├── up
+│   │   │   │   └── Owner: shencen,wangzhe01
+│   │   │   ├── upcredit
+│   │   │   │   └── Owner: shencen,wangzhe01
+│   │   │   ├── usersuit
+│   │   │   │   └── Owner: zhaogangtao
+│   │   │   ├── videoup
+│   │   │   │   └── Owner: shencen,wangzhe01
+│   │   │   ├── vip
+│   │   │   │   └── Owner: lintanghui,zhaogangtao
+│   │   │   └── workflow
+│   │   │       └── Owner: haoguanwei,zhapuyu,zhoushuguang
+│   │   └── openplatform
+│   │       ├── abtest
+│   │       │   └── Owner: lijiadong,qiuliang
+│   │       ├── anti-fraud
+│   │       │   └── Owner: wanglitao,wangminda,jiayanxiang
+│   │       ├── ticket-item
+│   │       │   └── Owner: yangyucheng
+│   │       └── ticket-sales
+│   │           └── Owner: liuzhan,yangyucheng
+│   └── tool
+│       ├── cache
+│       │   └── Owner: zhapuyu
+│       ├── ci
+│       │   └── Owner: tangyongqiang
+│       ├── creater
+│       │   └── Owner: chenjianrong
+│       ├── gdoc
+│       │   └── Owner: lintanghui
+│       ├── saga
+│       │   └── Owner: muyang,tangyongqiang
+│       └── warden
+│           └── Owner: weicheng
+└── library
+    ├── cache
+    │   ├── memcache
+    │   │   └── Owner: maojian
+    │   └── redis
+    │       └── Owner: maojian
+    ├── container
+    │   └── pool
+    │       └── Owner: zhapuyu
+    ├── database
+    │   ├── elastic
+    │   │   └── Owner: haoguanwei,renwei,zhapuyu
+    │   └── sql
+    │       └── Owner: 
+    ├── ecode
+    │   ├── Owner: all
+    │   └── tip
+    │       └── Owner: all
+    ├── exp
+    │   └── feature
+    │       └── Owner: zhoujiahui
+    ├── log
+    │   └── Owner: maojian
+    ├── naming
+    │   └── discovery
+    │       └── Owner: lintanghui,caoguoliang
+    ├── net
+    │   ├── http
+    │   │   ├── Owner: maojian
+    │   │   └── blademaster
+    │   │       ├── Owner: 
+    │   │       ├── middleware
+    │   │       │   ├── Owner: 
+    │   │       │   ├── antispam
+    │   │       │   │   └── Owner: 
+    │   │       │   ├── auth
+    │   │       │   │   └── Owner: maojian,zhoujiahui
+    │   │       │   ├── cache
+    │   │       │   │   └── Owner: 
+    │   │       │   ├── identify
+    │   │       │   │   └── Owner: 
+    │   │       │   ├── limit
+    │   │       │   │   └── aqm
+    │   │       │   │       └── Owner: 
+    │   │       │   ├── proxy
+    │   │       │   │   └── Owner: 
+    │   │       │   ├── rate
+    │   │       │   │   └── Owner: 
+    │   │       │   ├── supervisor
+    │   │       │   │   └── Owner: 
+    │   │       │   ├── tag
+    │   │       │   │   └── Owner: maojian
+    │   │       │   └── verify
+    │   │       │       └── Owner: maojian,zhoujiahui
+    │   │       └── render
+    │   │           └── Owner: 
+    │   ├── metadata
+    │   │   └── Owner: 
+    │   ├── netutil
+    │   │   └── breaker
+    │   │       └── Owner: 
+    │   ├── rpc
+    │   │   └── warden
+    │   │       ├── Owner: maojian,caoguoliang
+    │   │       ├── balancer
+    │   │       │   └── wrr
+    │   │       │       └── Owner: caoguoliang
+    │   │       └── resolver
+    │   │           └── Owner: caoguoliang
+    │   └── trace
+    │       └── Owner: maojian
+    ├── rate
+    │   └── limit
+    │       └── bench
+    │           └── stress
+    │               └── Owner: lintanghui
+    ├── stat
+    │   └── sys
+    │       └── cpu
+    │           └── Owner: caoguoliang
+    └── sync
+        └── errgroup
+            └── Owner: 
+</code></pre>
+</details>

+ 1 - 0
WORKSPACE

@@ -0,0 +1 @@
+build/root/WORKSPACE

+ 23 - 0
app/BUILD

@@ -0,0 +1,23 @@
+package(default_visibility = ["//visibility:public"])
+
+filegroup(
+    name = "package-srcs",
+    srcs = glob(["**"]),
+    tags = ["automanaged"],
+    visibility = ["//visibility:private"],
+)
+
+filegroup(
+    name = "all-srcs",
+    srcs = [
+        ":package-srcs",
+        "//app/admin:all-srcs",
+        "//app/common:all-srcs",
+        "//app/infra:all-srcs",
+        "//app/interface:all-srcs",
+        "//app/job:all-srcs",
+        "//app/service:all-srcs",
+        "//app/tool:all-srcs",
+    ],
+    tags = ["automanaged"],
+)

+ 10 - 0
app/CONTRIBUTORS.md

@@ -0,0 +1,10 @@
+# Owner
+maojian
+haoguanwei
+renwei
+
+# Author 
+all
+
+# Reviewer
+all

+ 6 - 0
app/OWNERS

@@ -0,0 +1,6 @@
+# See the OWNERS docs at https://go.k8s.io/owners
+
+approvers:
+- haoguanwei
+- maojian
+- renwei

+ 13 - 0
app/README.md

@@ -0,0 +1,13 @@
+# go-common/business
+
+业务仓库目录
+
+| 目录 | 描述 |
+| -------- | -------------- |
+| service  | rpc service    |
+| client   | rpc client sdk |
+| interface| gateway        |
+| admin    | 运营管理服务   |
+| job      | 后台异步服务job|
+| model    | 业务实体model  |
+| ecode    | 统一错误码     |

+ 21 - 0
app/admin/BUILD

@@ -0,0 +1,21 @@
+package(default_visibility = ["//visibility:public"])
+
+filegroup(
+    name = "package-srcs",
+    srcs = glob(["**"]),
+    tags = ["automanaged"],
+    visibility = ["//visibility:private"],
+)
+
+filegroup(
+    name = "all-srcs",
+    srcs = [
+        ":package-srcs",
+        "//app/admin/bbq:all-srcs",
+        "//app/admin/ep:all-srcs",
+        "//app/admin/live/live-admin:all-srcs",
+        "//app/admin/main:all-srcs",
+        "//app/admin/openplatform:all-srcs",
+    ],
+    tags = ["automanaged"],
+)

+ 5 - 0
app/admin/OWNERS

@@ -0,0 +1,5 @@
+# See the OWNERS docs at https://go.k8s.io/owners
+
+labels:
+- admin
+- new-project

+ 3 - 0
app/admin/README.md

@@ -0,0 +1,3 @@
+# go-common/app/admin
+
+运营管理服务

+ 16 - 0
app/admin/bbq/BUILD

@@ -0,0 +1,16 @@
+filegroup(
+    name = "package-srcs",
+    srcs = glob(["**"]),
+    tags = ["automanaged"],
+    visibility = ["//visibility:private"],
+)
+
+filegroup(
+    name = "all-srcs",
+    srcs = [
+        ":package-srcs",
+        "//app/admin/bbq/comment:all-srcs",
+    ],
+    tags = ["automanaged"],
+    visibility = ["//visibility:public"],
+)

+ 9 - 0
app/admin/bbq/OWNERS

@@ -0,0 +1,9 @@
+# See the OWNERS docs at https://go.k8s.io/owners
+
+approvers:
+- daiwei
+labels:
+- admin
+- bbq
+reviewers:
+- daiwei

+ 20 - 0
app/admin/bbq/comment/BUILD

@@ -0,0 +1,20 @@
+filegroup(
+    name = "package-srcs",
+    srcs = glob(["**"]),
+    tags = ["automanaged"],
+    visibility = ["//visibility:private"],
+)
+
+filegroup(
+    name = "all-srcs",
+    srcs = [
+        ":package-srcs",
+        "//app/admin/bbq/comment/cmd:all-srcs",
+        "//app/admin/bbq/comment/internal/dao:all-srcs",
+        "//app/admin/bbq/comment/internal/model:all-srcs",
+        "//app/admin/bbq/comment/internal/server/http:all-srcs",
+        "//app/admin/bbq/comment/internal/service:all-srcs",
+    ],
+    tags = ["automanaged"],
+    visibility = ["//visibility:public"],
+)

+ 2 - 0
app/admin/bbq/comment/CHANGELOG.md

@@ -0,0 +1,2 @@
+### v1.0.0
+1. 上线功能xxx

+ 6 - 0
app/admin/bbq/comment/CONTRIBUTORS.md

@@ -0,0 +1,6 @@
+# Owner
+daiwei
+
+# Author
+
+# Reviewer

+ 10 - 0
app/admin/bbq/comment/OWNERS

@@ -0,0 +1,10 @@
+# See the OWNERS docs at https://go.k8s.io/owners
+
+approvers:
+- daiwei
+labels:
+- admin
+- admin/bbq/comment
+- bbq
+options:
+  no_parent_owners: true

+ 12 - 0
app/admin/bbq/comment/README.md

@@ -0,0 +1,12 @@
+# comment-admin
+
+## 项目简介
+1.
+
+## 编译环境
+
+
+## 依赖包
+
+
+## 编译执行

+ 42 - 0
app/admin/bbq/comment/cmd/BUILD

@@ -0,0 +1,42 @@
+package(default_visibility = ["//visibility:public"])
+
+load(
+    "@io_bazel_rules_go//go:def.bzl",
+    "go_binary",
+    "go_library",
+)
+
+go_binary(
+    name = "cmd",
+    embed = [":go_default_library"],
+    tags = ["automanaged"],
+)
+
+go_library(
+    name = "go_default_library",
+    srcs = ["main.go"],
+    importpath = "go-common/app/admin/bbq/comment/cmd",
+    tags = ["automanaged"],
+    visibility = ["//visibility:public"],
+    deps = [
+        "//app/admin/bbq/comment/internal/server/http:go_default_library",
+        "//app/admin/bbq/comment/internal/service:go_default_library",
+        "//library/conf/paladin:go_default_library",
+        "//library/ecode/tip:go_default_library",
+        "//library/log:go_default_library",
+    ],
+)
+
+filegroup(
+    name = "package-srcs",
+    srcs = glob(["**"]),
+    tags = ["automanaged"],
+    visibility = ["//visibility:private"],
+)
+
+filegroup(
+    name = "all-srcs",
+    srcs = [":package-srcs"],
+    tags = ["automanaged"],
+    visibility = ["//visibility:public"],
+)

+ 48 - 0
app/admin/bbq/comment/cmd/main.go

@@ -0,0 +1,48 @@
+package main
+
+import (
+	"context"
+	"flag"
+	"os"
+	"os/signal"
+	"syscall"
+	"time"
+
+	"go-common/app/admin/bbq/comment/internal/server/http"
+	"go-common/app/admin/bbq/comment/internal/service"
+	"go-common/library/conf/paladin"
+	ecode "go-common/library/ecode/tip"
+	"go-common/library/log"
+)
+
+func main() {
+	flag.Parse()
+	if err := paladin.Init(); err != nil {
+		panic(err)
+	}
+	log.Init(nil) // debug flag: log.dir={path}
+	defer log.Close()
+	log.Info("comment-admin start")
+	ecode.Init(nil)
+	svc := service.New()
+	httpSrv := http.New(svc)
+	c := make(chan os.Signal, 1)
+	signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
+	for {
+		s := <-c
+		log.Info("get a signal %s", s.String())
+		switch s {
+		case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
+			ctx, cancel := context.WithTimeout(context.Background(), 35*time.Second)
+			defer cancel()
+			httpSrv.Shutdown(ctx)
+			log.Info("comment-admin exit")
+			svc.Close()
+			time.Sleep(time.Second)
+			return
+		case syscall.SIGHUP:
+		default:
+			return
+		}
+	}
+}

+ 2 - 0
app/admin/bbq/comment/configs/application.toml

@@ -0,0 +1,2 @@
+
+# This is a TOML document. Boom

+ 0 - 0
app/admin/bbq/comment/configs/grpc.toml


+ 4 - 0
app/admin/bbq/comment/configs/http.toml

@@ -0,0 +1,4 @@
+
+[server]
+    addr = "0.0.0.0:8000"
+    timeout = "1s"

+ 13 - 0
app/admin/bbq/comment/configs/memcache.toml

@@ -0,0 +1,13 @@
+
+demoExpire = "24h"
+
+[demo]
+	name = "comment"
+	proto = "tcp"
+	addr = "127.0.0.1:11211"
+	active = 50
+	idle = 10
+	dialTimeout = "100ms"
+	readTimeout = "200ms"
+	writeTimeout = "300ms"
+	idleTimeout = "80s"

+ 11 - 0
app/admin/bbq/comment/configs/mysql.toml

@@ -0,0 +1,11 @@
+
+[demo]
+	addr = "127.0.0.1:3306"
+	dsn = "{user}:{password}@tcp(127.0.0.1:3306)/{database}?timeout=1s&readTimeout=1s&writeTimeout=1s&parseTime=true&loc=Local&charset=utf8mb4,utf8"
+	readDSN = ["{user}:{password}@tcp(127.0.0.2:3306)/{database}?timeout=1s&readTimeout=1s&writeTimeout=1s&parseTime=true&loc=Local&charset=utf8mb4,utf8","{user}:{password}@tcp(127.0.0.3:3306)/{database}?timeout=1s&readTimeout=1s&writeTimeout=1s&parseTime=true&loc=Local&charset=utf8,utf8mb4"]
+	active = 20
+	idle = 10
+	idleTimeout ="4h"
+	queryTimeout = "200ms"
+	execTimeout = "300ms"
+	tranTimeout = "400ms"

+ 13 - 0
app/admin/bbq/comment/configs/redis.toml

@@ -0,0 +1,13 @@
+
+demoExpire = "24h"
+
+[demo]
+	name = "comment"
+	proto = "tcp"
+	addr = "127.0.0.1:6389"
+	idle = 10
+	active = 10
+	dialTimeout = "1s"
+	readTimeout = "1s"
+	writeTimeout = "1s"
+	idleTimeout = "10s"

+ 36 - 0
app/admin/bbq/comment/internal/dao/BUILD

@@ -0,0 +1,36 @@
+package(default_visibility = ["//visibility:public"])
+
+load(
+    "@io_bazel_rules_go//go:def.bzl",
+    "go_library",
+)
+
+go_library(
+    name = "go_default_library",
+    srcs = ["dao.go"],
+    importpath = "go-common/app/admin/bbq/comment/internal/dao",
+    tags = ["automanaged"],
+    visibility = ["//visibility:public"],
+    deps = [
+        "//library/cache/memcache:go_default_library",
+        "//library/cache/redis:go_default_library",
+        "//library/conf/paladin:go_default_library",
+        "//library/database/sql:go_default_library",
+        "//library/log:go_default_library",
+        "//library/time:go_default_library",
+    ],
+)
+
+filegroup(
+    name = "package-srcs",
+    srcs = glob(["**"]),
+    tags = ["automanaged"],
+    visibility = ["//visibility:private"],
+)
+
+filegroup(
+    name = "all-srcs",
+    srcs = [":package-srcs"],
+    tags = ["automanaged"],
+    visibility = ["//visibility:public"],
+)

+ 95 - 0
app/admin/bbq/comment/internal/dao/dao.go

@@ -0,0 +1,95 @@
+package dao
+
+import (
+	"context"
+	"time"
+
+	"go-common/library/cache/memcache"
+	"go-common/library/cache/redis"
+	"go-common/library/conf/paladin"
+	"go-common/library/database/sql"
+	"go-common/library/log"
+	xtime "go-common/library/time"
+)
+
+// Dao dao.
+type Dao struct {
+	db          *sql.DB
+	redis       *redis.Pool
+	redisExpire int32
+	mc          *memcache.Pool
+	mcExpire    int32
+}
+
+func checkErr(err error) {
+	if err != nil {
+		panic(err)
+	}
+}
+
+// New new a dao and return.
+func New() (dao *Dao) {
+	var (
+		dc struct {
+			Demo *sql.Config
+		}
+		rc struct {
+			Demo       *redis.Config
+			DemoExpire xtime.Duration
+		}
+		mc struct {
+			Demo       *memcache.Config
+			DemoExpire xtime.Duration
+		}
+	)
+	checkErr(paladin.Get("mysql.toml").UnmarshalTOML(&dc))
+	checkErr(paladin.Get("redis.toml").UnmarshalTOML(&rc))
+	checkErr(paladin.Get("memcache.toml").UnmarshalTOML(&mc))
+	dao = &Dao{
+		// mysql
+		db: sql.NewMySQL(dc.Demo),
+		// redis
+		redis:       redis.NewPool(rc.Demo),
+		redisExpire: int32(time.Duration(rc.DemoExpire) / time.Second),
+		// memcache
+		mc:       memcache.NewPool(mc.Demo),
+		mcExpire: int32(time.Duration(mc.DemoExpire) / time.Second),
+	}
+	return
+}
+
+// Close close the resource.
+func (d *Dao) Close() {
+	d.mc.Close()
+	d.redis.Close()
+	d.db.Close()
+}
+
+// Ping ping the resource.
+func (d *Dao) Ping(ctx context.Context) (err error) {
+	if err = d.pingMC(ctx); err != nil {
+		return
+	}
+	if err = d.pingRedis(ctx); err != nil {
+		return
+	}
+	return d.db.Ping(ctx)
+}
+
+func (d *Dao) pingMC(ctx context.Context) (err error) {
+	conn := d.mc.Get(ctx)
+	defer conn.Close()
+	if err = conn.Set(&memcache.Item{Key: "ping", Value: []byte("pong"), Expiration: 0}); err != nil {
+		log.Error("conn.Set(PING) error(%v)", err)
+	}
+	return
+}
+
+func (d *Dao) pingRedis(ctx context.Context) (err error) {
+	conn := d.redis.Get(ctx)
+	defer conn.Close()
+	if _, err = conn.Do("SET", "ping", "pong"); err != nil {
+		log.Error("conn.Set(PING) error(%v)", err)
+	}
+	return
+}

+ 28 - 0
app/admin/bbq/comment/internal/model/BUILD

@@ -0,0 +1,28 @@
+package(default_visibility = ["//visibility:public"])
+
+load(
+    "@io_bazel_rules_go//go:def.bzl",
+    "go_library",
+)
+
+go_library(
+    name = "go_default_library",
+    srcs = ["model.go"],
+    importpath = "go-common/app/admin/bbq/comment/internal/model",
+    tags = ["automanaged"],
+    visibility = ["//visibility:public"],
+)
+
+filegroup(
+    name = "package-srcs",
+    srcs = glob(["**"]),
+    tags = ["automanaged"],
+    visibility = ["//visibility:private"],
+)
+
+filegroup(
+    name = "all-srcs",
+    srcs = [":package-srcs"],
+    tags = ["automanaged"],
+    visibility = ["//visibility:public"],
+)

+ 1 - 0
app/admin/bbq/comment/internal/model/model.go

@@ -0,0 +1 @@
+package model

+ 35 - 0
app/admin/bbq/comment/internal/server/http/BUILD

@@ -0,0 +1,35 @@
+package(default_visibility = ["//visibility:public"])
+
+load(
+    "@io_bazel_rules_go//go:def.bzl",
+    "go_library",
+)
+
+go_library(
+    name = "go_default_library",
+    srcs = ["http.go"],
+    importpath = "go-common/app/admin/bbq/comment/internal/server/http",
+    tags = ["automanaged"],
+    visibility = ["//visibility:public"],
+    deps = [
+        "//app/admin/bbq/comment/internal/service:go_default_library",
+        "//library/conf/paladin:go_default_library",
+        "//library/log:go_default_library",
+        "//library/net/http/blademaster:go_default_library",
+        "//library/net/http/blademaster/middleware/verify:go_default_library",
+    ],
+)
+
+filegroup(
+    name = "package-srcs",
+    srcs = glob(["**"]),
+    tags = ["automanaged"],
+    visibility = ["//visibility:private"],
+)
+
+filegroup(
+    name = "all-srcs",
+    srcs = [":package-srcs"],
+    tags = ["automanaged"],
+    visibility = ["//visibility:public"],
+)

+ 61 - 0
app/admin/bbq/comment/internal/server/http/http.go

@@ -0,0 +1,61 @@
+package http
+
+import (
+	"net/http"
+
+	"go-common/app/admin/bbq/comment/internal/service"
+	"go-common/library/conf/paladin"
+	"go-common/library/log"
+	bm "go-common/library/net/http/blademaster"
+	"go-common/library/net/http/blademaster/middleware/verify"
+)
+
+var (
+	svc *service.Service
+)
+
+// New new a bm server.
+func New(s *service.Service) (engine *bm.Engine) {
+	var (
+		hc struct {
+			Server *bm.ServerConfig
+		}
+	)
+	if err := paladin.Get("http.toml").UnmarshalTOML(&hc); err != nil {
+		if err != paladin.ErrNotExist {
+			panic(err)
+		}
+	}
+	svc = s
+	engine = bm.DefaultServer(hc.Server)
+	initRouter(engine, verify.New(nil))
+	if err := engine.Start(); err != nil {
+		panic(err)
+	}
+	return
+}
+
+func initRouter(e *bm.Engine, v *verify.Verify) {
+	e.Ping(ping)
+	e.Register(register)
+	g := e.Group("/x/comment")
+	{
+		g.GET("/start", v.Verify, howToStart)
+	}
+}
+
+func ping(ctx *bm.Context) {
+	if err := svc.Ping(ctx); err != nil {
+		log.Error("ping error(%v)", err)
+		ctx.AbortWithStatus(http.StatusServiceUnavailable)
+	}
+}
+
+func register(c *bm.Context) {
+	c.JSON(map[string]interface{}{}, nil)
+}
+
+// example for http request handler.
+func howToStart(c *bm.Context) {
+	c.String(0, "Golang 大法好 !!!")
+}

+ 32 - 0
app/admin/bbq/comment/internal/service/BUILD

@@ -0,0 +1,32 @@
+package(default_visibility = ["//visibility:public"])
+
+load(
+    "@io_bazel_rules_go//go:def.bzl",
+    "go_library",
+)
+
+go_library(
+    name = "go_default_library",
+    srcs = ["service.go"],
+    importpath = "go-common/app/admin/bbq/comment/internal/service",
+    tags = ["automanaged"],
+    visibility = ["//visibility:public"],
+    deps = [
+        "//app/admin/bbq/comment/internal/dao:go_default_library",
+        "//library/conf/paladin:go_default_library",
+    ],
+)
+
+filegroup(
+    name = "package-srcs",
+    srcs = glob(["**"]),
+    tags = ["automanaged"],
+    visibility = ["//visibility:private"],
+)
+
+filegroup(
+    name = "all-srcs",
+    srcs = [":package-srcs"],
+    tags = ["automanaged"],
+    visibility = ["//visibility:public"],
+)

+ 37 - 0
app/admin/bbq/comment/internal/service/service.go

@@ -0,0 +1,37 @@
+package service
+
+import (
+	"context"
+
+	"go-common/app/admin/bbq/comment/internal/dao"
+	"go-common/library/conf/paladin"
+)
+
+// Service service.
+type Service struct {
+	ac  *paladin.Map
+	dao *dao.Dao
+}
+
+// New new a service and return.
+func New() (s *Service) {
+	var ac = new(paladin.TOML)
+	if err := paladin.Watch("application.toml", ac); err != nil {
+		panic(err)
+	}
+	s = &Service{
+		ac:  ac,
+		dao: dao.New(),
+	}
+	return s
+}
+
+// Ping ping the resource.
+func (s *Service) Ping(ctx context.Context) (err error) {
+	return s.dao.Ping(ctx)
+}
+
+// Close close the resource.
+func (s *Service) Close() {
+	s.dao.Close()
+}

+ 21 - 0
app/admin/ep/BUILD

@@ -0,0 +1,21 @@
+package(default_visibility = ["//visibility:public"])
+
+filegroup(
+    name = "package-srcs",
+    srcs = glob(["**"]),
+    tags = ["automanaged"],
+    visibility = ["//visibility:private"],
+)
+
+filegroup(
+    name = "all-srcs",
+    srcs = [
+        ":package-srcs",
+        "//app/admin/ep/marthe:all-srcs",
+        "//app/admin/ep/melloi:all-srcs",
+        "//app/admin/ep/merlin:all-srcs",
+        "//app/admin/ep/saga:all-srcs",
+        "//app/admin/ep/tapd:all-srcs",
+    ],
+    tags = ["automanaged"],
+)

+ 6 - 0
app/admin/ep/OWNERS

@@ -0,0 +1,6 @@
+# See the OWNERS docs at https://go.k8s.io/owners
+
+labels:
+- admin
+- ep
+- new-project

+ 22 - 0
app/admin/ep/marthe/BUILD

@@ -0,0 +1,22 @@
+package(default_visibility = ["//visibility:public"])
+
+filegroup(
+    name = "package-srcs",
+    srcs = glob(["**"]),
+    tags = ["automanaged"],
+    visibility = ["//visibility:private"],
+)
+
+filegroup(
+    name = "all-srcs",
+    srcs = [
+        ":package-srcs",
+        "//app/admin/ep/marthe/cmd:all-srcs",
+        "//app/admin/ep/marthe/conf:all-srcs",
+        "//app/admin/ep/marthe/dao:all-srcs",
+        "//app/admin/ep/marthe/model:all-srcs",
+        "//app/admin/ep/marthe/server/http:all-srcs",
+        "//app/admin/ep/marthe/service:all-srcs",
+    ],
+    tags = ["automanaged"],
+)

+ 4 - 0
app/admin/ep/marthe/CHANGELOG.md

@@ -0,0 +1,4 @@
+##### marthe
+
+##### Version 1. 0. 1
+1. 拉取bugly原始数据以自定义的格式落库

+ 14 - 0
app/admin/ep/marthe/CONTRIBUTORS.md

@@ -0,0 +1,14 @@
+# Owner
+maojian
+yuanmin
+fengyifeng
+xuneng
+
+# Author
+yuanmin
+fengyifeng
+xuneng
+
+# Reviewer
+zhapuyu
+wangxu01

+ 19 - 0
app/admin/ep/marthe/OWNERS

@@ -0,0 +1,19 @@
+# See the OWNERS docs at https://go.k8s.io/owners
+
+approvers:
+- fengyifeng
+- maojian
+- xuneng
+- yuanmin
+labels:
+- admin
+- admin/ep/marthe
+- ep
+options:
+  no_parent_owners: true
+reviewers:
+- fengyifeng
+- wangxu01
+- xuneng
+- yuanmin
+- zhapuyu

+ 19 - 0
app/admin/ep/marthe/README.md

@@ -0,0 +1,19 @@
+# Marthe-service
+
+# 项目简介
+### 背景/Background
+* 目前移动端crash信息会上报到bugly,bugly系统查询数据比较慢且没有足够的数据分析功能,影响开发查询分析crash的效率
+* 目前项目需求bug等信息记录在tapd, 同样tapd系统现有的报表功能并不满足我们PMO的需求,使PMO拿不到充足的数据做分析
+* 于是Marthe项目为了解决这些问题孕育而生
+
+### 概览/Overview
+* Marthe会根据开发和测试的需求,从bugly拉取原始数据并进行二次处理
+
+
+# 编译环境
+
+
+# 依赖包
+
+
+# 编译执行

+ 44 - 0
app/admin/ep/marthe/cmd/BUILD

@@ -0,0 +1,44 @@
+package(default_visibility = ["//visibility:public"])
+
+load(
+    "@io_bazel_rules_go//go:def.bzl",
+    "go_binary",
+    "go_library",
+)
+
+go_binary(
+    name = "cmd",
+    embed = [":go_default_library"],
+    tags = ["automanaged"],
+)
+
+go_library(
+    name = "go_default_library",
+    srcs = ["main.go"],
+    data = ["admin.toml"],
+    importpath = "go-common/app/admin/ep/marthe/cmd",
+    tags = ["automanaged"],
+    visibility = ["//visibility:public"],
+    deps = [
+        "//app/admin/ep/marthe/conf:go_default_library",
+        "//app/admin/ep/marthe/server/http:go_default_library",
+        "//app/admin/ep/marthe/service:go_default_library",
+        "//library/ecode/tip:go_default_library",
+        "//library/log:go_default_library",
+        "//library/net/trace:go_default_library",
+    ],
+)
+
+filegroup(
+    name = "package-srcs",
+    srcs = glob(["**"]),
+    tags = ["automanaged"],
+    visibility = ["//visibility:private"],
+)
+
+filegroup(
+    name = "all-srcs",
+    srcs = [":package-srcs"],
+    tags = ["automanaged"],
+    visibility = ["//visibility:public"],
+)

+ 130 - 0
app/admin/ep/marthe/cmd/admin.toml

@@ -0,0 +1,130 @@
+
+[bm]
+ addr = "0.0.0.0:9001"
+ timeout = "10s"
+
+[httpClient]
+    key = "c05dd4e1638a8af0"
+    secret = "7daa7f8c06cd33c5c3067063c746fdcb"
+    dial = "2s"
+    timeout = "100s"
+    keepAlive = "60s"
+    timer = 1000
+    [httpClient.breaker]
+    window  = "10s"
+    sleep   = "2000ms"
+    bucket  = 10
+    ratio   = 0.5
+    request = 100
+
+[mail]
+host = "smtp.exmail.qq.com"
+port = 465
+username = "merlin@bilibili.com"
+password = ""
+noticeOwner = ["fengyifeng@bilibili.com"]
+
+[memcache]
+	name = "merlin"
+	proto = "tcp"
+	addr = "172.18.33.61:11232"
+	idle = 5
+	active = 10
+	dialTimeout = "1s"
+	readTimeout = "1s"
+	writeTimeout = "1s"
+	idleTimeout = "10s"
+	expire = "12h"
+
+[bugly]
+#bugly URL
+host = "https://bugly.qq.com"
+
+#bugly请求尝试次数
+urlRetryCount = 3
+
+#cookie 使用上限次数
+cookieUsageUpper = 300
+
+#bugly 每页获取issue个数
+issuePageSize = 50
+
+#bugly 抓取issue上限个数
+issueCountUpper = 100
+
+#bugly 超级管理员
+superOwner = ["fengyifeng","yuanmin"]
+
+[tapd]
+#tapd bug相关操作 是否认证tapd权限
+bugOperateAuth = false
+
+
+[Scheduler]
+#每过30分钟 跑enable version 抓bugly数据
+batchRunEnableVersion =  "0 */30 * * * ?"
+
+#每天晚上23点 定时更新tapd bug
+batchRunUpdateTapdBug = "0 0 23 * * ?"
+
+#每10分钟,定时删除超过三小时为执行完毕的任务
+disableBatchRunOverTime =  "0 */10 * * * ?"
+#过期时限
+batchRunOverHourTime = 3
+
+#每天晚上22点,更新同步wechat contact
+syncWechatContact = "0 0 22 * * ?"
+
+#是否开启定时任务
+active = false
+
+
+[orm]
+dsn = "root:123456@tcp(172.18.33.130:3306)/Marthe2?timeout=200ms&readTimeout=2000ms&writeTimeout=2000ms&parseTime=true&loc=Local&charset=utf8,utf8mb4"
+active = 5
+idle = 5
+idleTimeout = "4h"
+
+[auth]
+    managerHost = "http://uat-manager.bilibili.co"
+    dashboardHost = "http://dashboard-mng.bilibili.co"
+    dashboardCaller = "marthe"
+    [auth.DsHTTPClient]
+    key = "marthe"
+    secret = "4344bea3587b383bc0b9de5de0efcc3f"
+    dial = "1s"
+    timeout = "1s"
+    keepAlive = "60s"
+    [auth.DsHTTPClient.breaker]
+    window  = "3s"
+    sleep   = "100ms"
+    bucket  = 10
+    ratio   = 0.5
+    request = 100
+    [auth.MaHTTPClient]
+    key = "f6433799dbd88751"
+    secret = "36f8ddb1806207fe07013ab6a77a3935"
+    dial = "1ms"
+    timeout = "1ms"
+    keepAlive = "60s"
+    [auth.MaHTTPClient.breaker]
+    window  = "3s"
+    sleep   = "100ms"
+    bucket  = 10
+    ratio   = 0.5
+    request = 100
+    [auth.session]
+    sessionIDLength = 32
+    cookieLifeTime = 1
+    cookieName = "mng-go"
+    domain = ".bilibili.co"
+    [auth.session.Memcache]
+    name = "go-business/auth"
+    proto = "tcp"
+    addr = "172.18.33.61:11232"
+    active = 10
+    idle = 10
+    dialTimeout = "1ms"
+    readTimeout = "1ms"
+    writeTimeout = "1ms"
+    idleTimeout = "80s"

+ 106 - 0
app/admin/ep/marthe/cmd/convey-test.toml

@@ -0,0 +1,106 @@
+
+[bm]
+ addr = "0.0.0.0:9001"
+ timeout = "10s"
+
+[httpClient]
+    key = "c05dd4e1638a8af0"
+    secret = "7daa7f8c06cd33c5c3067063c746fdcb"
+    dial = "2s"
+    timeout = "100s"
+    keepAlive = "60s"
+    timer = 1000
+    [httpClient.breaker]
+    window  = "10s"
+    sleep   = "2000ms"
+    bucket  = 10
+    ratio   = 0.5
+    request = 100
+
+[mail]
+host = "smtp.exmail.qq.com"
+port = 465
+username = "merlin@bilibili.com"
+password = ""
+noticeOwner = ["fengyifeng@bilibili.com"]
+
+[memcache]
+	name = "merlin"
+	proto = "tcp"
+	addr = "172.18.33.61:11232"
+	idle = 5
+	active = 10
+	dialTimeout = "1s"
+	readTimeout = "1s"
+	writeTimeout = "1s"
+	idleTimeout = "10s"
+	expire = "12h"
+
+[bugly]
+host = "https://bugly.qq.com"
+urlRetryCount = 3
+cookieUsageUpper = 300
+issuePageSize = 50
+issueCountUpper = 100
+
+[Scheduler]
+#每过10分钟 更新表
+batchRunEnableVersion =  "0 */10 * * * ?"
+
+batchRunUpdateTapdBug = "0 */10 * * * ?"
+
+disableBatchRunOverTime =  "0 */10 * * * ?"
+batchRunOverHourTime = 3
+
+active = false
+
+
+[orm]
+dsn = "root:123456@tcp(172.18.33.130:3306)/Marthe2?timeout=200ms&readTimeout=2000ms&writeTimeout=2000ms&parseTime=true&loc=Local&charset=utf8,utf8mb4"
+active = 5
+idle = 5
+idleTimeout = "4h"
+
+[auth]
+    managerHost = "http://uat-manager.bilibili.co"
+    dashboardHost = "http://dashboard-mng.bilibili.co"
+    dashboardCaller = "merlin"
+    [auth.DsHTTPClient]
+    key = "merlin"
+    secret = "4fb521f66dfd5efcf6e77d078ed2eb0a"
+    dial = "1s"
+    timeout = "1s"
+    keepAlive = "60s"
+    [auth.DsHTTPClient.breaker]
+    window  = "3s"
+    sleep   = "100ms"
+    bucket  = 10
+    ratio   = 0.5
+    request = 100
+    [auth.MaHTTPClient]
+    key = "f6433799dbd88751"
+    secret = "36f8ddb1806207fe07013ab6a77a3935"
+    dial = "1ms"
+    timeout = "1ms"
+    keepAlive = "60s"
+    [auth.MaHTTPClient.breaker]
+    window  = "3s"
+    sleep   = "100ms"
+    bucket  = 10
+    ratio   = 0.5
+    request = 100
+    [auth.session]
+    sessionIDLength = 32
+    cookieLifeTime = 1
+    cookieName = "mng-go"
+    domain = ".bilibili.co"
+    [auth.session.Memcache]
+    name = "go-business/auth"
+    proto = "tcp"
+    addr = "172.18.33.61:11232"
+    active = 10
+    idle = 10
+    dialTimeout = "1ms"
+    readTimeout = "1ms"
+    writeTimeout = "1ms"
+    idleTimeout = "80s"

+ 46 - 0
app/admin/ep/marthe/cmd/main.go

@@ -0,0 +1,46 @@
+package main
+
+import (
+	"flag"
+	"os"
+	"os/signal"
+	"syscall"
+
+	"go-common/app/admin/ep/marthe/conf"
+	"go-common/app/admin/ep/marthe/server/http"
+	"go-common/app/admin/ep/marthe/service"
+	ecode "go-common/library/ecode/tip"
+	"go-common/library/log"
+	"go-common/library/net/trace"
+)
+
+func main() {
+	flag.Parse()
+	if err := conf.Init(); err != nil {
+		panic(err)
+	}
+	log.Init(conf.Conf.Log)
+	defer log.Close()
+	log.Info("start")
+	defer trace.Close()
+	ecode.Init(conf.Conf.Ecode)
+
+	s := service.New(conf.Conf)
+
+	http.Init(conf.Conf, s)
+	c := make(chan os.Signal, 1)
+	signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
+	for {
+		si := <-c
+		log.Info("get a signal %s", si.String())
+		switch si {
+		case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
+			log.Info("exit")
+			s.Close()
+			return
+		case syscall.SIGHUP:
+		default:
+			return
+		}
+	}
+}

+ 39 - 0
app/admin/ep/marthe/conf/BUILD

@@ -0,0 +1,39 @@
+package(default_visibility = ["//visibility:public"])
+
+load(
+    "@io_bazel_rules_go//go:def.bzl",
+    "go_library",
+)
+
+go_library(
+    name = "go_default_library",
+    srcs = ["conf.go"],
+    importpath = "go-common/app/admin/ep/marthe/conf",
+    tags = ["automanaged"],
+    visibility = ["//visibility:public"],
+    deps = [
+        "//library/cache/memcache:go_default_library",
+        "//library/conf:go_default_library",
+        "//library/database/orm:go_default_library",
+        "//library/ecode/tip:go_default_library",
+        "//library/log:go_default_library",
+        "//library/net/http/blademaster:go_default_library",
+        "//library/net/http/blademaster/middleware/permit:go_default_library",
+        "//library/time:go_default_library",
+        "//vendor/github.com/BurntSushi/toml:go_default_library",
+    ],
+)
+
+filegroup(
+    name = "package-srcs",
+    srcs = glob(["**"]),
+    tags = ["automanaged"],
+    visibility = ["//visibility:private"],
+)
+
+filegroup(
+    name = "all-srcs",
+    srcs = [":package-srcs"],
+    tags = ["automanaged"],
+    visibility = ["//visibility:public"],
+)

+ 162 - 0
app/admin/ep/marthe/conf/conf.go

@@ -0,0 +1,162 @@
+package conf
+
+import (
+	"errors"
+	"flag"
+
+	"go-common/library/cache/memcache"
+	"go-common/library/conf"
+	"go-common/library/database/orm"
+	ecode "go-common/library/ecode/tip"
+	"go-common/library/log"
+	bm "go-common/library/net/http/blademaster"
+	"go-common/library/net/http/blademaster/middleware/permit"
+	xtime "go-common/library/time"
+
+	"github.com/BurntSushi/toml"
+)
+
+var (
+	confPath string
+	client   *conf.Client
+	// Conf Config
+	Conf = &Config{}
+)
+
+// Config .
+type Config struct {
+	Log *log.Config
+
+	Bugly *BuglyConf
+
+	BM *bm.ServerConfig
+
+	Ecode *ecode.Config
+
+	ORM *orm.Config
+
+	HTTPClient *bm.ClientConfig
+
+	Mail *Mail
+
+	Auth *permit.Config
+
+	Memcache *Memcache
+
+	Scheduler *Scheduler
+
+	Tapd *TapdConf
+}
+
+func init() {
+	flag.StringVar(&confPath, "conf", "", "default config path")
+}
+
+// Scheduler scheduler
+type Scheduler struct {
+	BatchRunEnableVersion string
+
+	BatchRunUpdateTapdBug string
+
+	DisableBatchRunOverTime string
+	BatchRunOverHourTime    int
+
+	SyncWechatContact string
+
+	Active bool
+}
+
+// Memcache memcache
+type Memcache struct {
+	*memcache.Config
+	Expire xtime.Duration
+}
+
+// Mail mail
+type Mail struct {
+	Host        string
+	Port        int
+	Username    string
+	Password    string
+	NoticeOwner []string
+}
+
+// BuglyConf Bugly Conf.
+type BuglyConf struct {
+	Host             string
+	UrlRetryCount    int
+	CookieUsageUpper int
+	IssuePageSize    int
+	IssueCountUpper  int
+
+	SuperOwner []string
+}
+
+// TapdConf Tapd Conf.
+type TapdConf struct {
+	BugOperateAuth bool
+}
+
+// Tapd Tapd info
+type Tapd struct {
+	IterationWorkspaceIDs []string
+	StoryWorkspaceIDs     []string
+	BugWorkspaceIDs       []string
+	IPS                   int
+	SPS                   int
+	SCPS                  int
+	CPS                   int
+	StoryFilePath         string
+	ChangeFilePath        string
+	IterationFilePath     string
+	BugFilePath           string
+	RetryTime             int
+	WaitTime              xtime.Duration
+}
+
+// Init init conf
+func Init() error {
+	if confPath != "" {
+		return local()
+	}
+	return remote()
+}
+
+func local() (err error) {
+	_, err = toml.DecodeFile(confPath, &Conf)
+	return
+}
+
+func remote() (err error) {
+	if client, err = conf.New(); err != nil {
+		return
+	}
+	if err = load(); err != nil {
+		return
+	}
+	go func() {
+		for range client.Event() {
+			log.Info("config reload")
+			if load() != nil {
+				log.Error("config reload error (%v)", err)
+			}
+		}
+	}()
+	return
+}
+
+func load() (err error) {
+	var (
+		s       string
+		ok      bool
+		tmpConf *Config
+	)
+	if s, ok = client.Toml2(); !ok {
+		return errors.New("load config center error")
+	}
+	if _, err = toml.Decode(s, &tmpConf); err != nil {
+		return errors.New("could not decode config")
+	}
+	*Conf = *tmpConf
+	return
+}

+ 87 - 0
app/admin/ep/marthe/dao/BUILD

@@ -0,0 +1,87 @@
+package(default_visibility = ["//visibility:public"])
+
+load(
+    "@io_bazel_rules_go//go:def.bzl",
+    "go_library",
+    "go_test",
+)
+
+go_library(
+    name = "go_default_library",
+    srcs = [
+        "bugly.go",
+        "dao.go",
+        "mail.go",
+        "mysql_bugly_batch_run.go",
+        "mysql_bugly_cookie.go",
+        "mysql_bugly_issue.go",
+        "mysql_bugly_project.go",
+        "mysql_bugly_version.go",
+        "mysql_contact_info.go",
+        "mysql_schedule_task.go",
+        "mysql_tapd_bug_priority_conf.go",
+        "mysql_tapd_bug_record.go",
+        "mysql_tapd_bug_template.go",
+        "mysql_tapd_bug_version_template.go",
+        "mysql_user.go",
+        "tapd.go",
+        "wechat.go",
+    ],
+    importpath = "go-common/app/admin/ep/marthe/dao",
+    tags = ["automanaged"],
+    visibility = ["//visibility:public"],
+    deps = [
+        "//app/admin/ep/marthe/conf:go_default_library",
+        "//app/admin/ep/marthe/model:go_default_library",
+        "//library/cache/memcache:go_default_library",
+        "//library/database/orm:go_default_library",
+        "//library/ecode:go_default_library",
+        "//library/log:go_default_library",
+        "//library/net/http/blademaster:go_default_library",
+        "//library/sync/pipeline/fanout:go_default_library",
+        "//vendor/github.com/jinzhu/gorm:go_default_library",
+        "//vendor/github.com/pkg/errors:go_default_library",
+        "//vendor/gopkg.in/gomail.v2:go_default_library",
+    ],
+)
+
+filegroup(
+    name = "package-srcs",
+    srcs = glob(["**"]),
+    tags = ["automanaged"],
+    visibility = ["//visibility:private"],
+)
+
+filegroup(
+    name = "all-srcs",
+    srcs = [":package-srcs"],
+    tags = ["automanaged"],
+    visibility = ["//visibility:public"],
+)
+
+go_test(
+    name = "go_default_test",
+    srcs = [
+        "dao_test.go",
+        "mysql_bugly_batch_run_test.go",
+        "mysql_bugly_cookie_test.go",
+        "mysql_bugly_issue_test.go",
+        "mysql_bugly_version_test.go",
+        "mysql_schedule_task_test.go",
+        "mysql_tapd_bug_priority_confg_test.go",
+        "mysql_tapd_bug_record_test.go",
+        "mysql_tapd_bug_template_test.go",
+        "mysql_tapd_bug_version_template_test.go",
+        "mysql_user_test.go",
+    ],
+    embed = [":go_default_library"],
+    tags = ["automanaged"],
+    deps = [
+        "//app/admin/ep/marthe/conf:go_default_library",
+        "//app/admin/ep/marthe/model:go_default_library",
+        "//vendor/github.com/go-sql-driver/mysql:go_default_library",
+        "//vendor/github.com/satori/go.uuid:go_default_library",
+        "//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
+        "//vendor/gopkg.in/h2non/gock.v1:go_default_library",
+    ],
+)

+ 192 - 0
app/admin/ep/marthe/dao/bugly.go

@@ -0,0 +1,192 @@
+package dao
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+	"strconv"
+
+	"go-common/app/admin/ep/marthe/model"
+	"go-common/library/ecode"
+	"go-common/library/log"
+)
+
+const (
+	_buglyOkCode            = 200
+	_buglyCookieExpiredCode = 100006
+	_issueDetailCode        = 100000
+
+	_issueDetailList    = "/v2/lastCrashInfo/appId/%s/platformId/%s/issues/%s/crashDataType/null?offsetTop=56&fsn=6d0260aa-331f-48b9-8557-c2aaf6e0be90"
+	_issueList          = "/v2/issueList?sortOrder=desc&sortField=uploadTime&rows=50&fsn=45cdb5aa-eb6f-4bda-9bba-ba0b264bfc93&appId=%s&platformId=%s&version=%s&start=%s&rows=%s&exceptionTypeList=%s"
+	_issueVersionList   = "/v2/getSelector/appId/%s/platformId/%s?types=version&fsn=8b8782b5-053d-4f58-bc17-d5c43d7f5ece"
+	_issueExceptionList = "/v2/issueInfo/appId/%s/platformId/%s/issueId/%s/exceptionTypeList/Crash,Native,ExtensionCrash?fsn=114a8d02-586d-4fe4-8c23-79003fbe6882"
+)
+
+// BuglyVersion Bugly Version .
+func (d *Dao) BuglyVersion(c context.Context, buglyCookie *model.BuglyCookie, projectID, platformID string) (ret []*model.BugVersion, err error) {
+	var (
+		req     *http.Request
+		res     *model.BugVersionResponse
+		hostStr string
+	)
+
+	hostStr = d.c.Bugly.Host + fmt.Sprintf(_issueVersionList, projectID, platformID)
+
+	if req, err = d.newRequest("GET", hostStr, nil); err != nil {
+		return
+	}
+
+	req.Header.Set("Cookie", buglyCookie.Cookie)
+	req.Header.Set("x-token", buglyCookie.Token)
+	req.Header.Set("content-type", "application/json;charset=utf-8")
+	req.Header.Set("x-csrf-token", "undefined")
+
+	if err = d.httpClient.Do(c, req, &res); err != nil {
+		log.Error("d.BugVersion url(%s) err(%v)", "BugVersion", err)
+		return
+	}
+
+	if res.Status != _buglyOkCode {
+		if res.Code == _buglyCookieExpiredCode {
+			err = ecode.MartheCookieExpired
+			log.Error("maybe need to update cookie and token")
+		} else {
+			err = ecode.MartheBuglyErr
+		}
+		log.Error("Status url(%s) res(%v) err(%v)", "BugVersion", res, err)
+		return
+	}
+
+	ret = res.Ret.BugVersionList
+	return
+}
+
+// BuglyIssueAndRetry Bugly Issue And Retry.
+func (d *Dao) BuglyIssueAndRetry(c context.Context, buglyCookie *model.BuglyCookie, bugIssueRequest *model.BugIssueRequest) (ret *model.BugRet, err error) {
+	for i := 0; i < d.c.Bugly.UrlRetryCount; i++ {
+		if ret, err = d.BuglyIssue(c, buglyCookie, bugIssueRequest); err == nil {
+			break
+		}
+	}
+	return
+}
+
+// BuglyIssue Get Issue.
+func (d *Dao) BuglyIssue(c context.Context, buglyCookie *model.BuglyCookie, bugIssueRequest *model.BugIssueRequest) (ret *model.BugRet, err error) {
+	var (
+		req     *http.Request
+		res     *model.BugIssueResponse
+		hostStr string
+	)
+
+	hostStr = d.c.Bugly.Host + fmt.Sprintf(_issueList, bugIssueRequest.ProjectID, bugIssueRequest.PlatformID, bugIssueRequest.Version, strconv.Itoa(bugIssueRequest.StartNum), strconv.Itoa(bugIssueRequest.Rows), bugIssueRequest.ExceptionType)
+
+	if req, err = d.newRequest("GET", hostStr, nil); err != nil {
+		return
+	}
+
+	req.Header.Set("Cookie", buglyCookie.Cookie)
+	req.Header.Set("x-token", buglyCookie.Token)
+	req.Header.Set("content-type", "application/json;charset=utf-8")
+	req.Header.Set("x-csrf-token", "undefined")
+
+	if err = d.httpClient.Do(c, req, &res); err != nil {
+		log.Error("d.BuglyIssue url(%s) err(%v)", "BuglyIssue", err)
+		return
+	}
+
+	if res.Status != _buglyOkCode {
+		if res.Code == _buglyCookieExpiredCode {
+			err = ecode.MartheCookieExpired
+			log.Error("maybe need to update cookie and token")
+		} else {
+			err = ecode.MartheBuglyErr
+		}
+		log.Error("Status url(%s) res(%v) err(%v)", "BuglyIssue", res, err)
+		return
+	}
+
+	ret = res.Ret
+	return
+}
+
+// BuglyIssueDetailAndRetry Bugly Issue Detail And Retry.
+func (d *Dao) BuglyIssueDetailAndRetry(c context.Context, buglyCookie *model.BuglyCookie, projectID, platformID, issueNo string) (bugIssueDetail *model.BugIssueDetail, err error) {
+	for i := 0; i < d.c.Bugly.UrlRetryCount; i++ {
+		if bugIssueDetail, err = d.BuglyIssueDetail(c, buglyCookie, projectID, platformID, issueNo); err == nil {
+			break
+		}
+	}
+	return
+}
+
+// BuglyIssueDetail Get Issue Detail.
+func (d *Dao) BuglyIssueDetail(c context.Context, buglyCookie *model.BuglyCookie, projectID, platformID, issueNo string) (bugIssueDetail *model.BugIssueDetail, err error) {
+	var (
+		req     *http.Request
+		res     *model.BugIssueDetailResponse
+		hostStr string
+	)
+
+	hostStr = d.c.Bugly.Host + fmt.Sprintf(_issueDetailList, projectID, platformID, issueNo)
+
+	if req, err = d.newRequest("GET", hostStr, nil); err != nil {
+		return
+	}
+
+	req.Header.Set("Cookie", buglyCookie.Cookie)
+	req.Header.Set("x-token", buglyCookie.Token)
+	req.Header.Set("content-type", "application/json;charset=utf-8")
+	req.Header.Set("x-csrf-token", "undefined")
+
+	if err = d.httpClient.Do(c, req, &res); err != nil {
+		log.Error("d.BuglyIssue url(%s) err(%v)", "BuglyIssue", err)
+		return
+	}
+
+	if res.Code != _issueDetailCode {
+		err = ecode.MartheBuglyErr
+		log.Error("Status url(%s) res(%v) err(%v)", "BuglyIssue", res, err)
+		return
+	}
+
+	bugIssueDetail = res.Data
+	return
+}
+
+// BuglyIssueExceptionList Bugly Issue Exception List.
+func (d *Dao) BuglyIssueExceptionList(c context.Context, buglyCookie *model.BuglyCookie, projectID, platformID, issueNo string) (bugIssueException *model.IssueException, err error) {
+	var (
+		req     *http.Request
+		res     *model.BugIssueExceptionListResponse
+		hostStr string
+	)
+
+	hostStr = d.c.Bugly.Host + fmt.Sprintf(_issueExceptionList, projectID, platformID, issueNo)
+
+	if req, err = d.newRequest("GET", hostStr, nil); err != nil {
+		return
+	}
+
+	req.Header.Set("Cookie", buglyCookie.Cookie)
+	req.Header.Set("x-token", buglyCookie.Token)
+	req.Header.Set("content-type", "application/json;charset=utf-8")
+	req.Header.Set("x-csrf-token", "undefined")
+
+	if err = d.httpClient.Do(c, req, &res); err != nil {
+		log.Error("d.BuglyIssueExceptionList url(%s) err(%v)", "BuglyIssueExceptionList", err)
+		return
+	}
+
+	if res.Status != _buglyOkCode {
+		err = ecode.MartheBuglyErr
+		log.Error("Status url(%s) res(%v) err(%v)", "BuglyIssueExceptionList", res, err)
+		return
+	}
+
+	if res.Ret != nil && len(res.Ret.IssueException) != 0 && res.Ret.IssueException[0].IssueID == issueNo {
+		bugIssueException = res.Ret.IssueException[0]
+	}
+
+	return
+}

+ 100 - 0
app/admin/ep/marthe/dao/dao.go

@@ -0,0 +1,100 @@
+package dao
+
+import (
+	"bytes"
+	"context"
+	"encoding/json"
+	"net/http"
+	"time"
+
+	"go-common/app/admin/ep/marthe/conf"
+	"go-common/library/cache/memcache"
+	"go-common/library/database/orm"
+	"go-common/library/log"
+	xhttp "go-common/library/net/http/blademaster"
+	"go-common/library/sync/pipeline/fanout"
+
+	"github.com/jinzhu/gorm"
+	"gopkg.in/gomail.v2"
+)
+
+const _wildcards = "%"
+
+// Dao dao
+type Dao struct {
+	c          *conf.Config
+	httpClient *xhttp.Client
+	email      *gomail.Dialer
+	db         *gorm.DB
+	cache      *fanout.Fanout
+	mc         *memcache.Pool
+	expire     int32
+}
+
+// New init mysql db
+func New(c *conf.Config) (dao *Dao) {
+	dao = &Dao{
+		c:          c,
+		email:      gomail.NewDialer(c.Mail.Host, c.Mail.Port, c.Mail.Username, c.Mail.Password),
+		httpClient: xhttp.NewClient(c.HTTPClient),
+		cache:      fanout.New("mcCache", fanout.Worker(1), fanout.Buffer(1024)),
+		mc:         memcache.NewPool(c.Memcache.Config),
+		expire:     int32(time.Duration(c.Memcache.Expire) / time.Second),
+	}
+	if c.ORM != nil {
+		dao.db = orm.NewMySQL(c.ORM)
+	}
+	return
+}
+
+// Close close the resource.
+func (d *Dao) Close() {
+	if d.db != nil {
+		d.db.Close()
+	}
+
+	if d.mc != nil {
+		d.mc.Close()
+	}
+}
+
+// Ping verify server is ok.
+func (d *Dao) Ping(c context.Context) (err error) {
+	if d.db != nil {
+		if err = d.db.DB().Ping(); err != nil {
+			log.Info("dao.cloudDB.Ping() error(%v)", err)
+		}
+	}
+	return
+}
+
+func (d *Dao) newRequest(method, url string, v interface{}) (req *http.Request, err error) {
+	body := &bytes.Buffer{}
+	if method != http.MethodGet {
+		if err = json.NewEncoder(body).Encode(v); err != nil {
+			log.Error("json encode value(%s) err(%v) ", v, err)
+			return
+		}
+	}
+	if req, err = http.NewRequest(method, url, body); err != nil {
+		log.Error("http new request url(%s) err(%v)", url, err)
+	}
+	return
+}
+
+// cacheSave cache Save.
+func (d *Dao) cacheSave(c context.Context, cacheItem *memcache.Item) {
+	var f = func(ctx context.Context) {
+		var (
+			conn = d.mc.Get(c)
+			err  error
+		)
+		defer conn.Close()
+		if err = conn.Set(cacheItem); err != nil {
+			log.Error("Add Cache conn.Set(%s) error(%v)", cacheItem.Key, err)
+		}
+	}
+	if err := d.cache.Do(c, f); err != nil {
+		log.Error("ReleaseName cache save err(%v)", err)
+	}
+}

+ 30 - 0
app/admin/ep/marthe/dao/dao_test.go

@@ -0,0 +1,30 @@
+package dao
+
+import (
+	"context"
+	"flag"
+	"path/filepath"
+
+	"go-common/app/admin/ep/marthe/conf"
+
+	_ "github.com/go-sql-driver/mysql"
+	"gopkg.in/h2non/gock.v1"
+)
+
+var (
+	d *Dao
+	c context.Context
+)
+
+func init() {
+	dir, _ := filepath.Abs("../cmd/convey-test.toml")
+	flag.Set("conf", dir)
+	conf.Init()
+	d = New(conf.Conf)
+	d.httpClient.SetTransport(gock.DefaultTransport)
+	c = ctx()
+}
+
+func ctx() context.Context {
+	return context.Background()
+}

+ 35 - 0
app/admin/ep/marthe/dao/mail.go

@@ -0,0 +1,35 @@
+package dao
+
+import (
+	"context"
+	"strings"
+
+	"gopkg.in/gomail.v2"
+)
+
+const (
+	_MailBoxNotFound = "Mailbox not found"
+)
+
+// SendMail asynchronous send mail.
+func (d *Dao) SendMail(message *gomail.Message) {
+	message.SetAddressHeader("From", d.email.Username, "merlin")
+	d.cache.Do(context.TODO(), func(ctx context.Context) {
+		d.SendMailIfFailed(message)
+	})
+}
+
+// SendMailIfFailed Send Mail If Failed
+func (d *Dao) SendMailIfFailed(message *gomail.Message) {
+	if err := d.email.DialAndSend(message); err != nil {
+		if strings.Contains(err.Error(), _MailBoxNotFound) {
+			headerMsg := message.GetHeader("Subject")
+			headerMsg = append(headerMsg, "Mail Send Error:"+err.Error()+",Receiver:")
+			headerMsg = append(headerMsg, message.GetHeader("To")...)
+
+			message.SetHeader("To", d.c.Mail.NoticeOwner...)
+			message.SetHeader("Subject", headerMsg...)
+			d.email.DialAndSend(message)
+		}
+	}
+}

+ 92 - 0
app/admin/ep/marthe/dao/mysql_bugly_batch_run.go

@@ -0,0 +1,92 @@
+package dao
+
+import (
+	"go-common/app/admin/ep/marthe/model"
+	"go-common/library/ecode"
+
+	pkgerr "github.com/pkg/errors"
+)
+
+// InsertBuglyBatchRuns Insert Bugly Batch Runs.
+func (d *Dao) InsertBuglyBatchRuns(buglyBatchRuns []*model.BuglyBatchRun) (err error) {
+	tx := d.db.Begin()
+
+	defer func() {
+		if err != nil {
+			if err == ecode.NothingFound {
+				err = nil
+			} else {
+				err = pkgerr.WithStack(err)
+			}
+		}
+	}()
+
+	if err = tx.Error; err != nil {
+		return
+	}
+
+	for _, buglyBatchRun := range buglyBatchRuns {
+		if err = d.db.Create(buglyBatchRun).Error; err != nil {
+			tx.Rollback()
+			return
+		}
+	}
+
+	if err = tx.Commit().Error; err != nil {
+		tx.Rollback()
+	}
+	return
+}
+
+// InsertBuglyBatchRun Insert Bugly Batch Run.
+func (d *Dao) InsertBuglyBatchRun(buglyBatchRun *model.BuglyBatchRun) error {
+	return pkgerr.WithStack(d.db.Create(buglyBatchRun).Error)
+}
+
+// UpdateBuglyBatchRun Update Bugly Batch Run.
+func (d *Dao) UpdateBuglyBatchRun(buglyBatchRun *model.BuglyBatchRun) error {
+	return pkgerr.WithStack(d.db.Model(&model.BuglyBatchRun{}).Where("id=?", buglyBatchRun.ID).Updates(buglyBatchRun).Error)
+}
+
+// FindBuglyBatchRuns Find Bugly Batch Runs.
+func (d *Dao) FindBuglyBatchRuns(req *model.QueryBuglyBatchRunsRequest) (total int64, buglyBatchRuns []*model.BuglyBatchRun, err error) {
+	gDB := d.db.Model(&model.BuglyBatchRun{})
+
+	if req.Version != "" {
+		gDB = gDB.Where("version=?", req.Version)
+	}
+
+	if req.Status != 0 {
+		gDB = gDB.Where("status=?", req.Status)
+	}
+
+	if req.BatchID != "" {
+		gDB = gDB.Where("batch_id=?", req.BatchID)
+	}
+
+	if err = pkgerr.WithStack(gDB.Count(&total).Error); err != nil {
+		return
+	}
+
+	err = pkgerr.WithStack(gDB.Order("ctime desc").Offset((req.PageNum - 1) * req.PageSize).Limit(req.PageSize).Find(&buglyBatchRuns).Error)
+	return
+}
+
+// QueryBuglyBatchRunsByStatus Find Bugly Batch Runs By Status.
+func (d *Dao) QueryBuglyBatchRunsByStatus(status int) (buglyBatchRuns []*model.BuglyBatchRun, err error) {
+	err = pkgerr.WithStack(d.db.Where("status = ?", status).Find(&buglyBatchRuns).Error)
+	return
+}
+
+// QueryLastSuccessBatchRunByVersion Find Last Success Batch Run By Version.
+func (d *Dao) QueryLastSuccessBatchRunByVersion(version string) (buglyBatchRun *model.BuglyBatchRun, err error) {
+	buglyBatchRun = &model.BuglyBatchRun{}
+	if err = d.db.Where("version = ? and status = ?", version, model.BuglyBatchRunStatusDone).Order("id desc").First(&buglyBatchRun).Error; err != nil {
+		if err == ecode.NothingFound {
+			err = nil
+		} else {
+			err = pkgerr.WithStack(err)
+		}
+	}
+	return
+}

+ 58 - 0
app/admin/ep/marthe/dao/mysql_bugly_batch_run_test.go

@@ -0,0 +1,58 @@
+package dao
+
+import (
+	"testing"
+	"time"
+
+	"go-common/app/admin/ep/marthe/model"
+
+	"github.com/satori/go.uuid"
+	. "github.com/smartystreets/goconvey/convey"
+)
+
+var (
+	tmpVersion    = time.Now().Format("2006_01_02_15_04_05")
+	buglyBatchRun = &model.BuglyBatchRun{
+		BuglyVersionID: 1,
+		Version:        tmpVersion,
+		BatchID:        uuid.NewV4().String(),
+		RetryCount:     0,
+		Status:         model.BuglyBatchRunStatusRunning,
+		ErrorMsg:       "no",
+	}
+
+	queryBuglyBatchRunsRequest = &model.QueryBuglyBatchRunsRequest{
+		Pagination: model.Pagination{
+			PageSize: 10,
+			PageNum:  1,
+		},
+		Version: tmpVersion,
+	}
+)
+
+func Test_Bugly_batch_run(t *testing.T) {
+	Convey("test insert bugly batch run", t, func() {
+		err := d.InsertBuglyBatchRun(buglyBatchRun)
+		So(err, ShouldBeNil)
+	})
+
+	Convey("test update bugly batch run", t, func() {
+		buglyBatchRun.Status = model.BuglyBatchRunStatusDone
+		err := d.UpdateBuglyBatchRun(buglyBatchRun)
+		So(err, ShouldBeNil)
+	})
+
+	Convey("test Find Bugly Batch Runs", t, func() {
+		buglyBatchRun.Status = model.BuglyBatchRunStatusDone
+		total, buglyBatchRuns, err := d.FindBuglyBatchRuns(queryBuglyBatchRunsRequest)
+		So(err, ShouldBeNil)
+		So(total, ShouldEqual, 1)
+		So(buglyBatchRun.BatchID, ShouldEqual, buglyBatchRuns[0].BatchID)
+	})
+
+	Convey("test Find Last Success Batch Run By Version", t, func() {
+		tmpBuglyBatchRun, err := d.QueryLastSuccessBatchRunByVersion(tmpVersion)
+		So(err, ShouldBeNil)
+		So(buglyBatchRun.BatchID, ShouldEqual, tmpBuglyBatchRun.BatchID)
+	})
+}

+ 67 - 0
app/admin/ep/marthe/dao/mysql_bugly_cookie.go

@@ -0,0 +1,67 @@
+package dao
+
+import (
+	"go-common/app/admin/ep/marthe/model"
+	"go-common/library/ecode"
+
+	pkgerr "github.com/pkg/errors"
+)
+
+// InsertCookie Insert Cookie.
+func (d *Dao) InsertCookie(buglyCookie *model.BuglyCookie) error {
+	return pkgerr.WithStack(d.db.Create(buglyCookie).Error)
+}
+
+// UpdateCookie Update cookie.
+func (d *Dao) UpdateCookie(buglyCookie *model.BuglyCookie) error {
+	return pkgerr.WithStack(d.db.Model(&model.BuglyCookie{}).Updates(buglyCookie).Error)
+}
+
+// UpdateCookieStatus Update Cookie Status.
+func (d *Dao) UpdateCookieStatus(id int64, status int) error {
+	return pkgerr.WithStack(d.db.Model(&model.BuglyCookie{}).Where("id = ?", id).Update("status", status).Error)
+}
+
+// UpdateCookieUsageCount Update Cookie Usage Count.
+func (d *Dao) UpdateCookieUsageCount(id int64, usageCount int) error {
+	return pkgerr.WithStack(d.db.Model(&model.BuglyCookie{}).Where("id = ?", id).Update("usage_count", usageCount).Error)
+}
+
+// QueryCookieByStatus Query Cookie By Status.
+func (d *Dao) QueryCookieByStatus(status int) (buglyCookies []*model.BuglyCookie, err error) {
+	err = pkgerr.WithStack(d.db.Where("status=?", status).Order("ctime desc").Find(&buglyCookies).Error)
+	return
+}
+
+// QueryCookieByQQAccount Query Cookie By QQ Account.
+func (d *Dao) QueryCookieByQQAccount(qqAccount int) (buglyCookie *model.BuglyCookie, err error) {
+	buglyCookie = &model.BuglyCookie{}
+	if err = d.db.Where("qq_account=?", qqAccount).First(buglyCookie).Error; err != nil {
+		if err == ecode.NothingFound {
+			err = nil
+		} else {
+			err = pkgerr.WithStack(err)
+		}
+	}
+	return
+}
+
+// FindCookies Find Cookies.
+func (d *Dao) FindCookies(req *model.QueryBuglyCookiesRequest) (total int64, buglyCookies []*model.BuglyCookie, err error) {
+	gDB := d.db.Model(&model.BuglyCookie{})
+
+	if req.QQAccount != 0 {
+		gDB = gDB.Where("qq_account=?", req.QQAccount)
+	}
+
+	if req.Status != 0 {
+		gDB = gDB.Where("status=?", req.Status)
+	}
+
+	if err = pkgerr.WithStack(gDB.Count(&total).Error); err != nil {
+		return
+	}
+
+	err = pkgerr.WithStack(gDB.Order("ctime desc").Offset((req.PageNum - 1) * req.PageSize).Limit(req.PageSize).Find(&buglyCookies).Error)
+	return
+}

文件差异内容过多而无法显示
+ 51 - 0
app/admin/ep/marthe/dao/mysql_bugly_cookie_test.go


+ 104 - 0
app/admin/ep/marthe/dao/mysql_bugly_issue.go

@@ -0,0 +1,104 @@
+package dao
+
+import (
+	"go-common/app/admin/ep/marthe/model"
+	"go-common/library/ecode"
+
+	pkgerr "github.com/pkg/errors"
+)
+
+// GetBuglyIssue Get Issue Record.
+func (d *Dao) GetBuglyIssue(issueNo, version string) (buglyIssue *model.BuglyIssue, err error) {
+	buglyIssue = &model.BuglyIssue{}
+	if err = d.db.Where("issue_no = ? and version = ?", issueNo, version).First(buglyIssue).Error; err != nil {
+		if err == ecode.NothingFound {
+			err = nil
+		} else {
+			err = pkgerr.WithStack(err)
+		}
+	}
+	return
+}
+
+// UpdateBuglyIssue Update Issue Record.
+func (d *Dao) UpdateBuglyIssue(buglyIssue *model.BuglyIssue) error {
+	return pkgerr.WithStack(d.db.Model(&model.BuglyIssue{}).Where("issue_no = ? and version = ?", buglyIssue.IssueNo, buglyIssue.Version).UpdateColumn(map[string]interface{}{
+		"last_time":    buglyIssue.LastTime,
+		"happen_times": buglyIssue.HappenTimes,
+		"user_times":   buglyIssue.UserTimes,
+	}).Error)
+}
+
+// InsertBuglyIssue Insert Issue Record.
+func (d *Dao) InsertBuglyIssue(buglyIssue *model.BuglyIssue) (err error) {
+	return pkgerr.WithStack(d.db.Model(&model.BuglyIssue{}).Create(buglyIssue).Error)
+}
+
+// GetBuglyIssuesByFilterSQL  Get Bugly Issues By Filter SQL.
+func (d *Dao) GetBuglyIssuesByFilterSQL(issueFilterSQL string) (buglyIssues []*model.BuglyIssue, err error) {
+	if err = d.db.Raw(issueFilterSQL).Order("id asc").Find(&buglyIssues).Error; err != nil {
+		if err == ecode.NothingFound {
+			err = nil
+		} else {
+			err = pkgerr.WithStack(err)
+		}
+	}
+	return
+}
+
+// UpdateBuglyIssueTapdBugID Update Issue Record Tapd Bug ID.
+func (d *Dao) UpdateBuglyIssueTapdBugID(id int64, tapdBugID string) error {
+	return pkgerr.WithStack(d.db.Model(&model.BuglyIssue{}).Where("id=?", id).Update("tapd_bug_id", tapdBugID).Error)
+}
+
+// FindBuglyIssues Find Bugly Issues.
+func (d *Dao) FindBuglyIssues(req *model.QueryBuglyIssueRequest) (total int64, buglyIssues []*model.BuglyIssue, err error) {
+	gDB := d.db.Model(&model.BuglyIssue{})
+
+	if req.IssueNo != "" {
+		gDB = gDB.Where("issue_no = ?", req.IssueNo)
+	}
+	if req.Title != "" {
+		gDB = gDB.Where("title like ?", _wildcards+req.Title+_wildcards)
+	}
+	if req.ExceptionMsg != "" {
+		gDB = gDB.Where("exception_msg like ?", _wildcards+req.ExceptionMsg+_wildcards)
+	}
+	if req.KeyStack != "" {
+		gDB = gDB.Where("key_stack like ?", _wildcards+req.KeyStack+_wildcards)
+	}
+	if req.Detail != "" {
+		gDB = gDB.Where("detail like ?", _wildcards+req.Detail+_wildcards)
+	}
+	if req.Tags != "" {
+		gDB = gDB.Where("tags like ?", _wildcards+req.Tags+_wildcards)
+	}
+	if req.Version != "" {
+		gDB = gDB.Where("version like ?", _wildcards+req.Version+_wildcards)
+	}
+	if req.ProjectID != "" {
+		gDB = gDB.Where("project_id like ?", _wildcards+req.ProjectID+_wildcards)
+	}
+	if req.TapdBugID != "" {
+		gDB = gDB.Where("tapd_bug_id = ?", req.TapdBugID)
+	}
+
+	if err = pkgerr.WithStack(gDB.Count(&total).Error); err != nil {
+		return
+	}
+
+	err = pkgerr.WithStack(gDB.Order("mtime desc").Offset((req.PageNum - 1) * req.PageSize).Limit(req.PageSize).Find(&buglyIssues).Error)
+	return
+}
+
+// GetBuglyIssuesHasInTapd Get Bugly Issues Has In Tapd.
+func (d *Dao) GetBuglyIssuesHasInTapd() (buglyIssues []*model.BuglyIssue, err error) {
+	if err = d.db.Where("tapd_bug_id<>''").Find(&buglyIssues).Error; err != nil {
+		if err == ecode.NothingFound {
+			err = nil
+		} else {
+			err = pkgerr.WithStack(err)
+		}
+	}
+	return
+}

+ 78 - 0
app/admin/ep/marthe/dao/mysql_bugly_issue_test.go

@@ -0,0 +1,78 @@
+package dao
+
+import (
+	"strconv"
+	"testing"
+	"time"
+
+	"go-common/app/admin/ep/marthe/model"
+
+	. "github.com/smartystreets/goconvey/convey"
+)
+
+var (
+	tmpIssueNoStr = strconv.FormatInt(time.Now().Unix(), 10)
+	tmpTapdBugID  = "bug" + tmpIssueNoStr
+
+	buglyIssue = &model.BuglyIssue{
+		IssueNo:      tmpIssueNoStr,
+		Title:        "Title" + tmpIssueNoStr,
+		ExceptionMsg: "ExceptionMsg" + tmpIssueNoStr,
+		KeyStack:     "KeyStack" + tmpIssueNoStr,
+		Detail:       "Detail" + tmpIssueNoStr,
+		Tags:         "Tags" + tmpIssueNoStr,
+		LastTime:     time.Now(),
+		HappenTimes:  10,
+		UserTimes:    20,
+		Version:      "Version" + tmpIssueNoStr,
+		ProjectID:    "ProjectID" + tmpIssueNoStr,
+		IssueLink:    "IssueLink" + tmpIssueNoStr,
+	}
+
+	queryBuglyIssueRequest = &model.QueryBuglyIssueRequest{
+		Pagination: model.Pagination{
+			PageSize: 10,
+			PageNum:  1,
+		},
+		IssueNo: buglyIssue.IssueNo,
+	}
+)
+
+func Test_Bugly_Issue(t *testing.T) {
+	Convey("test insert bugly issue", t, func() {
+		err := d.InsertBuglyIssue(buglyIssue)
+		So(err, ShouldBeNil)
+	})
+
+	Convey("test update Bugly Issue", t, func() {
+		buglyIssue.ExceptionMsg = "update exception message"
+		err := d.UpdateBuglyIssue(buglyIssue)
+		So(err, ShouldBeNil)
+	})
+
+	Convey("test update Bugly Issue tapd bug id", t, func() {
+		err := d.UpdateBuglyIssueTapdBugID(buglyIssue.ID, tmpTapdBugID)
+		So(err, ShouldBeNil)
+	})
+
+	Convey("test Get Bugly Issue", t, func() {
+		tmpBuglyIssue, err := d.GetBuglyIssue(buglyIssue.IssueNo, buglyIssue.Version)
+		So(err, ShouldBeNil)
+		So(tmpBuglyIssue.ID, ShouldEqual, buglyIssue.ID)
+	})
+
+	Convey("test Get Bugly Issues By Filter SQL", t, func() {
+		sql := "select * from bugly_issues where issue_no = '" + buglyIssue.IssueNo + "'"
+		tmpBuglyIssues, err := d.GetBuglyIssuesByFilterSQL(sql)
+		So(err, ShouldBeNil)
+		So(tmpBuglyIssues[0].IssueNo, ShouldEqual, buglyIssue.IssueNo)
+	})
+
+	Convey("test Find Bugly Issues", t, func() {
+		total, tmpBuglyIssues, err := d.FindBuglyIssues(queryBuglyIssueRequest)
+		So(err, ShouldBeNil)
+		So(total, ShouldBeGreaterThan, 0)
+		So(len(tmpBuglyIssues), ShouldBeGreaterThan, 0)
+	})
+
+}

+ 89 - 0
app/admin/ep/marthe/dao/mysql_bugly_project.go

@@ -0,0 +1,89 @@
+package dao
+
+import (
+	"database/sql"
+
+	"go-common/app/admin/ep/marthe/model"
+	"go-common/library/ecode"
+
+	pkgerr "github.com/pkg/errors"
+)
+
+// InsertBuglyProject Insert Bugly Project.
+func (d *Dao) InsertBuglyProject(buglyProject *model.BuglyProject) error {
+	return pkgerr.WithStack(d.db.Create(buglyProject).Error)
+}
+
+// UpdateBuglyProject Update Bugly Project.
+func (d *Dao) UpdateBuglyProject(buglyProject *model.BuglyProject) error {
+	return pkgerr.WithStack(d.db.Model(&model.BuglyProject{}).Updates(buglyProject).Error)
+}
+
+// QueryBuglyProject Query Bugly Project.
+func (d *Dao) QueryBuglyProject(id int64) (buglyProject *model.BuglyProject, err error) {
+	buglyProject = &model.BuglyProject{}
+	err = pkgerr.WithStack(d.db.Where("id = ?", id).First(buglyProject).Error)
+	return
+}
+
+// QueryBuglyProjectByName Query Bugly Project.
+func (d *Dao) QueryBuglyProjectByName(projectName string) (buglyProject *model.BuglyProject, err error) {
+	buglyProject = &model.BuglyProject{}
+	if err = d.db.Where("project_name = ?", projectName).First(buglyProject).Error; err == ecode.NothingFound {
+		err = nil
+	}
+	return
+}
+
+// QueryAllBuglyProjects Query All Bugly Project.
+func (d *Dao) QueryAllBuglyProjects() (buglyProjects []*model.BuglyProject, err error) {
+	err = pkgerr.WithStack(d.db.Model(&model.BuglyProject{}).Find(&buglyProjects).Error)
+	return
+}
+
+// FindBuglyProjects Find Bugly Project.
+func (d *Dao) FindBuglyProjects(req *model.QueryBuglyProjectRequest) (total int64, buglyProject []*model.BuglyProject, err error) {
+	gDB := d.db.Model(&model.BuglyProject{})
+
+	if req.ProjectID != "" {
+		gDB = gDB.Where("project_id=?", req.ProjectID)
+	}
+	if req.ProjectName != "" {
+		gDB = gDB.Where("project_name like ?", _wildcards+req.ProjectName+_wildcards)
+	}
+	if req.PlatformID != "" {
+		gDB = gDB.Where("platform_id=?", req.PlatformID)
+	}
+
+	if req.UpdateBy != "" {
+		gDB = gDB.Where("update_by=?", req.UpdateBy)
+	}
+
+	if err = pkgerr.WithStack(gDB.Count(&total).Error); err != nil {
+		return
+	}
+
+	err = pkgerr.WithStack(gDB.Order("ctime desc").Offset((req.PageNum - 1) * req.PageSize).Limit(req.PageSize).Find(&buglyProject).Error)
+	return
+}
+
+// QueryBuglyProjectList Query Bugly Project List.
+func (d *Dao) QueryBuglyProjectList() (projectList []string, err error) {
+	var (
+		rows *sql.Rows
+	)
+	sql := "select DISTINCT project_name from bugly_projects"
+	if rows, err = d.db.Raw(sql).Rows(); err != nil {
+		return
+	}
+
+	defer rows.Close()
+	for rows.Next() {
+		var ver string
+		if err = rows.Scan(&ver); err != nil {
+			return
+		}
+		projectList = append(projectList, ver)
+	}
+	return
+}

+ 139 - 0
app/admin/ep/marthe/dao/mysql_bugly_version.go

@@ -0,0 +1,139 @@
+package dao
+
+import (
+	"database/sql"
+	"fmt"
+
+	"go-common/app/admin/ep/marthe/model"
+	"go-common/library/ecode"
+
+	pkgerr "github.com/pkg/errors"
+)
+
+const (
+	_versionInnerJoinProjectSql      = "select a.id,a.version,a.bugly_project_id,a.action,a.task_status,a.update_by,a.ctime,a.mtime,b.project_name,b.exception_type from bugly_versions as a inner join bugly_projects as b on a.bugly_project_id = b.id"
+	_versionInnerJoinProjectSqlCount = "select count(-1) as totalCount from bugly_versions as a inner join bugly_projects as b on a.bugly_project_id = b.id"
+	_where                           = "WHERE"
+	_and                             = "AND"
+)
+
+// InsertBuglyVersion Insert Bugly Version.
+func (d *Dao) InsertBuglyVersion(buglyVersion *model.BuglyVersion) error {
+	return pkgerr.WithStack(d.db.Create(buglyVersion).Error)
+}
+
+// UpdateBuglyVersion Update Bugly Version.
+func (d *Dao) UpdateBuglyVersion(buglyVersion *model.BuglyVersion) error {
+	return pkgerr.WithStack(d.db.Model(&model.BuglyVersion{}).Updates(buglyVersion).Error)
+}
+
+// QueryBuglyVersionByVersion Query Bugly Version By Version.
+func (d *Dao) QueryBuglyVersionByVersion(version string) (buglyVersion *model.BuglyVersion, err error) {
+	buglyVersion = &model.BuglyVersion{}
+	if err = d.db.Where("version = ?", version).First(buglyVersion).Error; err != nil {
+		if err == ecode.NothingFound {
+			err = nil
+		} else {
+			err = pkgerr.WithStack(err)
+		}
+	}
+	return
+}
+
+// QueryBuglyVersion Query Bugly Version .
+func (d *Dao) QueryBuglyVersion(id int64) (buglyVersion *model.BuglyVersion, err error) {
+	buglyVersion = &model.BuglyVersion{}
+	if err = d.db.Where("id = ?", id).First(buglyVersion).Error; err != nil {
+		if err == ecode.NothingFound {
+			err = nil
+		} else {
+			err = pkgerr.WithStack(err)
+		}
+	}
+	return
+}
+
+// QueryBuglyVersionList Query Bugly Version List.
+func (d *Dao) QueryBuglyVersionList() (versionList []string, err error) {
+	var (
+		rows *sql.Rows
+	)
+	sql := "select DISTINCT version from bugly_versions"
+	if rows, err = d.db.Raw(sql).Rows(); err != nil {
+		return
+	}
+
+	defer rows.Close()
+	for rows.Next() {
+		var ver string
+		if err = rows.Scan(&ver); err != nil {
+			return
+		}
+		versionList = append(versionList, ver)
+	}
+	return
+}
+
+// FindBuglyProjectVersions Find Bugly Project Versions.
+func (d *Dao) FindBuglyProjectVersions(req *model.QueryBuglyVersionRequest) (total int64, buglyProjectVersions []*model.BuglyProjectVersion, err error) {
+	var (
+		qSQL = _versionInnerJoinProjectSql
+		cSQL = _versionInnerJoinProjectSqlCount
+		rows *sql.Rows
+	)
+
+	if req.UpdateBy != "" || req.ProjectName != "" || req.Action > 0 || req.Version != "" {
+		var (
+			partSql     string
+			logicalWord = _where
+		)
+
+		if req.UpdateBy != "" {
+			partSql = fmt.Sprintf("%s %s a.update_by = '%s'", partSql, logicalWord, req.UpdateBy)
+			logicalWord = _and
+		}
+
+		if req.ProjectName != "" {
+			partSql = fmt.Sprintf("%s %s b.project_name like '%s'", partSql, logicalWord, _wildcards+req.ProjectName+_wildcards)
+			logicalWord = _and
+		}
+
+		if req.Action > 0 {
+			partSql = fmt.Sprintf("%s %s a.action = %d", partSql, logicalWord, req.Action)
+			logicalWord = _and
+		}
+
+		if req.Version != "" {
+			partSql = fmt.Sprintf("%s %s a.version like '%s'", partSql, logicalWord, _wildcards+req.Version+_wildcards)
+			logicalWord = _and
+		}
+
+		qSQL = qSQL + partSql
+		cSQL = cSQL + partSql
+	}
+
+	cDB := d.db.Raw(cSQL)
+	if err = pkgerr.WithStack(cDB.Count(&total).Error); err != nil {
+		return
+	}
+	gDB := d.db.Raw(qSQL)
+	if rows, err = gDB.Order("a.ctime DESC").Offset((req.PageNum - 1) * req.PageSize).Limit(req.PageSize).Rows(); err != nil {
+		return
+	}
+	defer rows.Close()
+	for rows.Next() {
+		pv := &model.BuglyProjectVersion{}
+		if err = rows.Scan(&pv.ID, &pv.Version, &pv.BuglyProjectID, &pv.Action, &pv.TaskStatus, &pv.UpdateBy, &pv.CTime, &pv.MTime, &pv.ProjectName, &pv.ExceptionType); err != nil {
+			return
+		}
+		buglyProjectVersions = append(buglyProjectVersions, pv)
+	}
+
+	return
+}
+
+// FindEnableAndReadyVersions Find Enable And Ready Versions.
+func (d *Dao) FindEnableAndReadyVersions() (buglyVersions []*model.BuglyVersion, err error) {
+	err = pkgerr.WithStack(d.db.Where("action = ? and task_status = ?", model.BuglyVersionActionEnable, model.BuglyVersionTaskStatusReady).Find(&buglyVersions).Error)
+	return
+}

+ 49 - 0
app/admin/ep/marthe/dao/mysql_bugly_version_test.go

@@ -0,0 +1,49 @@
+package dao
+
+import (
+	"strconv"
+	"testing"
+	"time"
+
+	"go-common/app/admin/ep/marthe/model"
+
+	. "github.com/smartystreets/goconvey/convey"
+)
+
+var (
+	tmpIssueNoStr2 = strconv.FormatInt(time.Now().Unix(), 10)
+
+	buglyVersion = &model.BuglyVersion{
+		Version:        "Version" + tmpIssueNoStr2,
+		BuglyProjectID: 1,
+		Action:         model.BuglyVersionActionDisable,
+		TaskStatus:     1,
+		UpdateBy:       "fengyifeng",
+	}
+)
+
+func Test_Bugly_Version(t *testing.T) {
+	Convey("test insert bugly Version", t, func() {
+		err := d.InsertBuglyVersion(buglyVersion)
+		So(err, ShouldBeNil)
+	})
+
+	Convey("test update bugly Version", t, func() {
+		buglyVersion.Version = "update" + tmpIssueNoStr2
+		err := d.UpdateBuglyVersion(buglyVersion)
+		So(err, ShouldBeNil)
+	})
+
+	Convey("test Query Bugly Version By Version", t, func() {
+		tmpBuglyVersion, err := d.QueryBuglyVersionByVersion(buglyVersion.Version)
+		So(err, ShouldBeNil)
+		So(tmpBuglyVersion.Version, ShouldEqual, buglyVersion.Version)
+	})
+
+	Convey("test Query Bugly Version By Id", t, func() {
+		tmpBuglyVersion, err := d.QueryBuglyVersion(buglyVersion.ID)
+		So(err, ShouldBeNil)
+		So(tmpBuglyVersion.Version, ShouldEqual, buglyVersion.Version)
+	})
+
+}

+ 50 - 0
app/admin/ep/marthe/dao/mysql_contact_info.go

@@ -0,0 +1,50 @@
+package dao
+
+import (
+	"go-common/app/admin/ep/marthe/model"
+	"go-common/library/ecode"
+
+	pkgerr "github.com/pkg/errors"
+)
+
+// InsertContactInfo Insert Contact Info.
+func (d *Dao) InsertContactInfo(contactInfo *model.ContactInfo) error {
+	return pkgerr.WithStack(d.db.Create(contactInfo).Error)
+}
+
+// UpdateContactInfo Update Contact Info.
+func (d *Dao) UpdateContactInfo(contactInfo *model.ContactInfo) error {
+	return pkgerr.WithStack(d.db.Save(&contactInfo).Error)
+}
+
+// QueryContactInfoByUserID Query Contact Info By User ID
+func (d *Dao) QueryContactInfoByUserID(userID string) (contactInfo *model.ContactInfo, err error) {
+	contactInfo = &model.ContactInfo{}
+	if err = d.db.Where("user_id = ?", userID).First(contactInfo).Error; err != nil {
+		if err == ecode.NothingFound {
+			err = nil
+		} else {
+			err = pkgerr.WithStack(err)
+		}
+	}
+	return
+}
+
+// QueryContactInfoByUsername Query Contact Info By Username
+func (d *Dao) QueryContactInfoByUsername(username string) (contactInfo *model.ContactInfo, err error) {
+	contactInfo = &model.ContactInfo{}
+	if err = d.db.Where("username = ?", username).First(contactInfo).Error; err != nil {
+		if err == ecode.NothingFound {
+			err = nil
+		} else {
+			err = pkgerr.WithStack(err)
+		}
+	}
+	return
+}
+
+// QueryAllContactInfos Query All Contact Infos
+func (d *Dao) QueryAllContactInfos() (contactInfos []*model.ContactInfo, err error) {
+	err = pkgerr.WithStack(d.db.Find(&contactInfos).Error)
+	return
+}

+ 17 - 0
app/admin/ep/marthe/dao/mysql_schedule_task.go

@@ -0,0 +1,17 @@
+package dao
+
+import (
+	"go-common/app/admin/ep/marthe/model"
+
+	pkgerr "github.com/pkg/errors"
+)
+
+// InsertScheduleTask Insert Schedule Task.
+func (d *Dao) InsertScheduleTask(scheduleTask *model.ScheduleTask) error {
+	return pkgerr.WithStack(d.db.Create(scheduleTask).Error)
+}
+
+// UpdateScheduleTask Update Schedule Task.
+func (d *Dao) UpdateScheduleTask(scheduleTask *model.ScheduleTask) error {
+	return pkgerr.WithStack(d.db.Model(&model.ScheduleTask{}).Updates(scheduleTask).Error)
+}

+ 33 - 0
app/admin/ep/marthe/dao/mysql_schedule_task_test.go

@@ -0,0 +1,33 @@
+package dao
+
+import (
+	"strconv"
+	"testing"
+	"time"
+
+	"go-common/app/admin/ep/marthe/model"
+
+	. "github.com/smartystreets/goconvey/convey"
+)
+
+var (
+	tmpIssueNoStr3 = strconv.FormatInt(time.Now().Unix(), 10)
+
+	scheduleTask = &model.ScheduleTask{
+		Name:   tmpIssueNoStr3,
+		Status: model.TaskStatusRunning,
+	}
+)
+
+func Test_Schedule_task(t *testing.T) {
+	Convey("test insert schedule task", t, func() {
+		err := d.InsertScheduleTask(scheduleTask)
+		So(err, ShouldBeNil)
+	})
+
+	Convey("test update schedule task", t, func() {
+		scheduleTask.Status = model.TaskStatusDone
+		err := d.UpdateScheduleTask(scheduleTask)
+		So(err, ShouldBeNil)
+	})
+}

+ 47 - 0
app/admin/ep/marthe/dao/mysql_tapd_bug_priority_conf.go

@@ -0,0 +1,47 @@
+package dao
+
+import (
+	"go-common/app/admin/ep/marthe/model"
+
+	pkgerr "github.com/pkg/errors"
+)
+
+// InsertTapdBugPriorityConf Insert Tapd Bug Priority Conf.
+func (d *Dao) InsertTapdBugPriorityConf(tapdBugPriorityConf *model.TapdBugPriorityConf) error {
+	return pkgerr.WithStack(d.db.Create(tapdBugPriorityConf).Error)
+}
+
+// UpdateTapdBugPriorityConf Update Tapd Bug Priority Conf.
+func (d *Dao) UpdateTapdBugPriorityConf(tapdBugPriorityConf *model.TapdBugPriorityConf) error {
+	return pkgerr.WithStack(d.db.Save(&tapdBugPriorityConf).Error)
+}
+
+// QueryTapdBugPriorityConfsByProjectTemplateIdAndStatus Query Tapd Bug Priority Confs By Project TemplateId And tatus.
+func (d *Dao) QueryTapdBugPriorityConfsByProjectTemplateIdAndStatus(projectTemplateID int64, status int) (tapdBugPriorityConfs []*model.TapdBugPriorityConf, err error) {
+	err = pkgerr.WithStack(d.db.Where("project_template_id = ? and status = ?", projectTemplateID, status).Find(&tapdBugPriorityConfs).Error)
+	return
+}
+
+// FindTapdBugPriorityConfs Find Tapd Bug Priority Confs.
+func (d *Dao) FindTapdBugPriorityConfs(req *model.QueryTapdBugPriorityConfsRequest) (total int64, tapdBugPriorityConfs []*model.TapdBugPriorityConf, err error) {
+	gDB := d.db.Model(&model.TapdBugPriorityConf{})
+
+	if req.ProjectTemplateID > 0 {
+		gDB = gDB.Where("project_template_id=?", req.ProjectTemplateID)
+	}
+
+	if req.UpdateBy != "" {
+		gDB = gDB.Where("update_by=?", req.UpdateBy)
+	}
+
+	if req.Status > 0 {
+		gDB = gDB.Where("status=?", req.Status)
+	}
+
+	if err = pkgerr.WithStack(gDB.Count(&total).Error); err != nil {
+		return
+	}
+
+	err = pkgerr.WithStack(gDB.Order("ctime desc").Offset((req.PageNum - 1) * req.PageSize).Limit(req.PageSize).Find(&tapdBugPriorityConfs).Error)
+	return
+}

+ 52 - 0
app/admin/ep/marthe/dao/mysql_tapd_bug_priority_confg_test.go

@@ -0,0 +1,52 @@
+package dao
+
+import (
+	"testing"
+
+	"go-common/app/admin/ep/marthe/model"
+
+	. "github.com/smartystreets/goconvey/convey"
+	"time"
+)
+
+var (
+	tapdBugPriorityConf = &model.TapdBugPriorityConf{
+		ProjectTemplateID: 1000,
+		Urgent:            123,
+		High:              321,
+		Medium:            111,
+		UpdateBy:          "fengyifeng",
+		StartTime:         time.Now(),
+		EndTime:           time.Now().AddDate(0, 1, 0),
+		Status:            model.TapdBugPriorityConfDisable,
+	}
+
+	queryTapdBugPriorityConfsRequest = &model.QueryTapdBugPriorityConfsRequest{
+		Pagination: model.Pagination{
+			PageSize: 10,
+			PageNum:  1,
+		},
+		ProjectTemplateID: 1000,
+	}
+)
+
+func Test_Tapd_Bug_Priority_Conf(t *testing.T) {
+	Convey("test Insert Tapd Bug Priority Conf", t, func() {
+		err := d.InsertTapdBugPriorityConf(tapdBugPriorityConf)
+		So(err, ShouldBeNil)
+	})
+
+	Convey("test Update Tapd Bug Priority Conf", t, func() {
+		tapdBugPriorityConf.Urgent = 10010
+		err := d.UpdateTapdBugPriorityConf(tapdBugPriorityConf)
+		So(err, ShouldBeNil)
+	})
+
+	Convey("test Find Tapd Bug Priority Confs", t, func() {
+		total, tapdBugPriorityConfs, err := d.FindTapdBugPriorityConfs(queryTapdBugPriorityConfsRequest)
+		So(err, ShouldBeNil)
+		So(total, ShouldBeGreaterThan, 0)
+		So(len(tapdBugPriorityConfs), ShouldBeGreaterThan, 0)
+	})
+
+}

+ 57 - 0
app/admin/ep/marthe/dao/mysql_tapd_bug_record.go

@@ -0,0 +1,57 @@
+package dao
+
+import (
+	"go-common/app/admin/ep/marthe/model"
+
+	pkgerr "github.com/pkg/errors"
+)
+
+// InsertTapdBugRecord Insert Tapd Bug Insert Log.
+func (d *Dao) InsertTapdBugRecord(tapdBugRecord *model.TapdBugRecord) error {
+	return pkgerr.WithStack(d.db.Create(tapdBugRecord).Error)
+}
+
+// UpdateTapdBugRecord Update Tapd Bug Insert Log.
+func (d *Dao) UpdateTapdBugRecord(tapdBugRecord *model.TapdBugRecord) error {
+	return pkgerr.WithStack(d.db.Save(&tapdBugRecord).Error)
+}
+
+// QueryTapdBugRecordByProjectIDAndStatus Query Tapd Bug Record By Project ID and status
+func (d *Dao) QueryTapdBugRecordByProjectIDAndStatus(projectID int64, status int) (tapdBugRecords []*model.TapdBugRecord, err error) {
+	err = pkgerr.WithStack(d.db.Where("project_template_id = ? and status = ?", projectID, status).Find(&tapdBugRecords).Error)
+	return
+}
+
+// QueryTapdBugRecordByStatus Query Tapd Bug Record By and status
+func (d *Dao) QueryTapdBugRecordByStatus(status int) (tapdBugRecords []*model.TapdBugRecord, err error) {
+	err = pkgerr.WithStack(d.db.Where("status = ?", status).Find(&tapdBugRecords).Error)
+	return
+}
+
+// FindBugRecords Find Bug Records.
+func (d *Dao) FindBugRecords(req *model.QueryBugRecordsRequest) (total int64, tapdBugRecords []*model.TapdBugRecord, err error) {
+	gDB := d.db.Model(&model.TapdBugRecord{})
+
+	if req.ProjectTemplateID > 0 {
+		gDB = gDB.Where("project_template_id=?", req.ProjectTemplateID)
+	}
+
+	if req.VersionTemplateID > 0 {
+		gDB = gDB.Where("version_template_id=?", req.VersionTemplateID)
+	}
+
+	if req.Operator != "" {
+		gDB = gDB.Where("operator=?", req.Operator)
+	}
+
+	if req.Status > 0 {
+		gDB = gDB.Where("status=?", req.Status)
+	}
+
+	if err = pkgerr.WithStack(gDB.Count(&total).Error); err != nil {
+		return
+	}
+
+	err = pkgerr.WithStack(gDB.Order("ctime desc").Offset((req.PageNum - 1) * req.PageSize).Limit(req.PageSize).Find(&tapdBugRecords).Error)
+	return
+}

+ 42 - 0
app/admin/ep/marthe/dao/mysql_tapd_bug_record_test.go

@@ -0,0 +1,42 @@
+package dao
+
+import (
+	"testing"
+	"time"
+
+	"go-common/app/admin/ep/marthe/model"
+
+	. "github.com/smartystreets/goconvey/convey"
+)
+
+var (
+	tmpID = time.Now().Unix()
+
+	tapdBugRecord = &model.TapdBugRecord{
+		ProjectTemplateID: tmpID,
+		VersionTemplateID: tmpID + 1,
+		Operator:          "fengyifeng",
+		Count:             10,
+		Status:            model.InsertBugStatusRunning,
+		IssueFilterSQL:    "SELECT * FROM bugly_issues WHERE issue_no = '265'",
+	}
+)
+
+func Test_Tapd_bug_record(t *testing.T) {
+	Convey("test Insert Tapd Bug Record", t, func() {
+		err := d.InsertTapdBugRecord(tapdBugRecord)
+		So(err, ShouldBeNil)
+	})
+
+	Convey("test Update Tapd Bug Record", t, func() {
+		tapdBugRecord.Status = model.InsertBugStatusDone
+		err := d.UpdateTapdBugRecord(tapdBugRecord)
+		So(err, ShouldBeNil)
+	})
+
+	Convey("test Query Tapd Bug Record By Project ID And Status", t, func() {
+		tapdBugRecords, err := d.QueryTapdBugRecordByProjectIDAndStatus(tapdBugRecord.ProjectTemplateID, model.InsertBugStatusDone)
+		So(err, ShouldBeNil)
+		So(len(tapdBugRecords), ShouldEqual, 1)
+	})
+}

+ 66 - 0
app/admin/ep/marthe/dao/mysql_tapd_bug_template.go

@@ -0,0 +1,66 @@
+package dao
+
+import (
+	"go-common/app/admin/ep/marthe/model"
+	"go-common/library/ecode"
+
+	pkgerr "github.com/pkg/errors"
+)
+
+// InsertTapdBugTemplate Insert Tapd Bug Template.
+func (d *Dao) InsertTapdBugTemplate(tapdBugTemplate *model.TapdBugTemplate) error {
+	return pkgerr.WithStack(d.db.Create(tapdBugTemplate).Error)
+}
+
+// UpdateTapdBugTemplate Update Tapd Bug Template.
+func (d *Dao) UpdateTapdBugTemplate(tapdBugTemplate *model.TapdBugTemplate) error {
+	return pkgerr.WithStack(d.db.Save(&tapdBugTemplate).Error)
+}
+
+// QueryTapdBugTemplate Query Tapd Bug Template.
+func (d *Dao) QueryTapdBugTemplate(id int64) (tapdBugTemplate *model.TapdBugTemplate, err error) {
+	tapdBugTemplate = &model.TapdBugTemplate{}
+	if err = d.db.Where("id=?", id).First(&tapdBugTemplate).Error; err != nil {
+		if err == ecode.NothingFound {
+			err = nil
+		} else {
+			err = pkgerr.WithStack(err)
+		}
+	}
+	return
+}
+
+// QueryTapdBugTemplateByProjectID Query Tapd Bug Template by project id.
+func (d *Dao) QueryTapdBugTemplateByProjectID(projectID string) (tapdBugTemplate *model.TapdBugTemplate, err error) {
+	tapdBugTemplate = &model.TapdBugTemplate{}
+	if err = d.db.Where("project_id=?", projectID).First(&tapdBugTemplate).Error; err != nil {
+		if err == ecode.NothingFound {
+			err = nil
+		} else {
+			err = pkgerr.WithStack(err)
+		}
+	}
+	return
+}
+
+// QueryAllTapdBugTemplates Query All Tapd Bug Templates.
+func (d *Dao) QueryAllTapdBugTemplates() (tapdBugTemplates []*model.TapdBugTemplate, err error) {
+	err = pkgerr.WithStack(d.db.Model(&model.TapdBugTemplate{}).Find(&tapdBugTemplates).Error)
+	return
+}
+
+// FindTapdBugTemplates Find tapd Bug Templates.
+func (d *Dao) FindTapdBugTemplates(req *model.QueryTapdBugTemplateRequest) (total int64, tapdBugTemplates []*model.TapdBugTemplate, err error) {
+	gDB := d.db.Model(&model.TapdBugTemplate{})
+
+	if req.UpdateBy != "" {
+		gDB = gDB.Where("update_by=?", req.UpdateBy)
+	}
+
+	if err = pkgerr.WithStack(gDB.Count(&total).Error); err != nil {
+		return
+	}
+
+	err = pkgerr.WithStack(gDB.Order("ctime desc").Offset((req.PageNum - 1) * req.PageSize).Limit(req.PageSize).Find(&tapdBugTemplates).Error)
+	return
+}

+ 75 - 0
app/admin/ep/marthe/dao/mysql_tapd_bug_template_test.go

@@ -0,0 +1,75 @@
+package dao
+
+import (
+	"strconv"
+	"testing"
+	"time"
+
+	"go-common/app/admin/ep/marthe/model"
+
+	. "github.com/smartystreets/goconvey/convey"
+)
+
+var (
+	tmp5 = strconv.FormatInt(time.Now().Unix(), 10)
+
+	tapdBugTemplate = &model.TapdBugTemplate{
+		WorkspaceID:    tmp5,
+		IssueFilterSQL: "SELECT * FROM bugly_issues WHERE issue_no = '265'",
+		SeverityKey:    "SeverityKey" + tmp5,
+		UpdateBy:       "fengyifeng",
+
+		TapdProperty: model.TapdProperty{
+			Title:            "Title" + tmp5,
+			Description:      "Description" + tmp5,
+			CurrentOwner:     "CurrentOwner" + tmp5,
+			Platform:         "Platform" + tmp5,
+			Module:           "Module" + tmp5,
+			IterationID:      "IterationID" + tmp5,
+			ReleaseID:        "ReleaseID" + tmp5,
+			Priority:         "Priority" + tmp5,
+			Severity:         "Severity" + tmp5,
+			Source:           "Source" + tmp5,
+			CustomFieldFour:  "CustomFieldFour" + tmp5,
+			BugType:          "BugType" + tmp5,
+			OriginPhase:      "OriginPhase" + tmp5,
+			CustomFieldThree: "CustomFieldThree" + tmp5,
+			Reporter:         "Reporter" + tmp5,
+			Status:           "Status" + tmp5,
+		},
+	}
+
+	queryTapdBugTemplateRequest = &model.QueryTapdBugTemplateRequest{
+		Pagination: model.Pagination{
+			PageSize: 10,
+			PageNum:  1,
+		},
+	}
+)
+
+func Test_Tapd_bug_template(t *testing.T) {
+	Convey("test Insert Tapd Bug Template", t, func() {
+		err := d.InsertTapdBugTemplate(tapdBugTemplate)
+		So(err, ShouldBeNil)
+	})
+
+	Convey("test Update Tapd Bug Template", t, func() {
+		tapdBugTemplate.UpdateBy = "xuneng"
+		err := d.UpdateTapdBugTemplate(tapdBugTemplate)
+		So(err, ShouldBeNil)
+	})
+
+	Convey("test Query Tapd Bug Template", t, func() {
+		tmpTapdBugTemplate, err := d.QueryTapdBugTemplate(tapdBugTemplate.ID)
+		So(err, ShouldBeNil)
+		So(tmpTapdBugTemplate.ID, ShouldEqual, tapdBugTemplate.ID)
+	})
+
+	Convey("test Find Tapd Bug Templates", t, func() {
+		total, tapdBugTemplates, err := d.FindTapdBugTemplates(queryTapdBugTemplateRequest)
+		So(err, ShouldBeNil)
+		So(total, ShouldBeGreaterThan, 0)
+		So(len(tapdBugTemplates), ShouldBeGreaterThan, 0)
+	})
+
+}

+ 68 - 0
app/admin/ep/marthe/dao/mysql_tapd_bug_version_template.go

@@ -0,0 +1,68 @@
+package dao
+
+import (
+	"go-common/app/admin/ep/marthe/model"
+	"go-common/library/ecode"
+
+	pkgerr "github.com/pkg/errors"
+)
+
+// InsertTapdBugVersionTemplate Insert TapdBug Version Template.
+func (d *Dao) InsertTapdBugVersionTemplate(tapdBugVersionTemplate *model.TapdBugVersionTemplate) error {
+	return pkgerr.WithStack(d.db.Create(tapdBugVersionTemplate).Error)
+}
+
+// UpdateTapdBugVersionTemplate Update Tapd Bug Version Template.
+func (d *Dao) UpdateTapdBugVersionTemplate(tapdBugVersionTemplate *model.TapdBugVersionTemplate) error {
+	return pkgerr.WithStack(d.db.Save(&tapdBugVersionTemplate).Error)
+}
+
+// QueryTapdBugVersionTemplate Query Tapd Bug Version Template.
+func (d *Dao) QueryTapdBugVersionTemplate(id int64) (tapdBugVersionTemplate *model.TapdBugVersionTemplate, err error) {
+	tapdBugVersionTemplate = &model.TapdBugVersionTemplate{}
+	if err = d.db.Where("id=?", id).First(&tapdBugVersionTemplate).Error; err != nil {
+		if err == ecode.NothingFound {
+			err = nil
+		} else {
+			err = pkgerr.WithStack(err)
+		}
+	}
+	return
+}
+
+// QueryTapdBugVersionTemplateByVersion Query Tapd Bug Version Template.
+func (d *Dao) QueryTapdBugVersionTemplateByVersion(version string) (tapdBugVersionTemplate *model.TapdBugVersionTemplate, err error) {
+	tapdBugVersionTemplate = &model.TapdBugVersionTemplate{}
+	if err = d.db.Where("version=?", version).First(&tapdBugVersionTemplate).Error; err != nil {
+		if err == ecode.NothingFound {
+			err = nil
+		} else {
+			err = pkgerr.WithStack(err)
+		}
+	}
+	return
+}
+
+// FindTapdBugVersionTemplates Find Tapd Bug Version Templates.
+func (d *Dao) FindTapdBugVersionTemplates(req *model.QueryTapdBugVersionTemplateRequest) (total int64, tapdBugVersionTemplate []*model.TapdBugVersionTemplate, err error) {
+	gDB := d.db.Model(&model.TapdBugVersionTemplate{})
+
+	if req.ProjectID > 0 {
+		gDB = gDB.Where("project_template_id = ?", req.ProjectID)
+	}
+
+	if req.Version != "" {
+		gDB = gDB.Where("version like ?", req.Version+_wildcards)
+	}
+
+	if req.UpdateBy != "" {
+		gDB = gDB.Where("update_by = ?", req.UpdateBy)
+	}
+
+	if err = pkgerr.WithStack(gDB.Count(&total).Error); err != nil {
+		return
+	}
+
+	err = pkgerr.WithStack(gDB.Order("ctime desc").Offset((req.PageNum - 1) * req.PageSize).Limit(req.PageSize).Find(&tapdBugVersionTemplate).Error)
+	return
+}

+ 82 - 0
app/admin/ep/marthe/dao/mysql_tapd_bug_version_template_test.go

@@ -0,0 +1,82 @@
+package dao
+
+import (
+	"strconv"
+	"testing"
+	"time"
+
+	"go-common/app/admin/ep/marthe/model"
+
+	. "github.com/smartystreets/goconvey/convey"
+)
+
+var (
+	tmp6 = strconv.FormatInt(time.Now().Unix(), 10)
+
+	tapdBugVersionTemplate = &model.TapdBugVersionTemplate{
+		Version:           "version" + tmp6,
+		ProjectTemplateID: 10,
+		IssueFilterSQL:    "SELECT * FROM bugly_issues WHERE issue_no = '265'",
+		SeverityKey:       "SeverityKey" + tmp6,
+		UpdateBy:          "fengyifeng",
+
+		TapdProperty: model.TapdProperty{
+			Title:            "Title" + tmp6,
+			Description:      "Description" + tmp6,
+			CurrentOwner:     "CurrentOwner" + tmp6,
+			Platform:         "Platform" + tmp6,
+			Module:           "Module" + tmp6,
+			IterationID:      "IterationID" + tmp6,
+			ReleaseID:        "ReleaseID" + tmp6,
+			Priority:         "Priority" + tmp6,
+			Severity:         "Severity" + tmp6,
+			Source:           "Source" + tmp6,
+			CustomFieldFour:  "CustomFieldFour" + tmp6,
+			BugType:          "BugType" + tmp6,
+			OriginPhase:      "OriginPhase" + tmp6,
+			CustomFieldThree: "CustomFieldThree" + tmp6,
+			Reporter:         "Reporter" + tmp6,
+			Status:           "Status" + tmp6,
+		},
+	}
+
+	queryTapdBugVersionTemplateRequest = &model.QueryTapdBugVersionTemplateRequest{
+		Pagination: model.Pagination{
+			PageSize: 10,
+			PageNum:  1,
+		},
+		Version: tapdBugVersionTemplate.Version,
+	}
+)
+
+func Test_Tapd_bug_version_template(t *testing.T) {
+	Convey("test Insert Tapd Bug version Template", t, func() {
+		err := d.InsertTapdBugVersionTemplate(tapdBugVersionTemplate)
+		So(err, ShouldBeNil)
+	})
+
+	Convey("test Update Tapd Bug version Template", t, func() {
+		tapdBugTemplate.UpdateBy = "xuneng"
+		err := d.UpdateTapdBugVersionTemplate(tapdBugVersionTemplate)
+		So(err, ShouldBeNil)
+	})
+
+	Convey("test Query Tapd Bug version Template", t, func() {
+		tmpTapdBugVersionTemplate, err := d.QueryTapdBugVersionTemplate(tapdBugVersionTemplate.ID)
+		So(err, ShouldBeNil)
+		So(tmpTapdBugVersionTemplate.ID, ShouldEqual, tapdBugVersionTemplate.ID)
+	})
+
+	Convey("test Query Tapd Bug Version Template By version", t, func() {
+		tmpTapdBugVersionTemplate, err := d.QueryTapdBugVersionTemplateByVersion(tapdBugVersionTemplate.Version)
+		So(err, ShouldBeNil)
+		So(tmpTapdBugVersionTemplate.ID, ShouldEqual, tapdBugVersionTemplate.ID)
+	})
+
+	Convey("test Find Tapd Bug version Templates", t, func() {
+		total, tapdBugVersionTemplate, err := d.FindTapdBugVersionTemplates(queryTapdBugVersionTemplateRequest)
+		So(err, ShouldBeNil)
+		So(total, ShouldBeGreaterThan, 0)
+		So(len(tapdBugVersionTemplate), ShouldBeGreaterThan, 0)
+	})
+}

+ 0 - 0
app/admin/ep/marthe/dao/mysql_user.go


部分文件因为文件数量过多而无法显示