Compare commits

...

178 Commits

Author SHA1 Message Date
Xunhui Zhang 4e72e1e1bf 将创建项目时blockchain_init_token从百分比修改为具体值 2023-03-10 10:53:16 +08:00
xxq250 6bb3f92973 开启确权激励的用户或项目可配置 2023-03-09 17:45:03 +08:00
xxq250 d98b5b9f72 Merge branch 'develop' into standalone_develop 2023-03-09 16:23:20 +08:00
xxq250 6f7b40ec24 fixed 搜索用户项目时过虑表情字符 2023-03-09 16:22:48 +08:00
xxq250 d993df7df4 fixed 搜索用户时过虑表情字符 2023-03-09 16:07:38 +08:00
xxq250 701d5dea40 fixed 搜索用户时过虑表情字符 2023-03-09 16:06:04 +08:00
xxq250 6e16a9397c fixed 搜索用户时过虑表情字符 2023-03-09 16:02:50 +08:00
yystopf e9a152032c Merge branch 'develop' into standalone_develop 2023-03-09 11:09:14 +08:00
yystopf 917bae4321 新增:创建疑修消息修复 2023-03-09 11:09:04 +08:00
yystopf ccc8f13959 Merge branch 'develop' into standalone_develop 2023-03-09 10:33:12 +08:00
yystopf 4b618d1a61 修复:消息通知issue跳转使用index 2023-03-09 10:33:00 +08:00
xxq250 9da61bd69e 项目列表默认查询时count性能问题处理 2023-03-07 16:34:31 +08:00
xxq250 db845e7df0 开启确权激励的用户或项目 2023-03-07 09:59:42 +08:00
xxq250 d0209490c6 Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2023-03-07 09:49:24 +08:00
xxq250 9cd150be88 开启确权激励的用户或项目 2023-03-07 09:49:17 +08:00
yystopf de6ff8d863 Merge branch 'develop' into standalone_develop 2023-03-06 17:49:36 +08:00
yystopf 620ed26e45 更改:评论列表分页数量限制变大 2023-03-06 17:49:28 +08:00
yystopf 8a67c19c67 Merge branch 'develop' into standalone_develop 2023-03-06 10:42:21 +08:00
yystopf 5580a96fe7 修复 2023-03-06 10:42:12 +08:00
yystopf fd17fcab17 Merge branch 'develop' into standalone_develop 2023-03-06 10:33:49 +08:00
yystopf 2827e28b07 新增:评论/操作记录总数返回 2023-03-06 10:33:26 +08:00
yystopf b19560b174 Merge branch 'develop' into standalone_develop 2023-03-03 18:43:28 +08:00
yystopf b5383bfa5a 修复:消息内容修复 2023-03-03 18:43:18 +08:00
yystopf 5b056c04da Merge branch 'develop' into standalone_develop 2023-03-03 18:03:08 +08:00
yystopf bc862f8d98 修复:部分用户无法收到消息 2023-03-03 18:01:46 +08:00
xxq250 b1c10d66f9 区块链确权相关 2023-03-02 13:49:50 +08:00
yystopf 5216e61479 更改:移除操作日志中标记的色值 2023-03-01 16:34:27 +08:00
yystopf ce70378fc3 Merge branch 'develop' into standalone_develop 2023-03-01 16:29:45 +08:00
yystopf cbb0a639b2 Merge pull request '平台issue模块升级相关' (#24) from yystopf/forgeplus:hh_issue_upgrade into develop 2023-03-01 16:29:19 +08:00
yystopf a654a08e0c Merge branch 'develop' into standalone_develop 2023-03-01 14:40:26 +08:00
yystopf b8724c02f7 修复:组织权限关于操作权限部分 2023-03-01 14:40:11 +08:00
yystopf e59e4e4bd5 修复 2023-02-28 17:05:38 +08:00
yystopf ec44376e4d 修复:issue 评论中@无参与记录 2023-02-28 16:57:44 +08:00
xxq250 a80b4d954e dockerfile可预览 2023-02-28 16:03:33 +08:00
xxq250 2842df87c6 群智熵分析报表接口调整到项目动态接口 2023-02-28 15:01:23 +08:00
xxq250 6f9d902e70 群智熵分析报表接口调整到项目动态接口 2023-02-28 14:59:06 +08:00
yystopf 4091872091 更改:默认标记任务色值 2023-02-28 14:40:27 +08:00
xxq250 5a7aaff112 群智熵分析报表接口调整到项目动态接口 2023-02-28 09:37:18 +08:00
xxq250 558d720203 群智熵分析报表接口调整到项目动态接口 2023-02-28 09:26:45 +08:00
xxq250 46da5a0d84 群智熵分析报表接口调整 2023-02-27 16:41:40 +08:00
xxq250 f59ab5bcf1 Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2023-02-27 16:02:13 +08:00
xxq250 f8350c4043 群智熵分析报表接口 2023-02-27 16:02:08 +08:00
yystopf ed7f5a08bb 修复:rake任务 2023-02-27 15:17:10 +08:00
yystopf acaa28cd02 修复:last index 获取方式 2023-02-27 15:13:35 +08:00
yystopf ddb5010e6d 修复:issue 查询需排除pr下issue 2023-02-27 15:04:18 +08:00
yystopf f05cf0c000 修复 2023-02-27 14:12:25 +08:00
yystopf ff025e07dd 新增:里程碑列表支持id查询 2023-02-27 11:25:37 +08:00
yystopf 4c26085e58 修复 2023-02-27 11:07:09 +08:00
yystopf a30a3c56b6 修复 2023-02-27 10:55:53 +08:00
yystopf 2e0b32f86b 修复:atme和issue category 无法正常搜索 2023-02-27 10:46:56 +08:00
yystopf dab636d122 修复 2023-02-24 15:26:40 +08:00
yystopf b616c971db 修复 2023-02-24 14:32:21 +08:00
yystopf 35bb8fbc11 修复 2023-02-24 10:24:57 +08:00
yystopf 267279df2d 修复 2023-02-24 09:04:29 +08:00
yystopf e53d6cd763 修复 2023-02-23 21:03:40 +08:00
yystopf 3fd04109d6 修复 2023-02-23 20:54:48 +08:00
yystopf 8487c67bab 修复:issue_classify 使用小写 2023-02-23 17:32:55 +08:00
yystopf dc01d7fc3e 修复 2023-02-23 16:18:09 +08:00
yystopf 7cb2321c02 修复 2023-02-23 15:31:03 +08:00
yystopf c589fcc6a4 修复 2023-02-23 15:27:26 +08:00
yystopf 1fcdbe9dce 修复 2023-02-23 15:26:30 +08:00
yystopf dc9ca7d0ca 更改:疑修param为index以及新增统计标签下合并请求数量 2023-02-23 15:24:30 +08:00
yystopf edc00d28a7 新增:标记列表返回合并请求数量 2023-02-22 16:25:40 +08:00
yystopf 317ff3c76b 修复 2023-02-22 16:05:24 +08:00
yystopf c781ca0da1 更改:部分细节问题 2023-02-22 15:56:44 +08:00
yystopf a07da79e89 修复:重复初始化标签问题 2023-02-22 14:42:52 +08:00
yystopf 54a85a60fa 更改:疑修列表开启、关闭数量规则以及排序规则 2023-02-22 13:56:12 +08:00
xxq250 895cd8f737 Merge pull request '修复bug' (#26) from KingChan/forgeplus:chenjing into standalone_develop 2023-02-22 10:55:39 +08:00
chenjing 54b7f2c726 fix api/users/:login/headmaps.json bug 2023-02-22 10:48:23 +08:00
chenjing e440ee8483 api/users/{{user}}/messages add check auth 2023-02-22 10:38:40 +08:00
yystopf ed5d12654e 修复 2023-02-22 09:57:39 +08:00
yystopf 8239f13eaa 新增:要求登录接口处理 2023-02-22 09:55:05 +08:00
xxq250 6c1f9cabda fixed 左侧菜单组织列表 2023-02-22 09:38:32 +08:00
xxq250 5f3d51027e fixed 左侧菜单组织列表 2023-02-22 09:36:13 +08:00
xxq250 84029bf49a Merge pull request '用户版块增加组织列表' (#25) from KingChan/forgeplus:chenjing into standalone_develop 2023-02-22 09:29:39 +08:00
chenjing ee77be7c8a fix 2023-02-22 09:25:49 +08:00
chenjing 05f103b0c4 team count and delete orgs 2023-02-22 09:24:11 +08:00
chenjing ac8372d7e1 orgs search 2023-02-22 09:24:11 +08:00
chenjing b9e2a3736c admins org list 2023-02-22 09:24:11 +08:00
yystopf 311776a004 修复:标签列表无id返回 2023-02-21 18:30:29 +08:00
xxq250 12eaec0319 Merge remote-tracking branch 'origin/standalone_develop' into standalone_develop 2023-02-21 16:43:53 +08:00
xxq250 47a5fe24c8 调整Bot授权接口,fixed注册信息手机号 2023-02-21 16:43:28 +08:00
yystopf 4a3d1e5a69 Merge branch 'develop' into standalone_develop 2023-02-21 15:40:08 +08:00
yystopf 3a5d8e75df 修改:后台管理sidebar冲突 2023-02-21 15:39:52 +08:00
yystopf 23a65798df Merge branch 'develop' into standalone_develop 2023-02-21 15:31:58 +08:00
yystopf cab5466ae4 更改:项目排行榜项目需显示全称 2023-02-21 15:31:00 +08:00
yystopf b18051ec31 新增:里程碑相关数量返回 2023-02-21 13:55:40 +08:00
yystopf 02526878fe 修复:更新无法设置空值 2023-02-20 18:22:14 +08:00
yystopf 4657618753 新增:修复数据rake任务 2023-02-20 17:25:07 +08:00
yystopf cb3bb23e79 新增:操作权限 2023-02-20 16:47:19 +08:00
yystopf 656d5b69b6 新增:接口附件内容返回 2023-02-20 13:54:00 +08:00
yystopf 121fa2bff4 新增:里程碑相关接口 2023-02-20 11:42:22 +08:00
yystopf c63a86a797 新增:评论相关接口补足 2023-02-18 23:23:33 +08:00
yystopf 15d71bfd24 修复:创建数据无法创建负责人 2023-02-17 17:40:09 +08:00
xxq250 df38df45d5 fixed 向jianmu推送时项目不存在时处理 2023-02-17 17:24:27 +08:00
yystopf b85913e485 新增:疑修操作记录、评论列表接口以及更新疑修无法产生操作记录修复 2023-02-17 16:11:39 +08:00
xxq250 3427fa6768 新增时向grimoirelab推送事件,修正 2023-02-16 16:11:40 +08:00
xxq250 e92b288f41 新增时向grimoirelab推送事件,异常处理,log 2023-02-16 16:07:51 +08:00
xxq250 0725ef74de 新增时向grimoirelab推送事件,异常处理 2023-02-16 15:54:43 +08:00
xxq250 00bb1e82af 新增时向grimoirelab推送事件,异常处理 2023-02-16 15:51:06 +08:00
xxq250 f93e665239 新增时向grimoirelab推送事件 2023-02-16 15:46:15 +08:00
xxq250 d578c5c8df 新增时向grimoirelab推送事件 2023-02-16 15:44:42 +08:00
xxq250 c872f50685 新增时向grimoirelab推送事件 2023-02-16 15:42:15 +08:00
xxq250 368be1849c 新增时向grimoirelab推送事件 2023-02-16 15:41:20 +08:00
xxq250 7d3d95c406 新增时向grimoirelab推送事件 2023-02-16 15:37:01 +08:00
xxq250 5c3c47d13b 调整Bot授权接口 2023-02-16 14:37:16 +08:00
xxq250 676d2ddce1 调整Bot授权接口 2023-02-16 13:52:38 +08:00
xxq250 3d0fa0d50c 调整Bot授权接口 2023-02-16 11:01:21 +08:00
xxq250 3c51d69555 调整Bot授权接口 2023-02-16 09:57:25 +08:00
xxq250 1d9df770b5 调整Bot授权接口 2023-02-16 09:41:39 +08:00
xxq250 c0fcf3445a 调整Bot授权接口 2023-02-16 09:40:08 +08:00
xxq250 6fe16c7c82 调整Bot授权接口 2023-02-16 09:19:37 +08:00
xxq250 5e7fe0f28c Merge branch 'standalone_develop' of https://gitlink.org.cn/Trustie/forgeplus into standalone_develop 2023-02-15 18:38:24 +08:00
xxq250 1cd43de75b 增加Bot授权相关 2023-02-15 18:37:36 +08:00
yystopf 2a8e0d2be8 新增:标记管理接口,以及列表下拉框搜索参数 2023-02-15 17:24:36 +08:00
yystopf c38a4e4e08 Merge branch 'standalone_develop' of https://gitlink.org.cn/Trustie/forgeplus into standalone_develop 2023-02-15 11:31:02 +08:00
yystopf 202be8246b Merge branch 'develop' into standalone_develop 2023-02-15 11:30:54 +08:00
yystopf 774210206e 修复:支持以.开头文件的代码预览 2023-02-15 11:30:33 +08:00
yystopf 2899f3b18e 修复:删除、更改逻辑测试修改 2023-02-15 11:25:42 +08:00
yystopf 404a6a00e7 新增:issue编辑删除接口 2023-02-14 23:19:17 +08:00
xxq250 f0c1e9e319 项目搜索结果同步es结果 2023-02-13 11:27:44 +08:00
xxq250 7d17b654f6 更新gems源 2023-02-13 10:22:36 +08:00
yystopf db2d398d94 更改:participants创建规则 2023-02-13 09:43:26 +08:00
xxq250 c0271707a1 fixed merge 2023-02-10 21:46:24 +08:00
xxq250 0af87a67ec fixed merge 2023-02-10 17:54:12 +08:00
yystopf 7bce320113 修复:冲突代码回归 2023-02-10 16:23:06 +08:00
yystopf 4a8e3324af 修复 2023-02-10 15:39:09 +08:00
yystopf 3a14aa10ce Merge branch 'develop' into standalone_develop 2023-02-10 13:44:43 +08:00
yystopf b799780637 Merge pull request '分支标签页面改版' (#23) from yystopf/forgeplus:hh_branch_tag into develop 2023-02-10 13:43:57 +08:00
yystopf 53c2ffffb3 新增:疑修创建以及详情接口 2023-02-10 11:38:51 +08:00
yystopf 934b42f1a1 新增:优先级和所有仓库成员接口 2023-02-09 16:20:16 +08:00
yystopf c1d791741c 修复:无法正常查询关联数据 2023-02-09 10:15:59 +08:00
yystopf 1aeed8236d 更改:部分接口传参以及路由修复 2023-02-09 09:29:02 +08:00
yystopf f0750333f9 修复 2023-02-08 16:57:42 +08:00
yystopf 4085da7837 新增:issue和里程碑列表接口 2023-02-08 16:56:01 +08:00
yystopf ade03c5e2f 新增:issue参与记录 2023-02-08 16:55:20 +08:00
yystopf fe972f1141 新增:项目标记接口 2023-02-08 10:15:29 +08:00
yystopf af67d984b4 新增:里程碑接口 2023-02-08 09:49:02 +08:00
yystopf 241bbc06ca 新增:issue负责人变更为一对多 2023-02-07 17:47:28 +08:00
yystopf 587facfb3d 新增:发布人和状态接口 2023-02-07 17:12:43 +08:00
yystopf 8210404f4a Merge branch 'develop' into standalone_develop 2023-02-07 15:56:52 +08:00
yystopf 128c5c48e6 修复 2023-02-07 15:56:35 +08:00
yystopf 99be556d5b Merge branch 'develop' into standalone_develop 2023-02-07 14:24:26 +08:00
yystopf 353449c53c 修复 2023-02-07 14:23:24 +08:00
yystopf c983df57ee merge from develop 2023-02-07 14:21:32 +08:00
yystopf 39ae14bce7 新增:entry新增submodule url 2023-02-07 14:18:57 +08:00
yystopf 8c8925f3ae 修复 2023-02-07 10:39:25 +08:00
yystopf 6cfb5dfbe2 新增:删除保护分支和删除发行版提示 2023-02-07 10:33:46 +08:00
yystopf 0eb17aa90f 更改:分支列表搜索参数 2023-02-06 10:07:15 +08:00
yystopf 7c911f5b86 新增:路由匹配特殊符号 2023-02-06 09:49:36 +08:00
yystopf 0800f9e6e0 修复 2023-02-03 18:24:07 +08:00
yystopf 726453ea99 修复:tags detail 无法获取commit_user 2023-02-03 18:23:16 +08:00
yystopf bef1350933 新增:load_project新增repository 2023-02-03 18:20:00 +08:00
yystopf 2772b97e32 新增:标签列表和删除接口 2023-02-03 18:03:57 +08:00
xxq250 f2a456c6ed Merge remote-tracking branch 'origin/develop' into standalone_develop
# Conflicts:
#	app/queries/projects/list_query.rb
2023-02-03 15:52:28 +08:00
xxq250 4bc945028b fixed 项目搜索用户名失败修正 2023-02-03 15:50:33 +08:00
yystopf 8b90164247 新增:删除分支接口 2023-02-03 15:03:59 +08:00
yystopf f2cdba29ea 新增:更改默认分支接口 2023-02-03 13:45:32 +08:00
yystopf 3f78899c58 新增:branch列表接口 2023-02-03 10:53:24 +08:00
yystopf 5fc433d130 Merge branch 'develop' into standalone_develop 2023-02-02 18:30:33 +08:00
yystopf bfe14352c0 更改:codestats回退 2023-02-02 18:30:07 +08:00
yystopf a746e791b0 Merge branch 'develop' into standalone_develop 2023-02-02 18:13:19 +08:00
yystopf 87412643cd 修复 2023-02-02 18:13:02 +08:00
yystopf e4b5427725 Merge branch 'develop' into standalone_develop 2023-02-02 18:11:31 +08:00
yystopf 9fe3b5ae3e 新增:code_stats分页 2023-02-02 18:10:26 +08:00
xxq250 a03d1efcb0 fixed 如果有搜索关键字根据ES搜索结果排序 2023-02-02 14:05:40 +08:00
xxq250 2668630f2a 项目fored_count更新时updated_on同时更新 2023-02-01 17:23:48 +08:00
xxq250 785ae13649 创建仓库开通建木DevOps job延迟5秒 2023-02-01 17:03:16 +08:00
yystopf c4b93f2bd3 Merge branch 'develop' into standalone_develop 2023-02-01 15:47:43 +08:00
yystopf 913eb62923 新增:排行榜每日统计数据设置一个过期时间 2023-02-01 15:44:40 +08:00
yystopf 112ee39efd 修复:排行榜数据不展示已删除数据 2023-02-01 14:50:12 +08:00
yystopf 08d8abd163 Merge branch 'develop' into standalone_develop 2023-02-01 14:26:52 +08:00
yystopf 0229d99e74 新增:后台管理排行榜数据 2023-02-01 14:25:55 +08:00
yystopf 40873fe95d Merge branch 'develop' into standalone_develop 2023-01-09 15:40:12 +08:00
yystopf dfe56d9f75 修复 2023-01-09 15:39:49 +08:00
yystopf 5f46d205a9 Merge branch 'develop' into standalone_develop 2023-01-09 15:15:32 +08:00
yystopf fdb713b76d 更改:更改用户邮箱移除旧的cache 2023-01-09 15:14:51 +08:00
xxq250 7c80f74e26 fixed 代码贡献者email 2023-01-06 11:16:50 +08:00
165 changed files with 4694 additions and 183 deletions

View File

@ -1,4 +1,5 @@
source 'https://gems.ruby-china.com'
#source 'https://gems.ruby-china.com'
source 'https://mirrors.cloud.tencent.com/rubygems/'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
gem 'rails', '~> 5.2.0'
@ -139,4 +140,4 @@ gem 'doorkeeper'
gem 'doorkeeper-jwt'
gem 'gitea-client', '~> 0.11.1'
gem 'gitea-client', '~> 0.11.6'

View File

@ -0,0 +1,2 @@
// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.

View File

@ -0,0 +1,3 @@
// Place all the styles related to the admins/organizations controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/

View File

@ -0,0 +1,27 @@
class Admins::OrganizationsController < Admins::BaseController
before_action :finder_org, except: [:index]
def index
params[:sort_by] = params[:sort_by].presence || 'created_on'
params[:sort_direction] = params[:sort_direction].presence || 'desc'
orgs = Admins::OrganizationQuery.call(params)
@orgs = paginate orgs
end
def show
end
def destroy
@org.destroy!
Admins::DeleteOrganizationService.call(@org.login)
render_delete_success
end
private
def finder_org
@org = Organization.find(params[:id])
end
end

View File

@ -0,0 +1,16 @@
class Admins::ProjectsRankController < Admins::BaseController
def index
@rank_date = rank_date
deleted_data = $redis_cache.smembers("v2-project-rank-deleted")
$redis_cache.zrem("v2-project-rank-#{rank_date}", deleted_data) unless deleted_data.blank?
@date_rank = $redis_cache.zrevrange("v2-project-rank-#{rank_date}", 0, -1, withscores: true)
end
private
def rank_date
params.fetch(:date, Date.today.to_s)
end
end

View File

@ -0,0 +1,15 @@
class Admins::UsersRankController < Admins::BaseController
def index
@rank_date = rank_date
@date_rank = $redis_cache.zrevrange("v2-user-rank-#{rank_date}", 0, -1, withscores: true)
end
private
def rank_date
params.fetch(:date, Date.today.to_s)
end
end

View File

@ -20,10 +20,19 @@ class Api::V1::BaseController < ApplicationController
# User.find(doorkeeper_token.resource_owner_id) if doorkeeper_token
# end
# end
def kaminary_select_paginate(relation)
limit = params[:limit] || params[:per_page]
limit = (limit.to_i.zero? || limit.to_i > 200) ? 200 : limit.to_i
page = params[:page].to_i.zero? ? 1 : params[:page].to_i
relation.page(page).per(limit)
end
def limit
params.fetch(:limit, 15)
end
def page
params.fetch(:page, 1)
end
@ -43,7 +52,6 @@ class Api::V1::BaseController < ApplicationController
# 具有仓库的操作权限或者fork仓库的操作权限
def require_operate_above_or_fork_project
@project = load_project
puts !current_user.admin? && !@project.operator?(current_user) && !(@project.fork_project.present? && @project.fork_project.operator?(current_user))
return render_forbidden if !current_user.admin? && !@project.operator?(current_user) && !(@project.fork_project.present? && @project.fork_project.operator?(current_user))
end

View File

@ -0,0 +1,12 @@
class Api::V1::Issues::AssignersController < Api::V1::BaseController
before_action :require_public_and_member_above, only: [:index]
# 负责人列表
def index
@assigners = User.joins(assigned_issues: :project).where(projects: {id: @project&.id})
@assigners = @assigners.order("users.id=#{current_user.id} desc").distinct
@assigners = @assigners.ransack(login_or_nickname_cont: params[:keyword]).result if params[:keyword].present?
@assigners = kaminary_select_paginate(@assigners)
end
end

View File

@ -0,0 +1,11 @@
class Api::V1::Issues::AuthorsController < Api::V1::BaseController
before_action :require_public_and_member_above, only: [:index]
# 发布人列表
def index
@authors = User.joins(issues: :project).where(projects: {id: @project&.id})
@authors = @authors.order("users.id=#{current_user.id} desc").distinct
@authors = @authors.ransack(login_or_nickname_cont: params[:keyword]).result if params[:keyword].present?
@authors = kaminary_select_paginate(@authors)
end
end

View File

@ -0,0 +1,10 @@
class Api::V1::Issues::IssuePrioritiesController < Api::V1::BaseController
before_action :require_public_and_member_above, only: [:index]
def index
@priorities = IssuePriority.order(position: :asc)
@priorities = @priorities.ransack(name_cont: params[:keyword]).result if params[:keyword]
@priorities = kaminary_select_paginate(@priorities)
end
end

View File

@ -0,0 +1,65 @@
class Api::V1::Issues::IssueTagsController < Api::V1::BaseController
before_action :require_login, except: [:index]
before_action :require_public_and_member_above, only: [:index]
before_action :require_operate_above, only: [:create, :update, :destroy]
def index
@issue_tags = @project.issue_tags.reorder("#{sort_by} #{sort_direction}")
@issue_tags = @issue_tags.ransack(name_cont: params[:keyword]).result if params[:keyword].present?
if params[:only_name]
@issue_tags = kaminary_select_paginate(@issue_tags.select(:id, :name, :color))
else
@issue_tags = kaminari_paginate(@issue_tags.includes(:project, :user, :issue_issues, :pull_request_issues))
end
end
def create
@issue_tag = @project.issue_tags.new(issue_tag_params)
if @issue_tag.save!
render_ok
else
render_error("创建标记失败!")
end
end
before_action :load_issue_tag, only: [:update, :destroy]
def update
@issue_tag.attributes = issue_tag_params
if @issue_tag.save!
render_ok
else
render_error("更新标记失败!")
end
end
def destroy
if @issue_tag.destroy!
render_ok
else
render_error("删除标记失败!")
end
end
private
def sort_by
sort_by = params.fetch(:sort_by, "created_at")
sort_by = IssueTag.column_names.include?(sort_by) ? sort_by : "created_at"
sort_by
end
def sort_direction
sort_direction = params.fetch(:sort_direction, "desc").downcase
sort_direction = %w(desc asc).include?(sort_direction) ? sort_direction : "desc"
sort_direction
end
def issue_tag_params
params.permit(:name, :description, :color)
end
def load_issue_tag
@issue_tag = @project.issue_tags.find_by_id(params[:id])
end
end

View File

@ -0,0 +1,63 @@
class Api::V1::Issues::JournalsController < Api::V1::BaseController
before_action :require_login, except: [:index, :children_journals]
before_action :require_public_and_member_above
before_action :load_issue
before_action :load_journal, only: [:children_journals, :update, :destroy]
before_action :check_journal_operate_permission, only: [:update, :destroy]
def index
@object_result = Api::V1::Issues::Journals::ListService.call(@issue, query_params, current_user)
@total_journals_count = @object_result[:total_journals_count]
@total_operate_journals_count = @object_result[:total_operate_journals_count]
@total_comment_journals_count = @object_result[:total_comment_journals_count]
@journals = kaminary_select_paginate(@object_result[:data])
end
def create
@object_result = Api::V1::Issues::Journals::CreateService.call(@issue, journal_params, current_user)
end
def children_journals
@object_results = Api::V1::Issues::Journals::ChildrenListService.call(@issue, @journal, query_params, current_user)
@journals = kaminari_paginate(@object_results)
end
def update
@object_result = Api::V1::Issues::Journals::UpdateService.call(@issue, @journal, journal_params, current_user)
end
def destroy
if @journal.destroy!
render_ok
else
render_error("删除评论失败!")
end
end
private
def query_params
params.permit(:category, :keyword, :sort_by, :sort_direction)
end
def journal_params
params.permit(:notes, :parent_id, :reply_id, :attachment_ids => [], :receivers_login => [])
end
def load_issue
@issue = @project.issues.issue_issue.where(project_issues_index: params[:index]).where.not(id: params[:index]).take || Issue.find_by_id(params[:index])
if @issue.blank?
render_not_found("疑修不存在!")
end
end
def load_journal
@journal = Journal.find_by_id(params[:id])
return render_not_found("评论不存在!") unless @journal.present?
end
def check_journal_operate_permission
return render_forbidden("您没有操作权限!") unless @project.member?(current_user) || current_user.admin? || @issue.user == current_user || @journal.user == current_user || @journal.parent_journal&.user == current_user
end
end

View File

@ -0,0 +1,87 @@
class Api::V1::Issues::MilestonesController < Api::V1::BaseController
before_action :require_login, except: [:index, :show]
before_action :require_public_and_member_above, only: [:index, :show]
before_action :require_operate_above, only: [:create, :update, :destroy]
before_action :load_milestone, only: [:show, :update, :destroy]
# 里程碑列表
def index
@milestones = @project.versions
@milestones = @milestones.ransack(id_eq: params[:keyword]).result.or(@milestones.ransack(name_or_description_cont: params[:keyword]).result) if params[:keyword].present?
@closed_milestone_count = @milestones.closed.size
@opening_milestone_count = @milestones.opening.size
@milestones = params[:category] == "closed" ? @milestones.closed : @milestones.opening
@milestones = @milestones.reorder("versions.#{sort_by} #{sort_direction}")
if params[:only_name]
@milestones = @milestones.select(:id, :name)
@milestones = kaminary_select_paginate(@milestones)
else
@milestones = @milestones.includes(:issues, :closed_issues, :opened_issues)
@milestones = kaminari_paginate(@milestones)
end
end
def create
@milestone = @project.versions.new(milestone_params)
if @milestone.save!
render_ok
else
render_error(@milestone.errors.full_messages.join(","))
end
end
# 里程碑详情
def show
@object_result = Api::V1::Issues::Milestones::DetailIssuesService.call(@project, @milestone, query_params, current_user)
@total_issues_count = @object_result[:total_issues_count]
@opened_issues_count = @object_result[:opened_issues_count]
@closed_issues_count = @object_result[:closed_issues_count]
@issues = kaminari_paginate(@object_result[:data])
end
def update
@milestone.attributes = milestone_params
if @milestone.save!
render_ok
else
render_error(@milestone.errors.full_messages.join(","))
end
end
def destroy
if @milestone.destroy!
render_ok
else
render_error("删除里程碑失败!")
end
end
private
def milestone_params
params.permit(:name, :description, :effective_date)
end
def query_params
params.permit(:category, :author_id, :assigner_id, :sort_by, :sort_direction, :issue_tag_ids)
end
def load_milestone
@milestone = @project.versions.find_by_id(params[:id])
return render_not_found('里程碑不存在!') unless @milestone.present?
end
def sort_by
sort_by = params.fetch(:sort_by, "created_on")
sort_by = Version.column_names.include?(sort_by) ? sort_by : "created_on"
sort_by
end
def sort_direction
sort_direction = params.fetch(:sort_direction, "desc").downcase
sort_direction = %w(desc asc).include?(sort_direction) ? sort_direction : "desc"
sort_direction
end
end

View File

@ -0,0 +1,11 @@
class Api::V1::Issues::StatuesController < Api::V1::BaseController
before_action :require_public_and_member_above, only: [:index]
# 状态列表
def index
@statues = IssueStatus.order("position asc")
@statues = @statues.ransack(name_cont: params[:keyword]).result if params[:keyword].present?
@statues = kaminary_select_paginate(@statues)
end
end

View File

@ -0,0 +1,115 @@
class Api::V1::IssuesController < Api::V1::BaseController
before_action :require_login, except: [:index, :show]
before_action :require_public_and_member_above, only: [:index, :show, :create, :update, :destroy]
before_action :require_operate_above, only: [:batch_update, :batch_destroy]
def index
IssueTag.init_data(@project.id) unless $redis_cache.hget("project_init_issue_tags", @project.id)
@object_result = Api::V1::Issues::ListService.call(@project, query_params, current_user)
@total_issues_count = @object_result[:total_issues_count]
@opened_issues_count = @object_result[:opened_issues_count]
@closed_issues_count = @object_result[:closed_issues_count]
if params[:only_name].present?
@issues = kaminary_select_paginate(@object_result[:data].pluck(:id, :subject))
else
@issues = kaminari_paginate(@object_result[:data])
end
end
def create
@object_result = Api::V1::Issues::CreateService.call(@project, issue_params, current_user)
end
before_action :load_issue, only: [:show, :update, :destroy]
before_action :check_issue_operate_permission, only: [:update, :destroy]
def show
@user_permission = current_user.present? && current_user.logged? && (@project.member?(current_user) || current_user.admin? || @issue.user == current_user)
end
def update
@object_result = Api::V1::Issues::UpdateService.call(@project, @issue, issue_params, current_user)
end
def destroy
@object_result = Api::V1::Issues::DeleteService.call(@project, @issue, current_user)
if @object_result
render_ok
else
render_error("删除疑修失败!")
end
end
before_action :load_issues, only: [:batch_update, :batch_destroy]
def batch_update
@object_result = Api::V1::Issues::BatchUpdateService.call(@project, @issues, batch_issue_params, current_user)
if @object_result
render_ok
else
render_error("批量更新疑修失败!")
end
end
def batch_destroy
@object_result = Api::V1::Issues::BatchDeleteService.call(@project, @issues, current_user)
if @object_result
render_ok
else
render_error("批量删除疑修失败!")
end
end
private
def load_issue
@issue = @project.issues.issue_issue.where(project_issues_index: params[:index]).where.not(id: params[:index]).take || Issue.find_by_id(params[:index])
if @issue.blank?
render_not_found("疑修不存在!")
end
end
def load_issues
return render_error("请输入正确的ID数组") unless params[:ids].is_a?(Array)
params[:ids].each do |id|
@issue = Issue.find_by_id(id)
if @issue.blank?
return render_not_found("ID为#{id}的疑修不存在!")
end
end
@issues = Issue.where(id: params[:ids])
end
def check_issue_operate_permission
return render_forbidden("您没有操作权限!") unless @project.member?(current_user) || current_user.admin? || @issue.user == current_user
end
def query_params
params.permit(
:category,
:participant_category,
:keyword, :author_id,
:milestone_id, :assigner_id,
:status_id,
:sort_by, :sort_direction,
:issue_tag_ids)
end
def issue_params
params.permit(
:status_id, :priority_id, :milestone_id,
:branch_name, :start_date, :due_date,
:subject, :description,
:issue_tag_ids => [],
:assigner_ids => [],
:attachment_ids => [],
:receivers_login => [])
end
def batch_issue_params
params.permit(
:status_id, :priority_id, :milestone_id,
:issue_tag_ids => [],
:assigner_ids => [])
end
end

View File

@ -1,16 +1,40 @@
class Api::V1::Projects::BranchesController < Api::V1::BaseController
before_action :require_public_and_member_above, only: [:all]
before_action :require_public_and_member_above, only: [:index, :all]
def index
@result_object = Api::V1::Projects::Branches::ListService.call(@project, {name: params[:keyword], page: page, limit: limit}, current_user&.gitea_token)
end
def all
@result_object = Api::V1::Projects::Branches::AllListService.call(@project, current_user&.gitea_token)
end
before_action :require_operate_above, only: [:create]
before_action :require_operate_above, only: [:create, :destroy]
def create
@result_object = Api::V1::Projects::Branches::CreateService.call(@project, branch_params, current_user&.gitea_token)
end
def destroy
@result_object = Api::V1::Projects::Branches::DeleteService.call(@project, params[:name], current_user&.gitea_token)
if @result_object
return render_ok
else
return render_error('删除分支失败!')
end
end
before_action :require_manager_above, only: [:update_default_branch]
def update_default_branch
@result_object = Api::V1::Projects::Branches::UpdateDefaultBranchService.call(@project, params[:name], current_user&.gitea_token)
if @result_object
return render_ok
else
return render_error('更新默认分支失败!')
end
end
private
def branch_params
params.require(:branch).permit(:new_branch_name, :old_branch_name)

View File

@ -3,6 +3,6 @@ class Api::V1::Projects::CodeStatsController < Api::V1::BaseController
def index
@result_object = Api::V1::Projects::CodeStats::ListService.call(@project, {ref: params[:ref]}, current_user&.gitea_token)
puts @result_object
# puts @result_object
end
end

View File

@ -0,0 +1,10 @@
class Api::V1::Projects::CollaboratorsController < Api::V1::BaseController
before_action :require_public_and_member_above, only: [:index]
def index
@collaborators = @project.all_collaborators.like(params[:keyword])
@collaborators = kaminary_select_paginate(@collaborators)
end
end

View File

@ -0,0 +1,19 @@
class Api::V1::Projects::TagsController < Api::V1::BaseController
before_action :require_public_and_member_above, only: [:index]
def index
@release_tags = @repository.version_releases.pluck(:tag_name)
@result_object = Api::V1::Projects::Tags::ListService.call(@project, {page: page, limit: limit}, current_user&.gitea_token)
end
before_action :require_operate_above, only: [:destroy]
def destroy
@result_object = Api::V1::Projects::Tags::DeleteService.call(@project, params[:name], current_user&.gitea_token)
if @result_object
return render_ok
else
return render_error('删除标签失败!')
end
end
end

View File

@ -1,4 +1,6 @@
require 'oauth2'
# require 'openssl'
# require 'jwt'
class ApplicationController < ActionController::Base
include CodeExample
@ -295,17 +297,24 @@ class ApplicationController < ActionController::Base
# Find the current user
#Rails.logger.info("current_laboratory is #{current_laboratory} domain is #{request.subdomain}")
if request.headers["Authorization"].present? && request.headers["Authorization"].start_with?('Bearer')
tip_exception(401, "请登录后再操作!") unless valid_doorkeeper_token?
if @doorkeeper_token.present?
# client方法对接需要一直带着用户标识uid
if @doorkeeper_token.resource_owner_id.blank?
tip_exception(-1, "缺少用户标识!") if params[:uid].nil?
User.current = User.find(params[:uid])
else
User.current = User.find_by(id: @doorkeeper_token.resource_owner_id)
if !valid_doorkeeper_token?
header = request.authorization
pattern = /^Bearer /i
token = header.gsub(pattern, "")
User.current, message = Bot.decode_jwt_token(token)
tip_exception(401, message) if message.present?
else
if @doorkeeper_token.present?
# client方法对接需要一直带着用户标识uid
if @doorkeeper_token.resource_owner_id.blank?
tip_exception(-1, "缺少用户标识!") if params[:uid].nil?
User.current = User.find(params[:uid])
else
User.current = User.find_by(id: @doorkeeper_token.resource_owner_id)
end
end
end
else
else
User.current = find_current_user
uid_logger("user_setup: " + (User.current.logged? ? "#{User.current.try(:login)} (id=#{User.current.try(:id)})" : "anonymous"))
@ -823,6 +832,325 @@ class ApplicationController < ActionController::Base
HotSearchKeyword.add(keyword)
end
# author: zxh
# blockchain相关项目活动调用函数
# return true: 表示上链操作成功; return false: 表示上链操作失败
def push_activity_2_blockchain(activity_type, model)
if activity_type == "issue_create"
project_id = model['project_id']
project = Project.find(project_id)
if project['use_blockchain'] == 0 || project['use_blockchain'] == false
# 无需执行上链操作
return true
end
id = model['id']
owner_id = project['user_id']
owner = User.find(owner_id)
ownername = owner['login']
identifier = project['identifier']
author_id = project['user_id']
author = User.find(author_id)
username = author['login']
action = 'opened'
title = model['subject']
content = model['description']
created_at = model['created_on']
updated_at = model['updated_on']
# 调用区块链接口
params = {
"request-type": "upload issue info",
"issue_id": "gitlink-" + id.to_s,
"repo_id": "gitlink-" + project_id.to_s,
"issue_number": 0, # 暂时不需要改字段
"reponame": identifier,
"ownername": ownername,
"username": username,
"action": action,
"title": title,
"content": content,
"created_at": created_at,
"updated_at": updated_at
}.to_json
resp_body = Blockchain::InvokeBlockchainApi.call(params)
if resp_body['status'] == 10
raise Error, resp_body['message']
elsif resp_body['status'] != 0
raise Error, "区块链接口请求失败."
end
elsif activity_type == "issue_comment_create"
issue_comment_id = model['id']
issue_id = model['journalized_id']
parent_id = model['parent_id'].nil? ? "" : model['parent_id']
issue = Issue.find(issue_id)
issue_classify = issue['issue_classify'] # issue或pull_request
project_id = issue['project_id']
project = Project.find(project_id)
if project['use_blockchain'] == 0 || project['use_blockchain'] == false
# 无需执行上链操作
return
end
identifier = project['identifier']
owner_id = project['user_id']
owner = User.find(owner_id)
ownername = owner['login']
author_id = model['user_id']
author = User.find(author_id)
username = author['login']
action = 'created'
content = model['notes']
created_at = model['created_on']
if issue_classify == "issue"
params = {
"request-type": "upload issue comment info",
"issue_comment_id": "gitlink-" + issue_comment_id.to_s,
"issue_comment_number": 0, # 暂时不需要
"issue_number": 0, # 暂时不需要
"issue_id": "gitlink-" + issue_id.to_s,
"repo_id": "gitlink-" + project.id.to_s,
"parent_id": parent_id.to_s,
"reponame": identifier,
"ownername": ownername,
"username": username,
"action": action,
"content": content,
"created_at": created_at,
}.to_json
elsif issue_classify == "pull_request"
params = {
"request-type": "upload pull request comment info",
"pull_request_comment_id": "gitlink-" + issue_comment_id.to_s,
"pull_request_comment_number": 0, # 不考虑该字段
"pull_request_number": 0, # 不考虑该字段
"pull_request_id": "gitlink-" + issue_id.to_s,
"parent_id": parent_id.to_s,
"repo_id": "gitlink-" + project.id.to_s,
"reponame": identifier,
"ownername": ownername,
"username": username,
"action": action,
"content": content,
"created_at": created_at,
}.to_json
end
# 调用区块链接口
resp_body = Blockchain::InvokeBlockchainApi.call(params)
if resp_body['status'] == 10
raise Error, resp_body['message']
elsif resp_body['status'] != 0
raise Error, "区块链接口请求失败."
end
elsif activity_type == "pull_request_create"
# 调用区块链接口
project_id = model['project_id']
project = Project.find(project_id)
if project['use_blockchain'] == 0 || project['use_blockchain'] == false
# 无需执行上链操作
return
end
pull_request_id = model['id']
identifier = project['identifier']
owner_id = project['user_id']
owner = User.find(owner_id)
ownername = owner['login']
action = 'opened'
title = model['title']
content = model['body']
source_branch = model['head']
source_repo_id = model['fork_project_id'].nil? ? project_id : model['fork_project_id']
target_branch = model['base']
target_repo_id = project_id
author_id = model['user_id']
author = User.find(author_id)
username = author['login']
created_at = model['created_at']
updated_at = model['updated_at']
# 查询pull request对应的commit信息
commits = Gitea::PullRequest::CommitsService.call(ownername, identifier, model['gitea_number'])
if commits.nil?
raise Error, "区块链接口请求失败" # 获取pr中变更的commit信息失败
end
commit_shas = []
commits.each do |c|
commit_shas << c["Sha"]
end
params = {
"request-type": "upload pull request info",
"pull_request_id": "gitlink-" + pull_request_id.to_s,
"pull_request_number": 0, # trustie没有该字段
"repo_id": "gitlink-" + project_id.to_s,
"ownername": ownername,
"reponame": identifier,
"username": username,
"action": action,
"title": title,
"content": content,
"source_branch": source_branch,
"target_branch": target_branch,
"reviewers": [], # trustie没有该字段
"commit_shas": commit_shas,
"merge_user": "", # trustie没有该字段
"created_at": created_at,
"updated_at": updated_at
}.to_json
resp_body = Blockchain::InvokeBlockchainApi.call(params)
if resp_body['status'] == 9
raise Error, resp_body['message']
elsif resp_body['status'] != 0
raise Error, "区块链接口请求失败."
end
elsif activity_type == "pull_request_merge"
# 调用区块链接口
project_id = model['project_id']
project = Project.find(project_id)
if project['use_blockchain'] == 0 || project['use_blockchain'] == false
# 无需执行上链操作
return
end
pull_request_id = model['id']
identifier = project['identifier']
owner_id = project['user_id']
owner = User.find(owner_id)
ownername = owner['login']
action = 'merged'
created_at = model['created_at']
updated_at = model['updated_at']
# 查询pull request对应的commit信息
commits = Gitea::PullRequest::CommitsService.call(ownername, identifier, model['gitea_number'])
if commits.nil?
raise Error, "区块链接口请求失败" # 获取pr中变更的commit信息失败
end
commit_shas = []
commits.each do |c|
commit_shas << c["Sha"]
end
# 将pull request相关信息写入链上
params = {
"request-type": "upload pull request info",
"pull_request_id": "gitlink-" + pull_request_id.to_s,
"pull_request_number": 0, # trustie没有该字段
"repo_id": "gitlink-" + project_id.to_s,
"ownername": ownername,
"reponame": identifier,
"username": username,
"action": action,
"title": title,
"content": content,
"source_branch": source_branch,
"target_branch": target_branch,
"reviewers": [], # trustie没有该字段
"commit_shas": commit_shas,
"merge_user": "", # trustie没有该字段
"created_at": created_at,
"updated_at": updated_at
}.to_json
resp_body = Blockchain::InvokeBlockchainApi.call(params)
if resp_body['status'] == 9
raise Error, resp_body['message']
elsif resp_body['status'] != 0
raise Error, "区块链接口请求失败."
end
# 将commit相关信息写入链上
commit_shas.each do |commit_sha|
commit_diff = Gitea::Commit::DiffService.call(ownername, identifier, commit_sha, owner['gitea_token'])
commit = Gitea::Commit::InfoService.call(ownername, identifier, commit_sha, owner['gitea_token'])
params = {
"request-type": "upload commit info",
"commit_hash": commit_sha,
"repo_id": "gitlink-" + project_id.to_s,
"author": commit['commit']['author']['name'],
"author_email": commit['commit']['author']['email'],
"committer": commit['commit']['committer']['name'],
"committer_email": commit['commit']['committer']['email'],
"author_time": commit['commit']['author']['date'],
"committer_time": commit['commit']['committer']['date'],
"content": commit['commit']['message'],
"commit_diff": commit_diff['Files'].to_s
}.to_json
resp_body = Blockchain::InvokeBlockchainApi.call(params)
if resp_body['status'] == 7
raise Error, resp_body['message']
elsif resp_body['status'] != 0
raise Error, "区块链接口请求失败."
end
end
elsif activity_type == "pull_request_refuse"
# 调用区块链接口
project_id = model['project_id']
project = Project.find(project_id)
if project['use_blockchain'] == 0 || project['use_blockchain'] == false
# 无需执行上链操作
return true
end
pull_request_id = model['id']
identifier = project['identifier']
owner_id = project['user_id']
owner = User.find(owner_id)
ownername = owner['login']
action = 'refused'
# 将pull request相关信息写入链上
params = {
"request-type": "upload pull request info",
"pull_request_id": "gitlink-" + pull_request_id.to_s,
"pull_request_number": 0, # trustie没有该字段
"repo_id": "gitlink-" + project_id.to_s,
"ownername": ownername,
"reponame": identifier,
"username": username,
"action": action,
"title": title,
"content": content,
"source_branch": source_branch,
"target_branch": target_branch,
"reviewers": [], # trustie没有该字段
"commit_shas": commit_shas,
"merge_user": "", # trustie没有该字段
"created_at": created_at,
"updated_at": updated_at
}.to_json
resp_body = Blockchain::InvokeBlockchainApi.call(params)
if resp_body['status'] == 9
raise Error, resp_body['message']
elsif resp_body['status'] != 0
raise Error, "区块链接口请求失败."
end
end
end
def find_atme_receivers
@atme_receivers = User.where(login: params[:receivers_login])
end

View File

@ -6,7 +6,8 @@ module Api::ProjectHelper
repo = params[:repo]
@project, @owner = Project.find_with_namespace(namespace, repo)
@repository = @project&.repository
if @project
logger.info "###########project founded"
@project

View File

@ -0,0 +1,67 @@
class InstallationsController < ApplicationController
include RegisterHelper
before_action :require_login
def index
@install_bots = BotInstall.where(:installer_id => current_user.id)
end
def update_secret
ActiveRecord::Base.transaction do
bot = Bot.find params[:id]
application = Doorkeeper::Application.find_by(uid: bot.client_id, secret: bot.client_secret)
bot.client_secret = Doorkeeper::OAuth::Helpers::UniqueToken.generate
bot.save!
application.secret = bot.client_secret
application.save!
render_ok
end
end
def update_private_key
bot = Bot.find params[:id]
bot.private_key = OpenSSL::PKey::RSA::generate(2048).to_s
bot.save!
render_ok
end
def auth_active
begin
@bot = Bot.find params[:id]
tip_exception("该Bot已激活") if Doorkeeper::Application.find_by(uid: @bot.client_id, secret: @bot.client_secret).present?
@bot.client_id = Doorkeeper::OAuth::Helpers::UniqueToken.generate if params[:client_id].blank?
@bot.client_secret = Doorkeeper::OAuth::Helpers::UniqueToken.generate if params[:client_secret].blank?
@bot.private_key = OpenSSL::PKey::RSA::generate(2048).to_s
@bot.owner_id = current_user.id
ActiveRecord::Base.transaction do
# 注册bot对应oauth应用
Doorkeeper::Application.create!(name: @bot.name, uid: @bot.client_id, secret: @bot.client_secret, redirect_uri: "https://gitlink.org.cn")
# 注册bot对应用户
result = autologin_register(User.generate_user_login('b'), nil, "#{SecureRandom.hex(6)}", 'bot', nil, nickname: @bot.name)
tip_exception(-1, result[:message]) if result[:message].present?
@bot.uid = result[:user][:id]
@bot.save
render_ok
end
rescue Exception => e
tip_exception(-1, e.message)
end
end
def access_tokens
@install_bot = BotInstall.find params[:id]
@bot = @install_bot.bot
@application = Doorkeeper::Application.find_by(uid: @bot.client_id, secret: @bot.client_secret)
tip_exception("该Bot未激活") if @application.blank?
# 给bot生成token因为bot是机器人操作
@access_token = Doorkeeper::AccessToken.create!({ :application_id => @application.id,
:resource_owner_id => @bot.uid,
:scopes => "public write",
:expires_in => "604800",
:use_refresh_token => true
})
render_ok(token: @access_token.token)
end
end

View File

@ -111,7 +111,9 @@ class IssuesController < ApplicationController
issue_params = issue_send_params(params)
Issues::CreateForm.new({subject: issue_params[:subject], description: issue_params[:description].blank? ? issue_params[:description] : issue_params[:description].b}).validate!
@issue = Issue.new(issue_params)
@issue.project_issues_index = @project.get_last_project_issues_index + 1
if @issue.save!
@project.del_project_issue_cache_delete_count
SendTemplateMessageJob.perform_later('IssueAssigned', current_user.id, @issue&.id) if Site.has_notice_menu?
SendTemplateMessageJob.perform_later('ProjectIssue', current_user.id, @issue&.id) if Site.has_notice_menu?
if params[:attachment_ids].present?
@ -149,11 +151,23 @@ class IssuesController < ApplicationController
end
@issue.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: "create")
@issue.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: ProjectTrend::CLOSE) if params[:status_id].to_i == 5
Rails.logger.info "[ATME] maybe to at such users: #{@atme_receivers.pluck(:login)}"
AtmeService.call(current_user, @atme_receivers, @issue) if @atme_receivers.size > 0
# 新增时向grimoirelab推送事件
IssueWebhookJob.set(wait: 5.seconds).perform_later(@issue.id)
# author: zxh
# 扣除发起人的token
if @issue.blockchain_token_num > 0
Blockchain::CreateIssue.call(user_id: @issue.author_id, project_id: @issue.project_id, token_num: @issue.blockchain_token_num)
end
# 调用上链API存证
push_activity_2_blockchain("issue_create", @issue)
render json: {status: 0, message: "创建成功", id: @issue.id}
else
@ -302,6 +316,7 @@ class IssuesController < ApplicationController
login = @issue.user.try(:login)
SendTemplateMessageJob.perform_later('IssueDeleted', current_user.id, @issue&.subject, @issue.assigned_to_id, @issue.author_id) if Site.has_notice_menu?
if @issue.destroy
@project.incre_project_issue_cache_delete_count
if issue_type == "2" && status_id != 5
post_to_chain("add", token, login)
end
@ -541,7 +556,8 @@ class IssuesController < ApplicationController
branch_name: params[:branch_name].to_s,
issue_classify: "issue",
author_id: current_user.id,
project_id: @project.id
project_id: @project.id,
blockchain_token_num: params[:blockchain_token_num]
}
end

View File

@ -47,10 +47,15 @@ class JournalsController < ApplicationController
Rails.logger.info "[ATME] maybe to at such users: #{@atme_receivers.pluck(:login)}"
AtmeService.call(current_user, @atme_receivers, journal) if @atme_receivers.size > 0
# @issue.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: "journal")
# author: zxh
# 调用上链API
push_activity_2_blockchain("issue_comment_create", journal)
render :json => { status: 0, message: "评论成功", id: journal.id}
# normal_status(0, "评论成功")
else
normal_status(-1, "评论失败")
raise ActiveRecord::Rollback
end
end
end

View File

@ -8,14 +8,17 @@ class Organizations::ProjectsController < Organizations::BaseController
.joins(team_projects: {team: :team_users})
.where(team_users: {user_id: current_user.id}).to_sql
@projects = Project.from("( #{ public_projects_sql} UNION #{ private_projects_sql } ) AS projects")
@projects = @projects.ransack(name_or_identifier_cont: params[:search]).result if params[:search].present?
# 表情处理
keywords = params[:search].to_s.each_char.select { |c| c.bytes.first < 240 }.join('')
@projects = @projects.ransack(name_or_identifier_cont: keywords).result if params[:search].present?
@projects = @projects.includes(:owner).order("projects.#{sort} #{sort_direction}")
@projects = paginate(@projects)
end
def search
tip_exception("请输入搜索关键词") if params[:search].nil?
# 表情处理
keywords = params[:search].to_s.each_char.select { |c| c.bytes.first < 240 }.join('')
public_projects_sql = @organization.projects.where(is_public: true).to_sql
private_projects_sql = @organization.projects
.where(is_public: false)
@ -23,7 +26,7 @@ class Organizations::ProjectsController < Organizations::BaseController
.where(team_users: {user_id: current_user.id}).to_sql
@projects = Project.from("( #{ public_projects_sql} UNION #{ private_projects_sql } ) AS projects")
@projects = @projects.ransack(name_or_identifier_cont: params[:search]).result
@projects = @projects.ransack(name_or_identifier_cont: keywords).result
@projects = @projects.includes(:owner).order("projects.#{sort} #{sort_direction}")
end

View File

@ -39,13 +39,14 @@ class ProjectsController < ApplicationController
category_id = params[:category_id]
@total_count =
if category_id.blank?
# ps = ProjectStatistic.first
# ps.common_projects_count + ps.mirror_projects_count unless ps.blank?
if category_id.blank? && params[:search].blank?
# 默认查询时count性能问题处理
ProjectCategory.sum("projects_count") - Project.visible.joins("left join organization_extensions on organization_extensions.organization_id = projects.user_id").where("organization_extensions.visibility =2").count
elsif params[:search].present?
@projects.total_count
else
cate = ProjectCategory.find_by(id: category_id)
cate&.projects_count || 0
cate = ProjectCategory.find_by(id: category_id)
cate&.projects_count || 0
end
end
@ -53,7 +54,7 @@ class ProjectsController < ApplicationController
ActiveRecord::Base.transaction do
Projects::CreateForm.new(project_params).validate!
@project = Projects::CreateService.new(current_user, project_params).call
OpenProjectDevOpsJob.perform_later(@project&.id, current_user.id)
OpenProjectDevOpsJob.set(wait: 5.seconds).perform_later(@project&.id, current_user.id)
end
rescue Exception => e
uid_logger_error(e.message)
@ -273,7 +274,8 @@ class ProjectsController < ApplicationController
private
def project_params
params.permit(:user_id, :name, :description, :repository_name, :website, :lesson_url, :default_branch, :identifier,
:project_category_id, :project_language_id, :license_id, :ignore_id, :private)
:project_category_id, :project_language_id, :license_id, :ignore_id, :private,
:blockchain, :blockchain_token_all, :blockchain_init_token)
end
def mirror_params

View File

@ -6,6 +6,7 @@ class PullRequestsController < ApplicationController
before_action :find_pull_request, except: [:index, :new, :create, :check_can_merge,:get_branches,:create_merge_infos, :files, :commits]
before_action :load_pull_request, only: [:files, :commits]
before_action :find_atme_receivers, only: [:create, :update]
include TagChosenHelper
include ApplicationHelper
@ -73,6 +74,11 @@ class PullRequestsController < ApplicationController
SendTemplateMessageJob.perform_later('ProjectPullRequest', current_user.id, @pull_request&.id) if Site.has_notice_menu?
Rails.logger.info "[ATME] maybe to at such users: #{@atme_receivers.pluck(:login)}"
AtmeService.call(current_user, @atme_receivers, @pull_request) if @atme_receivers.size > 0
# author: zxh
# 调用上链API
push_activity_2_blockchain("pull_request_create", @pull_request)
else
render_error("create pull request error: #{@gitea_pull_request[:status]}")
raise ActiveRecord::Rollback
@ -119,7 +125,7 @@ class PullRequestsController < ApplicationController
end
else
return normal_status(-1, "请输入正确的标记。")
end
end
end
if params[:status_id].to_i == 5
@issue.issue_times.update_all(end_time: Time.now)
@ -149,12 +155,16 @@ class PullRequestsController < ApplicationController
ActiveRecord::Base.transaction do
begin
colsed = PullRequests::CloseService.call(@owner, @repository, @pull_request, current_user)
if colsed === true
# author: zxh
# 调用上链API
push_activity_2_blockchain("pull_request_refuse", @pull_request)
if colsed === true
@pull_request.project_trends.create!(user: current_user, project: @project,action_type: ProjectTrend::CLOSE)
# 合并请求下issue处理为关闭
@issue&.update_attributes!({status_id:5})
SendTemplateMessageJob.perform_later('PullRequestClosed', current_user.id, @pull_request.id) if Site.has_notice_menu?
normal_status(1, "已拒绝")
normal_status(1, "已拒绝")
else
normal_status(-1, '合并失败')
end
@ -198,6 +208,43 @@ class PullRequestsController < ApplicationController
# @pull_request.project_trend_status!
@pull_request.project_trends.create!(user: current_user, project: @project,action_type: ProjectTrend::MERGE)
@issue&.custom_journal_detail("merge", "", "该合并请求已被合并", current_user&.id)
# author: zxh
# 调用上链API
push_activity_2_blockchain("pull_request_merge", @pull_request)
# 查看是否fix了相关issue如果fix就转账
if params["fix_issue_id"].nil? || params["fix_issue_id"] == ""
else
issue = Issue.find_by(id: params["fix_issue_id"])
if issue.nil?
normal_status(-1, "关联issue失败")
raise ActiveRecord::Rollback
else
token_num = issue.blockchain_token_num
token_num = token_num.nil? ? 0 : token_num
owner = User.find_by(login: params["owner"])
pr = PullRequest.find_by(id: params["pull_request"]["id"])
if owner.nil? || pr.nil?
normal_status(-1, "关联issue失败")
raise ActiveRecord::Rollback
else
project = Project.find_by(user_id: owner.id, identifier: params["project_id"])
if project.nil?
normal_status(-1, "关联issue失败")
raise ActiveRecord::Rollback
else
author_id = pr.user_id
if token_num > 0
Blockchain::FixIssue.call({user_id: author_id.to_s, project_id: project.id.to_s, token_num: token_num})
end
# update issue to state 5
issue.update(status_id: 5)
end
end
end
end
# 合并请求下issue处理为关闭
@issue&.update_attributes!({status_id:5})
SendTemplateMessageJob.perform_later('PullRequestMerged', current_user.id, @pull_request.id) if Site.has_notice_menu?

View File

@ -10,15 +10,15 @@ class Users::HeadmapsController < Users::BaseController
private
def start_stamp
if params[:year].present?
Date.new(params[:year], 1).to_time.to_i
Date.new(params[:year].to_i, 1).to_time.to_i
else
Date.today.to_time.to_i - 365*24*60*60
(Date.today - 1.years).to_time.to_i
end
end
def end_stamp
if params[:year].present?
Date.new(params[:year], 1).to_time.to_i + 365*24*60*60
(Date.new(params[:year].to_i, 1) + 1.years).to_time.to_i
else
Date.today.to_time.to_i
end

View File

@ -1,6 +1,7 @@
class Users::MessagesController < Users::BaseController
before_action :private_user_resources!
before_action :find_receivers, only: [:create]
before_action :check_auth
def index
limit = params[:limit] || params[:per_page]
@ -63,6 +64,10 @@ class Users::MessagesController < Users::BaseController
end
private
def check_auth
return render_forbidden unless current_user.admin? || observed_logged_user?
end
def message_type
@message_type = begin
case params[:type]

View File

@ -2,8 +2,8 @@ class UsersController < ApplicationController
include ApplicationHelper
include Ci::DbConnectable
before_action :load_user, only: [:show, :homepage_info, :sync_token, :sync_gitea_pwd, :projects, :watch_users, :fan_users, :hovercard]
before_action :check_user_exist, only: [:show, :homepage_info,:projects, :watch_users, :fan_users, :hovercard]
before_action :load_user, only: [:show, :homepage_info, :sync_token, :sync_gitea_pwd, :projects, :watch_users, :fan_users, :hovercard, :hovercard4proj]
before_action :check_user_exist, only: [:show, :homepage_info,:projects, :watch_users, :fan_users, :hovercard, :hovercard4proj]
before_action :require_login, only: %i[me sync_user_info]
before_action :connect_to_ci_db, only: [:get_user_info]
before_action :convert_image!, only: [:update, :update_image]
@ -81,9 +81,134 @@ class UsersController < ApplicationController
@watchers = paginate(watchers)
end
def contribution_perc
project_id = params[:project_id]
@project = Project.find(project_id)
user_id = params[:id]
@user = User.find(user_id)
def cal_perc(count_user, count_all)
(count_user * 1.0 / (count_all + 0.000000001)).round(5)
end
if @project['use_blockchain'] == true or @project['use_blockchain'] == 1
balance_user = Blockchain::BalanceQueryOneProject.call({"user_id": user_id, "project_id": project_id})
balance_all = Blockchain::RepoBasicInfo.call({"project_id": project_id})["cur_supply"]
scores = {
"final" => cal_perc(balance_user, balance_all),
"blockchain" => true
}
else
# 获取所有行为对应的项目内记录总数和个人记录数
features = {
"requirement" => {},
"development" => {},
"review" => {}
}
# 1. issue创建
issues = Issue.where(project_id: project_id, issue_classify: 'issue')
issue_all = issues.count
issue_user = issues.where(author_id: user_id).count
features["requirement"] = features["requirement"].merge({"issue" => {"all" => issue_all, "perc" => cal_perc(issue_user, issue_all)}})
# 2. 里程碑创建
milestones = Version.where(project_id: project_id)
milestone_all = milestones.count
milestone_user = milestones.where(user_id: user_id).count
features["requirement"] = features["requirement"].merge({"milestone" => {"all" => milestone_all, "perc" => cal_perc(milestone_user, milestone_all)}})
# 3. issue评论
issue_comments = Journal.joins("INNER JOIN issues on journals.journalized_id=issues.id").where("issues.project_id=#{project_id} and journalized_type='Issue' and issues.issue_classify='issue'")
issue_comment_all = issue_comments.count
issue_comment_user = issue_comments.where("journals.user_id=#{user_id}").count
features["requirement"] = features["requirement"].merge({"issue_comment" => {"all" => issue_comment_all, "perc" => cal_perc(issue_comment_user, issue_comment_all)}})
# 4. 合并请求
prs = PullRequest.where(project_id: project_id)
pr_all = prs.count
pr_user = prs.where(user_id: user_id).count
features["development"] = features["development"].merge({"pr" => {"all" => pr_all, "perc" => cal_perc(pr_user, pr_all)}})
# 5. pr评论
pr_comments = Journal.joins("INNER JOIN issues on journals.journalized_id=issues.id").where("issues.project_id=#{project_id} and journalized_type='Issue' and issues.issue_classify='pull_request'")
pr_comment_all = pr_comments.count
pr_comment_user = pr_comments.where("journals.user_id=#{user_id}").count
features["review"] = features["review"].merge({"pr_comment" => {"all" => pr_comment_all, "perc" => cal_perc(pr_comment_user, pr_comment_all)}})
# 6. 代码行评论
line_comments = Journal.joins("INNER JOIN pull_requests on journals.journalized_id=pull_requests.id").where("pull_requests.project_id=#{project_id} and journalized_type='PullRequest'")
line_comment_all = line_comments.count
line_comment_user = line_comments.where("journals.user_id=#{user_id}").count
features["review"] = features["review"].merge({"line_comment" => {"all" => line_comment_all, "perc" => cal_perc(line_comment_user, line_comment_all)}})
# 7. 代码行、commit贡献统计
code_contributions = Api::V1::Projects::CodeStats::ListService.call(@project, {ref: nil})
commit_all = code_contributions["commit_count"]
addition_all = code_contributions["additions"]
deletion_all = code_contributions["deletions"]
commit_user = 0
addition_user = 0
deletion_user = 0
code_contributions["authors"].each do |author|
if author["name"] == @user.login
commit_user = author["commits"]
addition_user = author["additions"]
deletion_user = author["deletions"]
end
end
features["development"] = features["development"].merge({"commit" => {"all" => commit_all, "perc" => cal_perc(commit_user, commit_all)}})
features["development"] = features["development"].merge({"addition" => {"all" => addition_all, "perc" => cal_perc(addition_user, addition_all)}})
features["development"] = features["development"].merge({"deletion" => {"all" => deletion_all, "perc" => cal_perc(deletion_user, deletion_all)}})
def cal_weight(features)
weights = {} # 计算每一项的权重
categories = []
features.each do |key, _|
categories << key
weights[key] = Hash.new
end
count_all = 0
counts = {}
categories.each do |category|
count_1 = 0
features[category].each do |_, value|
count_1 += value["all"]
end
count_all += count_1
counts[category] = count_1
features[category].each do |key, value|
weight = cal_perc(value["all"], count_1)
weights[category] = weights[category].merge({key => weight})
end
end
categories.each do |category|
weight = cal_perc(counts[category], count_all)
weights[category] = weights[category].merge({"category_weight" => weight})
end
return weights
end
weights_categories = cal_weight(features)
scores = {
"final" => 0.0,
"blockchain" => false
}
features.each do |category, value_1|
category_score = 0.0
value_1.each do |action, value_2|
category_score += weights_categories[category][action] * value_2["perc"]
end
scores["final"] += weights_categories[category]["category_weight"] * category_score.round(4)
scores = scores.merge({category => category_score.round(4)})
end
end
render json: { scores: scores }
end
def hovercard
end
# author: zxh, 查询贡献者的贡献度
def hovercard4proj
end
def update
return render_not_found unless @user = User.find_by(login: params[:id]) || User.find_by_id(params[:id])
return render_forbidden unless User.current.logged? && (current_user&.admin? || current_user.id == @user.id)
@ -94,11 +219,11 @@ class UsersController < ApplicationController
end
end
def update_image
return render_not_found unless @user = User.find_by(login: params[:id]) || User.find_by_id(params[:id])
def update_image
return render_not_found unless @user = User.find_by(login: params[:id]) || User.find_by_id(params[:id])
return render_forbidden unless User.current.logged? && (current_user&.admin? || current_user.id == @user.id)
Util.write_file(@image, avatar_path(@user))
Util.write_file(@image, avatar_path(@user))
return render_ok({message: '头像修改成功'})
rescue Exception => e
uid_logger_error(e.message)
@ -284,6 +409,252 @@ class UsersController < ApplicationController
@projects = paginate(scope)
end
# query all projects with tokens by a user
def blockchain_balance
#is_current_admin_user = User.current.logged? && (current_user&.admin? || current_user.id == params['user_id'].to_i)
is_current_admin_user = true
results = Blockchain::BalanceQuery.call(params, is_current_admin_user)
if results[:status] == 0
@total_count = results[:projects].size
@projects = results[:projects]
else
@total_count = -1
@projects = []
end
render json: { status: results[:status], projects: @projects, total_count: @total_count }
end
# query one balance
def blockchain_balance_one_project
#is_current_admin_user = User.current.logged? && (current_user&.admin? || current_user.id == params['user_id'].to_i)
is_current_admin_user = true
if is_current_admin_user
owner = User.find_by(login: params['owner_login'])
if owner.nil?
normal_status(-1, "创建者无法找到")
else
p = Project.find_by(user_id: owner.id, identifier: params['project_identifier'])
balance = Blockchain::BalanceQueryOneProject.call({"user_id": params['user_id'].to_i, "project_id": p.id.to_i})
render json: { status: 0, balance: balance}
end
else
normal_status(-1, "缺少权限")
end
end
def blockchain_transfer
#is_current_admin_user = User.current.logged? && (current_user&.admin? || current_user.id == params['payer_id'].to_i)
is_current_admin_user = true
if is_current_admin_user
Blockchain::TransferService.call(params)
render json: {status: 2} # 重新查询余额
else
normal_status(-1, "缺少权限")
end
rescue Exception => e
normal_status(-1, e.to_s)
end
# exchange money
def blockchain_exchange
#is_current_admin_user = User.current.logged? && (current_user&.admin? || current_user.id == params['user_id'].to_i)
#require 'alipay'
## setup the client to communicate with either production API or sandbox API
## https://openapi.alipay.com/gateway.do (Production)
## https://openapi.alipaydev.com/gateway.do (Sandbox)
#api_url = 'https://openapi.alipay.com/gateway.do'
#
## setup your own credentials and certificates
#app_id = '2021002140631434'
#app_private_key="-----BEGIN RSA PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCPDsgc0n0RWRnbe9OMqtUbde8gu88OyjuZm8cJXeiSING18HX56C5zV4DsHQ6K9/JmQi/NYCc7/2Prh66Bei0L4Xm5TysJTPYi90+WlbJJESFF6fqULi8sSqZXUtaoMKRcUyeR144l2GQgXbZWBbVSQeZW5DqUDlurTF0I7vQ21wGqxQqBjQK8PSVw5hF+aIsNOfAY9M1tzdD5Jzo2Y3FJdsbXIJNMyhJJCZ+7KHFqma7lpjkXdCoyh/nOISkQdGtFI29gI94sqewI2AMU84uxuBIE3h90iLT+8xrd2dQKdBS7qfhQ3PgkBPVNs5jxsVBqSFdSFT6zcqFdHJzulCUJAgMBAAECggEAWocAGz0X5+J6emnhdSKluLrol85BORrAnHP3f/XtNouOKZQBFCPZQSQecUvx5/7/ZbZ8iXpPWahDkshJpaWq29nTLXDryvboyze1JZWVPKeaZqOp7htLvrt+h8PkEoq1d7cnUyMU0N4eflzPBaCXHXaWTGYgq5Bqcfvg48ZSxGBYeHt5WWU2+GW5fpsaVBBYkdyxxGMoy/bzYzGhvfSJkexqnl0XkAAODa02mu3WsHrzRid6Mf+3syYbq/MfUodU6Vng2tbCqwnWrHCyrD4RYl6rg1TKuAv2YAfBhLzwBxHYVC4SRqzjs+8AaOQoF+lCjr4JklPhEuRThzD31YwIAQKBgQDAg4S7ciMmxvbNxR+1eimoAYLLF9jPpPU6w3VFNPb4rDu4tX77bNf082YplfJX5TYtZKjKPKyYG87K6slGunyPL4AFRW81WPB9u1uP26dihyPCBSUE01jKRPPfRrQnnru5fpm8LL3L03V3yA6J+GyQ8wltRJJi1eBSSm+IWRsZewKBgQC+PBD9J1LYOEIVeK9KvN8BpkA1ZIkk//VuJaoGfVXn+1EzM1yFB05fnsEQkHFynisvuCIr7pH63HcdyffQhe1YOnw9iDCG1zPjsi5uTe9WAM0dnb7xdsaLPr/Q2LyoDOTN9344Qovy1AAnpWtGTn6omgHst5nZpp/mHOuBlKiqSwKBgBKRXM77fjpyPEGyfpFxW+0xYB0YirfUUDa/vWLUbfGkIwp4ruuvHtEoXLUsGjiyCdys9b6zxW3SWMqnhIxG1la1HSLlBInfryphVL52UBmnsSI4fs6NV+YCaocheaTMoYyNkmRc6F1tYsoPyJ80D7yXRFR+paPUvxMQzNsYxQ1bAoGAHd2uSSBQWFPUxCwzUQd/93FTaU6EXYO103okTGqG/ymsoN4ya0wvWMHCy8fxl64PV6mP69fDoV/Vb57SwjEUhyJ/eOWVwMWuhtPliDnCFn1/tmOao6wjFZ9fW/l6/OMxVMjDTy/bat8vuwm0YtBWAEBVhwV4KPyI5AasTqa5KCsCgYB/usnqhVx2zt+MxpBt2Q9Vxc0zXcZxMDs69UUdTY86gjcJyCFGe3bbumUcyfSJzIznC2hfFX5ZyS0oMwiAzWtslRMh9LRh3kofD/6BogL3RKOlBk3iekvQ8Gn0tbwk2Qzr4WJgfA7A4GTf5r7Y+bvOfazzsUQAfSK6nUTIlOj2Ew==\n-----END RSA PRIVATE KEY-----\n"
#alipay_public_key="-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgHXLD1BshMymbqqtZVKNyo95FNfxzXzaw3P1eI0KeO6RaL+JzrWxzIBFfTjkWv/8WM9u/NcXMOFt2QO9q5KIDx6PkqjRDTd1hgP/cTgdjOHQqnVSihCrQDVCDBSOXIujC8Lk/P4pFVRhQkYeZqEb1qb8b/2tzTY8g9PKKBSCQv7SfgL2TBcpAVbb+9xdJ6VainC/wYGk8T+c+st1hXnuBJSS0m7LFxJOsYkNk0wbA0tfdZLrO3us2F7sjC9t4h/05nr+gSuDkzo+1kCEefYLqScexN+vnQiLoylp/C82wNiP6okxfhmHz3EcYfUqUyGTN/oFaFcPFPpUtFNS8jFV9QIDAQAB\n-----END PUBLIC KEY-----\n"
#
## initialize a client to communicate with the Alipay API
#@alipay_client = Alipay::Client.new(
# url: api_url,
# app_id: app_id,
# app_private_key: app_private_key,
# alipay_public_key: alipay_public_key
#)
#
#return_result = @alipay_client.page_execute_url(
# method: 'alipay.trade.page.pay',
# biz_content: JSON.generate({
# out_trade_no: '20210420104600',
# product_code: 'FAST_INSTANT_TRADE_PAY',
# total_amount: '0.01',
# subject: 'test'
# }, ascii_only: true), # ascii_only is important!
# timestamp: '2021-04-20 10:46:00'
#)
#render json: { pay_url: return_result }
#
# 替代解决方案
# 读取所有交易信息
end
# #
# # def blockchain_create_trade
# # #is_current_admin_user = User.current.logged? && (current_user&.admin? || current_user.id == params['user_id'].to_i)
# # is_current_admin_user = true
# # if is_current_admin_user
# # user_id = params['user_id'].to_i
# # project_id = params['project_id'].to_i
# # money = params['money'].to_f
# # #description = params['description']
# # token_num = params['token_num'].to_i
# # # 锁仓
# # result = Blockchain::CreateTrade.call({user_id: user_id, project_id: project_id, token_num: token_num})
# # if result == false
# # normal_status(-1, "创建交易失败")
# # else
# # bt = BlockchainTrade.new(user_id: user_id, project_id: project_id, token_num: token_num, money: money, state: 0) # state=0表示创建交易; state=1表示执行中; state=2表示执行完成
# # bt.save()
# # status = 2 # 交易创建成功
# # render json: { status: status }
# # end
# # else
# # normal_status(-1, "缺少权限")
# # end
# # end
# #
# #
# # def blockchain_get_trades
# # trades = BlockchainTrade.where(state: 0).all()
# # results = []
# # trades.each do |t|
# # project_id = t.project_id
# # project = Project.find_by(id: project_id)
# # if !project.nil?
# # owner = User.find_by(id: project.user_id)
# # else
# # owner = nil
# # end
# # user_id = t.user_id
# # creator = User.find_by(id: user_id)
# # if project.nil? || owner.nil? || creator.nil?
# # else
# # results << [creator, owner, project, t]
# # end
# # end
# # render json: { results: results }
# # end
# #
# # def blockchain_trade
# # #is_current_admin_user = User.current.logged? && (current_user&.admin? || current_user.id == params['user_id'].to_i)
# # is_current_admin_user = true
# # if is_current_admin_user
# # user_id2 = params['user_id2'].to_i
# # trade_id = params['trade_id'].to_i
# # BlockchainTrade.find(trade_id).update(user_id2: user_id2, state: 1) # state=1表示锁定了等待线下卖家发货
# # render json: {status: 2} # window.location.reload()
# # else
# # normal_status(-1, "缺少权限")
# # end
# # end
#
#
# def blockchain_verify_trade
# #is_current_admin_user = User.current.logged? && (current_user&.admin? || current_user.id == params['user_id'].to_i)
# is_current_admin_user = true
# if is_current_admin_user
# trade_id = params['trade_id'].to_i
# BlockchainTrade.find(trade_id).update(state: 2) # state=2表示确认收货
# render json: {status: 2} # window.location.reload()
# else
# normal_status(-1, "缺少权限")
# end
# end
#
# def blockchain_get_verify_trades
# #is_current_admin_user = User.current.logged? && (current_user&.admin? || current_user.id == params['user_id'].to_i)
# is_current_admin_user = true
# if is_current_admin_user
# trades = BlockchainTrade.where(state: 1).all()
# results = []
# trades.each do |t|
# project_id = t.project_id
# project = Project.find_by(id: project_id)
# if !project.nil?
# owner = User.find_by(id: project.user_id)
# else
# owner = nil
# end
# user_id = t.user_id
# creator = User.find_by(id: user_id)
# user_id2 = t.user_id2
# buyer = User.find_by(id: user_id2)
# if project.nil? || owner.nil? || creator.nil? || buyer.nil?
# else
# results << [creator, owner, project, t, buyer]
# end
# end
# render json: { results: results }
# else
# normal_status(-1, "缺少权限")
# end
# end
#
# def blockchain_get_history_trades
# #is_current_admin_user = User.current.logged? && (current_user&.admin? || current_user.id == params['user_id'].to_i)
# is_current_admin_user = true
# if is_current_admin_user
# trades = BlockchainTrade.where(state: 2).all()
# results = []
# trades.each do |t|
# project_id = t.project_id
# project = Project.find_by(id: project_id)
# if !project.nil?
# owner = User.find_by(id: project.user_id)
# else
# owner = nil
# end
# user_id = t.user_id
# creator = User.find_by(id: user_id)
# user_id2 = t.user_id2
# buyer = User.find_by(id: user_id2)
# if project.nil? || owner.nil? || creator.nil? || buyer.nil?
# else
# results << [creator, owner, project, t, buyer]
# end
# end
# render json: { results: results }
# else
# normal_status(-1, "缺少权限")
# end
# end
def blockchain_get_issue_token_num
issue_id = params["issue_id"]['orderId'].to_i
issue = Issue.find_by(id: issue_id)
project = Project.find(issue.project_id)
if project[:use_blockchain]
render json: {"blockchain_token_num": issue.blockchain_token_num}
else
render json: {"blockchain_token_num": -1} # 未使用blockchain
end
end
def blockchain_get_unclosed_issue_list
ownername = params["ownername"]
identifier = params["reponame"]
owner = User.find_by(login: ownername)
project = Project.find_by(user_id: owner.id, identifier: identifier)
unclosed_issues = Issue.where(project_id: project.id, issue_classify: "issue").where.not(status_id: 5)
results = []
unclosed_issues.each do |i|
results << [i.id, i.subject]
end
render json: {unclosed_issues: results}
end
# TODO 其他平台登录时同步修改gitea平台对应用户的密码
# 该方法主要用于别的平台初次部署对接forge平台同步用户后gitea平台对应的用户密码与forge平台用户密码不一致是问题
def sync_gitea_pwd
@ -322,7 +693,7 @@ class UsersController < ApplicationController
end
end
def email_search
def email_search
return render_error('请输入email') if params[:email].blank?
@user = User.find_by(mail: params[:email])
end

View File

@ -30,6 +30,14 @@ class BaseForm
raise "项目名称已被使用." if Project.where(user_id: user_id, name: project_name.strip).exists?
end
def check_blockchain_token_all(blockchain_token_all)
raise "请正确填写项目token总数." if (Float(blockchain_token_all) rescue false) == false or blockchain_token_all.to_i < 0 or Float(blockchain_token_all) != blockchain_token_all.to_i
end
def check_blockchain_init_token(blockchain_init_token)
raise "请正确填写项目创始人token占比." if (Float(blockchain_init_token) rescue false) == false or blockchain_init_token.to_i < 0 or blockchain_init_token.to_i > 100 or Float(blockchain_init_token) != blockchain_init_token.to_i
end
def check_reversed_keyword(repository_name)
raise "项目标识已被占用." if ReversedKeyword.check_exists?(repository_name)
end

View File

@ -1,6 +1,7 @@
class Projects::CreateForm < BaseForm
attr_accessor :user_id, :name, :description, :repository_name, :project_category_id,
:project_language_id, :ignore_id, :license_id, :private, :owner
:project_language_id, :ignore_id, :license_id, :private, :owner,
:blockchain, :blockchain_token_all, :blockchain_init_token
validates :user_id, :name, :repository_name, presence: true
validates :repository_name, format: { with: CustomRegexp::REPOSITORY_NAME_REGEX, multiline: true, message: "只能含有数字、字母、下划线且不能以下划线开头和结尾" }
@ -15,6 +16,8 @@ class Projects::CreateForm < BaseForm
check_project_language(project_language_id)
check_project_name(user_id, name) unless name.blank?
check_repository_name(user_id, repository_name) unless repository_name.blank?
check_blockchain_token_all(blockchain_token_all) unless blockchain_token_all.blank?
check_blockchain_init_token(blockchain_init_token) unless blockchain_init_token.blank?
end
def check_license

View File

@ -0,0 +1,2 @@
module Admins::OrganizationsHelper
end

View File

@ -66,6 +66,7 @@ module ProjectsHelper
jianmu_devops: jianmu_devops_code(project, user),
jianmu_devops_url: jianmu_devops_url,
cloud_ide_saas_url: cloud_ide_saas_url(user),
open_blockchain: EduSetting.get("open_blockchain_projects").to_s.split(",").include?(project.id.to_s) || EduSetting.get("open_blockchain_projects").to_s.split(",").include?(project.identifier) || EduSetting.get("open_blockchain_users").to_s.split(",").include?(user.id) || EduSetting.get("open_blockchain_users").to_s.split(",").include?(user.login),
ignore_id: project.ignore_id
}).compact
@ -138,6 +139,62 @@ module ProjectsHelper
"#{saas_url}/oauth/login?product_account_id=PA1001218&tenant_code=TI1001383&oauth_url=#{oauth_url}&token=#{token.value}"
end
def ai_shang_v1_url(project)
url = EduSetting.get("ai_shang_url") || "https://shang.gitlink.org.cn"
case project.identifier.to_s.downcase
when nil then ""
when 'rails' then "#{url}/v1/rails/entropy"
when 'jittor' then "#{url}/v1/jittor/entropy"
when 'paddle' then "#{url}/v1/Paddle/entropy"
when 'vue' then "#{url}/v1/vue/entropy"
when 'bootstrap' then "#{url}/v1/bootstrap/entropy"
when 'tensorflow' then "#{url}/v1/tensorflow/entropy"
else ''
end
end
def ai_shang_v2_url(project)
url = EduSetting.get("ai_shang_url") || "https://shang.gitlink.org.cn"
case project.identifier.to_s.downcase
when nil then ""
when 'rails' then "#{url}/v2/getMediumData?repo_login=rails&repo_name=rails"
when 'jittor' then "#{url}/v2/getMediumData?repo_login=Jittor&repo_name=jittor"
when 'paddle' then "#{url}/v2/getMediumData?repo_login=PaddlePaddle&repo_name=Paddle"
when 'vue' then "#{url}/v2/getMediumData?repo_login=vuejs&repo_name=vue"
when 'bootstrap' then "#{url}/v2/getMediumData?repo_login=twbs&repo_name=bootstrap"
when 'tensorflow' then "#{url}/v2/getMediumData?repo_login=tensorflow&repo_name=tensorflow"
else ''
end
end
def ai_shang_v4_url(project)
url = EduSetting.get("ai_shang_url") || "https://shang.gitlink.org.cn"
case project.identifier.to_s.downcase
when nil then ""
when 'rails' then "#{url}/v2/getIndexData?repo_login=rails&repo_name=rails"
when 'jittor' then "#{url}/v2/getIndexData?repo_login=Jittor&repo_name=jittor"
when 'paddle' then "#{url}/v2/getIndexData?repo_login=PaddlePaddle&repo_name=Paddle"
when 'vue' then "#{url}/v2/getIndexData?repo_login=vuejs&repo_name=vue"
when 'bootstrap' then "#{url}/v2/getIndexData?repo_login=twbs&repo_name=bootstrap"
when 'tensorflow' then "#{url}/v2/getIndexData?repo_login=tensorflow&repo_name=tensorflow"
else ''
end
end
def ai_shang_v3_url(project)
url = EduSetting.get("ai_shang_v3_url") || "https://entropy.ingress.isa.buaanlsde.cn"
case project.identifier.to_s.downcase
when nil then ""
when 'rails' then "#{url}/rails/entropy"
when 'jittor' then "#{url}/jittor/entropy"
when 'paddle' then "#{url}/paddle/entropy"
when 'vue' then "#{url}/vue/entropy"
when 'bootstrap' then "#{url}/bootstrap/entropy"
when 'tensorflow' then "#{url}/tensorflow/entropy"
else ''
end
end
def aes_encrypt(key, des_text)
# des_text='{"access_key_id":"STS.NTuC9RVmWfJqj3JkcMzPnDf7X","access_key_secret":"E8NxRZWGNxxMfwgt5nFLnBFgg6AzgXCZkSNCyqygLuHM","end_point":"oss-accelerate.aliyuncs.com","security_token":"CAIS8gF1q6Ft5B2yfSjIr5fACIPmu7J20YiaaBX7j2MYdt9Cq6Ocujz2IHhMenVhA+8Wv/02n2hR7PcYlq9IS55VWEqc/VXLaywQo22beIPkl5Gfz95t0e+IewW6Dxr8w7WhAYHQR8/cffGAck3NkjQJr5LxaTSlWS7OU/TL8+kFCO4aRQ6ldzFLKc5LLw950q8gOGDWKOymP2yB4AOSLjIx6lAt2T8vs/7hmZPFukSFtjCglL9J/baWC4O/csxhMK14V9qIx+FsfsLDqnUIs0YWpf0p3P0doGyf54vMWUM05A6dduPS7txkLAJwerjVl1/ADxc0/hqAASXhPeiktbmDjwvnSn4iKcSGQ+xoQB468eHXNdvf13dUlbbE1+JhRi0pZIB2UCtN9oTsLHcwIHt+EJaoMd3+hGwPVmvHSXzECDFHylZ8l/pzTwlE/aCtZyVmI5cZEvmWu2xBa3GRbULo7lLvyeX1cHTVmVWf4Nk6D09PzTU8qlAj","bucket":"edu-bigfiles1","region":"oss-cn-hangzhou","callback_url":"https://data.educoder.net/api/buckets/callback.json","bucket_host":"data.educoder.net"}'
# des = OpenSSL::Cipher::Cipher.new('aes-256-ctr')

View File

@ -213,5 +213,12 @@ module RepositoriesHelper
def tmp_dir
"repo"
end
def repo_git_submodule_url(owner, repo, path)
unless (path.starts_with?('http://') || path.starts_with?('https://'))
path = File.expand_path(path, "/#{owner&.login}/#{repo&.identifier}")
end
return path
end
end

View File

@ -21,7 +21,8 @@ module TagChosenHelper
"issue_tag": render_issue_tags(project),
"issue_type": render_issue_species,
"all_issues": all_issues,
"branches": render_branches(project)
"branches": render_branches(project),
"use_blockchain": project['use_blockchain']
}
end

View File

@ -0,0 +1,186 @@
class IssueWebhookJob < ApplicationJob
include ApplicationHelper
queue_as :message
def perform(issue_id)
issue = Issue.find_by(id: issue_id)
return if issue.blank?
project = issue.project
user = issue.user
domain = "#{Rails.application.config_for(:configuration)['platform_url']}"
gitea_domain = GiteaService.gitea_config[:domain]
ssh_url = "git@#{gitea_domain.gsub("https://", "")}:#{project.owner.login}/#{project.identifier}.git"
event_json = {
"action": "opened",
"number": 1,
"issue": {
"id": issue.id,
"url": "#{domain}/api/v1/#{project.owner.login}/#{project.identifier}/issues/#{issue.id}",
"html_url": "#{domain}/#{project.owner.login}/#{project.identifier}/issues/#{issue.id}",
"number": 1,
"user": {
"id": user.id,
"login": "#{user.login}",
"full_name": "",
"email": "#{user.mail}",
"avatar_url": "#{domain}/#{url_to_avatar(user)}",
"language": "",
"is_admin": false,
"last_login": "#{user.last_login_on}",
"created": "#{user.created_on}",
"restricted": false,
"active": false,
"prohibit_login": false,
"location": "",
"website": "",
"description": "",
"visibility": "public",
"followers_count": 0,
"following_count": 0,
"starred_repos_count": 0,
"username": "#{user.login}"
},
"original_author": "",
"original_author_id": 0,
"title": "#{issue.subject}",
"body": "#{issue.description}",
"ref": "",
"labels": [],
"milestone": nil,
"assignee": nil,
"assignees": nil,
"state": "open",
"is_locked": false,
"comments": 0,
"created_at": "#{issue.created_on}",
"updated_at": "#{issue.updated_on}",
"closed_at": nil,
"due_date": nil,
"pull_request": nil,
"repository": {
"id": 11307,
"name": "#{project.identifier}",
"owner": "#{project.owner.login}",
"full_name": "#{project.owner.login}/#{project.identifier}"
}
},
"repository": {
"id": project.id,
"owner": {
"id": project.owner.id,
"login": "#{project.owner.login}",
"full_name": "",
"email": "#{project.owner.mail}",
"avatar_url": "#{domain}/#{url_to_avatar(project.owner)}",
"language": "",
"is_admin": false,
"last_login": "#{project.owner.last_login_on}",
"created": "#{project.owner.created_on}",
"restricted": false,
"active": false,
"prohibit_login": false,
"location": "",
"website": "",
"description": "",
"visibility": "public",
"followers_count": 0,
"following_count": 0,
"starred_repos_count": 0,
"username": "#{project.owner.login}"
},
"name": "#{project.identifier}",
"full_name": "#{project.owner.login}/#{project.identifier}",
"description": "",
"empty": false,
"private": false,
"fork": false,
"template": false,
"parent": nil,
"mirror": false,
"size": 843,
"html_url": "#{domain}/#{project.owner.login}/#{project.identifier}",
"ssh_url": "#{ssh_url}",
"clone_url": "#{domain}/#{project.owner.login}/#{project.identifier}.git",
"original_url": "#{project.repository.mirror_url}",
"website": "",
"stars_count": 0,
"forks_count": 0,
"watchers_count": 1,
"open_issues_count": 1,
"open_pr_counter": 0,
"release_counter": 0,
"default_branch": "master",
"archived": false,
"created_at": "#{project.created_on}",
"updated_at": "#{project.updated_on}",
"permissions": {
"admin": true,
"push": true,
"pull": true
},
"has_issues": true,
"internal_tracker": {
"enable_time_tracker": true,
"allow_only_contributors_to_track_time": true,
"enable_issue_dependencies": true
},
"has_wiki": true,
"has_pull_requests": true,
"has_projects": false,
"ignore_whitespace_conflicts": false,
"allow_merge_commits": true,
"allow_rebase": true,
"allow_rebase_explicit": true,
"allow_squash_merge": true,
"default_merge_style": "merge",
"avatar_url": "",
"internal": false,
"mirror_interval": ""
},
"sender": {
"id": user.id,
"login": "#{user.login}",
"full_name": "",
"email": "#{user.mail}",
"avatar_url": "#{domain}/#{url_to_avatar(user)}",
"language": "",
"is_admin": false,
"last_login": "#{user.last_login_on}",
"created": "#{user.created_on}",
"restricted": false,
"active": false,
"prohibit_login": false,
"location": "",
"website": "",
"description": "",
"visibility": "public",
"followers_count": 0,
"following_count": 0,
"starred_repos_count": 0,
"username": "#{user.login}"
}
}
# puts "event_json:#{event_json.to_json}"
begin
url = URI("http://117.50.185.50:80")
http = Net::HTTP.new(url.host, url.port)
request = Net::HTTP::Post.new(url)
request["Content-Type"] = "application/json"
uuid = SecureRandom.uuid
request['X-GitHub-Delivery'] = uuid
request['X-Gitea-Delivery'] = uuid
request['X-Gogs-Delivery'] = uuid
request['X-GitHub-Event'] = 'issues'
request['X-Gitea-Event'] = 'issues'
request['X-Gogs-Event'] = 'issues'
request.body = JSON.dump(event_json)
response = http.request(request)
Rails.logger.info "issue #{issue_id} webhook event======#{response.read_body}"
rescue Exception => e
Rails.logger.info "issue #{issue_id} webhook event error======#{e.message}"
puts "issue: #{issue_id} webhook event error======#{e.message}"
end
end
end

View File

@ -15,7 +15,7 @@ class MigrateRemoteRepositoryJob < ApplicationJob
## open jianmu devops
project_id = repo&.project&.id
puts "############ mirror project_id,user_id: #{project_id},#{user_id} ############"
OpenProjectDevOpsJob.perform_later(project_id, user_id) if project_id.present? && user_id.present?
OpenProjectDevOpsJob.set(wait: 5.seconds).perform_later(project_id, user_id) if project_id.present? && user_id.present?
puts "############ mirror status: #{repo.mirror.status} ############"
else
repo&.mirror&.failed!

View File

@ -5,6 +5,7 @@ class OpenProjectDevOpsJob < ApplicationJob
def perform(project_id, user_id)
project = Project.find_by(id: project_id)
return if project.blank?
user = User.find_by(id: user_id)
code = jianmu_devops_code(project, user)
uri = URI.parse("#{jianmu_devops_url}/activate?code=#{URI.encode_www_form_component(code)}")

View File

@ -34,8 +34,8 @@ class SendTemplateMessageJob < ApplicationJob
operator_id, issue_id = args[0], args[1]
operator = User.find_by_id(operator_id)
issue = Issue.find_by_id(issue_id)
return unless operator.present? && issue.present?
receivers = User.where(id: issue&.assigned_to_id).where.not(id: operator&.id)
return unless operator.present? && issue.present?
receivers = issue&.assigners.where.not(id: operator&.id)
receivers_string, content, notification_url = MessageTemplate::IssueAssigned.get_message_content(receivers, operator, issue)
Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, issue_id: issue.id})
receivers.find_each do |receiver|
@ -56,7 +56,7 @@ class SendTemplateMessageJob < ApplicationJob
operator = User.find_by_id(operator_id)
issue = Issue.find_by_id(issue_id)
return unless operator.present? && issue.present?
receivers = User.where(id: [issue&.assigned_to_id, issue&.author_id]).where.not(id: operator&.id)
receivers = User.where(id: issue.assigners.pluck(:id).append(issue&.author_id)).where.not(id: operator&.id)
receivers_string, content, notification_url = MessageTemplate::IssueChanged.get_message_content(receivers, operator, issue, change_params.symbolize_keys)
Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, issue_id: issue.id, change_params: change_params.symbolize_keys})
receivers.find_each do |receiver|
@ -67,7 +67,7 @@ class SendTemplateMessageJob < ApplicationJob
issue_id = args[0]
issue = Issue.find_by_id(issue_id)
return unless issue.present?
receivers = User.where(id: [issue&.assigned_to_id, issue&.author_id])
receivers = User.where(id: issue.assigners.pluck(:id).append(issue&.author_id))
receivers_string, content, notification_url = MessageTemplate::IssueExpire.get_message_content(receivers, issue)
Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {issue_id: issue.id})
receivers.find_each do |receiver|
@ -78,7 +78,11 @@ class SendTemplateMessageJob < ApplicationJob
operator_id, issue_title, issue_assigned_to_id, issue_author_id = args[0], args[1], args[2], args[3]
operator = User.find_by_id(operator_id)
return unless operator.present?
receivers = User.where(id: [issue_assigned_to_id, issue_author_id]).where.not(id: operator&.id)
if issue_assigned_to_id.is_a?(Array)
receivers = User.where(id: issue_assigned_to_id.append(issue_author_id)).where.not(id: operator&.id)
else
receivers = User.where(id: [issue_assigned_to_id, issue_author_id]).where.not(id: operator&.id)
end
receivers_string, content, notification_url = MessageTemplate::IssueDeleted.get_message_content(receivers, operator, issue_title)
Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, issue_title: issue_title})
receivers.find_each do |receiver|

51
app/models/bot.rb Normal file
View File

@ -0,0 +1,51 @@
# == Schema Information
#
# Table name: bot
#
# id :integer not null, primary key
# bot_name :string(255)
# bot_des :text(4294967295)
# webhook :string(255)
# is_public :integer
# logo :string(255)
# state :integer
# client_id :string(255)
# client_secret :string(255)
# web_url :string(255)
# category :string(255)
# install_num :integer default("0")
# update_time :datetime not null
# create_time :datetime not null
# private_key :text(65535)
# uid :integer
# owner_id :integer
#
# Indexes
#
# name (bot_name) UNIQUE
#
class Bot < ApplicationRecord
self.table_name = "bot"
has_many :install_bots
def name
self.bot_name
end
def self.decode_jwt_token(token)
decoded_token = JWT.decode token, nil, false
return [nil, "Token已过期"] if Time.now.to_i - 60 - decoded_token[0]["exp"].to_i > 0
bot = Bot.find_by(id: decoded_token[0]["iss"])
return [nil, "Token不存在"] if bot.blank?
rsa_private = OpenSSL::PKey::RSA.new(bot.private_key)
rsa_public = rsa_private.public_key
JWT.decode token, rsa_public, true, { algorithm: 'RS256' }
[User.find_by(id: bot.owner_id), ""]
rescue JWT::DecodeError
Rails.logger.error "jwt token decode error:#{token}"
[nil, "无效Token"]
end
end

20
app/models/bot_install.rb Normal file
View File

@ -0,0 +1,20 @@
# == Schema Information
#
# Table name: install_bot
#
# id :integer not null, primary key
# bot_id :integer not null
# installer_id :integer not null
# store_id :integer not null
# state :integer not null
# create_time :datetime not null
# update_time :datetime not null
#
# frozen_string_literal: true
class BotInstall < ApplicationRecord
self.table_name = "install_bot"
belongs_to :bot
end

View File

@ -194,7 +194,7 @@ module ProjectOperable
if owner.is_a?(User)
managers.exists?(user_id: user.id)
elsif owner.is_a?(Organization)
managers.exists?(user_id: user.id) || owner.is_owner?(user.id) || owner.is_only_admin?(user.id)
managers.exists?(user_id: user.id) || owner.is_owner?(user.id) || (owner.is_only_admin?(user.id) && (teams.pluck(:id) & user.teams.pluck(:id)).size > 0)
else
false
end
@ -205,7 +205,7 @@ module ProjectOperable
if owner.is_a?(User)
developers.exists?(user_id: user.id)
elsif owner.is_a?(Organization)
developers.exists?(user_id: user.id) || owner.is_only_write?(user.id)
developers.exists?(user_id: user.id) || (owner.is_only_write?(user.id) && (teams.pluck(:id) & user.teams.pluck(:id)).size > 0)
else
false
end

View File

@ -1,45 +1,160 @@
module Watchable
extend ActiveSupport::Concern
included do
has_many :watchers, as: :watchable, dependent: :destroy
has_many :watcher_users, through: :watchers, source: :user, validate: false
scope :watched_by, -> (user_id) { includes(:watchers).where(watchers: { user_id: user_id }) }
scope :following, -> (user_id) { watched_by(user_id) }
end
def watched?(watchable)
watchable.watchers.exists?(user: self)
end
def watch!(watchable)
watchable.watchers.create!(user: self, created_at: Time.current)
end
def unwatch!(watchable)
obj = watchable.watchers.find_by(user: self)
obj.destroy! if obj.present?
end
# 我正在关注的、我追随的
def following
User.following(self.id)
end
def following_count
following.size
end
# 关注我的、我的粉丝、我的追随者
def followers
watcher_users
end
def followers_count
followers.size
end
module ClassMethods
end
end
module Watchable
extend ActiveSupport::Concern
included do
has_many :watchers, as: :watchable, dependent: :destroy
has_many :watcher_users, through: :watchers, source: :user, validate: false
scope :watched_by, -> (user_id) { includes(:watchers).where(watchers: { user_id: user_id }) }
scope :following, -> (user_id) { watched_by(user_id) }
end
def watched?(watchable)
watchable.watchers.exists?(user: self)
end
def watch!(watchable)
watchable.watchers.create!(user: self, created_at: Time.current)
end
def unwatch!(watchable)
obj = watchable.watchers.find_by(user: self)
obj.destroy! if obj.present?
end
# 我正在关注的、我追随的
def following
User.following(self.id)
end
def following_count
following.size
end
def contribution_perc
project_identifier = params[:project_identifier]
user_login = params[:id]
@project = Project.find_by_identifier(project_identifier)
@user = User.find_by_login(user_login)
def cal_perc(count_user, count_all)
(count_user * 1.0 / (count_all + 0.000000001)).round(5)
end
if @project['use_blockchain'] == true or @project['use_blockchain'] == 1
balance_user = Blockchain::BalanceQueryOneProject.call({"user_id": @user.id, "project_id": @project.id})
balance_all = Blockchain::RepoBasicInfo.call({"project_id": @project.id})["cur_supply"]
score = cal_perc(balance_user, balance_all)
else
# 获取所有行为对应的项目内记录总数和个人记录数
features = {
"requirement" => {},
"development" => {},
"review" => {}
}
# 1. issue创建
issues = Issue.where(project_id: @project.id, issue_classify: 'issue')
issue_all = issues.count
issue_user = issues.where(author_id: @user.id).count
features["requirement"] = features["requirement"].merge({"issue" => {"all" => issue_all, "perc" => cal_perc(issue_user, issue_all)}})
# 2. 里程碑创建
milestones = Version.where(project_id: @project.id)
milestone_all = milestones.count
milestone_user = milestones.where(user_id: @user.id).count
features["requirement"] = features["requirement"].merge({"milestone" => {"all" => milestone_all, "perc" => cal_perc(milestone_user, milestone_all)}})
# 3. issue评论
issue_comments = Journal.joins("INNER JOIN issues on journals.journalized_id=issues.id").where("issues.project_id=#{@project.id} and journalized_type='Issue' and issues.issue_classify='issue'")
issue_comment_all = issue_comments.count
issue_comment_user = issue_comments.where("journals.user_id=#{@user.id}").count
features["requirement"] = features["requirement"].merge({"issue_comment" => {"all" => issue_comment_all, "perc" => cal_perc(issue_comment_user, issue_comment_all)}})
# 4. 合并请求
prs = PullRequest.where(project_id: @project.id)
pr_all = prs.count
pr_user = prs.where(user_id: @user.id).count
features["development"] = features["development"].merge({"pr" => {"all" => pr_all, "perc" => cal_perc(pr_user, pr_all)}})
# 5. pr评论
pr_comments = Journal.joins("INNER JOIN issues on journals.journalized_id=issues.id").where("issues.project_id=#{@project.id} and journalized_type='Issue' and issues.issue_classify='pull_request'")
pr_comment_all = pr_comments.count
pr_comment_user = pr_comments.where("journals.user_id=#{@user.id}").count
features["review"] = features["review"].merge({"pr_comment" => {"all" => pr_comment_all, "perc" => cal_perc(pr_comment_user, pr_comment_all)}})
# 6. 代码行评论
line_comments = Journal.joins("INNER JOIN pull_requests on journals.journalized_id=pull_requests.id").where("pull_requests.project_id=#{@project.id} and journalized_type='PullRequest'")
line_comment_all = line_comments.count
line_comment_user = line_comments.where("journals.user_id=#{@user.id}").count
features["review"] = features["review"].merge({"line_comment" => {"all" => line_comment_all, "perc" => cal_perc(line_comment_user, line_comment_all)}})
# 7. 代码行、commit贡献统计
code_contributions = Api::V1::Projects::CodeStats::ListService.call(@project, {ref: nil})
commit_all = code_contributions["commit_count"]
addition_all = code_contributions["additions"]
deletion_all = code_contributions["deletions"]
commit_user = 0
addition_user = 0
deletion_user = 0
code_contributions["authors"].each do |author|
if author["name"] == @user.login
commit_user = author["commits"]
addition_user = author["additions"]
deletion_user = author["deletions"]
end
end
features["development"] = features["development"].merge({"commit" => {"all" => commit_all, "perc" => cal_perc(commit_user, commit_all)}})
features["development"] = features["development"].merge({"addition" => {"all" => addition_all, "perc" => cal_perc(addition_user, addition_all)}})
features["development"] = features["development"].merge({"deletion" => {"all" => deletion_all, "perc" => cal_perc(deletion_user, deletion_all)}})
def cal_weight(features)
weights = {} # 计算每一项的权重
categories = []
features.each do |key, _|
categories << key
weights[key] = Hash.new
end
count_all = 0
counts = {}
categories.each do |category|
count_1 = 0
features[category].each do |_, value|
count_1 += value["all"]
end
count_all += count_1
counts[category] = count_1
features[category].each do |key, value|
weight = cal_perc(value["all"], count_1)
weights[category] = weights[category].merge({key => weight})
end
end
categories.each do |category|
weight = cal_perc(counts[category], count_all)
weights[category] = weights[category].merge({"category_weight" => weight})
end
return weights
end
weights_categories = cal_weight(features)
score = 0.0
features.each do |category, value_1|
category_score = 0.0
value_1.each do |action, value_2|
category_score += weights_categories[category][action] * value_2["perc"]
end
score += weights_categories[category]["category_weight"] * category_score.round(4)
end
end
score
(score * 100).round(1).to_s + "%"
end
# 关注我的、我的粉丝、我的追随者
def followers
watcher_users
end
def followers_count
followers.size
end
module ClassMethods
end
end

View File

@ -69,12 +69,24 @@ class Issue < ApplicationRecord
has_many :issue_tags, through: :issue_tags_relates
has_many :issue_times, dependent: :destroy
has_many :issue_depends, dependent: :destroy
has_many :issue_assigners, dependent: :destroy
has_many :assigners, through: :issue_assigners
has_many :issue_participants, dependent: :destroy
has_many :participants, through: :issue_participants
has_many :show_participants, -> {joins(:issue_participants).where.not(issue_participants: {participant_type: "atme"}).distinct}, through: :issue_participants, source: :participant
has_many :show_assigners, -> {joins(:issue_assigners).distinct}, through: :issue_assigners, source: :assigner
has_many :show_issue_tags, -> {joins(:issue_tags_relates).distinct}, through: :issue_tags_relates, source: :issue_tag
has_many :comment_journals, -> {where.not(notes: nil)}, class_name: "Journal", :as => :journalized
has_many :operate_journals, -> {where(notes: nil)}, class_name: "Journal", :as => :journalized
scope :issue_includes, ->{includes(:user)}
scope :issue_many_includes, ->{includes(journals: :user)}
scope :issue_issue, ->{where(issue_classify: [nil,"issue"])}
scope :issue_pull_request, ->{where(issue_classify: "pull_request")}
scope :issue_index_includes, ->{includes(:tracker, :priority, :version, :issue_status, :journals,:issue_tags,user: :user_extension)}
scope :closed, ->{where(status_id: 5)}
scope :opened, ->{where.not(status_id: 5)}
after_create :incre_project_common, :incre_user_statistic, :incre_platform_statistic
after_save :change_versions_count, :send_update_message_to_notice_system
after_destroy :update_closed_issues_count_in_project!, :decre_project_common, :decre_user_statistic, :decre_platform_statistic

View File

@ -0,0 +1,20 @@
# == Schema Information
#
# Table name: issue_assigners
#
# id :integer not null, primary key
# issue_id :integer
# assigner_id :integer
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_issue_assigners_on_assigner_id (assigner_id)
# index_issue_assigners_on_issue_id (issue_id)
#
class IssueAssigner < ApplicationRecord
belongs_to :issue
belongs_to :assigner, class_name: "User"
end

View File

@ -0,0 +1,9 @@
class IssueParticipant < ApplicationRecord
belongs_to :issue
belongs_to :participant, class_name: "User"
enum participant_type: {"authored": 0, "assigned": 1, "commented": 2, "edited": 3, "atme": 4}
end

View File

@ -15,4 +15,21 @@
class IssuePriority < ApplicationRecord
has_many :issues
def self.init_data
map = {
"1" => "",
"2" => "正常",
"3" => "",
"4" => "紧急"
}
IssuePriority.order(id: :asc).each do |prty|
if map["#{prty.id}"] == prty.name
IssuePriority.find_or_create_by(id: prty.id, name: prty.name)
else
Issue.where(priority_id: prty.id).each{|i| i.update_column(:priority_id, 2)}
prty.destroy!
end
end
end
end

View File

@ -20,10 +20,28 @@ class IssueStatus < ApplicationRecord
ADD = 1
SOLVING = 2
SOLVED = 3
FEEDBACK = 4
# FEEDBACK = 4
CLOSED = 5
REJECTED = 6
has_many :issues
belongs_to :project, optional: true
def self.init_data
map = {
"1" => "新增",
"2" => "正在解决",
"3" => "已解决",
"5" => "关闭",
"6" => "拒绝"
}
IssueStatus.order(id: :asc).each do |stat|
if map["#{stat.id}"] == stat.name
IssueStatus.find_or_create_by(id: stat.id, name: stat.name)
else
Issue.where(status_id: stat.id).each{|i| i.update_column(:status_id, 1)}
stat.destroy!
end
end
end
end

View File

@ -23,6 +23,34 @@ class IssueTag < ApplicationRecord
has_many :issue_tags_relates, dependent: :destroy
has_many :issues, through: :issue_tags_relates
has_many :issue_issues, -> {where(issue_classify: [nil,"issue"])}, source: :issue, through: :issue_tags_relates
has_many :pull_request_issues, -> {where(issue_classify: "pull_request")}, source: :issue, through: :issue_tags_relates
belongs_to :project, optional: true, counter_cache: true
belongs_to :user, optional: true
def self.init_data(project_id)
data = [
["缺陷", "表示项目存在问题", "#d92d4c"],
["功能", "表示新功能申请", "#ee955a"],
["疑问", "表示存在的问题", "#2d6ddc"],
["支持", "表示特定功能或特定需求", "#019549"],
["任务", "表示需要分配的任务", "#c1a30d"],
["协助", "表示需要社区用户协助", "#2a0dc1"],
["搁置", "表示此问题暂时不会继续处理", "#892794"],
["文档", "表示文档材料补充", "#9ed600"],
["测试", "表示需要测试的需求", "#2897b9"],
["重复", "表示已存在类似的疑修", "#bb5332"]
]
data.each do |item|
next if IssueTag.exists?(project_id: project_id, name: item[0])
IssueTag.create!(project_id: project_id, name: item[0], description: item[1], color: item[2])
end
$redis_cache.hset("project_init_issue_tags", project_id, 1)
end
def reset_counter_field
self.update_column(:issues_count, issue_issues.size)
self.update_column(:pull_requests_count, pull_request_issues.size)
end
end

View File

@ -15,5 +15,29 @@
class IssueTagsRelate < ApplicationRecord
belongs_to :issue
belongs_to :issue_tag, counter_cache: :issues_count
belongs_to :issue_tag
after_create :increment_issue_tags_counter_cache
after_destroy :decrement_issue_tags_counter_cache
def increment_issue_tags_counter_cache
Rails.logger.info "11111"
Rails.logger.info self.issue.issue_classify
if self.issue.issue_classify == "issue"
IssueTag.increment_counter :issues_count, issue_tag_id
else
IssueTag.increment_counter :pull_requests_count, issue_tag_id
end
end
def decrement_issue_tags_counter_cache
Rails.logger.info "2222222"
Rails.logger.info self.issue.issue_classify
if self.issue.issue_classify == "issue"
IssueTag.decrement_counter :issues_count, issue_tag_id
else
IssueTag.decrement_counter :pull_requests_count, issue_tag_id
end
end
end

View File

@ -40,9 +40,12 @@ class Journal < ApplicationRecord
belongs_to :journalized, polymorphic: true
belongs_to :review, optional: true
belongs_to :resolveer, class_name: 'User', foreign_key: :resolveer_id, optional: true
belongs_to :parent_journal, class_name: 'Journal', foreign_key: :parent_id, optional: true, counter_cache: :comments_count
belongs_to :reply_journal, class_name: 'Journal', foreign_key: :reply_id, optional: true
has_many :journal_details, :dependent => :delete_all
has_many :attachments, as: :container, dependent: :destroy
has_many :children_journals, class_name: 'Journal', foreign_key: :parent_id
has_many :first_ten_children_journals, -> { order(created_on: :asc).limit(10)}, class_name: 'Journal', foreign_key: :parent_id
has_many :children_journals, class_name: 'Journal', foreign_key: :parent_id, dependent: :destroy
scope :journal_includes, ->{includes(:user, :journal_details, :attachments)}
scope :parent_journals, ->{where(parent_id: nil)}
@ -54,6 +57,81 @@ class Journal < ApplicationRecord
self.notes.blank? && self.journal_details.present?
end
def operate_content
content = ""
detail = self.journal_details.take
case detail.property
when 'issue'
return "创建了<b>疑修</b>"
when 'attachment'
old_value = Attachment.where(id: detail.old_value.split(",")).pluck(:filename).join("")
new_value = Attachment.where(id: detail.value.split(",")).pluck(:filename).join("")
if old_value.nil? || old_value.blank?
content += "添加了<b>#{new_value}</b>附件"
else
new_value = "" if new_value.blank?
content += "将附件由<b>#{old_value}</b>更改为<b>#{new_value}</b>"
end
when 'issue_tag'
old_value = IssueTag.where(id: detail.old_value.split(",")).pluck(:name).join("")
new_value = IssueTag.where(id: detail.value.split(",")).pluck(:name).join("")
if old_value.nil? || old_value.blank?
content += "添加了<b>#{new_value}</b>标记"
else
new_value = "" if new_value.blank?
content += "将标记由<b>#{old_value}</b>更改为<b>#{new_value}</b>"
end
when 'assigner'
old_value = User.where(id: detail.old_value.split(",")).pluck(:nickname).join("")
new_value = User.where(id: detail.value.split(",")).pluck(:nickname).join("")
if old_value.nil? || old_value.blank?
content += "添加负责人<b>#{new_value}</b>"
else
new_value = "" if new_value.blank?
content += "将负责人由<b>#{old_value}</b>更改为<b>#{new_value}</b>"
end
when 'attr'
content = ""
case detail.prop_key
when 'subject'
return "修改了<b>标题</b>"
when 'description'
return "修改了<b>描述</b>"
when 'status_id'
old_value = IssueStatus.find_by_id(detail.old_value)&.name
new_value = IssueStatus.find_by_id(detail.value)&.name
content += "状态"
when 'priority_id'
old_value = IssuePriority.find_by_id(detail.old_value)&.name
new_value = IssuePriority.find_by_id(detail.value)&.name
content += "优先级"
when 'fixed_version_id'
old_value = Version.find_by_id(detail.old_value)&.name
new_value = Version.find_by_id(detail.value)&.name
content += "里程碑"
when 'branch_name'
old_value = detail.old_value
new_value = detail.value
content += "关联分支"
when 'start_date'
old_value = detail.old_value
new_value = detail.value
content += "开始日期"
when 'due_date'
old_value = detail.old_value
new_value = detail.value
content += "结束日期"
end
if old_value.nil? || old_value.blank?
content += "设置为<b>#{new_value}</b>"
else
new_value = "" if new_value.blank?
content += "由<b>#{old_value}</b>更改为<b>#{new_value}</b>"
end
end
end
def journal_content
send_details = []
if self.is_journal_detail?

View File

@ -26,7 +26,7 @@ class MessageTemplate::IssueAssigned < MessageTemplate
project = issue&.project
owner = project&.owner
content = sys_notice.gsub('{nickname1}', operator&.real_name).gsub('{nickname2}', owner&.real_name).gsub('{repository}', project&.name).gsub('{title}', issue&.subject)
url = notification_url.gsub('{owner}', owner&.login).gsub('{identifier}', project&.identifier).gsub('{id}', issue&.id.to_s)
url = notification_url.gsub('{owner}', owner&.login).gsub('{identifier}', project&.identifier).gsub('{id}', issue&.project_issues_index.to_s)
return receivers_string(receivers), content, url
rescue => e
Rails.logger.info("MessageTemplate::IssueAssigned.get_message_content [ERROR] #{e}")
@ -52,7 +52,7 @@ class MessageTemplate::IssueAssigned < MessageTemplate
content.gsub!('{repository}', project&.name)
content.gsub!('{baseurl}', base_url)
content.gsub!('{title}', issue&.subject)
content.gsub!('{id}', issue&.id.to_s)
content.gsub!('{id}', issue&.project_issues_index.to_s)
content.gsub!('{platform}', PLATFORM)
return receiver&.mail, title, content

View File

@ -20,7 +20,7 @@ class MessageTemplate::IssueAssignerExpire < MessageTemplate
project = issue&.project
owner = project&.owner
content = sys_notice.gsub('{title}', issue&.subject)
url = notification_url.gsub('{owner}', owner&.login).gsub('{identifier}', project&.identifier).gsub('{id}', issue&.id.to_s)
url = notification_url.gsub('{owner}', owner&.login).gsub('{identifier}', project&.identifier).gsub('{id}', issue&.project_issues_index.to_s)
return receivers_string(receivers), content, url
rescue => e
Rails.logger.info("MessageTemplate::IssueAssignerExpire.get_message_content [ERROR] #{e}")

View File

@ -20,7 +20,7 @@ class MessageTemplate::IssueAtme < MessageTemplate
project = issue&.project
owner = project&.owner
content = sys_notice.gsub('{nickname}', operator&.real_name).gsub('{title}', issue&.subject)
url = notification_url.gsub('{owner}', owner&.login).gsub('{identifier}', project&.identifier).gsub('{id}', issue&.id.to_s)
url = notification_url.gsub('{owner}', owner&.login).gsub('{identifier}', project&.identifier).gsub('{id}', issue&.project_issues_index.to_s)
return receivers_string(receivers), content, url
rescue => e
Rails.logger.info("MessageTemplate::IssueAtme.get_message_content [ERROR] #{e}")

View File

@ -27,20 +27,20 @@ class MessageTemplate::IssueChanged < MessageTemplate
project = issue&.project
owner = project&.owner
content = MessageTemplate::IssueChanged.sys_notice.gsub('{nickname1}', operator&.real_name).gsub('{nickname2}', owner&.real_name).gsub('{repository}', project&.name).gsub('{title}', issue&.subject)
url = notification_url.gsub('{owner}', owner&.login).gsub('{identifier}', project&.identifier).gsub('{id}', issue&.id.to_s)
url = notification_url.gsub('{owner}', owner&.login).gsub('{identifier}', project&.identifier).gsub('{id}', issue&.project_issues_index.to_s)
change_count = change_params.keys.size
# 疑修负责人修改
if change_params[:assigned_to_id].present?
assigner1 = User.find_by_id(change_params[:assigned_to_id][0])
assigner2 = User.find_by_id(change_params[:assigned_to_id][1])
assigner1 = User.where(id: change_params[:assigned_to_id][0])
assigner2 = User.where(id: change_params[:assigned_to_id][1])
if change_count > 1
content.sub!('{ifassigner}', '<br/>')
else
content.sub!('{ifassigner}', '')
end
content.sub!('{endassigner}', '')
content.gsub!('{assigner1}', assigner1.present? ? assigner1&.real_name : '未指派成员')
content.gsub!('{assigner2}', assigner2.present? ? assigner2&.real_name : '未指派成员')
content.gsub!('{assigner1}', assigner1.present? ? assigner1.map{|a| a&.real_name}.join("") : '未指派成员')
content.gsub!('{assigner2}', assigner2.present? ? assigner2.map{|a| a&.real_name}.join("") : '未指派成员')
else
content.gsub!(/({ifassigner})(.*)({endassigner})/, '')
end
@ -205,20 +205,20 @@ class MessageTemplate::IssueChanged < MessageTemplate
content.gsub!('{identifier}', project&.identifier)
content.gsub!('{repository}', project&.name)
content.gsub!('{title}', issue&.subject)
content.gsub!('{id}', issue&.id.to_s)
content.gsub!('{id}', issue&.project_issues_index.to_s)
change_count = change_params.keys.size
# 疑修负责人修改
if change_params[:assigned_to_id].present?
assigner1 = User.find_by_id(change_params[:assigned_to_id][0])
assigner2 = User.find_by_id(change_params[:assigned_to_id][1])
assigner1 = User.where(id: change_params[:assigned_to_id][0])
assigner2 = User.where(id: change_params[:assigned_to_id][1])
if change_count > 1
content.sub!('{ifassigner}', '<br/>')
else
content.sub!('{ifassigner}', '')
end
content.sub!('{endassigner}', '')
content.gsub!('{assigner1}', assigner1.present? ? assigner1&.real_name : '未指派成员')
content.gsub!('{assigner2}', assigner2.present? ? assigner2&.real_name : '未指派成员')
content.gsub!('{assigner1}', assigner1.present? ? assigner1.map{|a| a&.real_name}.join("") : '未指派成员')
content.gsub!('{assigner2}', assigner2.present? ? assigner2.map{|a| a&.real_name}.join("") : '未指派成员')
else
content.gsub!(/({ifassigner})(.*)({endassigner})/, '')
end

View File

@ -28,7 +28,7 @@ class MessageTemplate::IssueExpire < MessageTemplate
project = issue&.project
owner = project&.owner
content = sys_notice.gsub('{title}', issue&.subject)
url = notification_url.gsub('{owner}', owner&.login).gsub('{identifier}', project&.identifier).gsub('{id}', issue&.id.to_s)
url = notification_url.gsub('{owner}', owner&.login).gsub('{identifier}', project&.identifier).gsub('{id}', issue&.project_issues_index.to_s)
return receivers_string(receivers), content, url
rescue => e
@ -53,7 +53,7 @@ class MessageTemplate::IssueExpire < MessageTemplate
content.gsub!('{repository}', project&.name)
content.gsub!('{baseurl}', base_url)
content.gsub!('{title}', issue&.subject)
content.gsub!('{id}', issue&.id.to_s)
content.gsub!('{id}', issue&.project_issues_index.to_s)
content.gsub!('{platform}', PLATFORM)
return receiver&.mail, title, content

View File

@ -27,7 +27,7 @@ class MessageTemplate::ProjectIssue < MessageTemplate
receivers = managers + followers
return '', '', '' if receivers.blank?
content = sys_notice.gsub('{nickname1}', operator&.real_name).gsub('{nickname2}', owner&.real_name).gsub('{repository}', project&.name).gsub('{title}', issue&.subject)
url = notification_url.gsub('{owner}', owner&.login).gsub('{identifier}', project&.identifier).gsub('{id}', issue&.id.to_s)
url = notification_url.gsub('{owner}', owner&.login).gsub('{identifier}', project&.identifier).gsub('{id}', issue&.project_issues_index.to_s)
return receivers_string(receivers), content, url
rescue => e
@ -54,7 +54,7 @@ class MessageTemplate::ProjectIssue < MessageTemplate
content.gsub!('{repository}', project&.name)
content.gsub!('{login2}', owner&.login)
content.gsub!('{identifier}', project&.identifier)
content.gsub!('{id}', issue&.id.to_s)
content.gsub!('{id}', issue&.project_issues_index.to_s)
content.gsub!('{title}', issue&.subject)
content.gsub!('{platform}', PLATFORM)

View File

@ -144,6 +144,18 @@ class Organization < Owner
end
end
def projects_count
Project.where( user_id: self.id).count
end
def teams_count
teams.count
end
def organization_users_count
organization_users.count
end
def real_name
name = lastname + firstname
name = name.blank? ? (nickname.blank? ? login : nickname) : name

View File

@ -69,6 +69,8 @@ class Owner < ApplicationRecord
has_many :applied_transfer_projects, dependent: :destroy
scope :like, lambda { |keywords|
# 表情处理
keywords = keywords.to_s.each_char.select { |c| c.bytes.first < 240 }.join('')
sql = "CONCAT(lastname, firstname) LIKE :search OR nickname LIKE :search OR login LIKE :search "
where(sql, :search => "%#{keywords.strip}%") unless keywords.blank?
}

View File

@ -188,7 +188,7 @@ class Project < ApplicationRecord
forked_project = self.forked_from_project
if forked_project.present?
forked_project.decrement(:forked_count, 1)
forked_project.update_column(:forked_count, forked_project.forked_count)
forked_project.update_attribute(:forked_count, forked_project.forked_count)
end
end
@ -240,6 +240,8 @@ class Project < ApplicationRecord
end
def self.search_project(search)
# 表情处理
search = search.to_s.each_char.select { |c| c.bytes.first < 240 }.join('')
ransack(name_or_identifier_cont: search)
end
# 创建者
@ -421,4 +423,19 @@ class Project < ApplicationRecord
raise("项目名称包含敏感词汇,请重新输入") if name && !HarmoniousDictionary.clean?(name)
raise("项目描述包含敏感词汇,请重新输入") if description && !HarmoniousDictionary.clean?(description)
end
def get_last_project_issues_index
last_issue = self.issues.issue_issue.last
deleted_issue_count = ($redis_cache.hget("issue_cache_delete_count", self.id) || 0).to_i
last_issue&.project_issues_index.present? ? last_issue.project_issues_index + deleted_issue_count : 0
end
def incre_project_issue_cache_delete_count(count=1)
$redis_cache.hincrby("issue_cache_delete_count", self.id, count)
end
def del_project_issue_cache_delete_count
$redis_cache.hdel("issue_cache_delete_count", self.id)
end
end

View File

@ -114,7 +114,7 @@ class User < Owner
# trustie: 来自Trustie平台
# forge: 平台本身注册的用户
# military: 军科的用户
enumerize :platform, in: [:forge, :educoder, :trustie, :military, :github, :gitee, :qq, :wechat], default: :forge, scope: :shallow
enumerize :platform, in: [:forge, :educoder, :trustie, :military, :github, :gitee, :qq, :wechat, :bot], default: :forge, scope: :shallow
belongs_to :laboratory, optional: true
has_one :user_extension, dependent: :destroy
@ -178,9 +178,15 @@ class User < Owner
has_many :user_trace_tasks, dependent: :destroy
has_many :feedbacks, dependent: :destroy
has_many :issue_assigners, foreign_key: :assigner_id
has_many :assigned_issues, through: :issue_assigners, source: :issue
has_many :issue_participants, foreign_key: :participant_id
has_many :participant_issues, through: :issue_participants, source: :issue
# Groups and active users
scope :active, lambda { where(status: [STATUS_ACTIVE, STATUS_EDIT_INFO]) }
scope :like, lambda { |keywords|
# 表情处理
keywords = keywords.to_s.each_char.select { |c| c.bytes.first < 240 }.join('')
sql = "CONCAT(lastname, firstname) LIKE :search OR nickname LIKE :search OR login LIKE :search OR mail LIKE :search OR nickname LIKE :search"
where(sql, :search => "%#{keywords.strip}%") unless keywords.blank?
}

View File

@ -28,6 +28,9 @@ class Version < ApplicationRecord
has_many :issues, class_name: "Issue", foreign_key: "fixed_version_id"
belongs_to :user, optional: true
has_many :opened_issues, -> {where(issue_classify: "issue").where.not(status_id: 5)}, class_name: "Issue", foreign_key: :fixed_version_id
has_many :closed_issues, -> {where(issue_classify: "issue", status_id: 5)}, class_name: "Issue", foreign_key: :fixed_version_id
scope :version_includes, ->{includes(:issues, :user)}
scope :closed, ->{where(status: 'closed')}
scope :opening, ->{where(status: 'open')}
@ -43,6 +46,11 @@ class Version < ApplicationRecord
after_create :send_create_message_to_notice_system
after_save :send_update_message_to_notice_system
def issue_percent
issues_total_count = opened_issues.size + closed_issues.size
issues_total_count.zero? ? 0.0 : closed_issues.size.to_f / issues_total_count
end
def version_user
User.select(:login, :lastname,:firstname, :nickname)&.find_by_id(self.user_id)
end
@ -53,6 +61,6 @@ class Version < ApplicationRecord
end
def send_update_message_to_notice_system
SendTemplateMessageJob.perform_later('ProjectMilestoneCompleted', self.id) if Site.has_notice_menu? && self.percent == 1.0
SendTemplateMessageJob.perform_later('ProjectMilestoneCompleted', self.id) if Site.has_notice_menu? && self.issue_percent == 1.0
end
end

View File

@ -0,0 +1,21 @@
class Admins::OrganizationQuery < ApplicationQuery
include CustomSortable
attr_reader :params
sort_columns :created_on, :last_login_on, :experience, :grade, default_by: :created_on, default_direction: :desc
def initialize(params)
@params = params
end
def call
orgs = Organization.all
# 关键字检索
keyword = params[:keyword].to_s.strip.presence
if keyword
sql = 'nickname LIKE :keyword OR login LIKE :keyword'
orgs = orgs.where(sql, keyword: "%#{keyword}%")
end
custom_sort(orgs, params[:sort_by], params[:sort_direction])
end
end

View File

@ -6,4 +6,53 @@ class ApplicationQuery
def strip_param(key)
params[key].to_s.strip.presence
end
# find all the repos that a user has tokens
def find_repo_with_token(user_id)
params = {
"request-type": "query user balance of all repos",
"username": user_id.to_s
}.to_json
resp_body = Blockchain::InvokeBlockchainApi.call(params)
if resp_body['status'] != 0
raise "区块链接口请求失败."
else
token_list = resp_body['UserBalanceList'].nil? ? [] : resp_body['UserBalanceList']
return token_list
end
end
# find one repo that a user has tokens
def find_one_balance(user_id, project_id)
# return 3 statuses: UnknownErr/ResUserNotExisted/Success
params = {
"request-type": "query user balance of single repo",
"username": user_id.to_s,
"token_name": project_id.to_s
}.to_json
resp_body = Blockchain::InvokeBlockchainApi.call(params)
if resp_body['status'] == 0
return resp_body['balance']
elsif resp_body['status'] == 100
return 0 # 找不到用户返回0
else
raise "区块链接口请求失败."
end
end
# query the basic info of a repository
def find_blockchain_repo_info(project_id)
params = {
"request-type": "query repo basic info",
"token_name": project_id.to_s
}.to_json
resp_body = Blockchain::InvokeBlockchainApi.call(params)
if resp_body['status'] == 0
return resp_body
else
raise "区块链接口请求失败."
end
end
end

View File

@ -0,0 +1,31 @@
class Blockchain::BalanceQuery < ApplicationQuery
attr_reader :params, :is_current_admin_user
def initialize(params,is_current_admin_user)
@params = params
@is_current_admin_user = is_current_admin_user
end
def call
if is_current_admin_user
token_list = find_repo_with_token(params["user_id"])
result_list = []
token_list.each do |t|
project = Project.find_by(id: t['token_name'].to_i)
if project.nil?
next
end
owner = User.find_by(id: project.user_id)
if owner.nil? || project.nil?
else
balance = t['balance']
result_list << [owner, project, balance]
end
end
results = {"status": 0, "projects": result_list}
else
results = {"status": 1} # query failed
end
end
end

View File

@ -0,0 +1,12 @@
class Blockchain::BalanceQueryOneProject < ApplicationQuery
attr_reader :params, :is_current_admin_user
def initialize(params)
@params = params
end
def call
find_one_balance(params[:user_id].to_s, params[:project_id].to_s)
end
end

View File

@ -0,0 +1,13 @@
class Blockchain::RepoBasicInfo < ApplicationQuery
attr_reader :params, :is_current_admin_user
def initialize(params)
@params = params
end
def call
info = find_blockchain_repo_info(params[:project_id].to_s)
return info
end
end

View File

@ -38,6 +38,12 @@ class Projects::ListMyQuery < ApplicationQuery
# projects = projects.visible.joins(:members).where(members: { user_id: user.id })
# elsif params[:category].to_s == "private"
# projects = projects.is_private.joins(:members).where(members: { user_id: user.id })
elsif params[:category].to_s == "blockchain_token" # 所有钱包中有token的项目有哪些
token_list = find_repo_with_token(user.id)
project_names = token_list.map { |x| x['token_name'] }
projects = projects.where(name: project_names)
tokens = token_list.map { |x| x['balance'] }
puts "pause"
end
if params[:is_public].present?
@ -52,8 +58,10 @@ class Projects::ListMyQuery < ApplicationQuery
elsif params[:project_type].to_s === "sync_mirror"
projects = projects.sync_mirror
end
q = projects.ransack(name_or_identifier_cont: params[:search])
# 表情处理
keywords = params[:search].to_s.each_char.select { |c| c.bytes.first < 240 }.join('')
q = projects.ransack(name_or_identifier_cont: keywords)
scope = q.result.includes(:project_category, :project_language,:owner, :repository, :has_pinned_users)

View File

@ -11,21 +11,19 @@ class Projects::ListQuery < ApplicationQuery
end
def call
collection = Project.visible
# 增加私有组织中项目过滤
collection = collection.joins("left join organization_extensions on organization_extensions.organization_id = projects.user_id")
.where("organization_extensions.visibility is null or organization_extensions.visibility in (0,1)")
.where("projects.user_id > 0")
collection = main_collection
collection = filter_projects(collection)
sort = params[:sort_by] || "updated_on"
sort_direction = params[:sort_direction] || "desc"
collection = optimize_sorting(collection, sort) if params[:category_id].present?
custom_sort(collection, sort, sort_direction)
# scope = scope.reorder("projects.#{sort} #{sort_direction}")
# scope
# 如果有搜索关键字根据ES搜索结果排序
if params[:search].present? && @ids.present?
collection.reorder(Arel.sql("FIELD(projects.id,#{@ids.join(',')})"))
else
custom_sort(collection, sort, sort_direction)
end
end
def filter_projects(collection)
@ -37,15 +35,27 @@ class Projects::ListQuery < ApplicationQuery
collection
end
def main_collection
collection = Project.visible
# 增加私有组织中项目过滤
collection = collection.joins("left join organization_extensions on organization_extensions.organization_id = projects.user_id")
.where("organization_extensions.visibility is null or organization_extensions.visibility in (0,1)")
.where("projects.user_id > 0")
collection
end
def by_search(items)
ids = Projects::ElasticsearchService.call(params[:search])
@ids = Projects::ElasticsearchService.call(params[:search])
items = items.where(platform: 'forge')
if ids.present?
items = items.where(id: ids).by_name_or_identifier(params[:search])
if @ids.present?
# items = items.where(id: @ids).by_name_or_identifier(params[:search])
items = items.where(id: @ids)
else
items = items.by_name_or_identifier(params[:search])
# 表情处理
keywords = params[:search].to_s.each_char.select { |c| c.bytes.first < 240 }.join('')
items = items.by_name_or_identifier(keywords).or(main_collection.where(user_id: Owner.like(keywords).pluck(:id)))
end
items.or(items.where(user_id: Owner.like(params[:search]).pluck(:id)))
items
end
def by_project_type(items)
@ -78,5 +88,14 @@ class Projects::ListQuery < ApplicationQuery
relations
end
end
def by_recommend(items)
params[:recommend].to_s == "true" ? items.where(recommend: true) : items
end
def exclude_fork(items)
return items if params[:exclude_forked].blank?
params[:exclude_forked].to_s == "true" ? items.where("forked_from_project_id is null") : items.where("forked_from_project_id is not null")
end
end

View File

@ -0,0 +1,19 @@
class Admins::DeleteOrganizationService < Gitea::ClientService
attr_reader :token, :name
def initialize(name)
@name = name
end
def call
Gitea::Organization::DeleteService.call(token,name)
end
private
def token
{
username: GiteaService.gitea_config[:access_key_id],
password: GiteaService.gitea_config[:access_key_secret]
}
end
end

View File

@ -0,0 +1,38 @@
class Api::V1::Issues::BatchDeleteService < ApplicationService
include ActiveModel::Model
attr_reader :project, :issues, :current_user
validates :project, :issues, :current_user, presence: true
def initialize(project, issues, current_user = nil)
@project = project
@issues = issues.includes(:assigners)
@current_user = current_user
end
def call
raise Error, errors.full_messages.join(", ") unless valid?
try_lock("Api::V1::Issues::DeleteService:#{project.id}") # 开始写数据,加锁
delete_issues
project.incre_project_issue_cache_delete_count(@issues.size)
if Site.has_notice_menu?
@issues.each do |issue|
SendTemplateMessageJob.perform_later('IssueDeleted', current_user.id, @issue&.subject, @issue.assigners.pluck(:id), @issue.author_id)
end
end
unlock("Api::V1::Issues::DeleteService:#{project.id}")
return true
end
private
def delete_issues
raise Error, "批量删除疑修失败!" unless @issues.destroy_all
end
end

View File

@ -0,0 +1,33 @@
class Api::V1::Issues::BatchUpdateService < ApplicationService
include ActiveModel::Model
include Api::V1::Issues::Concerns::Checkable
include Api::V1::Issues::Concerns::Loadable
attr_reader :project, :issues, :params, :current_user
attr_reader :status_id, :priority_id, :milestone_id
attr_reader :issue_tag_ids, :assigner_ids
validates :project, :issues, :current_user, presence: true
def initialize(project, issues, params, current_user = nil)
@project = project
@issues = issues
@params = params
@current_user = current_user
end
def call
raise Error, errors.full_messages.join(", ") unless valid?
ActiveRecord::Base.transaction do
@issues.each do |issue|
if issue.issue_classify == "issue"
Api::V1::Issues::UpdateService.call(project, issue, params, current_user)
end
end
return true
end
end
end

View File

@ -0,0 +1,48 @@
module Api::V1::Issues::Concerns::Checkable
def check_issue_status(status_id)
raise ApplicationService::Error, "IssueStatus不存在" unless IssueStatus.find_by_id(status_id).present?
end
def check_issue_priority(priority_id)
raise ApplicationService::Error, "IssuePriority不存在" unless IssuePriority.find_by_id(priority_id).present?
end
def check_milestone(milestone_id)
raise ApplicationService::Error, "Milestone不存在" unless Version.find_by_id(milestone_id).present?
end
def check_issue_tags(issue_tag_ids)
raise ApplicationService::Error, "请输入正确的标记ID数组" unless issue_tag_ids.is_a?(Array)
raise ApplicationService::Error, "最多可选择3个标记" if issue_tag_ids.size > 3
issue_tag_ids.each do |tid|
raise ApplicationService::Error, "请输入正确的标记ID" unless IssueTag.exists?(id: tid)
end
end
def check_assigners(assigner_ids)
raise ApplicationService::Error, "请输入正确的负责人ID数组" unless assigner_ids.is_a?(Array)
raise ApplicationService::Error, "最多可选择5个负责人" if assigner_ids.size > 5
assigner_ids.each do |aid|
raise ApplicationService::Error, "请输入正确的负责人ID" unless User.exists?(id: aid)
end
end
def check_attachments (attachment_ids)
raise ApplicationService::Error, "请输入正确的附件ID数组" unless attachment_ids.is_a?(Array)
attachment_ids.each do |aid|
raise ApplicationService::Error, "请输入正确的附件ID" unless Attachment.exists?(id: aid)
end
end
def check_atme_receivers(receivers_login)
raise ApplicationService::Error, "请输入正确的用户标识数组!" unless receivers_login.is_a?(Array)
receivers_login.each do |rlogin|
raise ApplicationService::Error, "请输入正确的用户标识!" unless User.exists?(login: rlogin)
end
end
def check_parent_journal(parent_id)
raise ApplicationService::Error, "ParentJournal不存在" unless Journal.find_by_id(parent_id).present?
end
end

View File

@ -0,0 +1,19 @@
module Api::V1::Issues::Concerns::Loadable
def load_assigners(assigner_ids)
@assigners = User.where(id: assigner_ids)
end
def load_issue_tags(issue_tag_ids)
@issue_tags = IssueTag.where(id: issue_tag_ids)
end
def load_attachments(attachment_ids)
@attachments = Attachment.where(id: attachment_ids)
end
def load_atme_receivers(receivers_login)
@atme_receivers = User.where(login: receivers_login)
end
end

View File

@ -0,0 +1,126 @@
class Api::V1::Issues::CreateService < ApplicationService
include ActiveModel::Model
include Api::V1::Issues::Concerns::Checkable
include Api::V1::Issues::Concerns::Loadable
attr_reader :project, :current_user
attr_reader :status_id, :priority_id, :milestone_id, :branch_name, :start_date, :due_date, :subject, :description
attr_reader :issue_tag_ids, :assigner_ids, :attachment_ids, :receivers_login
attr_accessor :created_issue
validates :subject, presence: true
validates :status_id, :priority_id, presence: true
validates :project, :current_user, presence: true
def initialize(project, params, current_user = nil)
@project = project
@current_user = current_user
@status_id = params[:status_id]
@priority_id = params[:priority_id]
@milestone_id = params[:milestone_id]
@branch_name = params[:branch_name]
@start_date = params[:start_date]
@due_date = params[:due_date]
@subject = params[:subject]
@description = params[:description]
@issue_tag_ids = params[:issue_tag_ids]
@assigner_ids = params[:assigner_ids]
@attachment_ids = params[:attachment_ids]
@receivers_login = params[:receivers_login]
end
def call
raise Error, errors.full_messages.join(", ") unless valid?
ActiveRecord::Base.transaction do
check_issue_status(status_id)
check_issue_priority(priority_id)
check_milestone(milestone_id) if milestone_id.present?
check_issue_tags(issue_tag_ids) unless issue_tag_ids.blank?
check_assigners(assigner_ids) unless assigner_ids.blank?
check_attachments(attachment_ids) unless attachment_ids.blank?
check_atme_receivers(receivers_login) unless receivers_login.blank?
load_assigners(assigner_ids) unless assigner_ids.blank?
load_attachments(attachment_ids) unless attachment_ids.blank?
load_issue_tags(issue_tag_ids) unless issue_tag_ids.blank?
load_atme_receivers(receivers_login) unless receivers_login.blank?
try_lock("Api::V1::Issues::CreateService:#{project.id}") # 开始写数据,加锁
@created_issue = Issue.new(issue_attributes)
build_author_participants
build_assigner_participants unless assigner_ids.blank?
build_atme_participants if @atme_receivers.present?
build_issue_journal_details
build_issue_project_trends
@created_issue.assigners = @assigners unless assigner_ids.blank?
@created_issue.attachments = @attachments unless attachment_ids.blank?
@created_issue.issue_tags = @issue_tags unless issue_tag_ids.blank?
@created_issue.issue_tags_value = @issue_tags.order("id asc").pluck(:id).join(",") unless issue_tag_ids.blank?
@created_issue.save!
project.del_project_issue_cache_delete_count # 把缓存里存储项目删除issue的个数清除掉
# @信息发送
AtmeService.call(current_user, @atme_receivers, @created_issue) unless receivers_login.blank?
# 发消息
if Site.has_notice_menu?
SendTemplateMessageJob.perform_later('IssueAssigned', current_user.id, @created_issue&.id, assigner_ids) unless assigner_ids.blank?
SendTemplateMessageJob.perform_later('ProjectIssue', current_user.id, @created_issue&.id)
end
unlock("Api::V1::Issues::CreateService:#{project.id}") # 结束写数据,解锁
end
return @created_issue
end
private
def issue_attributes
issue_attributes = {
subject: subject,
project_id: project.id,
author_id: current_user.id,
tracker_id: Tracker.first.id,
status_id: status_id,
priority_id: priority_id,
project_issues_index: (project.get_last_project_issues_index + 1),
issue_type: "1",
issue_classify: "issue"
}
issue_attributes.merge!({description: description}) if description.present?
issue_attributes.merge!({fixed_version_id: milestone_id}) if milestone_id.present?
issue_attributes.merge!({start_date: start_date}) if start_date.present?
issue_attributes.merge!({due_date: due_date}) if due_date.present?
issue_attributes.merge!({branch_name: branch_name}) if branch_name.present?
issue_attributes
end
def build_author_participants
@created_issue.issue_participants.new({participant_type: "authored", participant_id: current_user.id})
end
def build_assigner_participants
assigner_ids.each do |aid|
@created_issue.issue_participants.new({participant_type: "assigned", participant_id: aid})
end
end
def build_atme_participants
@atme_receivers.each do |receiver|
@created_issue.issue_participants.new({participant_type: "atme", participant_id: receiver.id})
end
end
def build_issue_project_trends
@created_issue.project_trends.new({user_id: current_user.id, project_id: @project.id, action_type: "create"})
@created_issue.project_trends.new({user_id: current_user.id, project_id: @project.id, action_type: ProjectTrend::CLOSE}) if status_id.to_i == 5
end
def build_issue_journal_details
journal = @created_issue.journals.new({user_id: current_user.id})
journal.journal_details.new({property: "issue", prop_key: 1, old_value: '', value: ''})
end
end

View File

@ -0,0 +1,37 @@
class Api::V1::Issues::DeleteService < ApplicationService
include ActiveModel::Model
attr_reader :project, :issue, :current_user
validates :project, :issue, :current_user, presence: true
def initialize(project, issue, current_user = nil)
@project = project
@issue = issue
@current_user = current_user
end
def call
raise Error, errors.full_messages.join(", ") unless valid?
try_lock("Api::V1::Issues::DeleteService:#{project.id}") # 开始写数据,加锁
delete_issue
project.incre_project_issue_cache_delete_count
if Site.has_notice_menu?
SendTemplateMessageJob.perform_later('IssueDeleted', current_user.id, @issue&.subject, @issue.assigners.pluck(:id), @issue.author_id)
end
unlock("Api::V1::Issues::DeleteService:#{project.id}")
return true
end
private
def delete_issue
raise Error, "删除疑修失败!" unless issue.destroy!
end
end

View File

@ -0,0 +1,42 @@
class Api::V1::Issues::Journals::ChildrenListService < ApplicationService
include ActiveModel::Model
attr_reader :issue, :journal, :keyword, :sort_by, :sort_direction
attr_accessor :queried_journals
validates :sort_by, inclusion: {in: Journal.column_names, message: '请输入正确的SortBy'}, allow_blank: true
validates :sort_direction, inclusion: {in: %w(asc desc), message: '请输入正确的SortDirection'}, allow_blank: true
def initialize(issue, journal, params, current_user=nil)
@issue = issue
@journal = journal
@keyword = params[:keyword]
@sort_by = params[:sort_by].present? ? params[:sort_by] : 'created_on'
@sort_direction = (params[:sort_direction].present? ? params[:sort_direction] : 'asc').downcase
end
def call
raise Error, errors.full_messages.join(", ") unless valid?
begin
journal_query_data
return @queried_journals
rescue
raise Error, "服务器错误,请联系系统管理员!"
end
end
private
def journal_query_data
@queried_journals = journal.children_journals
@queried_journals = @queried_journals.ransack(notes_cont: keyword).result if keyword.present?
@queried_journals = @queried_journals.includes(:user, :attachments, :reply_journal)
@queried_journals = @queried_journals.reorder("journals.#{sort_by} #{sort_direction}").distinct
@queried_journals
end
end

View File

@ -0,0 +1,75 @@
class Api::V1::Issues::Journals::CreateService < ApplicationService
include ActiveModel::Model
include Api::V1::Issues::Concerns::Checkable
include Api::V1::Issues::Concerns::Loadable
attr_reader :issue, :current_user, :notes, :parent_id, :reply_id, :attachment_ids, :receivers_login
attr_accessor :created_journal, :atme_receivers
validates :notes, :issue, :current_user, presence: true
def initialize(issue, params, current_user=nil)
@issue = issue
@notes = params[:notes]
@parent_id = params[:parent_id]
@reply_id = params[:reply_id]
@attachment_ids = params[:attachment_ids]
@receivers_login = params[:receivers_login]
@current_user = current_user
end
def call
raise Error, errors.full_messages.join(", ") unless valid?
raise Error, "请输入回复的评论ID" if parent_id.present? && !reply_id.present?
ActiveRecord::Base.transaction do
check_parent_journal(parent_id) if parent_id.present?
check_parent_journal(reply_id) if reply_id.present?
check_attachments(attachment_ids) unless attachment_ids.nil?
check_atme_receivers(receivers_login) unless receivers_login.nil?
load_attachments(attachment_ids) unless attachment_ids.nil?
load_atme_receivers(receivers_login) unless receivers_login.nil?
try_lock("Api::V1::Issues::Journals::CreateService:#{@issue.id}")
@created_journal = @issue.journals.new(journal_attributes)
build_comment_participants
build_atme_participants if @atme_receivers.present?
@created_journal.attachments = @attachments unless attachment_ids.blank?
@created_journal.save!
@issue.save!
# @信息发送
AtmeService.call(current_user, @atme_receivers, @created_journal) unless receivers_login.blank?
unlock("Api::V1::Issues::Journals::CreateService:#{@issue.id}")
@created_journal
end
end
private
def journal_attributes
journal_attributes = {
notes: notes,
user_id: current_user.id
}
journal_attributes.merge!({parent_id: parent_id}) if parent_id.present?
journal_attributes.merge!({reply_id: reply_id}) if reply_id.present?
journal_attributes
end
def build_comment_participants
@issue.issue_participants.new({participant_type: "commented", participant_id: current_user.id}) unless @issue.issue_participants.exists?(participant_type: "commented", participant_id: current_user.id)
end
def build_atme_participants
@atme_receivers.each do |receiver|
next if @issue.issue_participants.exists?(participant_type: "atme", participant_id: receiver.id)
@issue.issue_participants.new({participant_type: "atme", participant_id: receiver.id})
end
end
end

View File

@ -0,0 +1,56 @@
class Api::V1::Issues::Journals::ListService < ApplicationService
include ActiveModel::Model
attr_reader :issue, :category, :keyword, :sort_by, :sort_direction
attr_accessor :queried_journals, :total_journals_count, :total_operate_journals_count, :total_comment_journals_count
validates :category, inclusion: {in: %w(all comment operate), message: "请输入正确的Category"}
validates :sort_by, inclusion: {in: Journal.column_names, message: '请输入正确的SortBy'}, allow_blank: true
validates :sort_direction, inclusion: {in: %w(asc desc), message: '请输入正确的SortDirection'}, allow_blank: true
def initialize(issue, params, current_user=nil)
@issue = issue
@keyword = params[:keyword]
@category = params[:category] || 'all'
@sort_by = params[:sort_by].present? ? params[:sort_by] : 'created_on'
@sort_direction = (params[:sort_direction].present? ? params[:sort_direction] : 'asc').downcase
end
def call
raise Error, errors.full_messages.join(", ") unless valid?
begin
journal_query_data
{data: @queried_journals, total_journals_count: @total_journals_count, total_operate_journals_count: total_operate_journals_count, total_comment_journals_count: total_comment_journals_count}
rescue
raise Error, "服务器错误,请联系系统管理员!"
end
end
private
def journal_query_data
@queried_journals = issue.journals
@queried_journals = @queried_journals.parent_journals
@queried_journals = @queried_journals.ransack(notes_cont: keyword).result if keyword.present?
@total_journals_count = queried_journals.distinct.size
@total_operate_journals_count = @queried_journals.where(notes: nil).distinct.size
@total_comment_journals_count = @queried_journals.where.not(notes: nil).distinct.size
case category
when 'comment'
@queried_journals = @queried_journals.where.not(notes: nil)
when 'operate'
@queried_journals = @queried_journals.where(notes: nil)
end
@queried_journals = @queried_journals.includes(:journal_details, :user, :attachments, first_ten_children_journals: [:parent_journal, :reply_journal])
@queried_journals = @queried_journals.reorder("journals.#{sort_by} #{sort_direction}").distinct
@queried_journals
end
end

View File

@ -0,0 +1,57 @@
class Api::V1::Issues::Journals::UpdateService < ApplicationService
include ActiveModel::Model
include Api::V1::Issues::Concerns::Checkable
include Api::V1::Issues::Concerns::Loadable
attr_reader :issue, :journal, :current_user, :notes, :attachment_ids, :receivers_login
attr_accessor :updated_journal, :atme_receivers
validates :notes, :issue, :journal, :current_user, presence: true
def initialize(issue, journal, params, current_user=nil)
@issue = issue
@journal = journal
@notes = params[:notes]
@attachment_ids = params[:attachment_ids]
@receivers_login = params[:receivers_login]
@current_user = current_user
end
def call
raise Error, errors.full_messages.join(", ") unless valid?
ActiveRecord::Base.transaction do
check_attachments(attachment_ids) unless attachment_ids.nil?
check_atme_receivers(receivers_login) unless receivers_login.nil?
load_attachments(attachment_ids) unless attachment_ids.nil?
load_atme_receivers(receivers_login) unless receivers_login.nil?
try_lock("Api::V1::Issues::Journals::UpdateService:#{@issue.id}:#{@journal.id}")
@updated_journal = @journal
@updated_journal.notes = notes
build_atme_participants if @atme_receivers.present?
@updated_journal.attachments = @attachments unless attachment_ids.nil?
@updated_journal.save!
@issue.save!
# @信息发送
AtmeService.call(current_user, @atme_receivers, @created_journal) unless receivers_login.blank?
unlock("Api::V1::Issues::Journals::UpdateService:#{@issue.id}:#{@journal.id}")
@updated_journal
end
end
private
def build_atme_participants
@atme_receivers.each do |receiver|
next if @issue.issue_participants.exists?(participant_type: "atme", participant_id: receiver.id)
@issue.issue_participants.new({participant_type: "atme", participant_id: receiver.id})
end
end
end

View File

@ -0,0 +1,91 @@
class Api::V1::Issues::ListService < ApplicationService
include ActiveModel::Model
attr_reader :project, :category, :participant_category, :keyword, :author_id, :issue_tag_ids
attr_reader :milestone_id, :assigner_id, :status_id, :sort_by, :sort_direction, :current_user
attr_accessor :queried_issues, :total_issues_count, :closed_issues_count, :opened_issues_count
validates :category, inclusion: {in: %w(all opened closed), message: "请输入正确的Category"}
validates :participant_category, inclusion: {in: %w(all aboutme authoredme assignedme atme), message: "请输入正确的ParticipantCategory"}
validates :sort_by, inclusion: {in: ['issues.created_on', 'issues.updated_on', 'issue_priorities.position'], message: '请输入正确的SortBy'}, allow_blank: true
validates :sort_direction, inclusion: {in: %w(asc desc), message: '请输入正确的SortDirection'}, allow_blank: true
validates :current_user, presence: true
def initialize(project, params, current_user=nil)
@project = project
@category = params[:category] || 'all'
@participant_category = params[:participant_category] || 'all'
@keyword = params[:keyword]
@author_id = params[:author_id]
@issue_tag_ids = params[:issue_tag_ids].present? ? params[:issue_tag_ids].split(",") : []
@milestone_id = params[:milestone_id]
@assigner_id = params[:assigner_id]
@status_id = params[:status_id]
@sort_by = params[:sort_by].present? ? params[:sort_by] : 'issues.updated_on'
@sort_direction = (params[:sort_direction].present? ? params[:sort_direction] : 'desc').downcase
@current_user = current_user
end
def call
raise Error, errors.full_messages.join(", ") unless valid?
# begin
issue_query_data
return {data: queried_issues, total_issues_count: @total_issues_count, closed_issues_count: @closed_issues_count, opened_issues_count: @opened_issues_count}
# rescue
# raise Error, "服务器错误,请联系系统管理员!"
# end
end
private
def issue_query_data
issues = @project.issues.issue_issue
case participant_category
when 'aboutme' # 关于我的
issues = issues.joins(:issue_participants).where(issue_participants: {participant_type: %w(authored assigned atme), participant_id: current_user&.id})
when 'authoredme' # 我创建的
issues = issues.joins(:issue_participants).where(issue_participants: {participant_type: 'authored', participant_id: current_user&.id})
when 'assignedme' # 我负责的
issues = issues.joins(:issue_participants).where(issue_participants: {participant_type: 'assigned', participant_id: current_user&.id})
when 'atme' # @我的
issues = issues.joins(:issue_participants).where(issue_participants: {participant_type: 'atme', participant_id: current_user&.id})
end
# author_id
issues = issues.where(author_id: author_id) if author_id.present?
# issue_tag_ids
issues = issues.ransack(issue_tags_value_cont: issue_tag_ids.sort!.join(",")).result unless issue_tag_ids.blank?
# milestone_id
issues = issues.where(fixed_version_id: milestone_id) if milestone_id.present?
# assigner_id
issues = issues.joins(:assigners).where(users: {id: assigner_id}) if assigner_id.present?
# status_id
issues = issues.where(status_id: status_id) if status_id.present?
# keyword
issues = issues.ransack(subject_or_description_cont: keyword).result if keyword.present?
@total_issues_count = issues.distinct.size
@closed_issues_count = issues.closed.distinct.size
@opened_issues_count = issues.opened.distinct.size
case category
when 'closed'
issues = issues.closed
when 'opened'
issues = issues.opened
end
scope = issues.includes(:priority, :issue_status, :user, :show_assigners, :show_issue_tags, :version, :comment_journals)
scope = scope.reorder("#{sort_by} #{sort_direction}").distinct
@queried_issues = scope
end
end

View File

@ -0,0 +1,65 @@
class Api::V1::Issues::Milestones::DetailIssuesService < ApplicationService
include ActiveModel::Model
attr_reader :project, :category, :author_id, :assigner_id, :issue_tag_ids, :sort_by, :sort_direction, :current_user
attr_accessor :queried_issues, :total_issues_count, :closed_issues_count, :opened_issues_count
validates :category, inclusion: {in: %w(all opened closed), message: "请输入正确的Category"}
validates :sort_by, inclusion: {in: ['issues.created_on', 'issues.updated_on', 'issue_priorities.position'], message: '请输入正确的SortBy'}, allow_blank: true
validates :sort_direction, inclusion: {in: %w(asc desc), message: '请输入正确的SortDirection'}, allow_blank: true
validates :current_user, presence: true
def initialize(project, milestone, params, current_user=nil)
@project = project
@milestone = milestone
@category = params[:category] || 'all'
@author_id = params[:author_id]
@assigner_id = params[:assigner_id]
@issue_tag_ids = params[:issue_tag_ids].present? ? params[:issue_tag_ids].split(",") : []
@sort_by = params[:sort_by].present? ? params[:sort_by] : 'issues.updated_on'
@sort_direction = (params[:sort_direction].present? ? params[:sort_direction] : 'desc').downcase
@current_user = current_user
end
def call
raise Error, errors.full_messages.join(", ") unless valid?
begin
issue_query_data
return {data: queried_issues, total_issues_count: @total_issues_count, closed_issues_count: @closed_issues_count, opened_issues_count: @opened_issues_count}
rescue
raise Error, "服务器错误,请联系系统管理员!"
end
end
private
def issue_query_data
issues = @milestone.issues.issue_issue
# author_id
issues = issues.where(author_id: author_id) if author_id.present?
# assigner_id
issues = issues.joins(:assigners).where(users: {id: assigner_id}).or(issues.joins(:assigners).where(assigned_to_id: assigner_id)) if assigner_id.present?
issues = issues.ransack(issue_tags_value_cont: issue_tag_ids.sort!.join(",")).result unless issue_tag_ids.blank?
@total_issues_count = issues.distinct.size
@closed_issues_count = issues.closed.distinct.size
@opened_issues_count = issues.opened.distinct.size
case category
when 'closed'
issues = issues.closed
when 'opened'
issues = issues.opened
end
scope = issues.includes(:priority, :issue_status, :user, :show_assigners, :version, :show_issue_tags, :comment_journals).references(:assigners)
scope = scope.reorder("#{sort_by} #{sort_direction}").distinct
@queried_issues = scope
end
end

View File

@ -0,0 +1,243 @@
class Api::V1::Issues::UpdateService < ApplicationService
include ActiveModel::Model
include Api::V1::Issues::Concerns::Checkable
include Api::V1::Issues::Concerns::Loadable
attr_reader :project, :issue, :current_user
attr_reader :status_id, :priority_id, :milestone_id, :branch_name, :start_date, :due_date, :subject, :description
attr_reader :issue_tag_ids, :assigner_ids, :attachment_ids, :receivers_login
attr_accessor :add_assigner_ids, :previous_issue_changes, :updated_issue, :atme_receivers
validates :project, :issue, :current_user, presence: true
def initialize(project, issue, params, current_user = nil)
@project = project
@issue = issue
@current_user = current_user
@status_id = params[:status_id]
@priority_id = params[:priority_id]
@milestone_id = params[:milestone_id]
@branch_name = params[:branch_name]
@start_date = params[:start_date]
@due_date = params[:due_date]
@subject = params[:subject]
@description = params[:description]
@issue_tag_ids = params[:issue_tag_ids]
@assigner_ids = params[:assigner_ids]
@attachment_ids = params[:attachment_ids]
@receivers_login = params[:receivers_login]
@add_assigner_ids = []
@previous_issue_changes = {}
end
def call
raise Error, errors.full_messages.join(", ") unless valid?
ActiveRecord::Base.transaction do
check_issue_status(status_id) if status_id.present?
check_issue_priority(priority_id) if priority_id.present?
check_milestone(milestone_id) if milestone_id.present?
check_issue_tags(issue_tag_ids) unless issue_tag_ids.nil?
check_assigners(assigner_ids) unless assigner_ids.nil?
check_attachments(attachment_ids) unless attachment_ids.nil?
check_atme_receivers(receivers_login) unless receivers_login.nil?
load_assigners(assigner_ids)
load_attachments(attachment_ids)
load_issue_tags(issue_tag_ids)
load_atme_receivers(receivers_login) unless receivers_login.nil?
try_lock("Api::V1::Issues::UpdateService:#{project.id}:#{issue.id}")
@updated_issue = @issue
issue_load_attributes
build_assigner_issue_journal_details unless assigner_ids.nil?# 操作记录
build_attachment_issue_journal_details unless attachment_ids.nil?
build_issue_tag_issue_journal_details unless issue_tag_ids.nil?
build_issue_project_trends if status_id.present? # 开关时间记录
build_assigner_participants unless assigner_ids.nil? # 负责人
build_edit_participants
build_atme_participants if @atme_receivers.present?
unless assigner_ids.nil?
@previous_issue_changes.merge!(assigned_to_id: [@updated_issue.assigners.pluck(:id), @assigners.pluck(:id)])
@updated_issue.assigners = @assigners || User.none
end
@updated_issue.attachments = @attachments || Attachment.none unless attachment_ids.nil?
@updated_issue.issue_tags_relates.destroy_all & @updated_issue.issue_tags = @issue_tags || IssueTag.none unless issue_tag_ids.nil?
@updated_issue.issue_tags_value = @issue_tags.order("id asc").pluck(:id).join(",") unless issue_tag_ids.nil?
@updated_issue.updated_on = Time.now
@updated_issue.save!
build_after_issue_journal_details if @updated_issue.previous_changes.present? # 操作记录
build_previous_issue_changes
# @信息发送
AtmeService.call(current_user, @atme_receivers, @issue) unless receivers_login.blank?
# 消息发送
if Site.has_notice_menu?
SendTemplateMessageJob.perform_later('IssueChanged', current_user.id, @issue&.id, previous_issue_changes) unless previous_issue_changes.blank?
SendTemplateMessageJob.perform_later('IssueAssigned', current_user.id, @issue&.id, add_assigner_ids) unless add_assigner_ids.blank?
end
unlock("Api::V1::Issues::UpdateService:#{project.id}:#{issue.id}")
return @updated_issue
end
end
private
def issue_load_attributes
@updated_issue.status_id = status_id if status_id.present?
@updated_issue.priority_id = priority_id if priority_id.present?
@updated_issue.fixed_version_id = milestone_id unless milestone_id.nil?
@updated_issue.branch_name = branch_name unless branch_name.nil?
@updated_issue.start_date = start_date unless start_date.nil?
@updated_issue.due_date = due_date unless due_date.nil?
@updated_issue.subject = subject if subject.present?
@updated_issue.description = description unless description.nil?
end
def build_assigner_participants
if assigner_ids.blank?
@updated_issue.issue_participants.where(participant_type: "assigned").each(&:destroy!)
else
@updated_issue.issue_participants.where(participant_type: "assigned").where.not(participant_id: assigner_ids).each(&:destroy!)
assigner_ids.each do |aid|
next if @updated_issue.issue_participants.exists?(participant_type: "assigned", participant_id: aid)
@updated_issue.issue_participants.new({participant_type: "assigned", participant_id: aid})
@add_assigner_ids << aid
end
end
end
def build_edit_participants
@updated_issue.issue_participants.new({participant_type: "edited", participant_id: current_user.id}) unless @updated_issue.issue_participants.exists?(participant_type: "edited", participant_id: current_user.id)
end
def build_atme_participants
@atme_receivers.each do |receiver|
next if @updated_issue.issue_participants.exists?(participant_type: "atme", participant_id: receiver.id)
@updated_issue.issue_participants.new({participant_type: "atme", participant_id: receiver.id})
end
end
def build_previous_issue_changes
@previous_issue_changes.merge!(@updated_issue.previous_changes.slice("status_id", "priority_id", "fixed_version_id", "issue_tags_value", "branch_name").symbolize_keys)
if @updated_issue.previous_changes[:start_date].present?
@previous_issue_changes.merge!(start_date: [@updated_issue.previous_changes[:start_date][0].to_s, @updated_issue.previous_changes[:start_date][1].to_s])
end
if @updated_issue.previous_changes[:due_date].present?
@previous_issue_changes.merge!(due_date: [@updated_issue.previous_changes[:due_date][0].to_s, @updated_issue.previous_changes[:due_date][1].to_s])
end
end
def build_issue_project_trends
if @updated_issue.previous_changes["status_id"].present? && @updated_issue.previous_changes["status_id"][1] == 5
@updated_issue.project_trends.new({user_id: current_user.id, project_id: @project.id, action_type: ProjectTrend::CLOSE})
end
if @updated_issue.previous_changes["status_id"].present? && @updated_issue.previous_changes["status_id"][0] == 5
@updated_issue.project_trends.where(action_type: ProjectTrend::CLOSE).each(&:destroy!)
end
end
def build_after_issue_journal_details
begin
# 更改标题
if @updated_issue.previous_changes["subject"].present?
journal = @updated_issue.journals.create!({user_id: current_user.id})
journal.journal_details.create!({property: "attr", prop_key: "subject", old_value: @updated_issue.previous_changes["subject"][0], value: @updated_issue.previous_changes["subject"][1]})
end
# 更改描述
if @updated_issue.previous_changes["description"].present?
journal = @updated_issue.journals.create!({user_id: current_user.id})
journal.journal_details.create!({property: "attr", prop_key: "description", old_value: @updated_issue.previous_changes["description"][0], value: @updated_issue.previous_changes["description"][1]})
end
# 修改状态
if @updated_issue.previous_changes["status_id"].present?
journal = @updated_issue.journals.create!({user_id: current_user.id})
journal.journal_details.create!({property: "attr", prop_key: "status_id", old_value: @updated_issue.previous_changes["status_id"][0], value: @updated_issue.previous_changes["status_id"][1]})
end
# 修改优先级
if @updated_issue.previous_changes["priority_id"].present?
journal = @updated_issue.journals.create!({user_id: current_user.id})
journal.journal_details.create!({property: "attr", prop_key: "priority_id", old_value: @updated_issue.previous_changes["priority_id"][0], value: @updated_issue.previous_changes["priority_id"][1]})
end
# 修改里程碑
if @updated_issue.previous_changes["fixed_version_id"].present?
journal = @updated_issue.journals.create!({user_id: current_user.id})
journal.journal_details.create!({property: "attr", prop_key: "fixed_version_id", old_value: @updated_issue.previous_changes["fixed_version_id"][0], value: @updated_issue.previous_changes["fixed_version_id"][1]})
end
# 更改分支
if @updated_issue.previous_changes["branch_name"].present?
journal = @updated_issue.journals.create!({user_id: current_user.id})
journal.journal_details.create!({property: "attr", prop_key: "branch_name", old_value: @updated_issue.previous_changes["branch_name"][0], value: @updated_issue.previous_changes["branch_name"][1]})
end
# 更改开始时间
if @updated_issue.previous_changes["start_date"].present?
journal = @updated_issue.journals.create!({user_id: current_user.id})
journal.journal_details.create!({property: "attr", prop_key: "start_date", old_value: @updated_issue.previous_changes["start_date"][0], value: @updated_issue.previous_changes["start_date"][1]})
end
# 更改结束时间
if @updated_issue.previous_changes["due_date"].present?
journal = @updated_issue.journals.create!({user_id: current_user.id})
journal.journal_details.create!({property: "attr", prop_key: "due_date", old_value: @updated_issue.previous_changes["due_date"][0], value: @updated_issue.previous_changes["due_date"][1]})
end
rescue
raise Error, "创建操作记录失败!"
end
end
def build_assigner_issue_journal_details
begin
# 更改负责人
new_assigner_ids = @assigner_ids
new_assigner_ids = [] if @assigner_ids.nil?
now_assigner_ids = @updated_issue.assigners.pluck(:id)
if !(now_assigner_ids & assigner_ids).empty? || !(now_assigner_ids.empty? && new_assigner_ids.empty?)
journal = @updated_issue.journals.create!({user_id: current_user.id})
journal.journal_details.create!({property: "assigner", prop_key: "#{new_assigner_ids.size}", old_value: now_assigner_ids.join(","), value: new_assigner_ids.join(",")})
end
rescue
raise Error, "创建操作记录失败!"
end
end
def build_issue_tag_issue_journal_details
begin
# 更改标记
new_issue_tag_ids = @issue_tag_ids
new_issue_tag_ids = [] if @issue_tag_ids.nil?
now_issue_tag_ids = @updated_issue.issue_tags.pluck(:id)
if !(now_issue_tag_ids & new_issue_tag_ids).empty? || !(now_issue_tag_ids.empty? && new_issue_tag_ids.empty?)
journal = @updated_issue.journals.create!({user_id: current_user.id})
journal.journal_details.create!({property: "issue_tag", prop_key: "#{new_issue_tag_ids.size}", old_value: now_issue_tag_ids.join(","), value: new_issue_tag_ids.join(",")})
end
rescue
raise Error, "创建操作记录失败!"
end
end
def build_attachment_issue_journal_details
begin
# 更改附件
new_attachment_ids = @attachment_ids
new_attachment_ids = [] if @attachment_ids.nil?
now_attachment_ids = @updated_issue.attachments.pluck(:id)
if !(now_attachment_ids & new_attachment_ids).empty? || !(now_attachment_ids.empty? && new_attachment_ids.empty?)
journal = @updated_issue.journals.create!({user_id: current_user.id})
journal.journal_details.create!({property: "attachment", prop_key: "#{new_attachment_ids.size}", old_value: now_attachment_ids.join(","), value: new_attachment_ids.join(",")})
end
rescue
raise Error, "创建操作记录失败!"
end
end
end

View File

@ -18,7 +18,7 @@ class Api::V1::Projects::Branches::CreateService < ApplicationService
def call
raise Error, errors.full_messages.join(",") unless valid?
check_new_branch_exist
check_branch_exist
excute_data_to_gitea
gitea_data
@ -43,9 +43,10 @@ class Api::V1::Projects::Branches::CreateService < ApplicationService
raise Error, '创建分支失败!' unless @gitea_data.is_a?(Hash)
end
def check_new_branch_exist
def check_branch_exist
result = $gitea_client.get_repos_branch_name_set_by_owner_repo(owner, repo, {query: request_params}) rescue nil
raise Error, '查询分支名称失败!' unless result.is_a?(Hash)
raise Error, '分支已存在!' if result['branch_name'].include?(@new_branch_name)
raise Error, '旧分支不存在!' if !result['branch_name'].include?(@old_branch_name)
raise Error, '新分支已存在!' if result['branch_name'].include?(@new_branch_name)
end
end

View File

@ -0,0 +1,47 @@
class Api::V1::Projects::Branches::DeleteService < ApplicationService
include ActiveModel::Model
attr_accessor :project, :token, :owner, :repo, :branch_name
attr_accessor :gitea_data
validates :branch_name, presence: true
def initialize(project, branch_name, token=nil)
@project = project
@owner = project&.owner.login
@repo = project&.identifier
@branch_name = branch_name
@token = token
end
def call
raise Error, errors.full_messages.join(",") unless valid?
check_branch_exist
excute_data_to_gitea
true
end
private
def request_params
{
access_token: token
}
end
def excute_data_to_gitea
begin
@gitea_data = $gitea_client.delete_repos_branches_by_owner_repo_branch(owner, repo, branch_name, {query: request_params})
rescue => e
raise Error, '保护分支无法删除!' if e.to_s.include?("branch protected")
raise Error, '删除分支失败!'
end
end
def check_branch_exist
result = $gitea_client.get_repos_branch_name_set_by_owner_repo(owner, repo, {query: request_params}) rescue nil
raise Error, '查询分支名称失败!' unless result.is_a?(Hash)
raise Error, '分支不存在!' if !result['branch_name'].include?(@branch_name)
end
end

View File

@ -0,0 +1,40 @@
class Api::V1::Projects::Branches::ListService < ApplicationService
attr_accessor :project, :token, :owner, :repo, :name, :page, :limit
attr_accessor :gitea_data
def initialize(project, params, token=nil)
@project = project
@owner = project&.owner.login
@repo = project&.identifier
@token = token
@name = params[:name]
@page = params[:page]
@limit = params[:limit]
end
def call
load_gitea_data
gitea_data
end
private
def request_params
params = {
access_token: token,
page: page,
limit: limit
}
params.merge!({name: name}) if name.present?
params
end
def load_gitea_data
puts request_params
@gitea_data = $gitea_client.get_repos_branches_by_owner_repo(owner, repo, {query: request_params}) rescue nil
puts @gitea_data
raise Error, '获取分支列表失败!' unless @gitea_data.is_a?(Hash)
end
end

View File

@ -0,0 +1,55 @@
class Api::V1::Projects::Branches::UpdateDefaultBranchService < ApplicationService
include ActiveModel::Model
attr_accessor :project, :token, :owner, :repo, :new_default_branch
attr_accessor :gitea_data
validates :new_default_branch, presence: true
def initialize(project, new_default_branch, token=nil)
@project = project
@owner = project&.owner.login
@repo = project&.identifier
@new_default_branch = new_default_branch
@token = token
end
def call
raise Error, errors.full_messages.join(",") unless valid?
check_branch_exist
update_project
excute_data_to_gitea
gitea_data
end
private
def request_params
{
access_token: token
}
end
def request_body
{
default_branch: new_default_branch
}
end
def update_project
@project.attributes = request_body
raise Error, '更新默认分支失败!' unless @project.save
end
def excute_data_to_gitea
@gitea_data = $gitea_client.patch_repos_by_owner_repo(owner, repo, {body: request_body.to_json, query: request_params}) rescue nil
raise Error, '更新默认分支失败!' unless @gitea_data.is_a?(Hash)
end
def check_branch_exist
result = $gitea_client.get_repos_branch_name_set_by_owner_repo(owner, repo, {query: request_params}) rescue nil
raise Error, '查询分支名称失败!' unless result.is_a?(Hash)
raise Error, '新默认分支不存在!' if !result['branch_name'].include?(@new_default_branch)
end
end

View File

@ -1,6 +1,6 @@
class Api::V1::Projects::CodeStats::ListService < ApplicationService
attr_reader :project, :ref, :owner, :repo, :token
attr_reader :project, :ref, :owner, :repo, :token, :page, :limit
attr_accessor :gitea_data
def initialize(project, params, token=nil)

View File

@ -0,0 +1,47 @@
class Api::V1::Projects::Tags::DeleteService < ApplicationService
include ActiveModel::Model
attr_accessor :project, :token, :owner, :repo, :tag_name
attr_accessor :gitea_data
validates :tag_name, presence: true
def initialize(project, tag_name, token=nil)
@project = project
@owner = project&.owner.login
@repo = project&.identifier
@tag_name = tag_name
@token = token
end
def call
raise Error, errors.full_messages.join(",") unless valid?
check_tag_exist
excute_data_to_gitea
true
end
private
def request_params
{
access_token: token
}
end
def excute_data_to_gitea
begin
@gitea_data = $gitea_client.delete_repos_tags_by_owner_repo_tag(owner, repo, tag_name, {query: request_params})
rescue => e
raise Error, '请先删除发行版!' if e.to_s.include?("409")
raise Error, '删除标签失败!'
end
end
def check_tag_exist
result = $gitea_client.get_repos_tag_name_set_by_owner_repo(owner, repo, {query: request_params}) rescue nil
raise Error, '查询标签名称失败!' unless result.is_a?(Array)
raise Error, '标签不存在!' if !result.include?(@tag_name)
end
end

View File

@ -0,0 +1,36 @@
class Api::V1::Projects::Tags::ListService < ApplicationService
attr_accessor :project, :token, :owner, :repo, :page, :limit
attr_accessor :gitea_data
def initialize(project, params, token=nil)
@project = project
@owner = project&.owner.login
@repo = project&.identifier
@token = token
@page = params[:page]
@limit = params[:limit]
end
def call
load_gitea_data
gitea_data
end
private
def request_params
params = {
access_token: token,
page: page,
limit: limit
}
params
end
def load_gitea_data
@gitea_data = $gitea_client.get_repos_tags_by_owner_repo(owner, repo, {query: request_params}) rescue nil
raise Error, '获取标签列表失败!' unless @gitea_data.is_a?(Hash)
end
end

View File

@ -73,8 +73,10 @@ class Api::V1::Users::Projects::ListService < ApplicationService
end
projects = projects.with_project_type(project_type)
projects = projects.with_project_type(project_type)
# 表情处理
search = search.to_s.each_char.select { |c| c.bytes.first < 240 }.join('')
q = projects.ransack(name_or_identifier_cont: search)
scope = q.result.includes(:project_category, :project_language,:owner, :repository, :has_pinned_users)

View File

@ -31,6 +31,7 @@ class Api::V1::Users::UpdateEmailService < ApplicationService
change_user_email
excute_data_to_gitea
excute_change_email_from_gitea
remove_old_cache_for_user
end
return gitea_data
@ -68,4 +69,8 @@ class Api::V1::Users::UpdateEmailService < ApplicationService
$gitea_client.delete_user_emails({body: {emails: [@old_mail]}.to_json, query: request_params})
$gitea_client.post_user_emails({body: {emails: [@mail]}.to_json, query: request_params})
end
def remove_old_cache_for_user
$redis_cache.del("v2-owner-common:#{@user.login}-#{@old_mail}")
end
end

View File

@ -1,26 +1,110 @@
class ApplicationService
include Callable
Error = Class.new(StandardError)
def regix_emoji content
" " if content.blank?
regex = /[^a-zA-Z0-9\u4E00-\u9FFF]/
content.gsub(regex, '')
end
private
def strip(str)
str.to_s.strip.presence
end
def str_to_boolean str
ActiveModel::Type::Boolean.new.cast str
end
def phone_mail_type value
value =~ /^1\d{10}$/ ? 1 : 0
end
end
class ApplicationService
include Callable
Error = Class.new(StandardError)
def regix_emoji content
" " if content.blank?
regex = /[^a-zA-Z0-9\u4E00-\u9FFF]/
content.gsub(regex, '')
end
protected
def try_lock(key)
raise Error, "请稍后再试!" unless $redis_cache.set(key, 1, nx: true, ex: 60.seconds)
end
def unlock(key)
$redis_cache.del(key)
end
private
def strip(str)
str.to_s.strip.presence
end
def str_to_boolean str
ActiveModel::Type::Boolean.new.cast str
end
# params: params from index.js page
def create_repo_on_blockchain(params, project)
username = params['user_id'].to_s
token_name = project.id.to_s
total_supply = params['blockchain_token_all'].to_i
# token_balance = [[username, (total_supply * params['blockchain_init_token'].to_i / 100).to_i]]
token_balance = [[username, params['blockchain_init_token'].to_i]]
params = {
"request-type": "create repo",
"username": username,
"token_name": token_name,
"total_supply": total_supply,
"token_balance": token_balance
}.to_json
resp_body = Blockchain::InvokeBlockchainApi.call(params)
if resp_body['status'] != 0
raise "区块链接口请求失败."
end
end
def transfer_balance_on_blockchain(payer, payee, token_name, amount)
params = {
"request-type": "transfer amount",
"payer": payer,
"payee": payee,
"token_name": token_name,
"amount": amount
}.to_json
resp_body = Blockchain::InvokeBlockchainApi.call(params)
if resp_body['status'] == 5 or resp_body['status'] == 106 or resp_body['status'] == 105
raise resp_body['message']
elsif resp_body['status'] != 0
raise "区块链接口请求失败."
else
end
end
def lock_balance_on_blockchain(username, tokenname, amount)
params = {
"request-type": "lock user balance",
"username": username,
"token_name": tokenname,
"amount": amount
}.to_json
resp_body = Blockchain::InvokeBlockchainApi.call(params)
if resp_body['status'] == 5 or resp_body['status'] == 106 or resp_body['status'] == 103
raise resp_body['message']
elsif resp_body['status'] != 0
raise "区块链接口请求失败."
else
end
end
def unlock_balance_on_blockchain(username, tokenname, amount)
params = {
"request-type": "unlock user balance",
"username": username,
"token_name": tokenname,
"amount": amount
}.to_json
resp_body = Blockchain::InvokeBlockchainApi.call(params)
if resp_body['status'] == 5 or resp_body['status'] == 106 or resp_body['status'] == 104
raise resp_body['message']
elsif resp_body['status'] != 0
raise "区块链接口请求失败."
else
end
end
def phone_mail_type value
value =~ /^1\d{10}$/ ? 1 : 0
end
end

View File

@ -0,0 +1,28 @@
class Blockchain::CreateIssue < ApplicationService
attr_reader :params
def initialize(params)
@params = params
end
def call
ActiveRecord::Base.transaction do
username = @params[:user_id].to_s
token_name = @params[:project_id].to_s
amount = @params[:token_num].to_i
# 调用token锁仓函数
lock_balance_on_blockchain(username, token_name, amount)
end
rescue => e
puts "转账失败: #{e.message}"
raise Error, e.message
end
private
def no_use
puts "this function does not have any usage"
end
end

View File

@ -0,0 +1,29 @@
class Blockchain::CreateTrade < ApplicationService
attr_reader :params
def initialize(params)
@params = params
end
def call
ActiveRecord::Base.transaction do
username = @params[:user_id].to_s
token_name = @params[:project_id].to_s
amount = @params[:token_num].to_i
# 调用token锁仓函数
results = lock_balance_on_blockchain(username, token_name, amount)
return results
end
rescue => e
puts "转账失败: #{e.message}"
raise Error, e.message
end
private
def no_use
puts "this function does not have any usage"
end
end

View File

@ -0,0 +1,28 @@
class Blockchain::FixIssue < ApplicationService
attr_reader :params
def initialize(params)
@params = params
end
def call
ActiveRecord::Base.transaction do
username = @params[:user_id].to_s
token_name = @params[:project_id].to_s
amount = @params[:token_num].to_i
# 调用token锁仓函数
unlock_balance_on_blockchain(username, token_name, amount)
end
rescue => e
puts "关联issue失败: #{e.message}"
raise Error, e.message
end
private
def no_use
puts "this function does not have any usage"
end
end

View File

@ -0,0 +1,34 @@
class Blockchain::InvokeBlockchainApi < ApplicationService
# 调用blockchain
attr_reader :params
def initialize(params)
@params = params
end
def call
begin
uri = Blockchain.blockchain_config[:api_url]
uri = URI.parse(URI.encode(uri.strip))
res = Net::HTTP.start(uri.host, uri.port) do |http|
req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req.body = @params
http.request(req)
end
if res.code.to_i != 200
raise "区块链接口请求失败."
else
res_body = JSON.parse(res.body)
if res_body.has_key?("status")
else
raise "区块链接口请求失败."
end
end
return res_body
rescue Exception => e
raise "区块链接口请求失败."
end
end
end

View File

@ -0,0 +1,36 @@
class Blockchain::TransferService < ApplicationService
attr_reader :params
def initialize(params)
@params = params
end
def call
ActiveRecord::Base.transaction do
transfer_amount = params['transfer_amount']
if (Float(transfer_amount) rescue false) == false or transfer_amount.to_i < 0 or Float(transfer_amount) != transfer_amount.to_i
raise Error, "请输入正确的转账金额"
end
transfer_amount = params['transfer_amount'].to_i
transfer_login = params['transfer_login']
payer = params['payer_id'].to_s
payee = User.find_by(login: transfer_login)
if payee.nil?
raise Error, "未找到接收转账的用户"
else
payee = payee.id.to_s
token_name = params['project_id'].to_s
# 调用token转移函数
transfer_balance_on_blockchain(payer, payee, token_name, transfer_amount)
end
end
end
private
def no_use
puts "this function does not have any usage"
end
end

View File

@ -28,6 +28,10 @@ class Cache::V2::ProjectDateRankService < ApplicationService
"v2-project-rank-#{@rank_date.to_s}"
end
def project_rank_statistic_key
"v2-project-statistic:#{@project_id}-#{@rank_date.to_s}"
end
def project_rank
$redis_cache.zscore(project_rank_key, @project_id)
end
@ -35,26 +39,37 @@ class Cache::V2::ProjectDateRankService < ApplicationService
def set_project_rank
if @visits.present?
$redis_cache.zincrby(project_rank_key, @visits.to_i * 1, @project_id)
$redis_cache.hincrby(project_rank_statistic_key, "visits", @visits.to_i)
end
if @watchers.present?
$redis_cache.zincrby(project_rank_key, @watchers.to_i * 5, @project_id)
$redis_cache.hincrby(project_rank_statistic_key, "watchers", @watchers.to_i)
end
if @praises.present?
$redis_cache.zincrby(project_rank_key, @praises.to_i * 5, @project_id)
$redis_cache.hincrby(project_rank_statistic_key, "praises", @praises.to_i)
end
if @forks.present?
$redis_cache.zincrby(project_rank_key, @forks.to_i * 10, @project_id)
$redis_cache.hincrby(project_rank_statistic_key, "forks", @forks.to_i)
end
if @issues.present?
$redis_cache.zincrby(project_rank_key, @issues.to_i * 5, @project_id)
$redis_cache.hincrby(project_rank_statistic_key, "issues", @issues.to_i)
end
if @pullrequests.present?
$redis_cache.zincrby(project_rank_key, @pullrequests.to_i * 10, @project_id)
$redis_cache.hincrby(project_rank_statistic_key, "pullrequests", @pullrequests.to_i)
end
if @commits.present?
$redis_cache.zincrby(project_rank_key, @commits.to_i * 5, @project_id)
$redis_cache.hincrby(project_rank_statistic_key, "commits", @commits.to_i)
end
$redis_cache.zscore(project_rank_key, @project_id)
# 设置过期时间(一个月)
$redis_cache.expireat(project_rank_key, (@rank_date+30.days).to_time.to_i)
$redis_cache.expireat(project_rank_statistic_key, (@rank_date+30.days).to_time.to_i)
end
end

Some files were not shown because too many files have changed in this diff Show More