diff --git a/.gitignore b/.gitignore
index 77104d20a..19dfa07fe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -36,6 +36,7 @@ public/react/yarn.lock
/.idea/*
# Ignore react node_modules
+public/system/*
public/react/*
/public/react/.cache
/public/react/node_modules/
@@ -73,6 +74,7 @@ vendor/bundle/
/log
/public/admin
/mysql_data
+/public/repo/
.generators
@@ -82,4 +84,6 @@ docker/
educoder.sql
redis_data/
Dockerfile
-dump.rdb
\ No newline at end of file
+dump.rdb
+.tags*
+ceshi_user.xlsx
\ No newline at end of file
diff --git a/.merge_file_a17580 b/.merge_file_a17580
deleted file mode 100644
index 47e471767..000000000
--- a/.merge_file_a17580
+++ /dev/null
@@ -1,529 +0,0 @@
-import React, {Component} from 'react';
-import logo from './logo.svg';
-import './App.css';
-import {LocaleProvider} from 'antd'
-import zhCN from 'antd/lib/locale-provider/zh_CN';
-import {
- BrowserRouter as Router,
- Route,
- Switch
-} from 'react-router-dom';
-import axios from 'axios';
-import '@icedesign/base/dist/ICEDesignBase.css';
-
-import '@icedesign/base/index.scss';
-
-import LoginDialog from './modules/login/LoginDialog';
-import Notcompletedysl from './modules/user/Notcompletedysl';
-import Trialapplicationysl from './modules/login/Trialapplicationysl';
-import Trialapplicationreview from './modules/user/Trialapplicationreview';
-import Addcourses from "./modules/courses/coursesPublic/Addcourses";
-import AccountProfile from"./modules/user/AccountProfile";
-
-
-import Trialapplication from './modules/login/Trialapplication'
-
-import NotFoundPage from './NotFoundPage'
-
-import Loading from './Loading'
-
-import Loadable from 'react-loadable';
-
-
-import moment from 'moment'
-
-import {MuiThemeProvider, createMuiTheme} from 'material-ui/styles';
-
-// import './AppConfig'
-
-import history from './history';
-
-import {SnackbarHOC} from 'educoder'
-import {initAxiosInterceptors} from './AppConfig'
-
-
-// !!!tpi需要这个来加载css
-import {TPMIndexHOC} from './modules/tpm/TPMIndexHOC';
-
-
-const theme = createMuiTheme({
- palette: {
- primary: {
- main: '#4CACFF',
- contrastText: 'rgba(255, 255, 255, 0.87)'
- },
- secondary: {main: '#4CACFF'}, // #11cb5f This is just green.A700 as hex.
- },
-});
-//
-// const Trialapplication= Loadable({
-// loader: () =>import('./modules/login/Trialapplication'),
-// loading:Loading,
-// })
-//登入
-const EducoderLogin = Loadable({
- loader: () => import('./modules/login/EducoderLogin'),
- loading: Loading,
-})
-const TestIndex = Loadable({
- loader: () => import('./modules/test'),
- loading: Loading,
-})
-
-const IndexWrapperComponent = Loadable({
- loader: () => import('./modules/page/IndexWrapper'),
- loading: Loading,
-})
-
-const CommentComponent = Loadable({
- loader: () => import('./modules/comment/CommentContainer'),
- loading: Loading,
-})
-
-const TestMaterialDesignComponent = Loadable({
- loader: () => import('./modules/test/md/TestMaterialDesign'),
- loading: Loading,
-})
-const TestCodeMirrorComponent = Loadable({
- loader: () => import('./modules/test/codemirror/TestCodeMirror'),
- loading: Loading,
-})
-
-const TestComponent = Loadable({
- loader: () => import('./modules/test/TestRC'),
- loading: Loading,
-})
-const TestUrlQueryComponent = Loadable({
- loader: () => import('./modules/test/urlquery/TestUrlQuery'),
- loading: Loading,
-})
-
-const TPMIndexComponent = Loadable({
- loader: () => import('./modules/tpm/TPMIndex'),
- loading: Loading,
-})
-const TPMShixunsIndexComponent = Loadable({
- loader: () => import('./modules/tpm/shixuns/ShixunsIndex'),
- loading: Loading,
-})
-
-//实训课程(原实训路径)
-const ShixunPaths = Loadable({
- loader: () => import('./modules/paths/Index'),
- loading: Loading,
-})
-
-//在线课堂
-const CoursesIndex = Loadable({
- loader: () => import('./modules/courses/Index'),
- loading: Loading,
-})
-const SearchPage = Loadable({
- loader: () => import('./search/SearchPage'),
- loading: Loading,
-})
-
-//教学案例
-const MoopCases = Loadable({
- loader: () => import('./modules/moop_cases/index'),
- loading: Loading,
-})
-
-// 课堂讨论
-// const BoardIndex = Loadable({
-// loader: () => import('./modules/courses/boards/BoardIndex'),
-// loading:Loading,
-// })
-
-// //课堂普通作业&分组作业
-// const CoursesWorkIndex = Loadable({
-// loader: () => import('./modules/courses/busyWork/Index'),
-// loading:Loading,
-// })
-//
-
-// const TPMShixunchildIndexComponent = Loadable({
-// loader: () => import('./modules/tpm/shixunchild/ShixunChildIndex'),
-// loading: Loading,
-// })
-
-
-// const TPMshixunfork_listIndexComponent = Loadable({
-// loader: () => import('./modules/tpm/shixunchild/Shixunfork_list'),
-// loading: Loading,
-// })
-
-
-const ForumsIndexComponent = Loadable({
- loader: () => import('./modules/forums/ForumsIndex'),
- loading: Loading,
-})
-
-// trustie plus forum
-// const TPForumsIndexComponent = Loadable({
-// loader: () => import('./modules/tp-forums/TPForumsIndex'),
-// loading: Loading,
-// })
-
-
-// const TestPageComponent = Loadable({
-// loader: () => import('./modules/page/Index'),
-// loading: Loading,
-// })
-
-
-//新建实训
-const Newshixuns = Loadable({
- loader: () => import('./modules/tpm/newshixuns/Newshixuns'),
- loading: Loading,
-})
-
-
-//实训首页
-const ShixunsHome = Loadable({
- loader: () => import('./modules/home/shixunsHome'),
- loading: Loading,
-})
-
-
-const CompatibilityPageLoadable = Loadable({
- loader: () => import('./modules/common/CompatibilityPage'),
- loading: Loading,
-})
-
-//403页面
-const Shixunauthority = Loadable({
- loader: () => import('./modules/403/Shixunauthority'),
- loading: Loading,
-})
-
-
-//404页面
-const Shixunnopage = Loadable({
- loader: () => import('./modules/404/Shixunnopage'),
- loading: Loading,
-})
-
-//500页面
-const http500 = Loadable({
- loader: () => import('./modules/500/http500'),
- loading: Loading,
-})
-
-// 登录注册
-const LoginRegisterPage = Loadable({
- loader: () => import('./modules/user/LoginRegisterPage'),
- loading: Loading,
-})
-const AccountPage = Loadable({
- loader: () => import('./modules/user/AccountPage'),
- loading: Loading,
-})
-
-// 个人主页
-const UsersInfo = Loadable({
- loader: () => import('./modules/user/usersInfo/Infos'),
- loading: Loading,
-})
-
-// 兴趣页面
-const Interestpage = Loadable({
- loader: () => import('./modules/login/EducoderInteresse'),
- loading: Loading,
-})
-
-//众包创新
-const ProjectPackages=Loadable({
- loader: () => import('./modules/projectPackages/ProjectPackageIndex'),
- loading: Loading,
-})
-
-class App extends Component {
- constructor(props) {
- super(props)
- // this.state = {
- // isRenders:false,
- // }
-
- }
-
- componentDidMount() {
- // force an update if the URL changes
- history.listen(() => {
- this.forceUpdate()
- const $ = window.$
- // https://www.trustie.net/issues/21919 可能会有问题
- $("html").animate({ scrollTop: $('html').scrollTop() - 0 })
- });
-
- initAxiosInterceptors(this.props)
-
- //
- // axios.interceptors.response.use((response) => {
- // // console.log("response"+response);
- // if(response!=undefined)
- // // console.log("response"+response.data.statu);
- // if (response&&response.data.status === 407) {
- // this.setState({
- // isRenders: true,
- // })
- // }
- // return response;
- // }, (error) => {
- // //TODO 这里如果样式变了会出现css不加载的情况
- // });
- }
- //修改登录方法
- Modifyloginvalue=()=>{
- this.setState({
- isRender:false,
- })
- }
-
- render() {
-
-
- return (
-
-
-
-
- this.Modifyloginvalue()}>
-
-
-
-
-
- {/*{*/}
- {/* isRender === true?*/}
- {/* : ""*/}
- {/*}*/}
-
- {/*{*/}
- {/* isRenders === true?*/}
- {/* */}
- {/*:""*/}
- {/*}*/}
-
-
-
- {/**/}
-
- {/*众包创新*/}
-
- {/*认证*/}
-
-
- {/*403*/}
-
-
-
-
- {/*404*/}
-
-
-
-
-
- ( )
- }>
- {/* */}
-
-
-
-
-
-
-
-
-
-
-
- {/*列表页*/}
-
-
- {/*
-
-
- */}
-
- {/**/}
- {/* */}
-
-
- {/*实训课程(原实训路径)*/}
-
-
- ( )
- }
- >
-
- {/*课堂*/}
-
-
- {/* 课堂讨论 */}
- {/* */}
-
- {/*
- */}
-
- {/* */}
- {/* 兴趣页面*/}
- {/**/}
-
-
-
-
-
-
-
-
- {/* 教学案例 */}
- ( )
- }/>
-
- {/* */}
- {/*列表页*/}
- {/**/}
- {/*首页*/}
-
-
-
- {/**/}
-
-
-
-
-
- );
- }
-}
-
-// moment国际化,设置为中文
-moment.defineLocale('zh-cn', {
- months: '一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月'.split('_'),
- monthsShort: '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'),
- weekdays: '星期日_星期一_星期二_星期三_星期四_星期五_星期六'.split('_'),
- weekdaysShort: '周日_周一_周二_周三_周四_周五_周六'.split('_'),
- weekdaysMin: '日_一_二_三_四_五_六'.split('_'),
- longDateFormat: {
- LT: 'Ah点mm分',
- LTS: 'Ah点m分s秒',
- L: 'YYYY-MM-DD',
- LL: 'YYYY年MMMD日',
- LLL: 'YYYY年MMMD日Ah点mm分',
- LLLL: 'YYYY年MMMD日ddddAh点mm分',
- l: 'YYYY-MM-DD',
- ll: 'YYYY年MMMD日',
- lll: 'YYYY年MMMD日Ah点mm分',
- llll: 'YYYY年MMMD日ddddAh点mm分'
- },
- meridiemParse: /凌晨|早上|上午|中午|下午|晚上/,
- meridiemHour: function (hour, meridiem) {
- if (hour === 12) {
- hour = 0;
- }
- if (meridiem === '凌晨' || meridiem === '早上' ||
- meridiem === '上午') {
- return hour;
- } else if (meridiem === '下午' || meridiem === '晚上') {
- return hour + 12;
- } else {
- // '中午'
- return hour >= 11 ? hour : hour + 12;
- }
- },
- meridiem: function (hour, minute, isLower) {
- var hm = hour * 100 + minute;
- if (hm < 600) {
- return '凌晨';
- } else if (hm < 900) {
- return '早上';
- } else if (hm < 1130) {
- return '上午';
- } else if (hm < 1230) {
- return '中午';
- } else if (hm < 1800) {
- return '下午';
- } else {
- return '晚上';
- }
- },
- calendar: {
- sameDay: function () {
- return this.minutes() === 0 ? '[今天]Ah[点整]' : '[今天]LT';
- },
- nextDay: function () {
- return this.minutes() === 0 ? '[明天]Ah[点整]' : '[明天]LT';
- },
- lastDay: function () {
- return this.minutes() === 0 ? '[昨天]Ah[点整]' : '[昨天]LT';
- },
- nextWeek: function () {
- var startOfWeek, prefix;
- startOfWeek = moment().startOf('week');
- prefix = this.unix() - startOfWeek.unix() >= 7 * 24 * 3600 ? '[下]' : '[本]';
- return this.minutes() === 0 ? prefix + 'dddAh点整' : prefix + 'dddAh点mm';
- },
- lastWeek: function () {
- var startOfWeek, prefix;
- startOfWeek = moment().startOf('week');
- prefix = this.unix() < startOfWeek.unix() ? '[上]' : '[本]';
- return this.minutes() === 0 ? prefix + 'dddAh点整' : prefix + 'dddAh点mm';
- },
- sameElse: 'LL'
- },
- ordinalParse: /\d{1,2}(日|月|周)/,
- ordinal: function (number, period) {
- switch (period) {
- case 'd':
- case 'D':
- case 'DDD':
- return number + '日';
- case 'M':
- return number + '月';
- case 'w':
- case 'W':
- return number + '周';
- default:
- return number;
- }
- },
- relativeTime: {
- future: '%s内',
- past: '%s前',
- s: '几秒',
- m: '1分钟',
- mm: '%d分钟',
- h: '1小时',
- hh: '%d小时',
- d: '1天',
- dd: '%d天',
- M: '1个月',
- MM: '%d个月',
- y: '1年',
- yy: '%d年'
- },
- week: {
- // GB/T 7408-1994《数据元和交换格式·信息交换·日期和时间表示法》与ISO 8601:1988等效
- dow: 1, // Monday is the first day of the week.
- doy: 4 // The week that contains Jan 4th is the first week of the year.
- }
-});
-export default SnackbarHOC()(App);
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 000000000..d0a865bba
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,129 @@
+# Changelog
+## [v3.2.0](https://forgeplus.trustie.net/projects/jasder/forgeplus/releases) - 2021-06-09
+
+### ENHANCEMENTS
+ * ADD 集成邮件和平台站内信等通知系统
+ * Fix 代码库二级页面-优化文件子目录浏览功能(#50388)
+ * Fix 代码库二级页面-优化commit提交详情页页面排版及数据显示(#50372)
+ * Fix 代码库二级页面-优化commit提交信息列表页加载方式和数据排序功能(#50348)
+ * Fix 代码库二级页面-优化创建发行版功能(#50346)
+ * Fix 代码库二级页面-优化标签列表页功能(#50344)
+ * Fix 代码库二级页面-优化发行版本列表页功能(#50345)
+ * Fix 代码库二级页面-优化分支列表页功能(#50343)
+ * Fix 其他问题优化(#51581) (#51343) (#51108)
+
+---
+
+### BUGFIXES
+ * Fix 发行版—标签跳转链接错误(#51666)
+ * Fix 文件预览报错(#51660)
+ * Fix 标签创建时间显示错误(#51658)
+ * Fix 分支列表中头像显示问题(#51656)
+ * Fix 文本信息过长(#51630) (#51626)
+ * Fix 版本库中附件下载400(#51625)
+ * Fix loading页面优化(#51588)
+ * Fix 提交详情页面优化(#51577)
+ * Fix 修复疑修复制功能(#51569)
+ * Fix 修复新建发行版用户信息显示错误的问题(#51665)
+ * Fix 修复查看文件详细信息报错的问题(#51561)
+ * Fix 修复提交记录中时间显示格式问题(#51526)
+ * Fix 组织下项目更加更新时间倒序排序(#50833)
+
+
+## [v3.1.0](https://forgeplus.trustie.net/projects/jasder/forgeplus/releases) - 2021-06-09
+
+* ENHANCEMENTS
+ * ADD 用户活动统计图表功能
+ * ADD 用户精选项目功能
+ * ADD 用户贡献度统计图表功能
+ * ADD 用户开发能力数据统计工
+ * ADD 用户角色定位展示功能
+ * ADD 用户专业定位标签展示功能
+ * ADD 修改用户基本资料功能
+ * ADD 更改密码功能
+ * ADD 用户个人主页基本现在展示可配置功能
+
+* BUGFIXES
+ * Fix 解决一些bug
+ * Fix 优化美化页面
+
+
+* BUGFIXES
+ * Fix 在线修改文件,页面文件显不及时的问题(46049)
+ * Fix Fork项目,接口多次调用问题(45052)
+ * FIX 页面置顶功能区域排版问题(45825)
+ * Fix 其他样式显示问题
+
+* ENHANCEMENTS
+ * ADD 合并请求页面显示有冲突文件状态(46016)
+ * ADD 创建组织各属性添加规则匹配功能(45707)
+ * ADD 微信分享功能(45707)
+
+## [v3.0.3](https://forgeplus.trustie.net/projects/jasder/forgeplus/releases) - 2021-05-08
+
+* BUGFIXES
+ * Fix 解决疑修标题过长导致的排版问题(45469)
+ * Fix 解决合并请求详情页面排版错误的问题(45457)
+ * FIX 解决转移仓库界面专有名词描述错误的问题(45455)
+ * Fix 解决markdown格式文件自动生成数字排序的问题(45454)
+ * Fix 解决镜像项目源地址不显示的问题(45403)
+ * Fix 解决镜像项目导航显示错误问题(45398)
+ * Fix 解决其他相关bug
+
+* ENHANCEMENTS
+ * UPDATE 用户注册时,账号和密码正则匹配调整(45336) (45318) (45290)
+ * ADD 创建组织各属性添加规则匹配功能(45313) (45289)
+ * ADD 创建团建各属性添加规则匹配功能(45334) (45325) (45287)
+ * ADD 仓库转移功能(45017) (45015)
+
+## [v3.0.2](https://forgeplus.trustie.net/projects/jasder/forgeplus/releases) - 2021-04-23
+
+* BUGFIXES
+ * Fix 解决部分用户头像不显示问题
+ * Fix 解决代码库模块中最左侧目录中的文件定位加载不准确的问题
+ * FIX 解决团队管理页面中项目链接错误问题
+ * Fix 解决markdown格式文件显示问题
+ * Fix 解决组织名下创建项目报错的问题
+ * Fix 解决组织名下的项目,创建issue报错的问题
+ * Fix 解决组织名下创建团队提示信息信息显示错误问题
+ * Fix 解决点击组织图片时,链接加载错误问题
+ * Fix 修复查询版本库信息安全漏洞
+ * Fix 解决修复团队成员操作访问组织仓库报403错误的问题
+ * Fix 解决owners团队成员对仓库添加成功失败的问题
+
+* ENHANCEMENTS
+ * ADD 自动生产用户头像功能
+ * ADD 创建组织支持中文名称
+ * ADD 创建团建支持中文名称
+ * ADD 组织名称统一显示中文名
+ * ADD 团队名称统一显示中文名
+ * ADD 用户头像悬浮时展示相关信息
+ * ADD 项目详情页添加实践课程链接入口
+ * ADD README文件页面添加添加目录导航功能
+ * UPDATE 升级改版底部footer信息
+ * UPDATE 升级用户操作版本库权限
+
+## [v3.0.1](https://forgeplus.trustie.net/projects/jasder/forgeplus/releases) - 2021-03-19
+
+* BUGFIXES
+ * Fix pull reqeust模块中用户头像显示问题
+ * Fix 解决issue模块中,指派用户的问题
+ * Fix 解决合并请求失败的后显示message信息不正确
+ * Fix 代码目录页面,readme文件提供单独的api
+
+* ENHANCEMENTS
+ * 重构项目详情页面
+
+## [v3.0.0](https://forgeplus.trustie.net/projects/jasder/forgeplus/releases) - 2020-12-29
+
+* BUGFIXES
+ * Fix pull reqeust的访问权限问题 (#14156) (#14171)
+ * Fix 部分页面访问403的问题
+ * Fix 合并请求失败的问题
+ * Fix 代码目录页面,文件夹和文件图片不显示的问题
+ * Fix Issue列表中按‘负责人’过滤筛选时,部分项目成员不在搜索范围没的问题
+ * Fix 项目更新时间不同步的问题, 添加了定时任务
+* ENHANCEMENTS
+ * 提升获取版本库目录代码查询速度 (#43296)
+ * tag 列表慢查询速度问题 (#43332)
+ * API builder 模版中 render partial builder的性能问题
diff --git a/Gemfile b/Gemfile
index 281dd705a..713eb860f 100644
--- a/Gemfile
+++ b/Gemfile
@@ -126,3 +126,5 @@ gem 'request_store'
gem 'harmonious_dictionary', '~> 0.0.1'
gem 'parallel', '~> 1.19', '>= 1.19.1'
+
+gem 'letter_avatar'
diff --git a/Gemfile.lock b/Gemfile.lock
index cbd598f59..b1dc2cca7 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -162,6 +162,7 @@ GEM
activerecord
kaminari-core (= 1.2.0)
kaminari-core (1.2.0)
+ letter_avatar (0.3.8)
listen (3.1.5)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
@@ -175,7 +176,9 @@ GEM
mimemagic (~> 0.3.2)
maruku (0.7.3)
method_source (0.9.2)
- mimemagic (0.3.4)
+ mimemagic (0.3.10)
+ nokogiri (~> 1)
+ rake
mini_mime (1.0.2)
mini_portile2 (2.4.0)
minitest (5.14.0)
@@ -456,6 +459,7 @@ DEPENDENCIES
jbuilder (~> 2.5)
jquery-rails
kaminari (~> 1.1, >= 1.1.1)
+ letter_avatar
listen (>= 3.0.5, < 3.2)
mysql2 (>= 0.4.4, < 0.6.0)
oauth2
diff --git a/LICENSE b/LICENSE
index 8aa26455d..2b9b385e6 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,21 +1,124 @@
-MIT License
+木兰宽松许可证, 第2版
-Copyright (c) [year] [fullname]
+2020年1月 http://license.coscl.org.cn/MulanPSL2
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
+您对“软件”的复制、使用、修改及分发受木兰宽松许可证,第2版(“本许可证”)的如下条款的约束:
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
+0. 定义
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+“软件” 是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。
+
+“贡献” 是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。
+
+“贡献者” 是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。
+
+“法人实体” 是指提交贡献的机构及其“关联实体”。
+
+“关联实体” 是指,对“本许可证”下的行为方而言,控制、受控制或与其共同受控制的机构,此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。
+
+1. 授予版权许可
+
+每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、分发其“贡献”,不论修改与否。
+
+2. 授予专利许可
+
+每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权行动之日终止。
+
+3. 无商标许可
+
+“本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可,但您为满足第4条规定的声明义务而必须使用除外。
+
+4. 分发限制
+
+您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。
+
+5. 免责声明与责任限制
+
+“软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。
+
+6. 语言
+
+“本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。
+
+条款结束
+
+如何将木兰宽松许可证,第2版,应用到您的软件
+
+如果您希望将木兰宽松许可证,第2版,应用到您的新软件,为了方便接收者查阅,建议您完成如下三步:
+
+1, 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字;
+
+2, 请您在软件包的一级目录下创建以“LICENSE”为名的文件,将整个许可证文本放入该文件中;
+
+3, 请将如下声明文本放入每个源文件的头部注释中。
+
+Copyright (c) [Year] [name of copyright holder]
+[Software Name] is licensed under Mulan PSL v2.
+You can use this software according to the terms and conditions of the Mulan PSL v2.
+You may obtain a copy of Mulan PSL v2 at:
+ http://license.coscl.org.cn/MulanPSL2
+THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
+EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
+MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+See the Mulan PSL v2 for more details.
+Mulan Permissive Software License,Version 2
+Mulan Permissive Software License,Version 2 (Mulan PSL v2)
+
+January 2020 http://license.coscl.org.cn/MulanPSL2
+
+Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v2 (this License) with the following terms and conditions:
+
+0. Definition
+
+Software means the program and related documents which are licensed under this License and comprise all Contribution(s).
+
+Contribution means the copyrightable work licensed by a particular Contributor under this License.
+
+Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License.
+
+Legal Entity means the entity making a Contribution and all its Affiliates.
+
+Affiliates means entities that control, are controlled by, or are under common control with the acting entity under this License, ‘control’ means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity.
+
+1. Grant of Copyright License
+
+Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or distribute its Contribution, with modification or not.
+
+2. Grant of Patent License
+
+Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, or by combination of the Contribution with the Software to which the Contribution was contributed. The patent license shall not apply to any modification of the Contribution, and any other combination which includes the Contribution. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that the Software or any Contribution in it infringes patents, then any patent license granted to you under this License for the Software shall terminate as of the date such litigation or activity is filed or taken.
+
+3. No Trademark License
+
+No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in section 4.
+
+4. Distribution Restriction
+
+You may distribute the Software in any medium with or without modification, whether in source or executable forms, provided that you provide recipients with a copy of this License and retain copyright, patent, trademark and disclaimer statements in the Software.
+
+5. Disclaimer of Warranty and Limitation of Liability
+
+THE SOFTWARE AND CONTRIBUTION IN IT ARE PROVIDED WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE SOFTWARE OR THE CONTRIBUTION IN IT, NO MATTER HOW IT’S CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+6. Language
+
+THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION SHALL PREVAIL.
+
+END OF THE TERMS AND CONDITIONS
+
+How to Apply the Mulan Permissive Software License,Version 2 (Mulan PSL v2) to Your Software
+
+To apply the Mulan PSL v2 to your work, for easy identification by recipients, you are suggested to complete following three steps:
+
+Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner;
+Create a file named "LICENSE" which contains the whole context of this License in the first directory of your software package;
+Attach the statement to the appropriate annotated syntax at the beginning of each source file.
+Copyright (c) [Year] [name of copyright holder]
+[Software Name] is licensed under Mulan PSL v2.
+You can use this software according to the terms and conditions of the Mulan PSL v2.
+You may obtain a copy of Mulan PSL v2 at:
+ http://license.coscl.org.cn/MulanPSL2
+THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
+EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
+MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+See the Mulan PSL v2 for more details.
\ No newline at end of file
diff --git a/README.md b/README.md
index b75fcd647..1bcd05cce 100644
--- a/README.md
+++ b/README.md
@@ -11,13 +11,32 @@ Trustie (确实)是一个以大众化协同开发、开放式资源共享、
## 部署
-### 1. 安装依赖包
+### Depends Versions
-```bash
-bundle install
+* Ruby 2.4.5
+
+* Rails ~> 5.2
+
+* MySql ~> 5.6
+
+* Redis 5+
+
+* imagemagick
+
+### Steps
+
+#### 1. 克隆稳定版本
+```
+git clone -b standalone https://git.trustie.net/jasder/forgeplus.git
```
-### 2. 配置初始化文件
+#### 2. 安装依赖包
+
+```bash
+cd forgeplus && bundle install
+```
+
+#### 3. 配置初始化文件
进入项目根目录执行一下命令:
```bash
@@ -27,12 +46,26 @@ touch config/redis.yml
touch config/elasticsearch.yml
```
-### 3. 配置gitea服务(可选)
+#### 4. 配置数据库
+数据库配置信息请查看/config/database.yml文件,
+项目默认采用mysql数据库, 如需更改,请自行修改配置信息,
+默认配置如下:
+
+```bash
+default: &default
+ adapter: mysql2
+ host: 127.0.0.1
+ encoding: utf8
+ username: root
+ password: 123456
+```
+
+#### 5. 配置gitea服务(可选)
**如需要部署自己的gitea平台,请参考gitea官方平台:https://docs.gitea.io/zh-cn/install-from-binary/**
-**因目前gitea平台api受限,暂时推荐从forge平台获取gitea部署文件进行部署:https://forgeplus.trustie.net/projects/jasder/gitea-binary
+**因目前gitea平台api受限,暂时推荐从forge平台获取gitea部署文件进行部署:https://forgeplus.trustie.net/projects/Trustie/gitea-binary**
-#### 配置gitea服务步骤
+**配置gitea服务步骤**
1. 部署gitea服务,并注册root账户
2. 修改forge平台的 config/configuration.yml中的gitea服务指向地址,如:
@@ -44,75 +77,93 @@ gitea:
base_url: '/api/v1'
```
-### 4. 安装redis环境
+#### 6. 安装redis环境
**请自行搜索各平台如何安装部署redis环境**
-
-### 5. 创建数据库
-
+#### 7. 安装imagemagick插件
+- Mac OS X
```bash
-rails db:create
+ brew install imagemagick ghostscript
```
-### 6. 导入数据表结构
+- Linux
+```bash
+sudo apt-get install -y imagemagick
+```
+
+#### 8. 创建数据库
+**开发环境为development, 生成环境为production**
+```bash
+rails db:create RAILS_ENV=development
+```
+
+#### 9. 导入数据表结构
```bash
bundle exec rake sync_table_structure:import_csv
```
-### 7. 执行migrate迁移文件
+#### 10. 执行migrate迁移文件
**开发环境为development, 生成环境为production**
```bash
rails db:migrate RAILS_ENV=development
```
-### 8. 启动redis(此处已mac系统为例)
+#### 11. clone前端代码
+**将前端代码克隆到public/react目录下,目录结构应该是: public/react/build**
+```bash
+git clone -b standalone https://git.trustie.net/jasder/build.git
+```
+
+#### 12. 启动redis(此处已mac系统为例)
```bash
redis-server&
```
-### 9. 启动sidekiq
+#### 13. 启动sidekiq
**开发环境为development, 生成环境为production**
```bash
bundle exec sidekiq -C config/sidekiq.yml -e production -d
```
-### 10. 启动rails服务
+#### 14. 启动rails服务
```bash
rails s
```
-### 11. 浏览器访问
+#### 15. 浏览器访问
在浏览器中输入如下地址访问:
```bash
http://localhost:3000/
```
+#### 16. 其他说明
+通过页面注册都第一个用户为平台管理员用户
## 页面展示
- 代码库
-
+
- 任务管理
-
+
- 任务查看
-
+
- 任务指派
-
+
- 里程碑
-
+
### API
-- [API](api_document.md)
+- [API文档](https://forgeplus.trustie.net/docs/api)
- [API](showdoc.com.cn)
账号:forgeplus@admin.com 密码:forge123
diff --git a/api_document.md b/api_document.md
index f2d4bca69..35ba91103 100644
--- a/api_document.md
+++ b/api_document.md
@@ -51,6 +51,51 @@ http://localhost:3000/api/accounts/remote_register | jq
|-- token |string|用户token|
+返回值
+```json
+{
+ "status": 0,
+ "message": "success",
+ "user": {
+ "id": 36400,
+ "token": "8c87a80d9cfacc92fcb2451845104f35119eda96"
+ }
+}
+```
+---
+
+#### 独立注册接口
+```
+POST accounts/register
+```
+*示例*
+```bash
+curl -X POST \
+-d "login=2456233122@qq.com" \
+-d "password=djs_D_00001" \
+-d "namespace=16895620" \
+-d "code=forge" \
+http://localhost:3000/api/accounts/remote_register | jq
+```
+*请求参数说明:*
+
+|参数名|必选|类型|说明|
+|-|-|-|-|
+|login |是|string |邮箱或者手机号 |
+|namespace |是|string |登录名 |
+|password |是|string |密码 |
+|code |是|string |验证码 |
+
+
+*返回参数说明:*
+
+|参数名|类型|说明|
+|-|-|-|
+|user|json object |返回数据|
+|-- id |int |用户id |
+|-- token |string|用户token|
+
+
返回值
```json
{
@@ -349,10 +394,10 @@ http://localhost:3000/api/projects/ | jq
|-|-|-|-|
|user_id |是|int |用户id或者组织id |
|name |是|string |项目名称 |
-|description |是|string |项目描述 |
+|description |否|string |项目描述 |
|repository_name |是|string |仓库名称, 只含有数字、字母、下划线不能以下划线开头和结尾,且唯一 |
-|project_category_id|是|int |项目类别id |
-|project_language_id|是|int |项目语言id |
+|project_category_id|否|int |项目类别id |
+|project_language_id|否|int |项目语言id |
|ignore_id |否|int |gitignore相关id |
|license_id |否|int |开源许可证id |
|private |否|boolean|项目是否私有, true:为私有,false: 公开,默认为公开 |
@@ -385,9 +430,7 @@ curl -X POST \
-d "user_id=36408" \
-d "clone_addr=https://gitea.com/mx8090alex/golden.git" \
-d "name=golden_mirror1" \
--d "description=golden_mirror" \
--d "project_category_id=1" \
--d "project_language_id=2" \
+-d "repository_name=golden_mirror1" \
http://localhost:3000/api/projects/migrate.json | jq
```
*请求参数说明:*
@@ -399,8 +442,8 @@ http://localhost:3000/api/projects/migrate.json | jq
|clone_addr |是|string |镜像项目clone地址 |
|description |否|string |项目描述 |
|repository_name |是|string |仓库名称, 只含有数字、字母、下划线不能以下划线开头和结尾,且唯一 |
-|project_category_id|是|int |项目类别id |
-|project_language_id|是|int |项目语言id |
+|project_category_id|否|int |项目类别id |
+|project_language_id|否|int |项目语言id |
|is_mirror |否|boolean|是否设置为镜像, true:是, false:否,默认为否 |
|auth_username |否|string|镜像源仓库的登录用户名 |
|auth_password |否|string|镜像源仓库的登录秘密 |
diff --git a/app/assets/images/logo.png b/app/assets/images/logo.png
index 436d23490..72505d8a2 100644
Binary files a/app/assets/images/logo.png and b/app/assets/images/logo.png differ
diff --git a/app/assets/javascripts/admin.js b/app/assets/javascripts/admin.js
index e5c7e6620..97e29247e 100644
--- a/app/assets/javascripts/admin.js
+++ b/app/assets/javascripts/admin.js
@@ -116,3 +116,38 @@ $(document).on("turbolinks:before-cache", function () {
$(function () {
});
+
+$(document).on('turbolinks:load', function() {
+
+ $('.logo-item-left').on("change", 'input[type="file"]', function () {
+ var $fileInput = $(this);
+ var file = this.files[0];
+ var imageType = /image.*/;
+ if (file && file.type.match(imageType)) {
+ var reader = new FileReader();
+ reader.onload = function () {
+ var $box = $fileInput.parent();
+ $box.find('img').attr('src', reader.result).css('display', 'block');
+ $box.addClass('has-img');
+ };
+ reader.readAsDataURL(file);
+ } else {
+ }
+ });
+
+ $('.attachment-item-left').on("change", 'input[type="file"]', function () {
+ var $fileInput = $(this);
+ var file = this.files[0];
+ var imageType = /image.*/;
+ if (file && file.type.match(imageType)) {
+ var reader = new FileReader();
+ reader.onload = function () {
+ var $box = $fileInput.parent();
+ $box.find('img').attr('src', reader.result).css('display', 'block');
+ $box.addClass('has-img');
+ };
+ reader.readAsDataURL(file);
+ } else {
+ }
+ });
+})
\ No newline at end of file
diff --git a/app/assets/javascripts/admins/courses/index.js b/app/assets/javascripts/admins/courses/index.js
index 0e3473dff..82867e3e0 100644
--- a/app/assets/javascripts/admins/courses/index.js
+++ b/app/assets/javascripts/admins/courses/index.js
@@ -1,7 +1,7 @@
$(document).on('turbolinks:load', function() {
if ($('body.admins-courses-index-page').length > 0) {
- let searchContainer = $(".course-list-form");
- let searchForm = $("form.search-form",searchContainer);
+ var searchContainer = $(".course-list-form");
+ var searchForm = $("form.search-form",searchContainer);
searchContainer.on('change', '.course-homepage-show', function(){
searchForm.find('input[type="submit"]').trigger('click');
diff --git a/app/assets/javascripts/admins/faqs.js b/app/assets/javascripts/admins/faqs.js
new file mode 100644
index 000000000..dee720fac
--- /dev/null
+++ b/app/assets/javascripts/admins/faqs.js
@@ -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.
diff --git a/app/assets/javascripts/admins/projects/index.js b/app/assets/javascripts/admins/projects/index.js
new file mode 100644
index 000000000..dbf710ea5
--- /dev/null
+++ b/app/assets/javascripts/admins/projects/index.js
@@ -0,0 +1,141 @@
+/*
+ * @Description: Do not edit
+ * @Date: 2021-08-31 11:16:45
+ * @LastEditors: viletyy
+ * @Author: viletyy
+ * @LastEditTime: 2021-08-31 14:19:46
+ * @FilePath: /forgeplus/app/assets/javascripts/admins/system_notifications/index.js
+ */
+$(document).on('turbolinks:load', function(){
+
+ var showSuccessNotify = function() {
+ $.notify({
+ message: '操作成功'
+ },{
+ type: 'success'
+ });
+ }
+
+ // close user
+ $('.project-list-container').on('click', '.recommend-action', function(){
+ var $closeAction = $(this);
+ var $uncloseAction = $closeAction.siblings('.unrecommend-action');
+ var $editAction = $closeAction.siblings('.edit-recommend-action');
+
+ var keywordID = $closeAction.data('id');
+ customConfirm({
+ content: '确认将该项目设置为推荐项目吗?',
+ ok: function(){
+ $.ajax({
+ url: '/admins/projects/' + keywordID,
+ method: 'PUT',
+ dataType: 'json',
+ data: {
+ project: {
+ recommend: true,
+ recommend_index: 1
+ }
+ },
+ success: function() {
+ showSuccessNotify();
+ $closeAction.hide();
+ $uncloseAction.show();
+ $editAction.show();
+ $(".project-item-"+keywordID).children('td').eq(5).text("√")
+ }
+ });
+ }
+ });
+ });
+
+ // unclose user
+ $('.project-list-container').on('click', '.unrecommend-action', function(){
+ var $uncloseAction = $(this);
+ var $closeAction = $uncloseAction.siblings('.recommend-action');
+ var $editAction = $closeAction.siblings('.edit-recommend-action');
+
+ var keywordID = $uncloseAction.data('id');
+ customConfirm({
+ content: '确认取消该推荐项目吗?',
+ ok: function () {
+ $.ajax({
+ url: '/admins/projects/' + keywordID,
+ method: 'PUT',
+ dataType: 'json',
+ data: {
+ project: {
+ recommend: false,
+ recommend_index: 0
+ }
+ },
+ success: function() {
+ showSuccessNotify();
+ $closeAction.show();
+ $uncloseAction.hide();
+ $editAction.hide();
+ $(".project-item-"+keywordID).children('td').eq(5).text("")
+ }
+ });
+ }
+ })
+ });
+
+
+ // close user
+ $('.project-list-container').on('click', '.pinned-action', function(){
+ var $closeAction = $(this);
+ var $uncloseAction = $closeAction.siblings('.unpinned-action');
+
+ var keywordID = $closeAction.data('id');
+ customConfirm({
+ content: '确认将该项目设置为精选项目吗?',
+ ok: function(){
+ $.ajax({
+ url: '/admins/projects/' + keywordID,
+ method: 'PUT',
+ dataType: 'json',
+ data: {
+ project: {
+ is_pinned: true,
+ }
+ },
+ success: function() {
+ showSuccessNotify();
+ $closeAction.hide();
+ $uncloseAction.show();
+ $(".project-item-"+keywordID).children('td').eq(4).text("√")
+ }
+ });
+ }
+ });
+ });
+
+ // unclose user
+ $('.project-list-container').on('click', '.unpinned-action', function(){
+ var $uncloseAction = $(this);
+ var $closeAction = $uncloseAction.siblings('.pinned-action');
+
+ var keywordID = $uncloseAction.data('id');
+ customConfirm({
+ content: '确认取消该精选项目吗?',
+ ok: function () {
+ $.ajax({
+ url: '/admins/projects/' + keywordID,
+ method: 'PUT',
+ dataType: 'json',
+ data: {
+ project: {
+ is_pinned: false,
+ }
+ },
+ success: function() {
+ showSuccessNotify();
+ $closeAction.show();
+ $uncloseAction.hide();
+ $(".project-item-"+keywordID).children('td').eq(4).text("")
+ }
+ });
+ }
+ })
+ });
+})
\ No newline at end of file
diff --git a/app/assets/javascripts/admins/reversed_keywords/index.js b/app/assets/javascripts/admins/reversed_keywords/index.js
new file mode 100644
index 000000000..df14219d2
--- /dev/null
+++ b/app/assets/javascripts/admins/reversed_keywords/index.js
@@ -0,0 +1,76 @@
+/*
+ * @Description: Do not edit
+ * @Date: 2021-08-31 11:16:45
+ * @LastEditors: viletyy
+ * @Author: viletyy
+ * @LastEditTime: 2021-08-31 14:19:46
+ * @FilePath: /forgeplus/app/assets/javascripts/admins/reversed_keywords/index.js
+ */
+$(document).on('turbolinks:load', function(){
+
+ var showSuccessNotify = function() {
+ $.notify({
+ message: '操作成功'
+ },{
+ type: 'success'
+ });
+ }
+
+ // close user
+ $('.reversed-keyword-list-container').on('click', '.close-action', function(){
+ var $closeAction = $(this);
+ var $uncloseAction = $closeAction.siblings('.unclose-action');
+
+ var keywordID = $closeAction.data('id');
+ customConfirm({
+ content: '确认关闭限制吗?',
+ ok: function(){
+ $.ajax({
+ url: '/admins/reversed_keywords/' + keywordID,
+ method: 'PUT',
+ dataType: 'json',
+ data: {
+ reversed_keyword: {
+ closed: true
+ }
+ },
+ success: function() {
+ showSuccessNotify();
+ $closeAction.hide();
+ $uncloseAction.show();
+ $(".reversed-keyword-item-"+keywordID).children('td').eq(3).text("")
+ }
+ });
+ }
+ });
+ });
+
+ // unclose user
+ $('.reversed-keyword-list-container').on('click', '.unclose-action', function(){
+ var $uncloseAction = $(this);
+ var $closeAction = $uncloseAction.siblings('.close-action');
+
+ var keywordID = $uncloseAction.data('id');
+ customConfirm({
+ content: '确认开启限制吗?',
+ ok: function () {
+ $.ajax({
+ url: '/admins/reversed_keywords/' + keywordID,
+ method: 'PUT',
+ dataType: 'json',
+ data: {
+ reversed_keyword: {
+ closed: false
+ }
+ },
+ success: function() {
+ showSuccessNotify();
+ $closeAction.show();
+ $uncloseAction.hide();
+ $(".reversed-keyword-item-"+keywordID).children('td').eq(3).text("√")
+ }
+ });
+ }
+ })
+ });
+})
\ No newline at end of file
diff --git a/app/assets/javascripts/admins/shixun_settings/index.js b/app/assets/javascripts/admins/shixun_settings/index.js
index cd3445c62..06b0f37e4 100644
--- a/app/assets/javascripts/admins/shixun_settings/index.js
+++ b/app/assets/javascripts/admins/shixun_settings/index.js
@@ -1,7 +1,15 @@
+/*
+ * @Description: Do not edit
+ * @Date: 2021-07-16 11:58:16
+ * @LastEditors: viletyy
+ * @Author: viletyy
+ * @LastEditTime: 2021-08-31 14:48:59
+ * @FilePath: /forgeplus/app/assets/javascripts/admins/shixun_settings/index.js
+ */
$(document).on('turbolinks:load', function() {
if ($('body.admins-shixun-settings-index-page').length > 0) {
- let searchContainer = $(".shixun-settings-list-form");
- let searchForm = $("form.search-form",searchContainer);
+ var searchContainer = $(".shixun-settings-list-form");
+ var searchForm = $("form.search-form",searchContainer);
searchContainer.on('change', '.shixun-settings-select', function(){
searchForm.find('input[type="submit"]').trigger('click');
diff --git a/app/assets/javascripts/admins/system_notifications/index.js b/app/assets/javascripts/admins/system_notifications/index.js
new file mode 100644
index 000000000..90809b344
--- /dev/null
+++ b/app/assets/javascripts/admins/system_notifications/index.js
@@ -0,0 +1,76 @@
+/*
+ * @Description: Do not edit
+ * @Date: 2021-08-31 11:16:45
+ * @LastEditors: viletyy
+ * @Author: viletyy
+ * @LastEditTime: 2021-08-31 14:19:46
+ * @FilePath: /forgeplus/app/assets/javascripts/admins/system_notifications/index.js
+ */
+$(document).on('turbolinks:load', function(){
+
+ var showSuccessNotify = function() {
+ $.notify({
+ message: '操作成功'
+ },{
+ type: 'success'
+ });
+ }
+
+ // close user
+ $('.system-notification-list-container').on('click', '.close-action', function(){
+ var $closeAction = $(this);
+ var $uncloseAction = $closeAction.siblings('.unclose-action');
+
+ var keywordID = $closeAction.data('id');
+ customConfirm({
+ content: '确认取消置顶吗?',
+ ok: function(){
+ $.ajax({
+ url: '/admins/system_notifications/' + keywordID,
+ method: 'PUT',
+ dataType: 'json',
+ data: {
+ system_notification: {
+ is_top: false
+ }
+ },
+ success: function() {
+ showSuccessNotify();
+ $closeAction.hide();
+ $uncloseAction.show();
+ $(".system-notification-item-"+keywordID).children('td').eq(3).text("")
+ }
+ });
+ }
+ });
+ });
+
+ // unclose user
+ $('.system-notification-list-container').on('click', '.unclose-action', function(){
+ var $uncloseAction = $(this);
+ var $closeAction = $uncloseAction.siblings('.close-action');
+
+ var keywordID = $uncloseAction.data('id');
+ customConfirm({
+ content: '确认置顶吗?',
+ ok: function () {
+ $.ajax({
+ url: '/admins/system_notifications/' + keywordID,
+ method: 'PUT',
+ dataType: 'json',
+ data: {
+ system_notification: {
+ is_top: true
+ }
+ },
+ success: function() {
+ showSuccessNotify();
+ $closeAction.show();
+ $uncloseAction.hide();
+ $(".system-notification-item-"+keywordID).children('td').eq(3).text("√")
+ }
+ });
+ }
+ })
+ });
+})
\ No newline at end of file
diff --git a/app/assets/javascripts/helps/faqs.js b/app/assets/javascripts/helps/faqs.js
new file mode 100644
index 000000000..dee720fac
--- /dev/null
+++ b/app/assets/javascripts/helps/faqs.js
@@ -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.
diff --git a/app/assets/stylesheets/admin.scss b/app/assets/stylesheets/admin.scss
index b11b0518c..03c3970a6 100644
--- a/app/assets/stylesheets/admin.scss
+++ b/app/assets/stylesheets/admin.scss
@@ -125,6 +125,80 @@ input.form-control {
font-size: 0.8rem;
}
+ &-title {
+ color: #23272B;
+ font-size: 1rem;
+ }
+}
+
+.attachment-item {
+ display: flex;
+
+ &-img {
+ display: block;
+ width: 160px;
+ height: 160px;
+ background: #e9ecef;
+ }
+
+ &-upload {
+ cursor: pointer;
+ position: absolute;
+ top: 0;
+ width: 160px;
+ height: 160px;
+ background: #e9ecef;
+ border: 1px solid #ced4da;
+
+ &::before {
+ content: '';
+ position: absolute;
+ top: 54px;
+ left: 78px;
+ width: 2px;
+ height: 52px;
+ background: #495057;
+ }
+
+ &::after {
+ content: '';
+ position: absolute;
+ top: 78px;
+ left: 54px;
+ width: 52px;
+ height: 2px;
+ background: #495057;
+ }
+ }
+
+ &-left {
+ position: relative;
+ width: 160px;
+ height: 160px;
+
+ &.has-img {
+ .attachment-item-upload {
+ display: none;
+ }
+
+ &:hover {
+ .attachment-item-upload {
+ display: block;
+ background: rgba(145, 145, 145, 0.8);
+ }
+ }
+ }
+ }
+
+ &-right {
+ padding-top: 100px;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ color: #777777;
+ font-size: 0.8rem;
+ }
+
&-title {
color: #23272B;
font-size: 1rem;
diff --git a/app/assets/stylesheets/admins/faqs.scss b/app/assets/stylesheets/admins/faqs.scss
new file mode 100644
index 000000000..8d1a08d30
--- /dev/null
+++ b/app/assets/stylesheets/admins/faqs.scss
@@ -0,0 +1,3 @@
+// Place all the styles related to the admins/faqs controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/app/assets/stylesheets/helps/faqs.scss b/app/assets/stylesheets/helps/faqs.scss
new file mode 100644
index 000000000..4d3d2bc49
--- /dev/null
+++ b/app/assets/stylesheets/helps/faqs.scss
@@ -0,0 +1,3 @@
+// Place all the styles related to the helps/faqs controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb
index 889b7ccfa..da51f0ba5 100644
--- a/app/controllers/accounts_controller.rb
+++ b/app/controllers/accounts_controller.rb
@@ -1,4 +1,5 @@
class AccountsController < ApplicationController
+ include ApplicationHelper
#skip_before_action :check_account, :only => [:logout]
@@ -8,7 +9,9 @@ class AccountsController < ApplicationController
# 其他平台同步注册的用户
def remote_register
+ Register::RemoteForm.new(remote_register_params).validate!
username = params[:username]&.gsub(/\s+/, "")
+ tip_exception("无法使用以下关键词:#{username},请重新命名") if ReversedKeyword.check_exists?(username)
email = params[:email]&.gsub(/\s+/, "")
phone = params[:phone]&.gsub(/\s+/, "")
password = params[:password]
@@ -111,60 +114,46 @@ class AccountsController < ApplicationController
# 用户注册
# 注意:用户注册需要兼顾本地版,本地版是不需要验证码及激活码以及使用授权的,注册完成即可使用
# params[:login] 邮箱或者手机号
+ # params[:namespace] 登录名
# params[:code] 验证码
# code_type 1:注册手机验证码 8:邮箱注册验证码
- # 本地forge注册入口
+ # 本地forge注册入口需要重新更改逻辑
def register
+ # type只可能是1或者8
+ user = nil
begin
- # 查询验证码是否正确;type只可能是1或者8
- type = phone_mail_type(params[:login].strip)
- # code = params[:code].strip
+ Register::Form.new(register_params).validate!
- if type == 1
- uid_logger("start register by phone: type is #{type}")
- pre = 'p'
- email = nil
- phone = params[:login]
- # verifi_code = VerificationCode.where(phone: phone, code: code, code_type: 1).last
- # TODO: 暂时限定邮箱注册
- return normal_status(-1, '只支持邮箱注册')
- else
- uid_logger("start register by email: type is #{type}")
- pre = 'm'
- email = params[:login]
- phone = nil
- return normal_status(-1, "该邮箱已注册") if User.exists?(mail: params[:login])
- return normal_status(-1, "邮箱格式错误") unless params[:login] =~ CustomRegexp::EMAIL
- # verifi_code = VerificationCode.where(email: email, code: code, code_type: 8).last
- end
- # uid_logger("start register: verifi_code is #{verifi_code}, code is #{code}, time is #{Time.now.to_i - verifi_code.try(:created_at).to_i}")
- # check_code = (verifi_code.try(:code) == code.strip && (Time.now.to_i - verifi_code.created_at.to_i) <= 10*60)
- # todo 上线前请删除万能验证码"513231"
- return normal_status(-1, "8~16位密码,支持字母数字和符号") unless params[:password] =~ CustomRegexp::PASSWORD
+ user = Users::RegisterService.call(register_params)
+ password = register_params[:password].strip
- code = generate_identifier User, 8, pre
- login = pre + code
- @user = User.new(admin: false, login: login, mail: email, phone: phone, type: "User")
- @user.password = params[:password]
- # 现在因为是验证码,所以在注册的时候就可以激活
- @user.activate
- # 必须要用save操作,密码的保存是在users中
-
- interactor = Gitea::RegisterInteractor.call({username: login, email: email, password: params[:password]})
+ # gitea用户注册, email, username, password
+ interactor = Gitea::RegisterInteractor.call({username: user.login, email: user.mail, password: password})
if interactor.success?
gitea_user = interactor.result
- result = Gitea::User::GenerateTokenService.new(login, params[:password]).call
- @user.gitea_token = result['sha1']
- @user.gitea_uid = gitea_user[:body]['id']
- if @user.save!
- UserExtension.create!(user_id: @user.id)
- successful_authentication(@user)
- normal_status("注册成功")
+ result = Gitea::User::GenerateTokenService.call(user.login, password)
+ user.gitea_token = result['sha1']
+ user.gitea_uid = gitea_user[:body]['id']
+ if user.save!
+ UserExtension.create!(user_id: user.id)
+ successful_authentication(user)
+ render_ok
end
else
tip_exception(-1, interactor.error)
end
+ rescue Register::BaseForm::EmailError => e
+ render_error(-2, e.message)
+ rescue Register::BaseForm::LoginError => e
+ render_error(-3, e.message)
+ rescue Register::BaseForm::PhoneError => e
+ render_error(-4, e.message)
+ rescue Register::BaseForm::PasswordFormatError => e
+ render_error(-5, e.message)
+ rescue Register::BaseForm::VerifiCodeError => e
+ render_error(-6, e.message)
rescue Exception => e
+ Gitea::User::DeleteService.call(user.login) unless user.nil?
uid_logger_error(e.message)
tip_exception(-1, e.message)
end
@@ -172,6 +161,7 @@ class AccountsController < ApplicationController
# 用户登录
def login
+ Users::LoginForm.new(account_params).validate!
@user = User.try_to_login(params[:login], params[:password])
return normal_status(-2, "错误的账号或密码") if @user.blank?
@@ -198,6 +188,25 @@ class AccountsController < ApplicationController
# session[:user_id] = @user.id
end
+ def change_password
+ @user = User.find_by(login: params[:login])
+ return render_error("未找到相关用户!") if @user.blank?
+ return render_error("旧密码不正确") unless @user.check_password?(params[:old_password])
+
+ sync_params = {
+ password: params[:password].to_s,
+ email: @user.mail
+ }
+
+ interactor = Gitea::User::UpdateInteractor.call(@user.login, sync_params)
+ if interactor.success?
+ @user.update_attribute(:password, params[:password])
+ render_ok
+ else
+ render_error(interactor.error)
+ end
+ end
+
# 忘记密码
def reset_password
begin
@@ -277,7 +286,7 @@ class AccountsController < ApplicationController
# 发送验证码
# params[:login] 手机号或者邮箱号
- # params[:type]为事件通知类型 1:用户注册注册 2:忘记密码 3: 绑定手机 4: 绑定邮箱, 5: 验收手机号有效 # 如果有新的继续后面加
+ # params[:type]为事件通知类型 1:用户注册 2:忘记密码 3: 绑定手机 4: 绑定邮箱, 5: 验收手机号有效 # 如果有新的继续后面加
# 发送验证码:send_type 1:注册手机验证码 2:找回密码手机验证码 3:找回密码邮箱验证码 4:绑定手机 5:绑定邮箱
# 6:手机验证码登录 7:邮箱验证码登录 8:邮箱注册验证码 9: 验收手机号有效
def get_verification_code
@@ -291,19 +300,22 @@ class AccountsController < ApplicationController
sign = Digest::MD5.hexdigest("#{OPENKEY}#{value}")
tip_exception(501, "请求不合理") if sign != params[:smscode]
+ logger.info "########### 验证码:#{verification_code}"
logger.info("########get_verification_code: login_type: #{login_type}, send_type:#{send_type}, ")
# 记录验证码
check_verification_code(verification_code, send_type, value)
- sucess_status
+ render_ok
end
- # 1 手机类型;0 邮箱类型
- # 注意新版的login是自动名生成的
- def phone_mail_type value
- value =~ /^1\d{10}$/ ? 1 : 0
+ # check user's login or email or phone is used
+ # params[:value] 手机号或者邮箱号或者登录名
+ # params[:type] 为事件类型 1:登录名(login) 2:email(邮箱) 3:phone(手机号)
+ def check
+ Register::CheckColumnsForm.new(check_params).validate!
+ render_ok
end
-
+
private
# type 事件类型 1:用户注册 2:忘记密码 3: 绑定手机 4: 绑定邮箱, 5: 验证手机号是否有效 # 如果有新的继续后面加
@@ -346,4 +358,20 @@ class AccountsController < ApplicationController
params.require(:user).permit(:login, :email, :phone)
end
+ def account_params
+ params.require(:account).permit(:login, :password)
+ end
+
+ def check_params
+ params.permit(:type, :value)
+ end
+
+ def register_params
+ params.permit(:login, :namespace, :password, :code)
+ end
+
+ def remote_register_params
+ params.permit(:username, :email, :password, :platform)
+ end
+
end
diff --git a/app/controllers/admins/auth_schools_controller.rb b/app/controllers/admins/auth_schools_controller.rb
index e7c0253b6..39d5b9683 100644
--- a/app/controllers/admins/auth_schools_controller.rb
+++ b/app/controllers/admins/auth_schools_controller.rb
@@ -32,7 +32,7 @@ class Admins::AuthSchoolsController < Admins::BaseController
def search_manager
school = School.find_by(id: params[:school_id])
user_ids = school&.ec_school_users&.pluck(:user_id)
- @users = User.where.not(id: user_ids).where("concat(lastname, firstname) like ?", "%#{params[:name].strip.to_s}%").limit(10)
+ @users = User.where.not(id: user_ids).where("CONCAT(lastname, firstname) like ? OR nickname like ?", "%#{params[:name].strip.to_s}%", "%#{params[:name].strip.to_s}%").limit(10)
end
# 添加认证学校管理员
diff --git a/app/controllers/admins/edu_settings_controller.rb b/app/controllers/admins/edu_settings_controller.rb
new file mode 100644
index 000000000..9d9334b23
--- /dev/null
+++ b/app/controllers/admins/edu_settings_controller.rb
@@ -0,0 +1,55 @@
+class Admins::EduSettingsController < Admins::BaseController
+ before_action :find_setting, only: [:edit,:update, :destroy]
+
+ def index
+ default_sort('id', 'desc')
+
+ edu_settings = Admins::EduSettingQuery.call(params)
+ @edu_settings = paginate edu_settings
+ end
+
+ def new
+ @edu_setting = EduSetting.new
+ end
+
+ def edit
+ end
+
+ def create
+ @edu_setting = EduSetting.new(edu_setting_params)
+ if @edu_setting.save
+ redirect_to admins_edu_settings_path
+ flash[:success] = '创建成功'
+ else
+ redirect_to admins_edu_settings_path
+ flash[:danger] = @edu_setting.errors.full_messages.join(",")
+ end
+ end
+
+ def update
+ if @edu_setting.update!(edu_setting_params)
+ flash[:success] = '更新成功'
+ else
+ flash[:danger] = @edu_setting.errors.full_messages.join(",")
+ end
+ redirect_to admins_edu_settings_path
+ end
+
+ def destroy
+ if @edu_setting.destroy!
+ flash[:success] = '删除成功'
+ else
+ lash[:danger] = '删除失败'
+ end
+ redirect_to admins_edu_settings_path
+ end
+
+ private
+ def find_setting
+ @edu_setting ||= EduSetting.find(params[:id])
+ end
+
+ def edu_setting_params
+ params.require(:edu_setting).permit(:name, :value, :description)
+ end
+end
diff --git a/app/controllers/admins/faqs_controller.rb b/app/controllers/admins/faqs_controller.rb
new file mode 100644
index 000000000..250d3d60a
--- /dev/null
+++ b/app/controllers/admins/faqs_controller.rb
@@ -0,0 +1,58 @@
+class Admins::FaqsController < Admins::BaseController
+ before_action :find_faq, only: [:edit,:update, :destroy]
+
+ def index
+ sort_by = Faq.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'updated_at'
+ sort_direction = %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc'
+
+ keyword = params[:keyword].to_s.strip
+ collection = Faq.search_question(keyword).order("#{sort_by} #{sort_direction}")
+ @faqs = paginate collection
+ end
+
+ def new
+ @faq = Faq.new
+ end
+
+ def edit
+ end
+
+ def update
+ begin
+ @faq.update!(faq_params)
+ flash[:success] = '修改成功'
+ rescue Exception
+ flash[:danger] = @faq.errors.full_messages.to_sentence
+ end
+
+ redirect_to admins_faqs_path
+ end
+
+ def destroy
+ @faq.destroy
+
+ redirect_to admins_faqs_path
+ flash[:success] = "删除成功"
+ end
+
+ def create
+ @faq = Faq.new(faq_params)
+ begin
+ @faq.save!
+ flash[:success] = '创建成功'
+ rescue Exception
+ flash[:danger] = @faq.errors.full_messages.to_sentence
+ end
+ redirect_to admins_faqs_path
+ end
+
+ private
+ def find_faq
+ @faq = Faq.find params[:id]
+ end
+
+ def faq_params
+ params.require(:faq).permit(:question, :url)
+ end
+
+end
diff --git a/app/controllers/admins/laboratories_controller.rb b/app/controllers/admins/laboratories_controller.rb
index f397b0f34..7044b6218 100644
--- a/app/controllers/admins/laboratories_controller.rb
+++ b/app/controllers/admins/laboratories_controller.rb
@@ -33,7 +33,7 @@ class Admins::LaboratoriesController < Admins::BaseController
keyword = params[:keyword].to_s.strip
if keyword.present?
like_sql = 'shixuns.name LIKE :keyword OR CONCAT(users.lastname, users.firstname) LIKE :keyword '\
- 'OR mirror_repositories.name LIKE :keyword'
+ 'users.nickname LIKE :keyword OR mirror_repositories.name LIKE :keyword'
shixuns = shixuns.joins(:user, :mirror_repositories).where(like_sql, keyword: "%#{keyword}%")
end
@@ -48,7 +48,7 @@ class Admins::LaboratoriesController < Admins::BaseController
keyword = params[:keyword].to_s.strip
if keyword.present?
- like_sql = 'subjects.name LIKE :keyword OR CONCAT(users.lastname, users.firstname) LIKE :keyword'
+ like_sql = 'subjects.name LIKE :keyword OR CONCAT(users.lastname, users.firstname) LIKE :keyword OR users.nickname LIKE :keyword'
subjects = subjects.joins(:user).where(like_sql, keyword: "%#{keyword}%")
end
diff --git a/app/controllers/admins/message_templates_controller.rb b/app/controllers/admins/message_templates_controller.rb
new file mode 100644
index 000000000..23c94e784
--- /dev/null
+++ b/app/controllers/admins/message_templates_controller.rb
@@ -0,0 +1,44 @@
+class Admins::MessageTemplatesController < Admins::BaseController
+ before_action :get_template, only: [:edit, :update, :destroy]
+
+ def index
+ message_templates = MessageTemplate.group(:type).count.keys
+ @message_templates = kaminari_array_paginate(message_templates)
+ end
+
+ def edit
+ end
+
+ def update
+ if @message_template.update_attributes(message_template_params)
+ redirect_to admins_message_templates_path
+ flash[:success] = '消息模版更新成功'
+ else
+ redirect_to admins_message_templates_path
+ flash[:danger] = @message_template.errors.full_messages.join(",")
+ end
+ end
+
+ def init_data
+ if MessageTemplate.build_init_data
+ redirect_to admins_message_templates_path
+ flash[:success] = '消息模版初始化成功'
+ else
+ redirect_to admins_message_templates_path
+ flash[:danger] = '消息模版初始化失败'
+ end
+ end
+
+ private
+ def message_template_params
+ params.require(@message_template.type.split("::").join("_").underscore.to_sym).permit!
+ end
+
+ def get_template
+ @message_template = MessageTemplate.find_by(id: params[:id])
+ unless @message_template.present?
+ redirect_to admins_message_templates_path
+ flash[:danger] = "消息模版不存在"
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/admins/project_categories_controller.rb b/app/controllers/admins/project_categories_controller.rb
index 944a2cf04..72cb833fa 100644
--- a/app/controllers/admins/project_categories_controller.rb
+++ b/app/controllers/admins/project_categories_controller.rb
@@ -3,8 +3,8 @@ class Admins::ProjectCategoriesController < Admins::BaseController
before_action :validate_names, only: [:create, :update]
def index
- sort_by = params[:sort_by] ||= 'created_at'
- sort_direction = params[:sort_direction] ||= 'desc'
+ sort_by = ProjectCategory.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_at'
+ sort_direction = %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc'
q = ProjectCategory.ransack(name_cont: params[:name])
project_categories = q.result(distinct: true).order("#{sort_by} #{sort_direction}")
@project_categories = paginate(project_categories)
@@ -22,7 +22,7 @@ class Admins::ProjectCategoriesController < Admins::BaseController
max_position_items = ProjectCategory.select(:id, :position).pluck(:position).reject!(&:blank?)
max_position = max_position_items.present? ? max_position_items.max.to_i : 0
- @project_category = ProjectCategory.new(name: @name,position: max_position)
+ @project_category = ProjectCategory.new(name: @name,position: max_position, pinned_index: params[:project_category][:pinned_index].to_i)
if @project_category.save
redirect_to admins_project_categories_path
flash[:success] = '创建成功'
@@ -33,17 +33,18 @@ class Admins::ProjectCategoriesController < Admins::BaseController
end
def update
- if @project_category.update_attribute(:name, @name)
+ if @project_category.update_attributes({name: @name, pinned_index: params[:project_category][:pinned_index].to_i})
+ save_image_file(params[:logo], 'logo')
redirect_to admins_project_categories_path
flash[:success] = '更新成功'
else
redirect_to admins_project_categories_path
- flash[:success] = '更新失败'
+ flash[:danger] = '更新失败'
end
end
def destroy
- if @project_language.destroy
+ if @project_category.destroy
redirect_to admins_project_categories_path
flash[:success] = "删除成功"
else
@@ -80,4 +81,12 @@ class Admins::ProjectCategoriesController < Admins::BaseController
flash[:danger] = '分类已存在'
end
end
+
+ def save_image_file(file, type)
+ return unless file.present? && file.is_a?(ActionDispatch::Http::UploadedFile)
+
+ file_path = Util::FileManage.source_disk_filename(@project_category, type)
+ File.delete(file_path) if File.exist?(file_path) # 删除之前的文件
+ Util.write_file(file, file_path)
+ end
end
\ No newline at end of file
diff --git a/app/controllers/admins/project_ignores_controller.rb b/app/controllers/admins/project_ignores_controller.rb
index 427ee86b8..360f189c0 100644
--- a/app/controllers/admins/project_ignores_controller.rb
+++ b/app/controllers/admins/project_ignores_controller.rb
@@ -3,8 +3,8 @@ class Admins::ProjectIgnoresController < Admins::BaseController
before_action :validate_params, only: [:create, :update]
def index
- sort_by = params[:sort_by] ||= 'created_at'
- sort_direction = params[:sort_direction] ||= 'desc'
+ sort_by = Ignore.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_at'
+ sort_direction = %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc'
q = Ignore.ransack(name_cont: params[:search])
project_ignores = q.result(distinct: true).order("#{sort_by} #{sort_direction}")
@project_ignores = paginate(project_ignores)
diff --git a/app/controllers/admins/project_languages_controller.rb b/app/controllers/admins/project_languages_controller.rb
index e188b75ef..0f26f25bb 100644
--- a/app/controllers/admins/project_languages_controller.rb
+++ b/app/controllers/admins/project_languages_controller.rb
@@ -3,8 +3,8 @@ class Admins::ProjectLanguagesController < Admins::BaseController
before_action :validate_names, only: [:create, :update]
def index
- sort_by = params[:sort_by] ||= 'created_at'
- sort_direction = params[:sort_direction] ||= 'desc'
+ sort_by = ProjectLanguage.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_at'
+ sort_direction = %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc'
q = ProjectLanguage.ransack(name_cont: params[:search])
project_languages = q.result(distinct: true).order("#{sort_by} #{sort_direction}")
@project_languages = paginate(project_languages)
diff --git a/app/controllers/admins/project_licenses_controller.rb b/app/controllers/admins/project_licenses_controller.rb
index bc5789026..5c16a884b 100644
--- a/app/controllers/admins/project_licenses_controller.rb
+++ b/app/controllers/admins/project_licenses_controller.rb
@@ -3,8 +3,8 @@ class Admins::ProjectLicensesController < Admins::BaseController
before_action :validate_params, only: [:create, :update]
def index
- sort_by = params[:sort_by] ||= 'created_at'
- sort_direction = params[:sort_direction] ||= 'desc'
+ sort_by = License.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_at'
+ sort_direction = %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc'
q = License.ransack(name_cont: params[:search])
project_licenses = q.result(distinct: true).order("#{sort_by} #{sort_direction}")
@project_licenses = paginate(project_licenses)
diff --git a/app/controllers/admins/projects_controller.rb b/app/controllers/admins/projects_controller.rb
index 9e33b6cef..40676fa66 100644
--- a/app/controllers/admins/projects_controller.rb
+++ b/app/controllers/admins/projects_controller.rb
@@ -1,15 +1,34 @@
class Admins::ProjectsController < Admins::BaseController
- before_action :load_project, only: [:sync_phenglei_user]
+ before_action :find_project, only: [:edit, :update, :sync_phenglei_user]
def index
- sort_by = params[:sort_by] ||= 'created_on'
- sort_direction = params[:sort_direction] ||= 'desc'
-
+ sort_by = Project.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_on'
+ sort_direction = %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc'
search = params[:search].to_s.strip
projects = Project.where("name like ?", "%#{search}%").order("#{sort_by} #{sort_direction}")
@projects = paginate projects.includes(:owner, :members, :issues, :versions, :attachments, :project_score, :license)
end
+ def edit ;end
+
+ def update
+ respond_to do |format|
+ if @project.update_attributes(project_update_params)
+ format.html do
+ redirect_to admins_projects_path
+ flash[:sucess] = "更新成功"
+ end
+ format.js {render_ok}
+ else
+ format.html do
+ redirect_to admins_projects_path
+ flash[:danger] = "更新失败"
+ end
+ format.js {render_js_error}
+ end
+ end
+ end
+
def destroy
project = Project.find_by!(id: params[:id])
ActiveRecord::Base.transaction do
@@ -35,8 +54,12 @@ class Admins::ProjectsController < Admins::BaseController
end
end
- private
- def load_project
- @project = Project.find_by!(id: params[:id])
+ private
+ def find_project
+ @project = Project.find_by_id(params[:id])
+ end
+
+ def project_update_params
+ params.require(:project).permit(:is_pinned, :recommend, :recommend_index)
end
end
\ No newline at end of file
diff --git a/app/controllers/admins/reversed_keywords_controller.rb b/app/controllers/admins/reversed_keywords_controller.rb
new file mode 100644
index 000000000..8a8442f72
--- /dev/null
+++ b/app/controllers/admins/reversed_keywords_controller.rb
@@ -0,0 +1,84 @@
+class Admins::ReversedKeywordsController < Admins::BaseController
+ before_action :get_keyword, only: [:edit,:update, :destroy]
+ # before_action :validate_identifer, only: [:create, :update]
+
+ def index
+ sort_by = ReversedKeyword.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_at'
+ sort_direction = %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc'
+ q = ReversedKeyword.ransack(identifier_cont: params[:search])
+ keywords = q.result(distinct: true).order("#{sort_by} #{sort_direction}")
+ @keywords = paginate(keywords)
+
+ end
+
+ def new
+ @keyword = ReversedKeyword.new
+ end
+
+ def edit
+ end
+
+ def create
+ @keyword = ReversedKeyword.new(keyword_params)
+ if @keyword.save
+ redirect_to admins_reversed_keywords_path
+ flash[:success] = '系统保留关键词创建成功'
+ else
+ redirect_to admins_reversed_keywords_path
+ flash[:danger] = @keyword.errors.full_messages.join(",")
+ end
+ end
+
+ def update
+
+ respond_to do |format|
+ if @keyword.update_attributes(keyword_params)
+ format.html do
+ redirect_to admins_reversed_keywords_path
+ flash[:success] = '系统保留关键词更新成功'
+ end
+ format.js {render_ok}
+ else
+ format.html do
+ redirect_to admins_reversed_keywords_path
+ flash[:danger] = @keyword.errors.full_messages.join(",")
+ end
+ format.js {render_js_error}
+ end
+ end
+ end
+
+ def destroy
+ if @keyword.destroy
+ redirect_to admins_reversed_keywords_path
+ flash[:success] = "系统保留关键词删除成功"
+ else
+ redirect_to admins_reversed_keywords_path
+ flash[:danger] = "系统保留关键词删除失败"
+ end
+ end
+
+ private
+ def keyword_params
+ params.require(:reversed_keyword).permit!
+ end
+
+ def get_keyword
+ @keyword = ReversedKeyword.find_by(id: params[:id])
+ unless @keyword.present?
+ redirect_to admins_reversed_keywords_path
+ flash[:danger] = "系统保留关键词不存在"
+ end
+ end
+
+ def validate_identifer
+ identifer = keyword_params[:identifier].to_s.downcase
+ if identifer.blank?
+ redirect_to admins_reversed_keywords_path
+ flash[:danger] = '系统保留关键词标识不能为空'
+ elsif ProjectLanguage.exists?(name: identifer)
+ redirect_to admins_reversed_keywords_path
+ flash[:danger] = '系统保留关键词已存在'
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/admins/sites_controller.rb b/app/controllers/admins/sites_controller.rb
new file mode 100644
index 000000000..f3da4ccf2
--- /dev/null
+++ b/app/controllers/admins/sites_controller.rb
@@ -0,0 +1,56 @@
+class Admins::SitesController < Admins::BaseController
+ before_action :find_site, only: [:edit,:update, :destroy]
+
+ def index
+ default_sort('id', 'desc')
+
+ sites = Admins::SiteQuery.call(params)
+ @sites = paginate sites
+ end
+
+ def new
+ @site = Site.new
+ end
+
+ def edit
+ end
+
+ def create
+ @site = Site.new(site_params)
+ if @site.save
+ redirect_to admins_sites_path
+ flash[:success] = '创建成功'
+ else
+ redirect_to admins_sites_path
+ flash[:danger] = @site.errors.full_messages.join(",")
+ end
+ end
+
+ def update
+ if @site.update!(site_params)
+ flash[:success] = '更新成功'
+ else
+ flash[:danger] = @site.errors.full_messages.join(",")
+ end
+ redirect_to admins_sites_path
+ end
+
+ def destroy
+ if @site.destroy!
+ flash[:success] = '删除成功'
+ else
+ lash[:danger] = '删除失败'
+ end
+ redirect_to admins_sites_path
+ end
+
+ private
+ def find_site
+ @site ||= Site.find(params[:id])
+ end
+
+ def site_params
+ params.require(:site).permit(:name, :url, :key, :site_type)
+ end
+
+end
diff --git a/app/controllers/admins/system_notifications_controller.rb b/app/controllers/admins/system_notifications_controller.rb
new file mode 100644
index 000000000..e2081f1a2
--- /dev/null
+++ b/app/controllers/admins/system_notifications_controller.rb
@@ -0,0 +1,75 @@
+class Admins::SystemNotificationsController < Admins::BaseController
+ before_action :get_notification, only: [:history, :edit,:update, :destroy]
+ # before_action :validate_identifer, only: [:create, :update]
+
+ def index
+ sort_by = SystemNotification.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_at'
+ sort_direction = %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc'
+ q = SystemNotification.ransack(subject_cont: params[:search])
+ notifications = q.result(distinct: true).reorder("#{sort_by} #{sort_direction},created_at desc")
+ @notifications = paginate(notifications)
+ end
+
+ def history
+ @users = @notification.users
+ end
+
+ def new
+ @notification = SystemNotification.new
+ end
+
+ def edit
+ end
+
+ def create
+ @notification = SystemNotification.new(notification_params)
+ if @notification.save
+ redirect_to admins_system_notifications_path
+ flash[:success] = '系统消息创建成功'
+ else
+ redirect_to admins_system_notifications_path
+ flash[:danger] = @notification.errors.full_messages.join(",")
+ end
+ end
+
+ def update
+ respond_to do |format|
+ if @notification.update_attributes(notification_params)
+ format.html do
+ redirect_to admins_system_notifications_path
+ flash[:success] = '系统消息更新成功'
+ end
+ format.js {render_ok}
+ else
+ format.html do
+ redirect_to admins_system_notifications_path
+ flash[:danger] = @notification.errors.full_messages.join(",")
+ end
+ format.js {render_js_error}
+ end
+ end
+ end
+
+ def destroy
+ if @notification.destroy
+ redirect_to admins_system_notifications_path
+ flash[:success] = "系统消息删除成功"
+ else
+ redirect_to admins_system_notifications_path
+ flash[:danger] = "系统消息删除失败"
+ end
+ end
+
+ private
+ def notification_params
+ params.require(:system_notification).permit!
+ end
+
+ def get_notification
+ @notification = SystemNotification.find_by(id: params[:id])
+ unless @notification.present?
+ redirect_to admins_system_notifications_path
+ flash[:danger] = "系统消息不存在"
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/admins/users_controller.rb b/app/controllers/admins/users_controller.rb
index 991487ed8..7020b6d98 100644
--- a/app/controllers/admins/users_controller.rb
+++ b/app/controllers/admins/users_controller.rb
@@ -102,6 +102,10 @@ class Admins::UsersController < Admins::BaseController
@user = User.find(params[:id])
end
+ def finder_user
+ @user = User.find(params[:id])
+ end
+
def update_params
params.require(:user).permit(%i[lastname nickname gender identity technical_title student_id is_shixun_marker
mail phone location location_city school_id department_id admin business is_test
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index ed3049d2e..794bf3c6a 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -70,49 +70,11 @@ class ApplicationController < ActionController::Base
(current_user.professional_certification && (ue.teacher? || ue.professional?))
end
- def shixun_marker
- unless current_user.is_shixun_marker? || current_user.admin_or_business?
- tip_exception(403, "..")
- end
- end
-
- # 实训的访问权限
- def shixun_access_allowed
- if !current_user.shixun_permission(@shixun)
- tip_exception(403, "..")
- end
- end
def admin_or_business?
User.current.admin? || User.current.business?
end
- # 访问课堂时没权限直接弹加入课堂的弹框 :409
- def user_course_identity
- @user_course_identity = current_user.course_identity(@course)
- if @user_course_identity > Course::STUDENT && @course.is_public == 0
- tip_exception(401, "..") unless User.current.logged?
- check_account
- tip_exception(@course.excellent ? 410 : 409, "您没有权限进入")
- end
- if @user_course_identity > Course::CREATOR && @user_course_identity <= Course::STUDENT && @course.tea_id != current_user.id
- # 实名认证和职业认证的身份判断
- tip_exception(411, "你的实名认证和职业认证审核未通过") if @course.authentication &&
- @course.professional_certification && (!current_user.authentication && !current_user.professional_certification)
- tip_exception(411, "你的实名认证审核未通过") if @course.authentication && !current_user.authentication
- tip_exception(411, "你的职业认证审核未通过") if @course.professional_certification && !current_user.professional_certification
- end
- uid_logger("###############user_course_identity:#{@user_course_identity}")
- end
-
- # 题库的访问权限
- def bank_visit_auth
- tip_exception(-2,"未通过职业认证") if current_user.is_teacher? && !current_user.certification_teacher? && !current_user.admin_or_business? && @bank.user_id != current_user.id && @bank.is_public
- tip_exception(403, "无权限") unless @bank.user_id == current_user.id || current_user.admin_or_business? ||
- (current_user.certification_teacher? && @bank.is_public)
- end
-
-
# 判断用户的邮箱或者手机是否可用
# params[:type] 1: 注册;2:忘记密码;3:绑定
def check_mail_and_phone_valid login, type
@@ -120,16 +82,16 @@ class ApplicationController < ActionController::Base
login =~ /^[a-zA-Z0-9]+([._\\]*[a-zA-Z0-9])$/
tip_exception(-2, "请输入正确的手机号或邮箱")
end
- # 考虑到安全参数问题,多一次查询,去掉Union
- user = User.where(phone: login).first || User.where(mail: login).first
- if type.to_i == 1 && !user.nil?
+
+ user_exist = Owner.exists?(phone: login) || Owner.exists?(mail: login)
+ if user_exist && type.to_i == 1
tip_exception(-2, "该手机号码或邮箱已被注册")
- elsif type.to_i == 2 && user.nil?
+ elsif type.to_i == 2 && !user_exist
tip_exception(-2, "该手机号码或邮箱未注册")
- elsif type.to_i == 3 && user.present?
+ elsif type.to_i == 3 && user_exist
tip_exception(-2, "该手机号码或邮箱已绑定")
end
- sucess_status
+ render_ok
end
# 发送及记录激活码
@@ -140,7 +102,7 @@ class ApplicationController < ActionController::Base
when 1, 2, 4, 9
# 手机类型的发送
sigle_para = {phone: value}
- status = Educoder::Sms.send(mobile: value, code: code)
+ status = Gitlink::Sms.send(mobile: value, code: code)
tip_exception(-2, code_msg(status)) if status != 0
when 8, 3, 5
# 邮箱类型的发送
@@ -186,26 +148,6 @@ class ApplicationController < ActionController::Base
end
end
- def find_course
- return normal_status(2, '缺少course_id参数!') if params[:course_id].blank?
- @course = Course.find(params[:course_id])
- tip_exception(404, "") if @course.is_delete == 1 && !current_user.admin_or_business?
- rescue Exception => e
- tip_exception(e.message)
- end
-
- def course_manager
- return normal_status(403, '只有课堂管理员才有权限') if @user_course_identity > Course::CREATOR
- end
-
- def find_board
- return normal_status(2, "缺少board_id参数") if params[:board_id].blank?
- @board = Board.find(params[:board_id])
- rescue Exception => e
- uid_logger_error(e.message)
- tip_exception(e.message)
- end
-
def validate_type(object_type)
normal_status(2, "参数") if params.has_key?(:sort_type) && !SORT_TYPE.include?(params[:sort_type].strip)
end
@@ -215,21 +157,6 @@ class ApplicationController < ActionController::Base
@page_size = params[:page_size] || 15
end
- # 课堂教师权限
- def teacher_allowed
- logger.info("#####identity: #{current_user.course_identity(@course)}")
- unless current_user.course_identity(@course) < Course::STUDENT
- normal_status(403, "")
- end
- end
-
- # 课堂教师、课堂管理员、超级管理员的权限(不包含助教)
- def teacher_or_admin_allowed
- unless current_user.course_identity(@course) < Course::ASSISTANT_PROFESSOR
- normal_status(403, "")
- end
- end
-
def require_admin
normal_status(403, "") unless User.current.admin?
end
@@ -246,9 +173,17 @@ class ApplicationController < ActionController::Base
tip_exception(401, "请登录后再操作") unless User.current.logged?
end
+ def require_profile_completed
+ tip_exception(411, "请完善资料后再操作") unless User.current.profile_is_completed?
+ end
+
+ def require_user_profile_completed(user)
+ tip_exception(412, "请用户完善资料后再操作") unless user.profile_is_completed?
+ end
+
# 异常提醒
def tip_exception(status = -1, message)
- raise Educoder::TipException.new(status, message)
+ raise Gitlink::TipException.new(status, message)
end
def missing_template
@@ -257,7 +192,7 @@ class ApplicationController < ActionController::Base
# 弹框提醒
def tip_show_exception(status = -2, message)
- raise Educoder::TipException.new(status, message)
+ raise Gitlink::TipException.new(status, message)
end
def normal_status(status = 0, message)
@@ -272,7 +207,7 @@ class ApplicationController < ActionController::Base
# 资料是否完善
def check_account
- if !current_user.profile_completed?
+ if !current_user. profile_is_completed?
#info_url = '/account/profile'
tip_exception(402, nil)
end
@@ -337,18 +272,18 @@ class ApplicationController < ActionController::Base
# 测试版前端需求
logger.info("subdomain:#{request.subdomain}")
- if request.subdomain != "www"
- if params[:debug] == 'teacher' #todo 为了测试,记得讲debug删除
- User.current = User.find 81403
- elsif params[:debug] == 'student'
- User.current = User.find 8686
- elsif params[:debug] == 'admin'
- logger.info "@@@@@@@@@@@@@@@@@@@@@@ debug mode....."
- user = User.find 36480
- User.current = user
- cookies.signed[:user_id] = user.id
- end
- end
+ # if request.subdomain != "www"
+ # if params[:debug] == 'teacher' #todo 为了测试,记得讲debug删除
+ # User.current = User.find 81403
+ # elsif params[:debug] == 'student'
+ # User.current = User.find 8686
+ # elsif params[:debug] == 'admin'
+ # logger.info "@@@@@@@@@@@@@@@@@@@@@@ debug mode....."
+ # user = User.find 36480
+ # User.current = user
+ # cookies.signed[:user_id] = user.id
+ # end
+ # end
# User.current = User.find 81403
end
@@ -438,7 +373,7 @@ class ApplicationController < ActionController::Base
JSON.parse(res)
rescue Exception => e
uid_logger_error("--uri_exec: exception #{e.message}")
- raise Educoder::TipException.new("实训平台繁忙(繁忙等级:84)")
+ raise Gitlink::TipException.new("实训平台繁忙(繁忙等级:84)")
end
end
@@ -457,7 +392,7 @@ class ApplicationController < ActionController::Base
end
rescue Exception => e
uid_logger("--uri_exec: exception #{e.message}")
- raise Educoder::TipException.new(message)
+ raise Gitlink::TipException.new(message)
end
end
@@ -481,7 +416,7 @@ class ApplicationController < ActionController::Base
end
rescue Exception => e
uid_logger("--uri_exec: exception #{e.message}")
- raise Educoder::TipException.new("服务器繁忙")
+ raise Gitlink::TipException.new("服务器繁忙")
end
end
@@ -615,8 +550,8 @@ class ApplicationController < ActionController::Base
end
# 排序
- rorder = option[:order] || "updated_at"
- b_order = option[:b_order] || "desc"
+ rorder = UserExtension.column_names.include?(option[:order]) ? option[:order] : "updated_at"
+ b_order = %w(desc asc).include?(option[:b_order]) ? option[:b_order] : "desc"
if rorder == "created_at" || rorder == "work_score"
work_list = work_list.order("graduation_works.#{rorder} #{b_order}")
elsif rorder == "student_id"
@@ -653,8 +588,8 @@ class ApplicationController < ActionController::Base
# 获取Oauth Client
def get_client(site)
- client_id = Rails.configuration.educoder['client_id']
- client_secret = Rails.configuration.educoder['client_secret']
+ client_id = Rails.configuration.Gitlink['client_id']
+ client_secret = Rails.configuration.Gitlink['client_secret']
OAuth2::Client.new(client_id, client_secret, site: site)
end
@@ -754,10 +689,10 @@ class ApplicationController < ActionController::Base
if @project and current_user.can_read_project?(@project)
logger.info "###########: has project and can read project"
@project
- elsif @project && current_user.is_a?(AnonymousUser)
- logger.info "###########:This is AnonymousUser"
- @project = nil if !@project.is_public?
- render_forbidden and return
+ # elsif @project && current_user.is_a?(AnonymousUser)
+ # logger.info "###########:This is AnonymousUser"
+ # @project = nil if !@project.is_public?
+ # render_forbidden and return
else
logger.info "###########:project not found"
@project = nil
@@ -774,6 +709,33 @@ class ApplicationController < ActionController::Base
Rails.application.config_for(:configuration)['platform_url'] || request.base_url
end
+ def image_type?(str)
+ default_type = %w(png jpg gif tif psd svg bmp webp jpeg ico psd)
+ default_type.include?(str&.downcase)
+ end
+
+ def convert_image!
+ @image = params[:image]
+ @image = @image.nil? && params[:user].present? ? params[:user][:image] : @image
+ return unless @image.present?
+ max_size = EduSetting.get('upload_avatar_max_size') || 2 * 1024 * 1024 # 2M
+ if @image.class == ActionDispatch::Http::UploadedFile
+ return render_error('请上传文件') if @image.size.zero?
+ return render_error('文件大小超过限制') if @image.size > max_size.to_i
+ return render_error('头像格式不正确!') unless image_type?(File.extname(@image.original_filename.to_s)[1..-1])
+ else
+ image = @image.to_s.strip
+ return render_error('请上传正确的图片') if image.blank?
+ @image = Util.convert_base64_image(image, max_size: max_size.to_i)
+ end
+ rescue Base64ImageConverter::Error => ex
+ render_error(ex.message)
+ end
+
+ def avatar_path(object)
+ ApplicationController.helpers.disk_filename(object.class, object.id)
+ end
+
private
def object_not_found
uid_logger("Missing template or cant't find record, responding with 404")
@@ -826,4 +788,8 @@ class ApplicationController < ActionController::Base
HotSearchKeyword.add(keyword)
end
+ def find_atme_receivers
+ @atme_receivers = User.where(login: params[:receivers_login])
+ end
+
end
diff --git a/app/controllers/applied_projects_controller.rb b/app/controllers/applied_projects_controller.rb
new file mode 100644
index 000000000..e2f55b27a
--- /dev/null
+++ b/app/controllers/applied_projects_controller.rb
@@ -0,0 +1,13 @@
+class AppliedProjectsController < ApplicationController
+ before_action :require_login
+ def create
+ @applied_project = Projects::ApplyJoinService.call(current_user, applied_params)
+ rescue Projects::ApplyJoinService::Error => ex
+ render_error(ex.message)
+ end
+
+ private
+ def applied_params
+ params.require(:applied_project).permit(:code, :role)
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/attachments_controller.rb b/app/controllers/attachments_controller.rb
index c24ed60a0..130e09e3e 100644
--- a/app/controllers/attachments_controller.rb
+++ b/app/controllers/attachments_controller.rb
@@ -32,8 +32,17 @@ class AttachmentsController < ApplicationController
def get_file
normal_status(-1, "参数缺失") if params[:download_url].blank?
url = URI.encode(params[:download_url].to_s.gsub("http:", "https:"))
- response = Faraday.get(url)
- filename = params[:download_url].to_s.split("/").pop()
+ if url.starts_with?(base_url)
+ domain = Gitea.gitea_config[:domain]
+ api_url = Gitea.gitea_config[:base_url]
+ url = url.split(base_url)[1].gsub("api", "repos").gsub('?filepath=', '/').gsub('&', '?')
+ request_url = [domain, api_url, url, "?ref=#{params[:ref]}&access_token=#{current_user&.gitea_token}"].join
+ response = Faraday.get(request_url)
+ filename = url.to_s.split("/").pop()
+ else
+ response = Faraday.get(url)
+ filename = params[:download_url].to_s.split("/").pop()
+ end
send_data(response.body.force_encoding("UTF-8"), filename: filename, type: "application/octet-stream", disposition: 'attachment')
end
@@ -196,7 +205,7 @@ class AttachmentsController < ApplicationController
end
def file_save_to_ucloud(path, file, content_type)
- ufile = Educoder::Ufile.new(
+ ufile = Gitlink::Ufile.new(
ucloud_public_key: edu_setting('public_key'),
ucloud_private_key: edu_setting('private_key'),
ucloud_public_read: true,
diff --git a/app/controllers/compare_controller.rb b/app/controllers/compare_controller.rb
index 63ca58aa6..07691d79f 100644
--- a/app/controllers/compare_controller.rb
+++ b/app/controllers/compare_controller.rb
@@ -6,26 +6,50 @@ class CompareController < ApplicationController
end
def show
+ load_compare_params
compare
+ @merge_status, @merge_message = get_merge_message
end
private
+ def get_merge_message
+ if @base.blank? || @head.blank?
+ return -2, "请选择分支"
+ else
+ if @head.include?(":")
+ fork_project = @project.forked_projects.joins(:owner).where(users: {login: @head.to_s.split("/")[0]}).take
+ return -2, "请选择正确的仓库" unless fork_project.present?
+ @exist_pullrequest = @project.pull_requests.where(is_original: true, head: @head.to_s.split(":")[1], base: @base, status: 0, fork_project_id: fork_project.id).take
+ else
+ @exist_pullrequest = @project.pull_requests.where(is_original: false, head: @base, base: @head, status: 0).take
+ end
+ if @exist_pullrequest.present?
+ return -2, "在这些分支之间的合并请求已存在:#{@exist_pullrequest.try(:title)} "
+ else
+ if @compare_result["Commits"].blank? && @compare_result["Diff"].blank?
+ return -2, "分支内容相同,无需创建合并请求"
+ end
+ end
+ end
+ return 0, "可以合并"
+ end
+
def compare
- base, head = compare_params
# TODO: 处理fork的项目向源项目发送PR的base、head参数问题
@compare_result ||=
- head.include?(":") ? gitea_compare(base, head) : gitea_compare(head, base)
+ @head.include?(":") ? gitea_compare(@base, @head) : gitea_compare(@head, @base)
end
- def compare_params
- base = Addressable::URI.unescape(params[:base])
- head = params[:head].include?('json') ? params[:head]&.split('.json')[0] : params[:head]
-
- [base, head]
+ def load_compare_params
+ # @base = Addressable::URI.unescape(params[:base])
+ @base = params[:base].include?(":") ? Addressable::URI.unescape(params[:base].split(":")[0]) + ':' + Base64.decode64(params[:base].split(":")[1]) : Base64.decode64(params[:base])
+ @head = params[:head].include?('.json') ? params[:head][0..-6] : params[:head]
+ # @head = Addressable::URI.unescape(@head)
+ @head = @head.include?(":") ? Addressable::URI.unescape(@head.split(":")[0]) + ':' + Base64.decode64(@head.split(":")[1]) : Base64.decode64(@head)
end
def gitea_compare(base, head)
- Gitea::Repository::Commits::CompareService.call(@owner.login, @project.identifier, base, head)
+ Gitea::Repository::Commits::CompareService.call(@owner.login, @project.identifier, CGI.escape(base), CGI.escape(head), current_user.gitea_token)
end
end
diff --git a/app/controllers/composes_controller.rb b/app/controllers/composes_controller.rb
index c1a7f524f..006604f29 100644
--- a/app/controllers/composes_controller.rb
+++ b/app/controllers/composes_controller.rb
@@ -3,13 +3,12 @@ class ComposesController < ApplicationController
before_action :find_compose, except: [:index, :new,:create]
def index
- @order_type = params[:order] || "created_at"
@search_name = params[:search]
composes = Compose.compose_includes
if @search_name.present?
composes = composes.where("title like ?", "%#{@search_name}%")
end
- composes = composes.order("#{@order_type} desc")
+ composes = composes.order("#{order_type} desc")
@page = params[:page] || 1
@limit = params[:limit] || 15
@composes_size = composes.size
@@ -96,4 +95,8 @@ class ComposesController < ApplicationController
end
end
+ def order_type
+ Compose.column_names.include?(params[:order_type]) ? params[:order_type] : 'created_at'
+ end
+
end
\ No newline at end of file
diff --git a/app/controllers/concerns/.keep b/app/controllers/concerns/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/app/controllers/concerns/acceleratorable.rb b/app/controllers/concerns/acceleratorable.rb
new file mode 100644
index 000000000..5243ac538
--- /dev/null
+++ b/app/controllers/concerns/acceleratorable.rb
@@ -0,0 +1,36 @@
+module Acceleratorable
+ extend ActiveSupport::Concern
+
+ def enable_accelerator?(clone_addr)
+ is_foreign_url?(clone_addr) && config_accelerator?
+ end
+
+ def accelerator_url(repo_name)
+ [accelerator_domain, accelerator_username, "#{repo_name}.git"].join('/')
+ end
+
+ def github_domain
+ 'github.com'
+ end
+
+ def gitlab_domain
+ 'gitlab.com'
+ end
+
+ def accelerator_domain
+ Gitea.gitea_config[:accelerator]["domain"]
+ end
+
+ def accelerator_username
+ Gitea.gitea_config[:accelerator]["access_key_id"]
+ end
+
+ def config_accelerator?
+ Gitea.gitea_config[:accelerator].present?
+ end
+
+ def is_foreign_url?(clone_addr)
+ clone_addr.include?(github_domain) || clone_addr.include?(gitlab_domain)
+ end
+
+end
\ No newline at end of file
diff --git a/app/controllers/concerns/ci/cloud_account_manageable.rb b/app/controllers/concerns/ci/cloud_account_manageable.rb
index 13edb09c6..9f0998edc 100644
--- a/app/controllers/concerns/ci/cloud_account_manageable.rb
+++ b/app/controllers/concerns/ci/cloud_account_manageable.rb
@@ -179,7 +179,7 @@ module Ci::CloudAccountManageable
def drone_oauth_user!(url, state)
logger.info "[drone] drone_oauth_user url: #{url}"
- conn = Faraday.new(url: url) do |req|
+ conn = Faraday.new(url: "#{Gitea.gitea_config[:domain]}#{url}") do |req|
req.request :url_encoded
req.adapter Faraday.default_adapter
req.headers["cookie"] = "_session_=#{SecureRandom.hex(28)}; _oauth_state_=#{state}"
@@ -188,7 +188,8 @@ module Ci::CloudAccountManageable
response = conn.get
logger.info "[drone] response headers: #{response.headers}"
- response.headers['location'].include?('error') ? false : true
+ true
+ # response.headers['location'].include?('error') ? false : true
end
private
diff --git a/app/controllers/concerns/controller_rescue_handler.rb b/app/controllers/concerns/controller_rescue_handler.rb
index 6ff15cfbc..acd9aa2ea 100644
--- a/app/controllers/concerns/controller_rescue_handler.rb
+++ b/app/controllers/concerns/controller_rescue_handler.rb
@@ -20,7 +20,7 @@ module ControllerRescueHandler
end
# rescue_from ActionView::MissingTemplate, with: :object_not_found
# rescue_from ActiveRecord::RecordNotFound, with: :object_not_found
- rescue_from Educoder::TipException, with: :tip_show
+ rescue_from Gitlink::TipException, with: :tip_show
rescue_from ::ActionView::MissingTemplate, with: :missing_template
rescue_from ActiveRecord::RecordNotFound, with: :object_not_found
rescue_from ActionController::ParameterMissing, with: :render_parameter_missing
diff --git a/app/controllers/concerns/git_common.rb b/app/controllers/concerns/git_common.rb
index eab069b8e..fbda95f55 100644
--- a/app/controllers/concerns/git_common.rb
+++ b/app/controllers/concerns/git_common.rb
@@ -36,10 +36,10 @@ module GitCommon
begin
@commits = GitService.commits(repo_path: @repo_path)
logger.info("git first commit is #{@commits.try(:first)}")
- raise Educoder::TipException.new("请先创建版本库") if @commits.nil?
+ raise Gitlink::TipException.new("请先创建版本库") if @commits.nil?
rescue Exception => e
uid_logger_error(e.message)
- raise Educoder::TipException.new("提交记录异常")
+ raise Gitlink::TipException.new("提交记录异常")
end
end
diff --git a/app/controllers/concerns/git_helper.rb b/app/controllers/concerns/git_helper.rb
index d8479d458..ede90dc6c 100644
--- a/app/controllers/concerns/git_helper.rb
+++ b/app/controllers/concerns/git_helper.rb
@@ -34,7 +34,7 @@ module GitHelper
rescue Exception => e
Rails.logger.error(e.message)
- raise Educoder::TipException.new("文档内容获取异常")
+ raise Gitlink::TipException.new("文档内容获取异常")
end
end
@@ -64,7 +64,7 @@ module GitHelper
# 版本库Fork功能
def project_fork(container, original_rep_path, username)
- raise Educoder::TipException.new("fork源路径为空,fork失败!") if original_rep_path.blank?
+ raise Gitlink::TipException.new("fork源路径为空,fork失败!") if original_rep_path.blank?
# 将要生成的仓库名字
new_repo_name = "#{username.try(:strip)}/#{container.try(:identifier)}#{ Time.now.strftime("%Y%m%d%H%M%S")}"
# uid_logger("start fork container: repo_name is #{new_repo_name}")
diff --git a/app/controllers/concerns/render_helper.rb b/app/controllers/concerns/render_helper.rb
index fad401539..81e85d1ce 100644
--- a/app/controllers/concerns/render_helper.rb
+++ b/app/controllers/concerns/render_helper.rb
@@ -3,8 +3,8 @@ module RenderHelper
render json: { status: 0, message: 'success' }.merge(data)
end
- def render_error(message = '')
- render json: { status: -1, message: message }
+ def render_error(status = -1, message = '')
+ render json: { status: status, message: message }
end
def render_not_acceptable(message = '请求已拒绝')
diff --git a/app/controllers/concerns/repository/languages_percentagable.rb b/app/controllers/concerns/repository/languages_percentagable.rb
index 620d93bf9..dc6abcb9c 100644
--- a/app/controllers/concerns/repository/languages_percentagable.rb
+++ b/app/controllers/concerns/repository/languages_percentagable.rb
@@ -14,6 +14,6 @@ module Repository::LanguagesPercentagable
hash.transform_values { |v|
ActionController::Base.helpers
.number_to_percentage((v * 100.0 / total_byte_size), precision: 1)
- }
+ }.select{|k,v| v != "0.0%"}
end
end
diff --git a/app/controllers/edu_settings_controller.rb b/app/controllers/edu_settings_controller.rb
index d3b796da7..340b03ddc 100644
--- a/app/controllers/edu_settings_controller.rb
+++ b/app/controllers/edu_settings_controller.rb
@@ -29,10 +29,8 @@ class EduSettingsController < ApplicationController
respond_to do |format|
if @edu_setting.save
- format.html { redirect_to @edu_setting, notice: 'Edu setting was successfully created.' }
format.json { render :show, status: :created, location: @edu_setting }
else
- format.html { render :new }
format.json { render json: @edu_setting.errors, status: :unprocessable_entity }
end
end
@@ -43,10 +41,8 @@ class EduSettingsController < ApplicationController
def update
respond_to do |format|
if @edu_setting.update(edu_setting_params)
- format.html { redirect_to @edu_setting, notice: 'Edu setting was successfully updated.' }
format.json { render :show, status: :ok, location: @edu_setting }
else
- format.html { render :edit }
format.json { render json: @edu_setting.errors, status: :unprocessable_entity }
end
end
@@ -57,7 +53,6 @@ class EduSettingsController < ApplicationController
def destroy
@edu_setting.destroy
respond_to do |format|
- format.html { redirect_to edu_settings_url, notice: 'Edu setting was successfully destroyed.' }
format.json { head :no_content }
end
end
diff --git a/app/controllers/forks_controller.rb b/app/controllers/forks_controller.rb
index cb18091c1..c740c8b03 100644
--- a/app/controllers/forks_controller.rb
+++ b/app/controllers/forks_controller.rb
@@ -1,5 +1,6 @@
class ForksController < ApplicationController
before_action :require_login
+ before_action :require_profile_completed, only: [:create]
before_action :load_project
before_action :authenticate_project!, :authenticate_user!
@@ -12,7 +13,7 @@ class ForksController < ApplicationController
if current_user&.id == @project.user_id
render_result(-1, "自己不能fork自己的项目")
elsif Project.exists?(user_id: current_user.id, identifier: @project.identifier)
- render_result(-1, "fork失败,你已拥有了这个项目")
+ render_result(0, "fork失败,你已拥有了这个项目")
end
# return if current_user != @project.owner
# render_result(-1, "自己不能fork自己的项目")
diff --git a/app/controllers/helps/faqs_controller.rb b/app/controllers/helps/faqs_controller.rb
new file mode 100644
index 000000000..a18f49270
--- /dev/null
+++ b/app/controllers/helps/faqs_controller.rb
@@ -0,0 +1,8 @@
+class Helps::FaqsController < ApplicationController
+ skip_before_action :check_sign, :user_setup
+
+ def index
+ faqs = Faq.select_without_id.order(updated_at: :desc)
+ render json: faqs.as_json(:except => [:id])
+ end
+end
diff --git a/app/controllers/issue_tags_controller.rb b/app/controllers/issue_tags_controller.rb
index 312de7842..011735ff7 100644
--- a/app/controllers/issue_tags_controller.rb
+++ b/app/controllers/issue_tags_controller.rb
@@ -2,15 +2,12 @@ class IssueTagsController < ApplicationController
before_action :require_login, except: [:index]
before_action :load_repository
before_action :set_user
- before_action :check_issue_permission, except: :index
+ before_action :check_issue_tags_permission
before_action :set_issue_tag, only: [:edit, :update, :destroy]
def index
- order_name = params[:order_name] || "created_at"
- order_type = params[:order_type] || "desc"
-
- issue_tags = @project.issue_tags.order("#{order_name} #{order_type}")
+ issue_tags = @project.issue_tags.reorder("#{order_name} #{order_type}")
@user_admin_or_member = current_user.present? && (current_user.admin || @project.member?(current_user))
@page = params[:page] || 1
@limit = params[:limit] || 15
@@ -20,7 +17,7 @@ class IssueTagsController < ApplicationController
def create
- title = params[:name].to_s.strip.first(10)
+ title = params[:name].to_s.strip.first(15)
desc = params[:description].to_s.first(30)
color = params[:color] || "#ccc"
@@ -32,7 +29,7 @@ class IssueTagsController < ApplicationController
if title.present?
if IssueTag.exists?(name: title, project_id: @project.id)
- normal_status(-1, "标签已存在")
+ normal_status(-1, "项目标记已存在")
else
ActiveRecord::Base.transaction do
begin
@@ -40,12 +37,12 @@ class IssueTagsController < ApplicationController
if issue_tag.save
# gitea_tag = Gitea::Labels::CreateService.new(current_user, @repository.try(:identifier), tag_params).call
# if gitea_tag && issue_tag.update_attributes(gid: gitea_tag["id"], gitea_url: gitea_tag["url"])
- # normal_status(0, "标签创建成功")
+ normal_status(0, "项目标记创建成功!")
# else
- # normal_status(-1, "标签创建失败")
+ # normal_status(-1, "项目标记创建失败")
# end
else
- normal_status(-1, "标签创建失败")
+ normal_status(-1, "项目标记创建失败")
end
rescue => e
puts "create version release error: #{e.message}"
@@ -54,7 +51,7 @@ class IssueTagsController < ApplicationController
end
end
else
- normal_status(-1, "标签名称不能为空")
+ normal_status(-1, "项目标记名称不能为空")
end
end
@@ -63,8 +60,8 @@ class IssueTagsController < ApplicationController
end
def update
- title = params[:name]
- desc = params[:description]
+ title = params[:name].to_s.strip.first(15)
+ desc = params[:description].to_s.first(30)
color = params[:color] || "#ccc"
tag_params = {
@@ -74,19 +71,19 @@ class IssueTagsController < ApplicationController
}
if title.present?
if IssueTag.exists?(name: title, project_id: @project.id) && (@issue_tag.name != title)
- normal_status(-1, "标签已存在")
+ normal_status(-1, "项目标记已存在")
else
ActiveRecord::Base.transaction do
begin
if @issue_tag.update_attributes(tag_params)
# gitea_tag = Gitea::Labels::UpdateService.new(current_user, @repository.try(:identifier),@issue_tag.try(:gid), tag_params).call
# if gitea_tag
- # normal_status(0, "标签更新成功")
+ # normal_status(0, "项目标记更新成功")
# else
- # normal_status(-1, "标签更新失败")
+ # normal_status(-1, "项目标记更新失败")
# end
else
- normal_status(-1, "标签更新失败")
+ normal_status(-1, "项目标记更新失败")
end
rescue => e
puts "create version release error: #{e.message}"
@@ -95,7 +92,7 @@ class IssueTagsController < ApplicationController
end
end
else
- normal_status(-1, "标签名称不能为空")
+ normal_status(-1, "项目标记名称不能为空")
end
end
@@ -105,12 +102,12 @@ class IssueTagsController < ApplicationController
if @issue_tag.destroy
# issue_tag = Gitea::Labels::DeleteService.new(@user, @repository.try(:identifier), @issue_tag.try(:gid)).call
# if issue_tag
- # normal_status(0, "标签删除成功")
+ # normal_status(0, "项目标记删除成功")
# else
- # normal_status(-1, "标签删除失败")
+ # normal_status(-1, "项目标记删除失败")
# end
else
- normal_status(-1, "标签删除失败")
+ normal_status(-1, "项目标记删除失败")
end
rescue => e
puts "create version release error: #{e.message}"
@@ -125,17 +122,27 @@ class IssueTagsController < ApplicationController
@user = @project.owner
end
- def check_issue_permission
- unless @project.member?(current_user) || current_user.admin?
- normal_status(-1, "您没有权限")
+ def check_issue_tags_permission
+ unless @project.manager?(current_user) || current_user.admin?
+ return render_forbidden('你不是管理员,没有权限操作')
end
end
def set_issue_tag
@issue_tag = IssueTag.find_by_id(params[:id])
unless @issue_tag.present?
- normal_status(-1, "标签不存在")
+ normal_status(-1, "项目标记不存在")
end
end
+ private
+
+ def order_name
+ IssueTag.column_names.include?(params[:order_name]) ? params[:order_name] : 'created_at'
+ end
+
+ def order_type
+ %w(desc asc).include?(params[:order_type]) ? params[:order_type] : 'desc'
+ end
+
end
diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb
index 0723090f3..816cbbe20 100644
--- a/app/controllers/issues_controller.rb
+++ b/app/controllers/issues_controller.rb
@@ -1,25 +1,34 @@
class IssuesController < ApplicationController
before_action :require_login, except: [:index, :show, :index_chosen]
+ before_action :require_profile_completed, only: [:create]
before_action :load_project
before_action :set_user
+ before_action :check_menu_authorize, except: [:index_chosen]
before_action :check_issue_permission
+ before_action :operate_issue_permission, only:[:create, :update, :destroy, :clean, :series_update, :copy]
before_action :check_project_public, only: [:index ,:show, :copy, :index_chosen, :close_issue]
before_action :set_issue, only: [:edit, :update, :destroy, :show, :copy, :close_issue, :lock_issue]
+ before_action :check_token_enough, :find_atme_receivers, only: [:create, :update]
include ApplicationHelper
include TagChosenHelper
def index
- @user_admin_or_member = current_user.present? && current_user.logged? && (current_user.admin || @project.member?(current_user))
+ @user_operate_issue = current_user.present? && current_user.logged? && (current_user.admin || @project.member?(current_user))
+ @user_admin_or_member = current_user.present? && current_user.logged? && (current_user.admin || @project.member?(current_user) || @project.is_public?)
issues = @project.issues.issue_issue.issue_index_includes
issues = issues.where(is_private: false) unless @user_admin_or_member
- @all_issues_size = issues.size
- @open_issues_size = issues.where.not(status_id: 5).size
- @close_issues_size = issues.where(status_id: 5).size
- @assign_to_me_size = issues.where(assigned_to_id: current_user&.id).size
- @my_published_size = issues.where(author_id: current_user&.id).size
+ @all_issues = issues
+ @filter_issues = @all_issues
+ @filter_issues = @filter_issues.where.not(status_id: IssueStatus::CLOSED) if params[:status_type].to_i == IssueStatus::ADD
+ @filter_issues = @filter_issues.where(status_id: IssueStatus::CLOSED) if params[:status_type].to_i == IssueStatus::SOLVING
+ @filter_issues = @filter_issues.where("subject LIKE ? OR description LIKE ? ", "%#{params[:search]}%", "%#{params[:search]}%") if params[:search].present?
+ @open_issues = @all_issues.where.not(status_id: IssueStatus::CLOSED)
+ @close_issues = @all_issues.where(status_id: IssueStatus::CLOSED)
+ @assign_to_me = @filter_issues.where(assigned_to_id: current_user&.id)
+ @my_published = @filter_issues.where(author_id: current_user&.id)
scopes = Issues::ListQueryService.call(issues,params.delete_if{|k,v| v.blank?}, "Issue")
@issues_size = scopes.size
@issues = paginate(scopes)
@@ -100,46 +109,59 @@ class IssuesController < ApplicationController
end
def create
- if params[:subject].blank?
- normal_status(-1, "标题不能为空")
- elsif params[:subject].to_s.size > 255
- normal_status(-1, "标题不能超过255个字符")
- else
- issue_params = issue_send_params(params)
-
- @issue = Issue.new(issue_params)
- if @issue.save!
- if params[:attachment_ids].present?
- params[:attachment_ids].each do |id|
- attachment = Attachment.select(:id, :container_id, :container_type)&.find_by_id(id)
- unless attachment.blank?
- attachment.container = @issue
- attachment.author_id = current_user.id
- attachment.description = ""
- attachment.save
- end
+ issue_params = issue_send_params(params)
+ Issues::CreateForm.new({subject:issue_params[:subject]}).validate!
+ @issue = Issue.new(issue_params)
+ if @issue.save!
+ 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?
+ params[:attachment_ids].each do |id|
+ attachment = Attachment.select(:id, :container_id, :container_type)&.find_by_id(id)
+ unless attachment.blank?
+ attachment.container = @issue
+ attachment.author_id = current_user.id
+ attachment.description = ""
+ attachment.save
end
end
- if params[:issue_tag_ids].present?
+ end
+ if params[:issue_tag_ids].present?
+ if params[:issue_tag_ids].is_a?(Array) && params[:issue_tag_ids].size > 1
+ return normal_status(-1, "最多只能创建一个标记。")
+ elsif params[:issue_tag_ids].is_a?(Array) && params[:issue_tag_ids].size == 1
params[:issue_tag_ids].each do |tag|
IssueTagsRelate.create!(issue_id: @issue.id, issue_tag_id: tag)
end
- end
- if params[:assigned_to_id].present?
- Tiding.create!(user_id: params[:assigned_to_id], trigger_user_id: current_user.id,
- container_id: @issue.id, container_type: 'Issue',
- parent_container_id: @project.id, parent_container_type: "Project",
- tiding_type: 'issue', status: 0)
- end
-
- @issue.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: "create")
- render json: {status: 0, message: "创建成", id: @issue.id}
- else
- normal_status(-1, @issue.errors.messages.values[0][0])
+ else
+ return normal_status(-1, "请输入正确的标记。")
+ end
end
- end
+ if params[:assigned_to_id].present?
+ Tiding.create!(user_id: params[:assigned_to_id], trigger_user_id: current_user.id,
+ container_id: @issue.id, container_type: 'Issue',
+ parent_container_id: @project.id, parent_container_type: "Project",
+ tiding_type: 'issue', status: 0)
+ end
+ #为悬赏任务时, 扣除当前用户的积分
+ if params[:issue_type].to_s == "2"
+ post_to_chain("minus", params[:token].to_i, current_user.try(:login))
+ end
+
+ @issue.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: "create")
+
+ 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
+
+ render json: {status: 0, message: "创建成功", id: @issue.id}
+ else
+ normal_status(-1, "创建失败")
+ end
+ rescue Exception => exception
+ puts exception.message
+ normal_status(-1, exception.message)
end
def edit
@@ -150,30 +172,35 @@ class IssuesController < ApplicationController
def update
issue_params = issue_send_params(params).except(:issue_classify, :author_id, :project_id)
return normal_status(-1, "您没有权限修改token") if @issue.will_save_change_to_token? && @issue.user_id != current_user&.id
- if params[:issue_tag_ids].present? && !@issue&.issue_tags_relates.where(issue_tag_id: params[:issue_tag_ids]).exists?
- @issue&.issue_tags_relates&.destroy_all
- params[:issue_tag_ids].each do |tag|
- IssueTagsRelate.create(issue_id: @issue.id, issue_tag_id: tag)
- end
+ last_token = @issue.token
+ last_status_id = @issue.status_id
+ @issue&.issue_tags_relates&.destroy_all if params[:issue_tag_ids].blank?
+ if params[:issue_tag_ids].present?
+ if params[:issue_tag_ids].is_a?(Array) && params[:issue_tag_ids].size > 1
+ return normal_status(-1, "最多只能创建一个标记。")
+ elsif params[:issue_tag_ids].is_a?(Array) && params[:issue_tag_ids].size == 1
+ @issue&.issue_tags_relates&.destroy_all
+ params[:issue_tag_ids].each do |tag|
+ IssueTagsRelate.create!(issue_id: @issue.id, issue_tag_id: tag)
+ end
+ else
+ return normal_status(-1, "请输入正确的标记。")
+ end
end
- if @issue.update_attributes(issue_params)
- issue_files = params[:attachment_ids]
- change_files = false
- issue_file_ids = []
-
- if issue_files.present?
- change_files = true
- issue_files.each do |id|
- attachment = Attachment.select(:id, :container_id, :container_type)&.find_by_id(id)
- unless attachment.blank?
- attachment.container = @issue
- attachment.author_id = current_user.id
- attachment.description = ""
- attachment.save
- end
+ if issue_files.present?
+ change_files = true
+ issue_files.each do |id|
+ attachment = Attachment.select(:id, :container_id, :container_type)&.find_by_id(id)
+ unless attachment.blank?
+ attachment.container = @issue
+ attachment.author_id = current_user.id
+ attachment.description = ""
+ attachment.save
end
end
+ end
+
# if params[:issue_tag_ids].present?
# issue_current_tags = @issue&.issue_tags&.select(:id)&.pluck(:id)
@@ -189,20 +216,69 @@ class IssuesController < ApplicationController
# end
# end
- if params[:status_id].to_i == 5
- @issue.issue_times.update_all(end_time: Time.now)
- #@issue.update_closed_issues_count_in_project!
+ if @issue.issue_type.to_s == "2" && params[:status_id].to_i == 5 && @issue.author_id != current_user.try(:id)
+ normal_status(-1, "不允许修改为关闭状态")
+ else
+ issue_params = issue_send_params(params).except(:issue_classify, :author_id, :project_id)
+ Issues::UpdateForm.new({subject:issue_params[:subject]}).validate!
+ if @issue.update_attributes(issue_params)
+ if @issue&.pull_request.present?
+ SendTemplateMessageJob.perform_later('PullRequestChanged', current_user.id, @issue&.pull_request&.id, @issue.previous_changes.slice(:assigned_to_id, :priority_id, :fixed_version_id, :issue_tags_value)) if Site.has_notice_menu?
+ SendTemplateMessageJob.perform_later('PullRequestAssigned', current_user.id, @issue&.pull_request&.id ) if @issue.previous_changes[:assigned_to_id].present? && Site.has_notice_menu?
+ else
+ previous_changes = @issue.previous_changes.slice(:status_id, :assigned_to_id, :tracker_id, :priority_id, :fixed_version_id, :done_ratio, :issue_tags_value, :branch_name)
+ if @issue.previous_changes[:start_date].present?
+ previous_changes.merge!(start_date: [@issue.previous_changes[:start_date][0].to_s, @issue.previous_changes[:start_date][1].to_s])
+ end
+ if @issue.previous_changes[:due_date].present?
+ previous_changes.merge!(due_date: [@issue.previous_changes[:due_date][0].to_s, @issue.previous_changes[:due_date][1].to_s])
+ end
+ if @issue.previous_changes[:status_id].present? && @issue.previous_changes[:status_id][1] == 5
+ @issue.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: ProjectTrend::CLOSE)
+ end
+ if @issue.previous_changes[:status_id].present? && @issue.previous_changes[:status_id][0] == 5
+ @issue.project_trends.where(action_type: ProjectTrend::CLOSE).destroy_all
+ end
+ SendTemplateMessageJob.perform_later('IssueChanged', current_user.id, @issue&.id, previous_changes) if Site.has_notice_menu?
+ SendTemplateMessageJob.perform_later('IssueAssigned', current_user.id, @issue&.id) if @issue.previous_changes[:assigned_to_id].present? && Site.has_notice_menu?
+ end
+ if params[:status_id].to_i == 5 #任务由非关闭状态到关闭状态时
+ @issue.issue_times.update_all(end_time: Time.now)
+ @issue.update_closed_issues_count_in_project!
+ if @issue.issue_type.to_s == "2" && last_status_id != 5
+ if @issue.assigned_to_id.present? && last_status_id == 3 #只有当用户完成100%时,才给token
+ post_to_chain("add", @issue.token, @issue.get_assign_user.try(:login))
+ else
+ post_to_chain("add", @issue.token, @issue.user.try(:login))
+ end
+ end
+ end
+
+ if @issue.issue_type.to_s == "2" && @issue.status_id != 5 && @issue.saved_change_to_attribute("token")
+ #表示修改token值
+ change_token = last_token - @issue.token
+ change_type = change_token > 0 ? "add" : "minus"
+ post_to_chain(change_type, change_token.abs, current_user.try(:login))
+ end
+ @issue.create_journal_detail(change_files, issue_files, issue_file_ids, current_user&.id) if @issue.previous_changes.present?
+
+ 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
+
+ normal_status(0, "更新成功")
+ else
+ normal_status(-1, "更新失败")
end
- @issue.create_journal_detail(change_files, issue_files, issue_file_ids, current_user&.id)
- normal_status(0, "更新成功")
- else
- normal_status(-1, @issue.errors.messages.values[0][0])
+
end
+ rescue Exception => exception
+ puts exception.message
+ normal_status(-1, exception.message)
end
def show
- @user_permission = current_user.present? && current_user.logged? && (!@issue.is_lock || @project.member?(current_user) || current_user.admin? || @issue.user == current_user)
+ @user_permission = current_user.present? && current_user.logged? && (@project.member?(current_user) || current_user.admin? || @issue.user == current_user)
@issue_attachments = @issue.attachments
@issue_user = @issue.user
@issue_assign_to = @issue.get_assign_user
@@ -218,8 +294,23 @@ class IssuesController < ApplicationController
end
def destroy
- if @issue.destroy
- normal_status(0, "删除成功")
+ begin
+ issue_type = @issue.issue_type
+ status_id = @issue.status_id
+ token = @issue.token
+ 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
+ if issue_type == "2" && status_id != 5
+ post_to_chain("add", token, login)
+ end
+ normal_status(0, "删除成功")
+ else
+ normal_status(-1, "删除失败")
+ end
+ rescue => exception
+ Rails.logger.info("#########_______exception.message_________##########{exception.message}")
+ normal_status(-1, "删除失败")
else
normal_status(-1, "删除失败")
end
@@ -227,8 +318,12 @@ class IssuesController < ApplicationController
def clean
issue_ids = params[:ids]
- if issue_ids.present?
- if Issue.where(id: issue_ids).destroy_all
+ issues = Issue.where(id: issue_ids, issue_type: "1")
+ if issues.present?
+ issues.find_each do |i|
+ SendTemplateMessageJob.perform_later('IssueDeleted', current_user.id, i&.subject, i.assigned_to_id, i.author_id) if Site.has_notice_menu?
+ end
+ if issues.destroy_all
normal_status(0, "删除成功")
else
normal_status(-1, "删除失败")
@@ -258,9 +353,28 @@ class IssuesController < ApplicationController
# update_hash = params[:issue]
issue_ids = params[:ids]
if issue_ids.present?
+ issues = Issue.where(id: issue_ids)
if update_hash.blank?
normal_status(-1, "请选择批量更新内容")
- elsif Issue.where(id: issue_ids).update_all(update_hash)
+ elsif issues&.update(update_hash)
+ issues.each do |i|
+ i.create_journal_detail(false, [], [], current_user&.id) if i.previous_changes.present?
+ previous_changes = i.previous_changes.slice(:status_id, :assigned_to_id, :tracker_id, :priority_id, :fixed_version_id, :done_ratio, :issue_tags_value, :branch_name)
+ if i.previous_changes[:start_date].present?
+ previous_changes.merge!(start_date: [i.previous_changes[:start_date][0].to_s, i.previous_changes[:start_date][1].to_s])
+ end
+ if i.previous_changes[:due_date].present?
+ previous_changes.merge!(due_date: [i.previous_changes[:due_date][0].to_s, i.previous_changes[:due_date][1].to_s])
+ end
+ if i.previous_changes[:status_id].present? && i.previous_changes[:status_id][1] == 5
+ i.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: ProjectTrend::CLOSE)
+ end
+ if i.previous_changes[:status_id].present? && i.previous_changes[:status_id][0] == 5
+ i.project_trends.where(action_type: ProjectTrend::CLOSE).destroy_all
+ end
+ SendTemplateMessageJob.perform_later('IssueChanged', current_user.id, i&.id, previous_changes) if Site.has_notice_menu?
+ SendTemplateMessageJob.perform_later('IssueAssigned', current_user.id, i&.id) if i.previous_changes[:assigned_to_id].present? && Site.has_notice_menu?
+ end
normal_status(0, "批量更新成功")
else
normal_status(-1, "批量更新失败")
@@ -272,7 +386,10 @@ class IssuesController < ApplicationController
def copy
@new_issue = @issue.dup
+ @new_issue.author_id = current_user.id
if @new_issue.save
+ SendTemplateMessageJob.perform_later('IssueAssigned', current_user.id, @new_issue&.id) if Site.has_notice_menu?
+ SendTemplateMessageJob.perform_later('ProjectIssue', current_user.id, @new_issue&.id) if Site.has_notice_menu?
issue_tags = @issue.issue_tags.pluck(:id)
if issue_tags.present?
issue_tags.each do |tag|
@@ -350,25 +467,30 @@ class IssuesController < ApplicationController
def check_project_public
unless @project.is_public || @project.member?(current_user) || current_user.admin? || (@project.user_id == current_user.id)
- normal_status(-1, "您没有权限")
+ return render_forbidden
end
end
def set_issue
@issue = Issue.find_by_id(params[:id])
if @issue.blank?
- normal_status(-1, "标签不存在")
- elsif @issue.is_lock &&!(@project.member?(current_user) || current_user.admin?)
- normal_status(-1, "您没有权限")
+ return render_not_found
+ elsif !(@project.is_public || (current_user.present? && (@project.member?(current_user) || current_user&.admin? || (@project.user_id == current_user&.id))))
+ return render_forbidden
end
end
def check_issue_permission
unless @project.is_public || (current_user.present? && (@project.member?(current_user) || current_user&.admin? || (@project.user_id == current_user&.id)))
- normal_status(-1, "您没有权限")
+ return render_forbidden
end
end
+ def operate_issue_permission
+ @issue = Issue.find_by_id(params[:id]) unless @issue.present?
+ return render_forbidden("您没有权限进行此操作.") unless current_user.present? && current_user.logged? && (current_user.admin? || @project.member?(current_user) || (@project.is_public && @issue.nil?) || (@project.is_public && @issue.present? && @issue.author_id == current_user.id))
+ end
+
def export_issues(issues)
@table_columns = %w(ID 类型 标题 描述 状态 指派给 优先级 标签 发布人 创建时间 里程碑 开始时间 截止时间 完成度 分类 金额 属于)
@export_issues = []
@@ -420,4 +542,36 @@ class IssuesController < ApplicationController
project_id: @project.id
}
end
+
+ def post_to_chain(type, amount,login)
+ change_params = {
+ type: type,
+ chain_params: {
+ amount: amount,
+ reponame: @project.try(:identifier),
+ username: login
+ }
+ }
+ PostChainJob.perform_later(change_params)
+ end
+
+ def check_token_enough
+ if params[:issue_type].to_s == "2" && (@issue.blank? || (@issue.present? && @issue.author_id == current_user.try(:id)))
+ return normal_status(-1, "悬赏的奖金必须大于0") if params[:token].to_i == 0
+ query_params = {
+ type: "query",
+ chain_params: {
+ reponame: @project.try(:identifier),
+ username: current_user.try(:login)
+ }
+ }
+ response = Gitea::Chain::ChainGetService.new(query_params).call
+ return normal_status(-1, "获取token失败,请稍后重试") if response.status != 200
+ return normal_status(-1, "您的token值不足") if JSON.parse(response.body)["balance"].to_i < params[:token].to_i
+ end
+ end
+
+ def check_menu_authorize
+ return render_not_found unless @project.has_menu_permission("issues")
+ end
end
diff --git a/app/controllers/journals_controller.rb b/app/controllers/journals_controller.rb
index 352844633..2d0b9af95 100644
--- a/app/controllers/journals_controller.rb
+++ b/app/controllers/journals_controller.rb
@@ -1,5 +1,6 @@
class JournalsController < ApplicationController
before_action :require_login, except: [:index, :get_children_journals]
+ before_action :require_profile_completed, :find_atme_receivers, only: [:create]
before_action :set_issue
before_action :check_issue_permission
before_action :set_journal, only: [:destroy, :edit, :update]
@@ -21,32 +22,35 @@ class JournalsController < ApplicationController
if notes.blank?
normal_status(-1, "评论内容不能为空")
else
- journal_params = {
- journalized_id: @issue.id ,
- journalized_type: "Issue",
- user_id: current_user.id ,
- notes: notes.to_s.strip,
- parent_id: params[:parent_id]
- }
- journal = Journal.new journal_params
- if journal.save
- if params[:attachment_ids].present?
- params[:attachment_ids].each do |id|
- attachment = Attachment.select(:id, :container_id, :container_type)&.find_by_id(id)
- unless attachment.blank?
- attachment.container = journal
- attachment.author_id = current_user.id
- attachment.description = ""
- attachment.save
+ ActiveRecord::Base.transaction do
+ journal_params = {
+ journalized_id: @issue.id ,
+ journalized_type: "Issue",
+ user_id: current_user.id ,
+ notes: notes.to_s.strip,
+ parent_id: params[:parent_id]
+ }
+ journal = Journal.new journal_params
+ if journal.save
+ if params[:attachment_ids].present?
+ params[:attachment_ids].each do |id|
+ attachment = Attachment.select(:id, :container_id, :container_type)&.find_by_id(id)
+ unless attachment.blank?
+ attachment.container = journal
+ attachment.author_id = current_user.id
+ attachment.description = ""
+ attachment.save
+ end
end
end
+ 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")
+ render :json => { status: 0, message: "评论成功", id: journal.id}
+ # normal_status(0, "评论成功")
+ else
+ normal_status(-1, "评论失败")
end
-
- # @issue.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: "journal")
- render :json => { status: 0, message: "评论成功", id: journal.id}
- # normal_status(0, "评论成功")
- else
- normal_status(-1, journal.errors.messages.values[0][0])
end
end
end
diff --git a/app/controllers/main_controller.rb b/app/controllers/main_controller.rb
index 7b7468f93..afe767897 100644
--- a/app/controllers/main_controller.rb
+++ b/app/controllers/main_controller.rb
@@ -23,9 +23,9 @@ class MainController < ApplicationController
# TODO: 这块之后需要整合,者架构重新变化,统一跳转到index后再路由分发
if params[:path] && params[:path]&.include?("h5educoderbuild") && params[:path].split("/").first == "h5educoderbuild"
- render file: 'public/h5educoderbuild/index.html', :layout => false
+ render file: 'public/h5educoderbuild/index.html', :layout => false, :content_type=> 'text/html'
else
- render file: 'public/react/build/index.html', :layout => false
+ render file: 'public/react/build/index.html', :layout => false, :content_type=> 'text/html'
end
end
diff --git a/app/controllers/members_controller.rb b/app/controllers/members_controller.rb
index 302dc6248..92ad4ccbb 100644
--- a/app/controllers/members_controller.rb
+++ b/app/controllers/members_controller.rb
@@ -2,12 +2,15 @@ class MembersController < ApplicationController
before_action :require_login
before_action :load_project
before_action :find_user_with_id, only: %i[create remove change_role]
- before_action :operate!, except: %i[index]
+ before_action :check_user_profile_completed, only: [:create]
+ before_action :operate!
before_action :check_member_exists!, only: %i[create]
before_action :check_member_not_exists!, only: %i[remove change_role]
def create
interactor = Projects::AddMemberInteractor.call(@project.owner, @project, @user)
+ SendTemplateMessageJob.perform_later('ProjectJoined', current_user.id, @user.id, @project.id) if Site.has_notice_menu?
+ SendTemplateMessageJob.perform_later('ProjectMemberJoined', current_user.id, @user.id, @project.id) if Site.has_notice_menu?
render_response(interactor)
rescue Exception => e
uid_logger_error(e.message)
@@ -18,7 +21,7 @@ class MembersController < ApplicationController
scope = @project.members.includes(:roles, user: :user_extension)
search = params[:search].to_s.downcase
role = params[:role].to_s
- scope = scope.joins(:user).where("LOWER(CONCAT_WS(users.lastname, users.firstname, users.login, users.mail, users.nickname)) LIKE ?", "%#{search.split(" ").join('|')}%") if search.present?
+ scope = scope.joins(:user).merge(User.like(search))
scope = scope.joins(:roles).where("roles.name LIKE ?", "%#{role}%") if role.present?
@total_count = scope.size
@@ -27,6 +30,8 @@ class MembersController < ApplicationController
def remove
interactor = Projects::DeleteMemberInteractor.call(@project.owner, @project, @user)
+ SendTemplateMessageJob.perform_later('ProjectLeft', current_user.id, @user.id, @project.id) if Site.has_notice_menu?
+ SendTemplateMessageJob.perform_later('ProjectMemberLeft', current_user.id, @user.id, @project.id) if Site.has_notice_menu?
render_response(interactor)
rescue Exception => e
uid_logger_error(e.message)
@@ -35,6 +40,7 @@ class MembersController < ApplicationController
def change_role
interactor = Projects::ChangeMemberRoleInteractor.call(@project.owner, @project, @user, params[:role])
+ SendTemplateMessageJob.perform_later('ProjectRole', current_user.id, @user.id, @project.id, message_role_name) if Site.has_notice_menu?
render_response(interactor)
rescue Exception => e
uid_logger_error(e.message)
@@ -47,7 +53,7 @@ class MembersController < ApplicationController
end
def member_exists?
- @project.member?(params[:user_id])
+ @project.members.exists?(user_id: params[:user_id])
end
def operate!
@@ -55,10 +61,24 @@ class MembersController < ApplicationController
end
def check_member_exists!
- return render_result(1, "user_id为#{params[:user_id]}的用户已经是项目成员") if member_exists?
+ return render_error("user_id为#{params[:user_id]}的用户已经是项目成员") if member_exists?
end
def check_member_not_exists!
- return render_result(1, "user_id为#{params[:user_id]}的用户还不是项目成员") unless member_exists?
+ return render_error("user_id为#{params[:user_id]}的用户还不是项目成员") unless member_exists?
+ end
+
+ def check_user_profile_completed
+ require_user_profile_completed(@user)
+ end
+
+ def message_role_name
+ case params[:role]
+ when 'Manager' then '管理员'
+ when 'Developer' then '开发者'
+ when 'Reporter' then '报告者'
+ else
+ ''
+ end
end
end
diff --git a/app/controllers/organizations/base_controller.rb b/app/controllers/organizations/base_controller.rb
index 2e8ae0cf9..e18ab9aef 100644
--- a/app/controllers/organizations/base_controller.rb
+++ b/app/controllers/organizations/base_controller.rb
@@ -17,11 +17,12 @@ class Organizations::BaseController < ApplicationController
end
def org_privacy_condition
+ return false if current_user.admin?
@organization.organization_extension.privacy? && @organization.organization_users.where(user_id: current_user.id).blank?
end
def team_not_found_condition
- @team.team_users.where(user_id: current_user.id).blank? && !@organization.is_owner?(current_user.id)
+ !current_user&.admin? && @team.team_users.where(user_id: current_user.id).blank? && !@organization.is_owner?(current_user.id)
end
def user_mark
@@ -31,4 +32,4 @@ class Organizations::BaseController < ApplicationController
def project_mark
params[:repo_name] || params[:id]
end
-end
\ No newline at end of file
+end
diff --git a/app/controllers/organizations/organization_users_controller.rb b/app/controllers/organizations/organization_users_controller.rb
index 8a7755a1b..1cddabc90 100644
--- a/app/controllers/organizations/organization_users_controller.rb
+++ b/app/controllers/organizations/organization_users_controller.rb
@@ -5,7 +5,7 @@ class Organizations::OrganizationUsersController < Organizations::BaseController
def index
@organization_users = @organization.organization_users.includes(:user)
search = params[:search].to_s.downcase
- @organization_users = @organization_users.joins(:user).where("LOWER(CONCAT_WS(users.lastname, users.firstname, users.login, users.mail)) LIKE ?", "%#{search.split(" ").join('|')}%") if search.present?
+ @organization_users = @organization_users.joins(:user).merge(User.like(search))
@organization_users = kaminari_paginate(@organization_users)
end
diff --git a/app/controllers/organizations/organizations_controller.rb b/app/controllers/organizations/organizations_controller.rb
index e03ddf121..58aa0d29d 100644
--- a/app/controllers/organizations/organizations_controller.rb
+++ b/app/controllers/organizations/organizations_controller.rb
@@ -1,5 +1,6 @@
class Organizations::OrganizationsController < Organizations::BaseController
- before_action :require_login, except: [:index, :show]
+ before_action :require_login, except: [:index, :show, :recommend]
+ before_action :require_profile_completed, only: [:create]
before_action :convert_image!, only: [:create, :update]
before_action :load_organization, only: [:show, :update, :destroy]
before_action :check_user_can_edit_org, only: [:update, :destroy]
@@ -21,10 +22,13 @@ class Organizations::OrganizationsController < Organizations::BaseController
@can_create_project = @organization.can_create_project?(current_user.id)
@is_admin = can_edit_org?
@is_member = @organization.is_member?(current_user.id)
+ Cache::V2::OwnerCommonService.new(@organization.id).read
end
def create
ActiveRecord::Base.transaction do
+ tip_exception("无法使用以下关键词:#{organization_params[:name]},请重新命名") if ReversedKeyword.check_exists?(organization_params[:name])
+ Organizations::CreateForm.new(organization_params.merge(original_name: "")).validate!
@organization = Organizations::CreateService.call(current_user, organization_params)
Util.write_file(@image, avatar_path(@organization)) if params[:image].present?
end
@@ -35,10 +39,14 @@ class Organizations::OrganizationsController < Organizations::BaseController
def update
ActiveRecord::Base.transaction do
+ Organizations::CreateForm.new(organization_params.merge(original_name: @organization.login)).validate!
login = @organization.login
- @organization.update!(login: organization_params[:name]) if organization_params[:name].present?
- @organization.organization_extension.update_attributes!(organization_params.except(:name))
- Gitea::Organization::UpdateService.call(@organization.gitea_token, login, @organization.reload)
+ @organization.login = organization_params[:name] if organization_params[:name].present?
+ @organization.nickname = organization_params[:nickname] if organization_params[:nickname].present?
+ @organization.save!
+ sync_organization_extension!
+
+ Gitea::Organization::UpdateService.call(current_user.gitea_token, login, @organization.reload)
Util.write_file(@image, avatar_path(@organization)) if params[:image].present?
end
rescue Exception => e
@@ -49,40 +57,37 @@ class Organizations::OrganizationsController < Organizations::BaseController
def destroy
tip_exception("密码不正确") unless current_user.check_password?(password)
ActiveRecord::Base.transaction do
- Gitea::Organization::DeleteService.call(@organization.gitea_token, @organization.login)
- @organization.destroy!
+ gitea_destroy = Gitea::Organization::DeleteService.call(current_user.gitea_token, @organization.login)
+ if gitea_destroy[:status] == 204
+ @organization.destroy!
+ render_ok
+ elsif gitea_destroy[:status] == 500
+ tip_exception("当组织内含有仓库时,无法删除此组织")
+ else
+ tip_exception("")
+ end
end
- render_ok
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
- private
- def convert_image!
- return unless params[:image].present?
- max_size = EduSetting.get('upload_avatar_max_size') || 2 * 1024 * 1024 # 2M
- if params[:image].class == ActionDispatch::Http::UploadedFile
- @image = params[:image]
- render_error('请上传文件') if @image.size.zero?
- render_error('文件大小超过限制') if @image.size > max_size.to_i
- else
- image = params[:image].to_s.strip
- return render_error('请上传正确的图片') if image.blank?
- @image = Util.convert_base64_image(image, max_size: max_size.to_i)
- end
- rescue Base64ImageConverter::Error => ex
- render_error(ex.message)
+ def recommend
+ recommend = %W(xuos Huawei_Technology openatom_foundation pkecosystem TensorLayer)
+
+ @organizations = Organization.includes(:organization_extension).where(organization_extensions: {recommend: true}).to_a.each_slice(group_size).to_a
end
- def avatar_path(organization)
- ApplicationController.helpers.disk_filename(organization.class, organization.id)
- end
+ private
def organization_params
params.permit(:name, :description, :website, :location,
:repo_admin_change_team_access, :visibility,
- :max_repo_creation)
+ :max_repo_creation, :nickname)
+ end
+
+ def group_size
+ params.fetch(:group_size, 4).to_i
end
def password
@@ -96,11 +101,25 @@ class Organizations::OrganizationsController < Organizations::BaseController
end
def sort_by
- params.fetch(:sort_by, "created_at")
+ OrganizationExtension.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_at'
end
def sort_direction
- params.fetch(:sort_direction, "desc")
+ %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc'
end
+ def set_max_repo_creation
+ organization_params[:max_repo_creation].blank? ? -1 : organization_params[:max_repo_creation]
+ end
+
+ def organization_extension_params
+ organization_params
+ .except(:name, :nickname)
+ .merge(max_repo_creation: set_max_repo_creation)
+ end
+
+ def sync_organization_extension!
+ @organization.organization_extension.update_attributes!(organization_extension_params)
+ end
+
end
\ No newline at end of file
diff --git a/app/controllers/organizations/projects_controller.rb b/app/controllers/organizations/projects_controller.rb
index cc275b090..b36a76125 100644
--- a/app/controllers/organizations/projects_controller.rb
+++ b/app/controllers/organizations/projects_controller.rb
@@ -36,10 +36,10 @@ class Organizations::ProjectsController < Organizations::BaseController
end
def sort
- params.fetch(:sort_by, "updated_on")
+ Project.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'updated_on'
end
def sort_direction
- params.fetch(:sort_direction, "desc")
+ %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc'
end
end
\ No newline at end of file
diff --git a/app/controllers/organizations/team_users_controller.rb b/app/controllers/organizations/team_users_controller.rb
index d8694a6c5..7a71dbbad 100644
--- a/app/controllers/organizations/team_users_controller.rb
+++ b/app/controllers/organizations/team_users_controller.rb
@@ -1,6 +1,7 @@
class Organizations::TeamUsersController < Organizations::BaseController
before_action :load_organization, :load_team
before_action :load_operate_user, only: [:create, :destroy]
+ before_action :check_user_profile_completed, only: [:create]
before_action :load_team_user, only: [:destroy]
before_action :check_user_can_edit_org, only: [:create, :destroy]
@@ -8,7 +9,7 @@ class Organizations::TeamUsersController < Organizations::BaseController
@team_users = @team.team_users.includes(:user)
search = params[:search].to_s.downcase
- @team_users = @team_users.joins(:user).where("LOWER(CONCAT_WS(users.lastname, users.firstname, users.login, users.mail, users.nickname)) LIKE ?", "%#{search.split(" ").join('|')}%") if search.present?
+ @team_users = @team_users.joins(:user).merge(User.like(search))
@team_users = kaminari_paginate(@team_users)
end
@@ -17,6 +18,7 @@ class Organizations::TeamUsersController < Organizations::BaseController
ActiveRecord::Base.transaction do
@team_user = TeamUser.build(@organization.id, @operate_user.id, @team.id)
@organization_user = OrganizationUser.build(@organization.id, @operate_user.id)
+ SendTemplateMessageJob.perform_later('TeamJoined', @operate_user.id, @organization.id, @team.id) if Site.has_notice_menu?
Gitea::Organization::TeamUser::CreateService.call(@organization.gitea_token, @team.gtid, @operate_user.login)
end
rescue Exception => e
@@ -29,6 +31,12 @@ class Organizations::TeamUsersController < Organizations::BaseController
ActiveRecord::Base.transaction do
@team_user.destroy!
Gitea::Organization::TeamUser::DeleteService.call(@organization.gitea_token, @team.gtid, @operate_user.login)
+ SendTemplateMessageJob.perform_later('TeamLeft', @operate_user.id, @organization.id, @team.id) if Site.has_notice_menu?
+ org_team_users = @organization.team_users.where(user_id: @operate_user.id)
+ unless org_team_users.present?
+ @organization.organization_users.find_by(user_id: @operate_user.id).destroy!
+ Gitea::Organization::OrganizationUser::DeleteService.call(@organization.gitea_token, @organization.login, @operate_user.login)
+ end
render_ok
end
rescue Exception => e
@@ -43,6 +51,11 @@ class Organizations::TeamUsersController < Organizations::BaseController
ActiveRecord::Base.transaction do
@team_user.destroy!
Gitea::Organization::TeamUser::DeleteService.call(@organization.gitea_token, @team.gtid, current_user.login)
+ org_team_users = @organization.team_users.where(user_id: current_user.id)
+ unless org_team_users.present?
+ @organization.organization_users.find_by(user_id: current_user.id).destroy!
+ Gitea::Organization::OrganizationUser::DeleteService.call(@organization.gitea_token, @organization.login, current_user.login)
+ end
render_ok
end
rescue Exception => e
@@ -73,4 +86,8 @@ class Organizations::TeamUsersController < Organizations::BaseController
tip_exception("组织团队成员不存在") if @team_user.nil?
end
+ def check_user_profile_completed
+ require_user_profile_completed(@operate_user)
+ end
+
end
\ No newline at end of file
diff --git a/app/controllers/organizations/teams_controller.rb b/app/controllers/organizations/teams_controller.rb
index 5bc01fe39..09f5bc3f0 100644
--- a/app/controllers/organizations/teams_controller.rb
+++ b/app/controllers/organizations/teams_controller.rb
@@ -4,15 +4,24 @@ class Organizations::TeamsController < Organizations::BaseController
before_action :check_user_can_edit_org, only: [:create, :update, :destroy]
def index
- #if @organization.is_owner?(current_user) || current_user.admin?
- @teams = @organization.teams
- #else
- # @teams = @organization.teams.joins(:team_users).where(team_users: {user_id: current_user.id})
- #end
- @is_admin = can_edit_org?
- @teams = @teams.includes(:team_units, :team_users)
- @teams = kaminari_paginate(@teams)
+ if params[:is_full].present?
+ if can_edit_org?
+ @teams = @organization.teams
+ else
+ @teams = []
+ end
+ else
+ #if @organization.is_owner?(current_user) || current_user.admin?
+ @teams = @organization.teams
+ #else
+ # @teams = @organization.teams.joins(:team_users).where(team_users: {user_id: current_user.id})
+ #end
+ @is_admin = can_edit_org?
+ @teams = @teams.includes(:team_units, :team_users)
+
+ @teams = kaminari_paginate(@teams)
+ end
end
def search
@@ -33,13 +42,21 @@ class Organizations::TeamsController < Organizations::BaseController
end
def create
- @team = Organizations::Teams::CreateService.call(current_user, @organization, team_params)
+ ActiveRecord::Base.transaction do
+ if @organization.teams.count >= 50
+ return tip_exception("组织的团队数量已超过限制!")
+ else
+ Organizations::CreateTeamForm.new(team_params).validate!
+ @team = Organizations::Teams::CreateService.call(current_user, @organization, team_params)
+ end
+ end
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
def update
+ Organizations::CreateTeamForm.new(team_params).validate!
@team = Organizations::Teams::UpdateService.call(current_user, @team, team_params)
rescue Exception => e
uid_logger_error(e.message)
@@ -60,7 +77,7 @@ class Organizations::TeamsController < Organizations::BaseController
private
def team_params
- params.permit(:name, :description, :authorize, :includes_all_project, :can_create_org_project, :unit_types => [])
+ params.permit(:name, :nickname, :description, :authorize, :includes_all_project, :can_create_org_project, :unit_types => [])
end
def load_organization
diff --git a/app/controllers/owners_controller.rb b/app/controllers/owners_controller.rb
index 97444f7a4..067fe4da1 100644
--- a/app/controllers/owners_controller.rb
+++ b/app/controllers/owners_controller.rb
@@ -1,5 +1,5 @@
class OwnersController < ApplicationController
- before_action :require_login
+ before_action :require_login, only: [:index]
def index
@owners = []
@@ -9,4 +9,52 @@ class OwnersController < ApplicationController
teams: {can_create_org_project: true})
.distinct
end
-end
\ No newline at end of file
+
+ def show
+ @owner = Owner.find_by(login: params[:id]) || Owner.find_by(id: params[:id])
+ # return render_not_found unless @owner.present?
+ # 组织
+ if @owner.is_a?(Organization)
+ return render_forbidden("没有查看组织的权限") if org_limited_condition || org_privacy_condition
+ @can_create_project = @owner.can_create_project?(current_user.id)
+ @is_admin = current_user.admin? || @owner.is_owner?(current_user.id)
+ @is_member = @owner.is_member?(current_user.id)
+ # 用户
+ elsif @owner.is_a?(User)
+ #待办事项,现在未做
+ if User.current.admin? || User.current.login == @owner.login
+ @waiting_applied_messages = @owner.applied_messages.waiting
+ @common_applied_transfer_projects = AppliedTransferProject.where(owner_id: @owner.id).common + AppliedTransferProject.where(owner_id: Organization.joins(team_users: :team).where(team_users: {user_id: @owner.id}, teams: {authorize: %w(admin owner)} )).common
+ @common_applied_projects = AppliedProject.where(project_id: @owner.full_admin_projects).common
+ @undo_events = @waiting_applied_messages.size + @common_applied_transfer_projects.size + @common_applied_projects.size
+ else
+ @waiting_applied_messages = AppliedMessage.none
+ @common_applied_transfer_projects = AppliedTransferProject.none
+ @common_applied_projects = AppliedProject.none
+ @undo_events = 0
+ end
+ #用户的组织数量
+ # @user_composes_count = @user.composes.size
+ @user_composes_count = 0
+ user_organizations = User.current.logged? ? @owner.organizations.with_visibility(%w(common limited)) + @owner.organizations.with_visibility("privacy").joins(:team_users).where(team_users: {user_id: current_user.id}) : @owner.organizations.with_visibility("common")
+ @user_org_count = user_organizations.size
+ normal_projects = Project.members_projects(@owner.id).to_sql
+ org_projects = Project.joins(team_projects: [team: :team_users]).where(team_users: {user_id: @owner.id}).to_sql
+ projects = Project.from("( #{ normal_projects} UNION #{ org_projects } ) AS projects").distinct
+ user_projects = User.current.logged? && (User.current.admin? || User.current.login == @owner.login) ? projects : projects.visible
+ @projects_common_count = user_projects.common.size
+ @projects_mirrior_count = user_projects.mirror.size
+ @projects_sync_mirrior_count = user_projects.sync_mirror.size
+ end
+ end
+
+ private
+ def org_limited_condition
+ @owner.organization_extension.limited? && !current_user.logged?
+ end
+
+ def org_privacy_condition
+ return false if current_user.admin?
+ @owner.organization_extension.privacy? && @owner.organization_users.where(user_id: current_user.id).blank?
+ end
+end
diff --git a/app/controllers/praise_tread_controller.rb b/app/controllers/praise_tread_controller.rb
index 983697e08..c355e8fd2 100644
--- a/app/controllers/praise_tread_controller.rb
+++ b/app/controllers/praise_tread_controller.rb
@@ -1,5 +1,6 @@
class PraiseTreadController < ApplicationController
before_action :require_login, except: %i[index]
+ before_action :require_profile_completed, only: [:like]
before_action :find_project_with_id
def index
diff --git a/app/controllers/project_categories_controller.rb b/app/controllers/project_categories_controller.rb
index c6c2cd21f..774b336c0 100644
--- a/app/controllers/project_categories_controller.rb
+++ b/app/controllers/project_categories_controller.rb
@@ -5,6 +5,10 @@ class ProjectCategoriesController < ApplicationController
@project_categories = q.result(distinct: true)
end
+ def pinned_index
+ @project_categories = ProjectCategory.where.not(pinned_index: 0).order(pinned_index: :desc)
+ end
+
def group_list
@project_categories = ProjectCategory.where('projects_count > 0').order(projects_count: :desc)
end
diff --git a/app/controllers/project_rank_controller.rb b/app/controllers/project_rank_controller.rb
new file mode 100644
index 000000000..7bd62987e
--- /dev/null
+++ b/app/controllers/project_rank_controller.rb
@@ -0,0 +1,26 @@
+class ProjectRankController < ApplicationController
+ # 根据时间获取热门项目
+ def index
+ $redis_cache.zunionstore("recent-days-project-rank", get_timeable_key_names)
+ deleted_data = $redis_cache.smembers("v2-project-rank-deleted")
+ $redis_cache.zrem("recent-days-project-rank", deleted_data) unless deleted_data.blank?
+ @project_rank = $redis_cache.zrevrange("recent-days-project-rank", 0, 4, withscores: true)
+ rescue Exception => e
+ @project_rank = []
+ end
+
+ private
+ # 默认显示7天的
+ def time
+ params.fetch(:time, 7).to_i
+ end
+
+ def get_timeable_key_names
+ names_array = []
+ (0...time).to_a.each do |i|
+ date_time_string = (Date.today - i.days).to_s
+ names_array << "v2-project-rank-#{date_time_string}"
+ end
+ names_array
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/project_trends_controller.rb b/app/controllers/project_trends_controller.rb
index dc1ffbdb4..f283d05f7 100644
--- a/app/controllers/project_trends_controller.rb
+++ b/app/controllers/project_trends_controller.rb
@@ -3,7 +3,7 @@ class ProjectTrendsController < ApplicationController
before_action :check_project_public
def index
- project_trends = @project.project_trends.includes(:user, trend: :user)
+ project_trends = @project.project_trends.preload(:user, trend: :user, project: :owner)
check_time = params[:time] #时间的筛选
check_type = params[:type] #动态类型的筛选,目前已知的有 Issue, PullRequest, Version
@@ -14,20 +14,25 @@ class ProjectTrendsController < ApplicationController
project_trends = project_trends.where("created_at between ? and ?",(Time.now.beginning_of_day - check_time.days), Time.now.end_of_day)
end
- @project_open_issues_count = project_trends.where(trend_type: "Issue", action_type: "create").size
@project_close_issues_count = project_trends.where(trend_type: "Issue", action_type: "close").size
- @project_issues_count = @project_open_issues_count + @project_close_issues_count
-
- @project_pr_count = project_trends.where(trend_type: "PullRequest", action_type: "close").size
- @project_new_pr_count = project_trends.where(trend_type: "PullRequest", action_type: "create").size
- @project_pr_all_count = @project_pr_count + @project_new_pr_count
+ @project_issues_count = project_trends.where(trend_type: "Issue", action_type: "create").size
+ @project_open_issues_count = @project_issues_count - @project_close_issues_count
+ @project_pr_count = project_trends.where(trend_type: "PullRequest", action_type: ["close", "merge"]).size
+ @project_pr_all_count = project_trends.where(trend_type: "PullRequest", action_type: "create").size
+ @project_new_pr_count = @project_pr_all_count - @project_pr_count
if check_type.present?
project_trends = project_trends.where(trend_type: check_type.to_s.strip)
end
if check_status.present?
- project_trends = project_trends.where(action_type: check_status.to_s.strip)
+ if check_status == "delay" || check_status == "close"
+ project_trends = project_trends.where(action_type: ["close", "merge"])
+ else
+ project_trends = project_trends.where(action_type: ["create"]).where.not(trend_id: project_trends.where(action_type: ["close", "merge"]).pluck(:trend_id))
+ end
+ else
+ project_trends = project_trends.where(action_type: "create")
end
project_trends = project_trends.order("created_at desc")
diff --git a/app/controllers/projects/applied_transfer_projects_controller.rb b/app/controllers/projects/applied_transfer_projects_controller.rb
new file mode 100644
index 000000000..d46274d88
--- /dev/null
+++ b/app/controllers/projects/applied_transfer_projects_controller.rb
@@ -0,0 +1,33 @@
+class Projects::AppliedTransferProjectsController < Projects::BaseController
+ before_action :check_auth
+ before_action :check_user_profile_completed, only: [:create]
+
+ def organizations
+ @organizations = Organization.includes(:organization_extension).joins(team_users: :team).where(team_users: {user_id: current_user.id}, teams: {authorize: %w(admin owner)})
+ end
+
+ def create
+ @applied_transfer_project = Projects::ApplyTransferService.call(current_user, @project, params)
+ rescue Exception => e
+ uid_logger_error(e.message)
+ tip_exception(e.message)
+ end
+
+ def cancel
+ @applied_transfer_project = Projects::CancelTransferService.call(current_user, @project)
+ rescue Exception => e
+ uid_logger_error(e.message)
+ tip_exception(e.message)
+ end
+
+ private
+ def check_auth
+ return render_forbidden unless current_user.admin? ||@project.owner?(current_user)
+ end
+
+ def check_user_profile_completed
+ @owner = Owner.find_by(login: params[:owner_name])
+ return if @owner.is_a?(Organization)
+ require_user_profile_completed(@owner)
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/projects/base_controller.rb b/app/controllers/projects/base_controller.rb
index 9811a2136..240bc91f1 100644
--- a/app/controllers/projects/base_controller.rb
+++ b/app/controllers/projects/base_controller.rb
@@ -4,4 +4,7 @@ class Projects::BaseController < ApplicationController
before_action :load_project
before_action :load_repository
+ def require_manager!
+ return render_forbidden('你没有权限操作') unless current_user.admin? || @project.manager?(current_user)
+ end
end
diff --git a/app/controllers/projects/members_controller.rb b/app/controllers/projects/members_controller.rb
new file mode 100644
index 000000000..9c78229dd
--- /dev/null
+++ b/app/controllers/projects/members_controller.rb
@@ -0,0 +1,6 @@
+class Projects::MembersController < Projects::BaseController
+ def index
+ users = @project.all_collaborators.like(params[:search]).includes(:user_extension)
+ @users = kaminari_paginate(users)
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/projects/project_applies_controller.rb b/app/controllers/projects/project_applies_controller.rb
index 37d9d615e..63922a461 100644
--- a/app/controllers/projects/project_applies_controller.rb
+++ b/app/controllers/projects/project_applies_controller.rb
@@ -1,4 +1,5 @@
class Projects::ProjectAppliesController < Projects::BaseController
+ before_action :require_profile_completed, only: [:create]
def create
project = Projects::ApplyJoinService.call(current_user, create_params)
render_ok(project_id: project.id)
diff --git a/app/controllers/projects/project_units_controller.rb b/app/controllers/projects/project_units_controller.rb
new file mode 100644
index 000000000..52cd857d1
--- /dev/null
+++ b/app/controllers/projects/project_units_controller.rb
@@ -0,0 +1,25 @@
+class Projects::ProjectUnitsController < Projects::BaseController
+ def index
+ @project_units = @project.project_units
+ end
+
+ def create
+ if current_user.admin? || @project.manager?(current_user)
+ ActiveRecord::Base.transaction do
+ before_units, after_units = ProjectUnit.update_by_unit_types!(@project, unit_types)
+ SendTemplateMessageJob.perform_later('ProjectSettingChanged', current_user.id, @project&.id, {navbar: true}) unless before_units.eql?(after_units) if Site.has_notice_menu?
+ render_ok
+ end
+ else
+ render_forbidden('你没有权限操作')
+ end
+ rescue Exception => e
+ uid_logger_error(e.message)
+ tip_exception(e.message)
+ end
+
+ private
+ def unit_types
+ params.fetch(:unit_types, [])
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/projects/teams_controller.rb b/app/controllers/projects/teams_controller.rb
index b6ea32185..3ffc78001 100644
--- a/app/controllers/projects/teams_controller.rb
+++ b/app/controllers/projects/teams_controller.rb
@@ -14,7 +14,7 @@ class Projects::TeamsController < Projects::BaseController
def create
ActiveRecord::Base.transaction do
@team_project = TeamProject.build(@owner.id, @operate_team.id, @project.id)
- Gitea::Organization::TeamProject::CreateService.call(@owner.gitea_token, @operate_team.gtid, @owner.login, @project.identifier)
+ Gitea::Organization::TeamProject::CreateService.call(current_user.gitea_token, @operate_team.gtid, @owner.login, @project.identifier)
render_ok
end
rescue Exception => e
@@ -25,7 +25,7 @@ class Projects::TeamsController < Projects::BaseController
def destroy
ActiveRecord::Base.transaction do
@team_project.destroy!
- Gitea::Organization::TeamProject::DeleteService.call(@owner.gitea_token, @operate_team.gtid, @owner.login, @project.identifier)
+ Gitea::Organization::TeamProject::DeleteService.call(current_user.gitea_token, @operate_team.gtid, @owner.login, @project.identifier)
render_ok
end
rescue Exception => e
diff --git a/app/controllers/projects/webhooks_controller.rb b/app/controllers/projects/webhooks_controller.rb
new file mode 100644
index 000000000..9f36da206
--- /dev/null
+++ b/app/controllers/projects/webhooks_controller.rb
@@ -0,0 +1,116 @@
+class Projects::WebhooksController < Projects::BaseController
+ before_action :require_manager!
+ before_action :find_webhook, only:[:edit, :update, :destroy, :tasks, :test]
+
+ def index
+ @webhooks = @project.webhooks
+ @webhooks = kaminari_paginate(@webhooks)
+ end
+
+ def create
+ ActiveRecord::Base.transaction do
+ return render_error("webhooks数量已到上限!请删除暂不使用的webhooks以进行添加操作") if @project.webhooks.size > 19
+ return render_error("参数错误.") unless webhook_params.present?
+ form = Projects::Webhooks::CreateForm.new(webhook_params)
+ return render json: {status: -1, message: form.errors} unless form.validate!
+ response = Gitea::Repository::Webhooks::CreateService.new(operating_token, @project&.owner&.login, @project&.identifier, gitea_webhooks_params).call
+ if response[0] == 201
+ @webhook = response[2]
+ else
+ render_error("创建失败.")
+ end
+ end
+ rescue Exception => e
+ uid_logger_error(e.message)
+ tip_exception(e.message)
+ end
+
+ def edit
+
+ end
+
+ def update
+ return render_error("参数错误.") unless webhook_params.present?
+ form = Projects::Webhooks::CreateForm.new(webhook_params)
+ return render json: {status: -1, message: form.errors} unless form.validate!
+ response = Gitea::Repository::Webhooks::UpdateService.call(operating_token, @project&.owner&.login, @project&.identifier, @webhook.id, gitea_webhooks_params)
+ if response[0] == 200
+ @webhook = response[2]
+ render_ok
+ else
+ render_error("更新失败.")
+ end
+ rescue Exception => e
+ uid_logger_error(e.message)
+ tip_exception(e.message)
+ end
+
+ def destroy
+ response = Gitea::Repository::Webhooks::DeleteService.call(operating_token, @project&.owner&.login, @project&.identifier, @webhook.id)
+ if response[0] == 204
+ @webhook = response[2]
+ render_ok
+ else
+ render_error("删除失败.")
+ end
+ rescue Exception => e
+ uid_logger_error(e.message)
+ tip_exception(e.message)
+ end
+
+ def tasks
+ @tasks = @webhook.tasks.where(is_delivered: true).order("delivered desc")
+ @tasks = kaminari_paginate(@tasks)
+ end
+
+ def test
+ ActiveRecord::Base.transaction do
+ response = Gitea::Repository::Webhooks::TestService.call(operating_token, @project&.owner&.login, @project&.identifier, @webhook.id)
+ if response[0] == 204
+ render_ok
+ else
+ render_error("测试推送失败.")
+ end
+ end
+ rescue Exception => e
+ uid_logger_error(e.message)
+ tip_exception(e.message)
+ end
+
+ private
+ def find_webhook
+ @webhook = @project.webhooks.find_by_id(params[:id])
+ return render_not_found if @webhook.nil?
+ end
+
+ def webhook_params
+ params.require(:webhook).permit(:url, :type, :http_method, :content_type, :secret, :active, :branch_filter, events: [])
+ end
+
+ def webhook_type
+ webhook_params.fetch(:type, "gitea")
+ end
+
+ def webhook_branch_filter
+ webhook_params.fetch(:branch_filter, "*")
+ end
+
+ def gitea_webhooks_params
+ {
+ active: webhook_params[:active],
+ branch_filter: webhook_branch_filter,
+ config: {
+ content_type: webhook_params[:content_type],
+ url: webhook_params[:url],
+ http_method: webhook_params[:http_method],
+ secret: webhook_params[:secret]
+ },
+ events: webhook_params[:events],
+ type: webhook_type,
+ }
+ end
+
+ def operating_token
+ @project.member?(current_user) ? current_user.gitea_token : @project&.owner&.gitea_token
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 12aec7f52..a57ffc975 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -2,17 +2,36 @@ class ProjectsController < ApplicationController
include ApplicationHelper
include OperateProjectAbilityAble
include ProjectsHelper
- before_action :require_login, except: %i[index branches group_type_list simple show fork_users praise_users watch_users recommend about]
- before_action :load_repository, except: %i[index group_type_list migrate create recommend]
+ include Acceleratorable
+
+ before_action :require_login, except: %i[index branches branches_slice group_type_list simple show fork_users praise_users watch_users recommend banner_recommend about menu_list]
+ before_action :require_profile_completed, only: [:create, :migrate]
+ before_action :load_repository, except: %i[index group_type_list migrate create recommend banner_recommend]
before_action :authorizate_user_can_edit_project!, only: %i[update]
before_action :project_public?, only: %i[fork_users praise_users watch_users]
- def index
- PlatformStatistic.data.increment!(:visits)
- scope = Projects::ListQuery.call(params)
+ def menu_list
+ menu = []
- # @projects = kaminari_paginate(scope)
- @projects = kaminari_paginate scope.includes(:project_category, :project_language, :repository, :project_educoder, :apply_signatures, :members, :owner)
+ menu.append(menu_hash_by_name("home"))
+ menu.append(menu_hash_by_name("code")) if @project.has_menu_permission("code")
+ menu.append(menu_hash_by_name("issues")) if @project.has_menu_permission("issues")
+ menu.append(menu_hash_by_name("pulls")) if @project.has_menu_permission("pulls") && @project.forge?
+ menu.append(menu_hash_by_name("devops")) if @project.has_menu_permission("devops") && @project.forge?
+ menu.append(menu_hash_by_name("versions")) if @project.has_menu_permission("versions")
+ menu.append(menu_hash_by_name("wiki")) if @project.has_menu_permission("wiki") && @project.forge?
+ menu.append(menu_hash_by_name("resources")) if @project.has_menu_permission("resources") && @project.forge?
+ menu.append(menu_hash_by_name("activity"))
+ menu.append(menu_hash_by_name("settings")) if (current_user.admin? || @project.manager?(current_user)) && @project.forge?
+
+ render json: menu
+ end
+
+ def index
+ scope = current_user.logged? ? Projects::ListQuery.call(params, current_user.id) : Projects::ListQuery.call(params)
+
+ @projects = kaminari_paginate(scope.includes(:project_category, :project_language, :repository, :project_educoder, :owner, :project_units))
+ # @projects = paginate scope.includes(:project_category, :project_language, :repository, :project_educoder, :owner, :project_units)
category_id = params[:category_id]
@total_count = @projects.total_count
@@ -38,14 +57,46 @@ class ProjectsController < ApplicationController
def migrate
Projects::MigrateForm.new(mirror_params).validate!
- @project = Projects::MigrateService.new(current_user, mirror_params).call
+
+ @project =
+ # if enable_accelerator?(mirror_params[:clone_addr])
+ # source_clone_url = mirror_params[:clone_addr]
+ # uid_logger("########## 已动加速器 ##########")
+ # result = Gitea::Accelerator::MigrateService.call(mirror_params)
+ # if result[:status] == :success
+ # Rails.logger.info "########## 加速镜像成功 ########## "
+ # Projects::MigrateService.call(current_user,
+ # mirror_params.merge(source_clone_url: source_clone_url,
+ # clone_addr: accelerator_url(mirror_params[:repository_name])))
+ # else
+ # Projects::MigrateService.call(current_user, mirror_params)
+ # end
+ if mirror_params[:clone_addr].include?("github.com")
+ source_clone_url = mirror_params[:clone_addr]
+ clone_url = source_clone_url.gsub('github.com', 'github.com.cnpmjs.org')
+ uid_logger("########## 更改clone_addr ##########")
+ Projects::MigrateService.call(current_user, mirror_params.merge(source_clone_url: source_clone_url, clone_addr: clone_url))
+ else
+ Projects::MigrateService.call(current_user, mirror_params)
+ end
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
def branches
- @branches = @project.forge? ? Gitea::Repository::Branches::ListService.new(@owner, @project.identifier).call : []
+ return @branches = [] unless @project.forge?
+
+ # result = Gitea::Repository::Branches::ListService.call(@owner, @project.identifier)
+ result = Gitea::Repository::Branches::ListNameService.call(@owner, @project.identifier)
+ @branches = result.is_a?(Hash) ? (result.key?(:status) ? [] : result["branch_name"]) : result
+ end
+
+ def branches_slice
+ return @branches = [] unless @project.forge?
+
+ slice_result = Gitea::Repository::Branches::ListSliceService.call(@owner, @project.identifier)
+ @branches_slice = slice_result.is_a?(Hash) && slice_result.key?(:status) ? [] : slice_result
end
def group_type_list
@@ -61,18 +112,37 @@ class ProjectsController < ApplicationController
def update
ActiveRecord::Base.transaction do
- # Projects::CreateForm.new(project_params).validate!
- private = params[:private] || @project.is_public
- gitea_params = {
- private: private,
- default_branch: params[:default_branch]
- }.compact
- if [true, false].include? private
- new_project_params = project_params.except(:private).merge(is_public: !private)
+ # TODO:
+ # 临时特殊处理修改website、lesson_url操作方法
+ if project_params.has_key?("website")
+ @project.update(project_params)
+ elsif project_params.has_key?("default_branch")
+ @project.update(project_params)
+ gitea_params = {
+ default_branch: @project.default_branch
+ }
Gitea::Repository::UpdateService.call(@owner, @project.identifier, gitea_params)
- @project.repository.update_column(:hidden, private)
+ else
+ validate_params = project_params.slice(:name, :description,
+ :project_category_id, :project_language_id, :private, :identifier)
+
+ Projects::UpdateForm.new(validate_params.merge(user_id: @project.user_id, project_identifier: @project.identifier)).validate!
+
+ private = @project.forked_from_project.present? ? !@project.forked_from_project.is_public : params[:private] || false
+
+ new_project_params = project_params.except(:private).merge(is_public: !private)
+ @project.update_attributes!(new_project_params)
+ @project.forked_projects.update_all(is_public: @project.is_public)
+ gitea_params = {
+ private: private,
+ default_branch: @project.default_branch,
+ website: @project.website,
+ name: @project.identifier
+ }
+ gitea_repo = Gitea::Repository::UpdateService.call(@owner, @project&.repository&.identifier, gitea_params)
+ @project.repository.update_attributes({hidden: gitea_repo["private"], identifier: gitea_repo["name"]})
end
- @project.update_attributes!(new_project_params)
+ SendTemplateMessageJob.perform_later('ProjectSettingChanged', current_user.id, @project&.id, @project.previous_changes.slice(:name, :description, :project_category_id, :project_language_id, :is_public, :identifier)) if Site.has_notice_menu?
end
rescue Exception => e
uid_logger_error(e.message)
@@ -83,10 +153,11 @@ class ProjectsController < ApplicationController
end
def destroy
- if current_user.admin? || @project.owner?(current_user)
+ if current_user.admin? || @project.manager?(current_user)
ActiveRecord::Base.transaction do
Gitea::Repository::DeleteService.new(@project.owner, @project.identifier).call
@project.destroy!
+ @project.forked_projects.update_all(forked_from_project_id: nil)
render_ok
end
else
@@ -116,11 +187,17 @@ class ProjectsController < ApplicationController
end
def simple
+ # 为了缓存活跃项目的基本信息,后续删除
+ Cache::V2::ProjectCommonService.new(@project.id).read
json_response(@project, current_user)
end
def recommend
- @projects = Project.recommend.includes(:repository, :project_category, :owner).limit(5)
+ @projects = Project.recommend.includes(:repository, :project_category, :owner).order(visits: :desc)
+ end
+
+ def banner_recommend
+ @projects = Project.recommend.where.not(recommend_index: 0).includes(:project_category, :owner, :project_language).order(recommend_index: :desc)
end
def about
@@ -153,7 +230,7 @@ class ProjectsController < ApplicationController
private
def project_params
- params.permit(:user_id, :name, :description, :repository_name,
+ 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)
end
diff --git a/app/controllers/public_keys_controller.rb b/app/controllers/public_keys_controller.rb
new file mode 100644
index 000000000..327e719cc
--- /dev/null
+++ b/app/controllers/public_keys_controller.rb
@@ -0,0 +1,64 @@
+class PublicKeysController < ApplicationController
+ before_action :require_login
+ before_action :find_public_key, only: [:destroy]
+
+ def index
+ @public_keys = current_user.public_keys
+ @public_keys = kaminari_paginate(@public_keys)
+ rescue Exception => e
+ uid_logger_error(e.message)
+ tip_exception(e.message)
+ end
+
+ def create
+ return render_error("参数错误") if public_key_params.blank?
+ return render_ok({status: 10002, message: "请输入密钥"}) if public_key_params[:key].blank?
+ return render_ok({status: 10001, message: "请输入标题"}) if public_key_params[:title].blank?
+ @gitea_response = Gitea::User::Keys::CreateService.call(current_user.gitea_token, public_key_params)
+ if @gitea_response[0] == 201
+ @public_key = @gitea_response[2]
+ else
+ return render_error("创建ssh key失败") if @gitea_response[2].blank?
+ return render_ok({status: 10002, message: "密钥格式不正确"}) if @gitea_response[2]["message"].starts_with?("Invalid key content")
+ exist_public_key = Gitea::PublicKey.find_by(content: public_key_params[:key])
+ return render_ok({status: 10002, message: "密钥已被占用"}) if @gitea_response[2]["message"].starts_with?("Key content has been used as non-deploy key") && exist_public_key.present? && exist_public_key&.owner_id != current_user.gitea_uid
+ return render_ok({status: 10002, message: "密钥已存在,请勿重复添加"}) if @gitea_response[2]["message"].starts_with?("Key content has been used as non-deploy key")
+ @public_key = nil
+ end
+ rescue Exception => e
+ uid_logger_error(e.message)
+ tip_exception(e.message)
+ end
+
+ def destroy
+ return render_not_found unless @public_key.present?
+ result = Gitea::User::Keys::DeleteService.call(current_user.gitea_token, @public_key.id)
+ if result[0] == 204
+ render_ok
+ else
+ render_error
+ end
+ rescue Exception => e
+ uid_logger_error(e.message)
+ tip_exception(e.message)
+ end
+
+ private
+
+ def page
+ params[:page].to_i.zero? ? 1 : params[:page].to_i
+ end
+
+ def limit
+ limit = params[:limit] || params[:per_page]
+ limit = (limit.to_i.zero? || limit.to_i > 15) ? 15 : limit.to_i
+ end
+
+ def public_key_params
+ params.require(:public_key).permit(:key, :title)
+ end
+
+ def find_public_key
+ @public_key = current_user.public_keys.find_by_id(params[:id])
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/pull_requests_controller.rb b/app/controllers/pull_requests_controller.rb
index 69d9e60be..cff00df26 100644
--- a/app/controllers/pull_requests_controller.rb
+++ b/app/controllers/pull_requests_controller.rb
@@ -1,8 +1,11 @@
class PullRequestsController < ApplicationController
before_action :require_login, except: [:index, :show, :files, :commits]
+ before_action :require_profile_completed, only: [:create]
before_action :load_repository
+ before_action :check_menu_authorize
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
@@ -11,11 +14,14 @@ class PullRequestsController < ApplicationController
# @issues = Gitea::PullRequest::ListService.new(@user,@repository.try(:identifier)).call #通过gitea获取
issues = @project.issues.issue_pull_request.issue_index_includes.includes(pull_request: :user)
issues = issues.where(is_private: false) unless current_user.present? && (current_user.admin? || @project.member?(current_user))
- @all_issues_size = issues.size
- @open_issues_size = issues.joins(:pull_request).where(pull_requests: {status: 0}).size
- @close_issues_size = issues.joins(:pull_request).where(pull_requests: {status: 2}).size
- @merged_issues_size = issues.joins(:pull_request).where(pull_requests: {status: 1}).size
+ @all_issues = issues.distinct
+ @filter_issues = @all_issues
+ @filter_issues = @filter_issues.where("subject LIKE ? OR description LIKE ? ", "%#{params[:search]}%", "%#{params[:search]}%") if params[:search].present?
+ @open_issues = @filter_issues.joins(:pull_request).where(pull_requests: {status: PullRequest::OPEN})
+ @close_issues = @filter_issues.joins(:pull_request).where(pull_requests: {status: PullRequest::CLOSED})
+ @merged_issues = @filter_issues.joins(:pull_request).where(pull_requests: {status: PullRequest::MERGED})
@user_admin_or_member = current_user.present? && (current_user.admin || @project.member?(current_user))
+ @user_admin_or_developer = current_user.present? && (current_user.admin || @project.all_developers.include?(current_user))
scopes = Issues::ListQueryService.call(issues,params.delete_if{|k,v| v.blank?}, "PullRequest")
@issues_size = scopes.size
@@ -50,55 +56,79 @@ class PullRequestsController < ApplicationController
end
def create
+ # return normal_status(-1, "您不是目标分支开发者,没有权限,请联系目标分支作者.") unless @project.operator?(current_user)
ActiveRecord::Base.transaction do
@pull_request, @gitea_pull_request = PullRequests::CreateService.call(current_user, @owner, @project, params)
if @gitea_pull_request[:status] == :success
- @pull_request.bind_gitea_pull_request!(@gitea_pull_request[:body]["number"])
- render_ok
+ @pull_request.bind_gitea_pull_request!(@gitea_pull_request[:body]["number"], @gitea_pull_request[:body]["id"])
+ SendTemplateMessageJob.perform_later('PullRequestAssigned', current_user.id, @pull_request&.id) if Site.has_notice_menu?
+ 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
else
render_error("create pull request error: #{@gitea_pull_request[:status]}")
raise ActiveRecord::Rollback
end
end
+ rescue => e
+ normal_status(-1, e.message)
end
def edit
- @fork_project_user_name = @project&.fork_project&.owner.try(:show_real_name)
- @fork_project_user = @project&.fork_project&.owner.try(:login)
- @fork_project_identifier = @project&.fork_project&.repository.try(:identifier)
+ @fork_project_user_name = @pull_request&.fork_project&.owner.try(:show_real_name)
+ @fork_project_user = @pull_request&.fork_project&.owner.try(:login)
+ @fork_project_identifier = @pull_request&.fork_project&.repository.try(:identifier)
end
def update
+ return render_forbidden("你没有权限操作.") unless @project.operator?(current_user)
if params[:title].nil?
normal_status(-1, "名称不能为空")
elsif params[:issue_tag_ids].nil?
- normal_status(-1, "标签不能为空")
+ normal_status(-1, "标记不能为空")
else
ActiveRecord::Base.transaction do
begin
+ return normal_status(-1, "title不能超过255个字符") if params[:title].length > 255
merge_params
+ @issue&.issue_tags_relates&.destroy_all if params[:issue_tag_ids].blank?
if params[:issue_tag_ids].present? && !@issue&.issue_tags_relates.where(issue_tag_id: params[:issue_tag_ids]).exists?
- @issue&.issue_tags_relates&.destroy_all
- params[:issue_tag_ids].each do |tag|
- IssueTagsRelate.create(issue_id: @issue.id, issue_tag_id: tag)
- end
+ if params[:issue_tag_ids].is_a?(Array) && params[:issue_tag_ids].size > 1
+ return normal_status(-1, "最多只能创建一个标记。")
+ elsif params[:issue_tag_ids].is_a?(Array) && params[:issue_tag_ids].size == 1
+ @issue&.issue_tags_relates&.destroy_all
+ params[:issue_tag_ids].each do |tag|
+ IssueTagsRelate.create!(issue_id: @issue.id, issue_tag_id: tag)
+ end
+ else
+ return normal_status(-1, "请输入正确的标记。")
+ end
end
if @issue.update_attributes(@issue_params)
if @pull_request.update_attributes(@local_params.compact)
gitea_pull = Gitea::PullRequest::UpdateService.call(@owner.login, @repository.identifier,
- @pull_request.gpid, @requests_params, current_user.gitea_token)
+ @pull_request.gitea_number, @requests_params, current_user.gitea_token)
if gitea_pull[:status] === :success
if params[:issue_tag_ids].present?
- params[:issue_tag_ids].each do |tag|
- IssueTagsRelate.create(issue_id: @issue.id, issue_tag_id: tag)
- end
+ if params[:issue_tag_ids].is_a?(Array) && params[:issue_tag_ids].size > 1
+ return normal_status(-1, "最多只能创建一个标记。")
+ elsif params[:issue_tag_ids].is_a?(Array) && params[:issue_tag_ids].size == 1
+ @issue&.issue_tags_relates&.destroy_all
+ params[:issue_tag_ids].each do |tag|
+ IssueTagsRelate.create!(issue_id: @issue.id, issue_tag_id: tag)
+ end
+ else
+ return normal_status(-1, "请输入正确的标记。")
+ end
end
if params[:status_id].to_i == 5
@issue.issue_times.update_all(end_time: Time.now)
end
+ 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
normal_status(0, "PullRequest更新成功")
else
normal_status(-1, "PullRequest更新失败")
@@ -113,6 +143,8 @@ class PullRequestsController < ApplicationController
normal_status(-1, e.message)
raise ActiveRecord::Rollback
end
+ SendTemplateMessageJob.perform_later('PullRequestChanged', current_user.id, @pull_request&.id, @issue.previous_changes.slice(:assigned_to_id, :priority_id, :fixed_version_id, :issue_tags_value)) if Site.has_notice_menu?
+ SendTemplateMessageJob.perform_later('PullRequestAssigned', current_user.id, @pull_request&.id ) if @issue.previous_changes[:assigned_to_id].present? && Site.has_notice_menu?
end
end
@@ -122,7 +154,13 @@ class PullRequestsController < ApplicationController
ActiveRecord::Base.transaction do
begin
colsed = PullRequests::CloseService.call(@owner, @repository, @pull_request, current_user)
- colsed === true ? normal_status(1, "已拒绝") : normal_status(-1, '合并失败')
+ if colsed === true
+ @pull_request.project_trends.create!(user: current_user, project: @project,action_type: ProjectTrend::CLOSE)
+ SendTemplateMessageJob.perform_later('PullRequestClosed', current_user.id, @pull_request.id) if Site.has_notice_menu?
+ normal_status(1, "已拒绝")
+ else
+ normal_status(-1, '合并失败')
+ end
rescue => e
normal_status(-1, e.message)
raise ActiveRecord::Rollback
@@ -137,25 +175,35 @@ class PullRequestsController < ApplicationController
def show
@issue_user = @issue.user
@issue_assign_to = @issue.get_assign_user
-
+ @gitea_pull = Gitea::PullRequest::GetService.call(@owner.login,
+ @repository.identifier, @pull_request.gitea_number, current_user&.gitea_token)
end
def pr_merge
- return render_forbidden("你没有权限操作.") if @project.reporter?(current_user)
+ return render_forbidden("你没有权限操作.") unless @project.operator?(current_user)
if params[:do].blank?
normal_status(-1, "请选择合并方式")
else
ActiveRecord::Base.transaction do
begin
- result = PullRequests::MergeService.call(@owner, @repository, @pull_request, current_user, params)
+ @gitea_pull = Gitea::PullRequest::GetService.call(@owner.login, @repository.identifier, @pull_request.gitea_number, current_user&.gitea_token)
- if result && @pull_request.merge!
- @pull_request.project_trend_status!
+ if @gitea_pull["merged_by"].present?
+ success_condition = true
+ else
+ result = PullRequests::MergeService.call(@owner, @repository, @pull_request, current_user, params)
+ success_condition = result.status == 200
+ end
+
+ if success_condition && @pull_request.merge!
+ # @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)
+ SendTemplateMessageJob.perform_later('PullRequestMerged', current_user.id, @pull_request.id) if Site.has_notice_menu?
normal_status(1, "合并成功")
else
- normal_status(-1, "合并失败")
+ normal_status(-1, result.message)
end
rescue => e
normal_status(-1, e.message)
@@ -169,7 +217,7 @@ class PullRequestsController < ApplicationController
def check_can_merge
target_head = params[:head] #源分支
target_base = params[:base] #目标分支
- is_original = params[:is_original]
+ is_original = params[:is_original] || false
if target_head.blank? || target_base.blank?
normal_status(-2, "请选择分支")
elsif target_head === target_base && !is_original
@@ -179,7 +227,7 @@ class PullRequestsController < ApplicationController
if can_merge.present?
render json: {
status: -2,
- message: "在这些分支之间的合并请求已存在:#{can_merge.first.try(:title)} ",
+ message: "在这些分支之间的合并请求已存在:#{can_merge.first.try(:title)} ",
}
else
compare_result = Repositories::CompareService.call(@owner, @project, params)
@@ -194,22 +242,22 @@ class PullRequestsController < ApplicationController
def files
- @files_result = Gitea::PullRequest::FilesService.call(@owner.login, @project.identifier, @pull_request.gpid, current_user&.gitea_token)
+ @files_result = Gitea::PullRequest::FilesService.call(@owner.login, @project.identifier, @pull_request.gitea_number, current_user&.gitea_token)
# render json: @files_result
end
def commits
- @commits_result = Gitea::PullRequest::CommitsService.call(@owner.login, @project.identifier, @pull_request.gpid, current_user&.gitea_token)
+ @commits_result = Gitea::PullRequest::CommitsService.call(@owner.login, @project.identifier, @pull_request.gitea_number, current_user&.gitea_token)
# render json: @commits_result
end
private
def load_pull_request
- @pull_request = PullRequest.find params[:id]
+ @pull_request = @project.pull_requests.where(gitea_number: params[:id]).where.not(id: params[:id]).take || PullRequest.find_by_id(params[:id])
end
def find_pull_request
- @pull_request = PullRequest.find_by_id(params[:id])
+ @pull_request = @project.pull_requests.where(gitea_number: params[:id]).where.not(id: params[:id]).take || PullRequest.find_by_id(params[:id])
@issue = @pull_request&.issue
if @pull_request.blank?
normal_status(-1, "合并请求不存在")
@@ -221,7 +269,7 @@ class PullRequestsController < ApplicationController
def get_relatived
@project_tags = @project.issue_tags&.select(:id,:name, :color).as_json
@project_versions = @project.versions&.select(:id,:name, :status).as_json
- @project_members = @project.members_user_infos
+ @project_members = @project.all_developers
@project_priories = IssuePriority&.select(:id,:name, :position).as_json
end
@@ -233,12 +281,12 @@ class PullRequestsController < ApplicationController
base: params[:base], #目标分支
milestone: 0, #里程碑,未与本地的里程碑关联
}
+ assignee_login = User.find_by_id(params[:assigned_to_id])&.login
@requests_params = @local_params.merge({
- assignee: current_user.try(:login),
# assignees: ["#{params[:assigned_login].to_s}"],
- assignees: ["#{current_user.try(:login).to_s}"],
- labels: params[:issue_tag_ids],
- due_date: Time.now
+ assignees: ["#{assignee_login.to_s}"],
+ labels: params[:issue_tag_ids]
+ # due_date: Time.now
})
@issue_params = {
author_id: current_user.id,
@@ -248,11 +296,15 @@ class PullRequestsController < ApplicationController
assigned_to_id: params[:assigned_to_id],
fixed_version_id: params[:fixed_version_id],
issue_tags_value: params[:issue_tag_ids].present? ? params[:issue_tag_ids].join(",") : "",
- priority_id: params[:priority_id] || "2",
+ priority_id: params[:priority_id],
issue_classify: "pull_request",
issue_type: params[:issue_type] || "1",
tracker_id: 2,
status_id: 1,
}
end
+
+ def check_menu_authorize
+ return render_not_found unless @project.has_menu_permission("pulls")
+ end
end
diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb
index 886a9824f..4bd5b5e5b 100644
--- a/app/controllers/repositories_controller.rb
+++ b/app/controllers/repositories_controller.rb
@@ -1,9 +1,11 @@
class RepositoriesController < ApplicationController
+ include RepositoriesHelper
include ApplicationHelper
include OperateProjectAbilityAble
include Repository::LanguagesPercentagable
before_action :require_login, only: %i[edit update create_file update_file delete_file sync_mirror]
+ before_action :require_profile_completed, only: [:create_file]
before_action :load_repository
before_action :authorizate!, except: [:sync_mirror, :tags, :commit, :archive]
before_action :authorizate_user_can_edit_repo!, only: %i[sync_mirror]
@@ -11,6 +13,25 @@ class RepositoriesController < ApplicationController
before_action :get_latest_commit, only: %i[entries sub_entries top_counts]
before_action :get_statistics, only: %i[top_counts]
+ def files
+ result = @project.educoder? ? nil : Gitea::Repository::Files::GetService.call(@owner, @project.identifier, @ref, params[:search], @owner.gitea_token)
+ render json: result
+ end
+
+ # 新版项目详情
+ def detail
+ @user = current_user
+ @result = Repositories::DetailService.call(@owner, @repository, @user)
+ @project_fork_id = @project.try(:forked_from_project_id)
+ if @project_fork_id.present?
+ @fork_project = Project.find_by(id: @project_fork_id)
+ @fork_project_user = @fork_project.owner
+ end
+ rescue Exception => e
+ uid_logger_error(e.message)
+ tip_exception(e.message)
+ end
+
def show
@user = current_user
@repo = @project.repository
@@ -27,7 +48,7 @@ class RepositoriesController < ApplicationController
def entries
@project.increment!(:visits)
-
+ CacheAsyncSetJob.perform_later("project_common_service", {visits: 1}, @project.id)
if @project.educoder?
@entries = Educoder::Repository::Entries::ListService.call(@project&.project_educoder.repo_name)
else
@@ -50,18 +71,35 @@ class RepositoriesController < ApplicationController
logger.info "######### sub_entries: #{@sub_entries}"
return render_error('该文件暂未开放,敬请期待.') if @sub_entries['status'].to_i === -1
- tmp_entries = [{
+ tmp_entries = {
"content" => @sub_entries['data']['content'],
"type" => "blob"
- }]
+ }
@sub_entries = {
"trees"=>tmp_entries,
"commits" => [{}]
}
else
- @sub_entries = Educoder::Repository::Entries::ListService.call(@project&.project_educoder&.repo_name, {path: file_path_uri})
+ begin
+ @sub_entries = Educoder::Repository::Entries::ListService.call(@project&.project_educoder&.repo_name, {path: file_path_uri})
+ if @sub_entries.blank? || @sub_entries['status'].to_i === -1
+ @sub_entries = Educoder::Repository::Entries::GetService.call(@project&.project_educoder&.repo_name, file_path_uri)
+ return render_error('该文件暂未开放,敬请期待.') if @sub_entries['status'].to_i === -1
+ tmp_entries = {
+ "content" => @sub_entries['data']['content'],
+ "type" => "blob"
+ }
+ @sub_entries = {
+ "trees"=>tmp_entries,
+ "commits" => [{}]
+ }
+ end
+ rescue
+ return render_error('该文件暂未开放,敬请期待.')
+ end
end
else
+ @path = Gitea.gitea_config[:domain]+"/#{@project.owner.login}/#{@project.identifier}/raw/branch/#{@ref}/"
interactor = Repositories::EntriesInteractor.call(@owner, @project.identifier, file_path_uri, ref: @ref)
if interactor.success?
result = interactor.result
@@ -73,18 +111,47 @@ class RepositoriesController < ApplicationController
end
def commits
- @hash_commit = Gitea::Repository::Commits::ListService.new(@owner.login, @project.identifier,
- sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token).call
+ if @project.educoder?
+ @commits = Educoder::Repository::Commits::ListService.call(@project&.project_educoder&.repo_name)
+ else
+ if params[:filepath].present?
+ file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip))
+ @hash_commit = Gitea::Repository::Commits::FileListService.new(@owner.login, @project.identifier, file_path_uri,
+ sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token).call
+ else
+ @hash_commit = Gitea::Repository::Commits::ListService.new(@owner.login, @project.identifier,
+ sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token).call
+ end
+ end
end
+ def commits_slice
+ @hash_commit = Gitea::Repository::Commits::ListSliceService.call(@owner.login, @project.identifier,
+ sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token)
+ end
+
def commit
@sha = params[:sha]
- @commit = Gitea::Repository::Commits::GetService.call(@owner.login, @repository.identifier, @sha, current_user&.gitea_token)
- @commit_diff = Gitea::Repository::Commits::GetService.call(@owner.login, @repository.identifier, @sha, current_user&.gitea_token, {diff: true})
+ if @project.educoder?
+ return render_error('暂未开放,敬请期待.')
+ else
+ @commit = Gitea::Repository::Commits::GetService.call(@owner.login, @repository.identifier, @sha, current_user&.gitea_token)
+ @commit_diff = Gitea::Repository::Commits::GetService.call(@owner.login, @repository.identifier, @sha, current_user&.gitea_token, {diff: true})
+ end
end
def tags
- @tags = Gitea::Repository::Tags::ListService.call(current_user&.gitea_token, @owner.login, @project.identifier, {page: params[:page], limit: params[:limit]})
+ result = Gitea::Repository::Tags::ListService.call(current_user&.gitea_token, @owner.login, @project.identifier, {page: params[:page], limit: params[:limit]})
+
+ @tags = result.is_a?(Hash) && result.key?(:status) ? [] : result
+ end
+
+ def contributors
+ if params[:filepath].present? || @project.educoder?
+ @contributors = []
+ else
+ @contributors = Gitea::Repository::Contributors::GetService.call(@owner, @repository.identifier)
+ end
end
def edit
@@ -147,14 +214,49 @@ class RepositoriesController < ApplicationController
end
def readme
- result = Gitea::Repository::Readme::GetService.call(@owner.login, @repository.identifier, params[:ref], current_user&.gitea_token)
-
+ if params[:filepath].present?
+ result = Gitea::Repository::Readme::DirService.call(@owner.login, @repository.identifier, params[:filepath], params[:ref], current_user&.gitea_token)
+ else
+ result = Gitea::Repository::Readme::GetService.call(@owner.login, @repository.identifier, params[:ref], current_user&.gitea_token)
+ end
+ @path = Gitea.gitea_config[:domain]+"/#{@owner.login}/#{@repository.identifier}/raw/branch/#{params[:ref]}/"
@readme = result[:status] === :success ? result[:body] : nil
- render json: @readme
+ @readme['content'] = decode64_content(@readme, @owner, @repository, params[:ref], @path)
+ render json: @readme.slice("type", "encoding", "size", "name", "path", "content", "sha")
+ rescue
+ render json: nil
end
def languages
- render json: languages_precentagable
+ if @project.educoder?
+ render json: {}
+ else
+ render json: languages_precentagable
+ end
+ end
+
+ def archive
+ domain = Gitea.gitea_config[:domain]
+ api_url = Gitea.gitea_config[:base_url]
+ archive_url = "/repos/#{@owner.login}/#{@repository.identifier}/archive/#{CGI.escape(params[:archive])}"
+
+ file_path = [domain, api_url, archive_url].join
+ file_path = [file_path, "access_token=#{current_user&.gitea_token}"].join("?") if @repository.hidden?
+
+ return render_not_found if !request.format.zip? && !request.format.gzip?
+
+ redirect_to file_path
+ end
+
+ def raw
+ domain = Gitea.gitea_config[:domain]
+ api_url = Gitea.gitea_config[:base_url]
+
+ url = "/repos/#{@owner.login}/#{@repository.identifier}/raw/#{CGI.escape(params[:filepath])}?ref=#{CGI.escape(params[:ref])}"
+ file_path = [domain, api_url, url].join
+ file_path = [file_path, "access_token=#{current_user&.gitea_token}"].join("&")
+
+ redirect_to file_path
end
def archive
@@ -190,8 +292,14 @@ class RepositoriesController < ApplicationController
# TODO 获取最新commit信息
def project_commits
- Gitea::Repository::Commits::ListService.new(@project.owner.login, @project.identifier,
- sha: get_ref, page: 1, limit: 1, token: current_user&.gitea_token).call
+ if params[:filepath].present?
+ file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip))
+ Gitea::Repository::Commits::FileListService.new(@project.owner.login, @project.identifier, file_path_uri,
+ sha: get_ref, page: 1, limit: 1, token: current_user&.gitea_token).call
+ else
+ Gitea::Repository::Commits::ListService.new(@project.owner.login, @project.identifier,
+ sha: get_ref, page: 1, limit: 1, token: current_user&.gitea_token).call
+ end
end
def get_statistics
@@ -200,7 +308,7 @@ class RepositoriesController < ApplicationController
end
def get_ref
- @ref = params[:ref] || "master"
+ @ref = params[:ref] || @project&.default_branch
end
def get_latest_commit
diff --git a/app/controllers/settings_controller.rb b/app/controllers/settings_controller.rb
index 896e2a525..b2dc2e900 100644
--- a/app/controllers/settings_controller.rb
+++ b/app/controllers/settings_controller.rb
@@ -1,4 +1,61 @@
class SettingsController < ApplicationController
def show
+ @old_projects_url = nil
+ get_add_menu
+ get_common_menu
+ get_personal_menu
+ get_top_system_notification
end
+
+ private
+ def get_add_menu
+ @add = []
+ Site.add.select(:id, :name, :url, :key).to_a.map(&:serializable_hash).each do |site|
+ hash = {}
+ site.each {|k, v|
+ hash.merge!("#{k}": get_site_url(k, v))
+ }
+ @add << hash
+ end
+ end
+
+ def get_common_menu
+ @common = {}
+ Site.common.select(:url, :key).each do |site|
+ next if site["url"].to_s.include?("current_user") && !User.current.logged?
+ @common.merge!("#{site["key"]}": append_http(reset_site_url(site["url"])))
+ end
+ end
+
+ def get_personal_menu
+ @personal = []
+ if User.current.logged?
+ Site.personal.select(:id, :name, :url, :key).to_a.map(&:serializable_hash).each do |site|
+ hash = {}
+ site.each {|k, v|
+ hash.merge!("#{k}": get_site_url(k, v))
+ }
+ @personal << hash
+ end
+ end
+ end
+
+ def get_top_system_notification
+ @top_system_notification = SystemNotification.is_top.first
+ end
+
+ def get_site_url(key, value)
+ key.to_s === "url" ? append_http(reset_site_url(value)) : reset_site_url(value)
+ end
+
+ def reset_site_url(url)
+ return url unless url.to_s.include?("current_user")
+
+ split_arr = url.split('current_user')
+ split_arr.length > 1 ? split_arr.join(current_user&.login) : (split_arr << current_user&.login).join('')
+ end
+
+ def append_http(url)
+ url.to_s.start_with?("http") ? url : [request.protocol, request.host_with_port, url].join('')
+ end
end
diff --git a/app/controllers/template_message_settings_controller.rb b/app/controllers/template_message_settings_controller.rb
new file mode 100644
index 000000000..967481f2a
--- /dev/null
+++ b/app/controllers/template_message_settings_controller.rb
@@ -0,0 +1,8 @@
+class TemplateMessageSettingsController < ApplicationController
+ before_action :require_login
+
+ def index
+ @group_settings = TemplateMessageSetting.group(:type).count
+ end
+
+end
\ No newline at end of file
diff --git a/app/controllers/user_rank_controller.rb b/app/controllers/user_rank_controller.rb
new file mode 100644
index 000000000..dddca485c
--- /dev/null
+++ b/app/controllers/user_rank_controller.rb
@@ -0,0 +1,24 @@
+class UserRankController < ApplicationController
+ # 根据时间获取热门开发者
+ def index
+ $redis_cache.zunionstore("recent-days-user-rank", get_timeable_key_names)
+ @user_rank = $redis_cache.zrevrange("recent-days-user-rank", 0, 3, withscores: true)
+ rescue Exception => e
+ @user_rank = []
+ end
+
+ private
+ # 默认显示7天的
+ def time
+ params.fetch(:time, 7).to_i
+ end
+
+ def get_timeable_key_names
+ names_array = []
+ (0...time).to_a.each do |i|
+ date_time_string = (Date.today - i.days).to_s
+ names_array << "v2-user-rank-#{date_time_string}"
+ end
+ names_array
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/users/applied_messages_controller.rb b/app/controllers/users/applied_messages_controller.rb
new file mode 100644
index 000000000..950caa2b3
--- /dev/null
+++ b/app/controllers/users/applied_messages_controller.rb
@@ -0,0 +1,18 @@
+class Users::AppliedMessagesController < Users::BaseController
+ before_action :check_auth
+ after_action :view_messages, only: [:index]
+
+ def index
+ @applied_messages = @_observed_user.applied_messages.order(viewed: :asc, created_at: :desc)
+ @applied_messages = paginate @applied_messages
+ end
+
+ private
+ def check_auth
+ return render_forbidden unless current_user.admin? || observed_logged_user?
+ end
+
+ def view_messages
+ @applied_messages.update_all(viewed: 'viewed')
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/users/applied_projects_controller.rb b/app/controllers/users/applied_projects_controller.rb
new file mode 100644
index 000000000..3663a6d34
--- /dev/null
+++ b/app/controllers/users/applied_projects_controller.rb
@@ -0,0 +1,39 @@
+class Users::AppliedProjectsController < Users::BaseController
+ before_action :check_auth
+ before_action :find_applied_project, except: [:index]
+ before_action :find_project, except: [:index]
+
+ def index
+ @applied_projects = AppliedProject.where(project_id: observed_user.full_admin_projects)
+ @applied_projects = paginate @applied_projects.order("created_at desc")
+ end
+
+ # 接受申请
+ def accept
+ @applied_project = Projects::AcceptJoinService.call(current_user, @applied_project)
+ rescue Exception => e
+ uid_logger_error(e.message)
+ tip_exception(e.message)
+ end
+
+ # 拒绝申请
+ def refuse
+ @applied_project = Projects::RefuseJoinService.call(current_user, @applied_project)
+ rescue Exception => e
+ uid_logger_error(e.message)
+ tip_exception(e.message)
+ end
+
+ private
+ def check_auth
+ return render_forbidden unless current_user.admin? || observed_logged_user?
+ end
+
+ def find_applied_project
+ @applied_project = AppliedProject.find_by_id params[:id]
+ end
+
+ def find_project
+ @project = @applied_project.project
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/users/applied_transfer_projects_controller.rb b/app/controllers/users/applied_transfer_projects_controller.rb
new file mode 100644
index 000000000..a40833943
--- /dev/null
+++ b/app/controllers/users/applied_transfer_projects_controller.rb
@@ -0,0 +1,41 @@
+class Users::AppliedTransferProjectsController < Users::BaseController
+ before_action :check_auth
+ before_action :find_applied_transfer_project, except: [:index]
+ before_action :find_project, except: [:index]
+
+ def index
+ user_collection_sql = AppliedTransferProject.where(owner_id: @_observed_user.id).to_sql
+ org_collection_sql = AppliedTransferProject.where(owner_id: Organization.joins(team_users: :team).where(team_users: {user_id: @_observed_user.id}, teams: {authorize: %w(admin owner)} )).to_sql
+ @applied_transfer_projects = AppliedTransferProject.from("( #{ user_collection_sql } UNION #{ org_collection_sql } ) AS applied_transfer_projects")
+ @applied_transfer_projects = paginate @applied_transfer_projects.order("created_at desc")
+ end
+
+ # 接受迁移
+ def accept
+ @applied_transfer_project = Projects::AcceptTransferService.call(current_user, @project)
+ rescue Exception => e
+ uid_logger_error(e.message)
+ tip_exception(e.message)
+ end
+
+ # 拒绝迁移
+ def refuse
+ @applied_transfer_project = Projects::RefuseTransferService.call(current_user, @project)
+ rescue Exception => e
+ uid_logger_error(e.message)
+ tip_exception(e.message)
+ end
+
+ private
+ def check_auth
+ return render_forbidden unless current_user.admin? || observed_logged_user?
+ end
+
+ def find_applied_transfer_project
+ @applied_transfer_project = AppliedTransferProject.find_by_id params[:id]
+ end
+
+ def find_project
+ @project = @applied_transfer_project.project
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/users/banks_controller.rb b/app/controllers/users/banks_controller.rb
index 2c792151e..99a55e6c4 100644
--- a/app/controllers/users/banks_controller.rb
+++ b/app/controllers/users/banks_controller.rb
@@ -1,8 +1,8 @@
class Users::BanksController < Users::BaseController
before_action :params_filter
def index
- order = params[:order] || "updated_at"
- sort = params[:sort] || "desc"
+ order = CourseList.column_names.include?(params[:order]) ? params[:order] : "updated_at"
+ sort = %w(desc asc).includes?(params[:sort]) ? params[:sort] : "desc"
@banks = @object_type.classify.constantize.where(@object_filter)
@course_lists = CourseList.where(id: @banks.pluck(:course_list_id))
@banks = @banks.where(course_list_id: params[:tag_id]) unless params[:tag_id].blank?
diff --git a/app/controllers/users/base_controller.rb b/app/controllers/users/base_controller.rb
index 128dc539b..214427798 100644
--- a/app/controllers/users/base_controller.rb
+++ b/app/controllers/users/base_controller.rb
@@ -5,7 +5,7 @@ class Users::BaseController < ApplicationController
helper_method :observed_logged_user?, :observed_user
def observed_user
- @_observed_user ||= (User.find_by_id(params[:user_id]) || User.find_by_login(params[:user_id]))
+ @_observed_user ||= (User.find_by_login(params[:user_id]) || User.find_by_id(params[:user_id]))
end
def observed_logged_user?
diff --git a/app/controllers/users/headmaps_controller.rb b/app/controllers/users/headmaps_controller.rb
new file mode 100644
index 000000000..7c88b5681
--- /dev/null
+++ b/app/controllers/users/headmaps_controller.rb
@@ -0,0 +1,26 @@
+class Users::HeadmapsController < Users::BaseController
+ def index
+ result = Gitea::User::HeadmapService.call(observed_user.login, start_stamp, end_stamp)
+ @headmaps = result[2].blank? ? [] : result[2]
+ rescue Exception => e
+ uid_logger_error(e.message)
+ tip_exception(e.message)
+ end
+
+ private
+ def start_stamp
+ if params[:year].present?
+ Date.new(params[:year], 1).to_time.to_i
+ else
+ Date.today.to_time.to_i - 365*24*60*60
+ end
+ end
+
+ def end_stamp
+ if params[:year].present?
+ Date.new(params[:year], 1).to_time.to_i + 365*24*60*60
+ else
+ Date.today.to_time.to_i
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/users/is_pinned_projects_controller.rb b/app/controllers/users/is_pinned_projects_controller.rb
new file mode 100644
index 000000000..1ddadd277
--- /dev/null
+++ b/app/controllers/users/is_pinned_projects_controller.rb
@@ -0,0 +1,45 @@
+class Users::IsPinnedProjectsController < Users::BaseController
+ before_action :private_user_resources!, only: [:pin]
+ def index
+ @is_pinned_projects = observed_user.pinned_projects.order(position: :desc, created_at: :asc).includes(project: [:project_category, :project_language, :repository]).order(position: :desc)
+ @is_pinned_projects = kaminari_paginate(@is_pinned_projects)
+ end
+
+ def pin
+ observed_user.is_pinned_project_ids = is_pinned_project_ids
+ render_ok
+ rescue ActiveRecord::RecordNotFound => e
+ render_not_found
+ rescue Exception => e
+ uid_logger_error(e.message)
+ tip_exception(e.message)
+ end
+
+ def update
+ @pinned_project = PinnedProject.find_by_id(params[:id])
+ @pinned_project.attributes = pinned_project_params
+ if @pinned_project.save
+ render_ok
+ else
+ render_error
+ end
+ rescue Exception => e
+ uid_logger_error(e.message)
+ tip_exception(e.message)
+ end
+
+ private
+ def is_pinned_project_ids
+ if params[:is_pinned_project_ids].present?
+ return params[:is_pinned_project_ids].select{|id| observed_user.full_member_projects.visible.pluck(:id).include?(id.to_i) }
+ end
+ if params[:is_pinned_project_id].present?
+ return observed_user.is_pinned_project_ids unless observed_user.full_member_projects.visible.pluck(:id).include?(params[:is_pinned_project_id].to_i)
+ return observed_user.is_pinned_project_ids.include?(params[:is_pinned_project_id].to_i) ? observed_user.is_pinned_project_ids : observed_user.is_pinned_project_ids.push(params[:is_pinned_project_id].to_i)
+ end
+ end
+
+ def pinned_project_params
+ params.require(:pinned_project).permit(:position)
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/users/messages_controller.rb b/app/controllers/users/messages_controller.rb
new file mode 100644
index 000000000..5116f580f
--- /dev/null
+++ b/app/controllers/users/messages_controller.rb
@@ -0,0 +1,96 @@
+class Users::MessagesController < Users::BaseController
+ before_action :private_user_resources!
+ before_action :find_receivers, only: [:create]
+
+ def index
+ limit = params[:limit] || params[:per_page]
+ limit = (limit.to_i.zero? || limit.to_i > 15) ? 15 : limit.to_i
+ page = params[:page].to_i.zero? ? 1 : params[:page].to_i
+ result = Notice::Read::ListService.call(observed_user.id, message_type, message_status, page, limit)
+ return render_error if result.nil?
+ @data = result[2]
+ end
+
+ def create
+ return render_forbidden unless %w(atme).include?(params[:type])
+ case params[:type]
+ when 'atme'
+ Notice::Write::CreateAtmeForm.new(atme_params).validate!
+ case atme_params[:atmeable_type]
+ when 'Issue'
+ SendTemplateMessageJob.perform_now('IssueAtme', @receivers, current_user.id, atme_params[:atmeable_id]) if Site.has_notice_menu?
+ when 'PullRequest'
+ SendTemplateMessageJob.perform_now('PullRequestAtme', @receivers, current_user.id, atme_params[:atmeable_id]) if Site.has_notice_menu?
+ when 'Journal'
+ journal = Journal.find_by_id(atme_params[:atmeable_id])
+ if journal.present?
+ if journal&.issue&.pull_request.present?
+ SendTemplateMessageJob.perform_now('PullRequestAtme', @receivers, current_user.id, atme_params[:atmeable_id]) if Site.has_notice_menu?
+ else
+ SendTemplateMessageJob.perform_now('IssueAtme', @receivers, current_user.id, atme_params[:atmeable_id]) if Site.has_notice_menu?
+ end
+ end
+ end
+ end
+ render_ok
+ rescue Exception => e
+ uid_logger_error(e.message)
+ tip_exception(e.message)
+ end
+
+ def delete
+ return render_forbidden unless %w(atme).include?(params[:type])
+ result = Notice::Write::DeleteService.call(params[:ids], observed_user.id, message_type)
+ return render_error if result.nil?
+
+ render_ok
+ rescue Exception => e
+ uid_logger_error(e.message)
+ tip_exception(e.message)
+ end
+
+ def read
+ return render_forbidden unless %w(notification atme).include?(params[:type])
+ result = Notice::Write::ChangeStatusService.call(params[:ids], observed_user.id, message_type)
+ if result.nil?
+ render_error
+ else
+ render_ok
+ end
+ rescue Exception => e
+ uid_logger_error(e.message)
+ tip_exception(e.message)
+ end
+
+ private
+ def message_type
+ @message_type = begin
+ case params[:type]
+ when "notification" then 1
+ when "atme" then 2
+ else
+ -1
+ end
+ end
+ end
+
+ def message_status
+ @message_status = begin
+ case params[:status]
+ when "1" then 1
+ when "2" then 2
+ else
+ -1
+ end
+ end
+ end
+
+ def atme_params
+ params.permit(:atmeable_type, :atmeable_id, receivers_login: [])
+ end
+
+ def find_receivers
+ @receivers = User.where(login: params[:receivers_login])
+ return render_not_found if @receivers.size == 0
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/users/organizations_controller.rb b/app/controllers/users/organizations_controller.rb
index 721339e84..2d949da7d 100644
--- a/app/controllers/users/organizations_controller.rb
+++ b/app/controllers/users/organizations_controller.rb
@@ -16,10 +16,10 @@ class Users::OrganizationsController < Users::BaseController
private
def sort_by
- params.fetch(:sort_by, "created_at")
+ OrganizationExtension.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_at'
end
def sort_direction
- params.fetch(:sort_direction, "desc")
+ %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc'
end
end
\ No newline at end of file
diff --git a/app/controllers/users/project_trends_controller.rb b/app/controllers/users/project_trends_controller.rb
new file mode 100644
index 000000000..9edd56f18
--- /dev/null
+++ b/app/controllers/users/project_trends_controller.rb
@@ -0,0 +1,11 @@
+class Users::ProjectTrendsController < Users::BaseController
+
+ def index
+ if params[:date].present?
+ @project_trends = observed_user.project_trends.where("DATE(created_at) = ?", params[:date])
+ else
+ @project_trends = observed_user.project_trends
+ end
+ @project_trends = kaminari_paginate(@project_trends.includes(:trend, :project).order(created_at: :desc))
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/users/statistics_controller.rb b/app/controllers/users/statistics_controller.rb
new file mode 100644
index 000000000..dffd3f607
--- /dev/null
+++ b/app/controllers/users/statistics_controller.rb
@@ -0,0 +1,219 @@
+class Users::StatisticsController < Users::BaseController
+ before_action :preload_develop_data, only: [:develop]
+
+ # 近期活动统计
+ def activity
+ date_range = (1.week.ago.to_date..Date.today).to_a
+ commit_request = Gitea::User::HeadmapService.call(observed_user.login, 1.week.ago.to_date.to_time.to_i, Date.today.to_time.to_i)
+ commit_data = commit_request[2]
+ @date_data = []
+ @issue_data = []
+ @pull_request_data = []
+ @commit_data = []
+ date_range.each do |date|
+ @date_data << date.strftime("%Y.%m.%d")
+ @issue_data << observed_user.issues.where("DATE(created_on) = ?", date).size
+ @pull_request_data << observed_user.pull_requests.where("DATE(created_at) = ?", date).size
+ date_commit_data = commit_data.blank? ? nil : commit_data.select{|item| item["timestamp"] == date.to_time.to_i}
+ @commit_data << (date_commit_data.blank? ? 0 : date_commit_data[0]["contributions"].to_i)
+ end
+ render :json => {dates: @date_data, issues_count: @issue_data, pull_requests_count: @pull_request_data, commits_count: @commit_data}
+ end
+
+ # 开发能力
+ def develop
+ if params[:start_time].present? && params[:end_time].present?
+ # 影响力
+ @influence = (60.0 + @follow_count / (@follow_count + 10.0) * 40.0).to_i
+ @platform_influence = (60.0 + @platform_follow_count / (@platform_follow_count + 10.0) * 40.0).to_i
+ # 贡献度
+ @contribution = (60.0 + @pullrequest_count / (@pullrequest_count + 10.0) * 40.0).to_i
+ @platform_contribution = (60.0 + @platform_pullrequest_count / (@platform_pullrequest_count + 10.0) * 40.0).to_i
+ # 活跃度
+ @activity = (60.0 + @issues_count / (@issues_count + 10.0) * 40.0).to_i
+ @platform_activity = (60.0 + @platform_issues_count / (@platform_issues_count + 10.0) * 40.0).to_i
+ # 项目经验
+ @experience = 10 * @project_count + 5 * @fork_count + @project_watchers_count + @project_praises_count
+ @experience = (60.0 + @experience / (@experience + 100.0) * 40.0).to_i
+ @platform_experience = 10 * @platform_project_count + 5 * @platform_fork_count + @platform_project_watchers_count + @platform_project_praises_count
+ @platform_experience = (60.0 + @platform_experience / (@platform_experience + 100.0) * 40.0).to_i
+ # 语言能力
+ @language = (60.0 + @project_languages_count.length / (@project_languages_count.length + 5.0) * 40.0).to_i
+ @platform_language = (60.0 + @platform_project_languages_count.length / (@platform_project_languages_count.length + 5.0) * 40.0).to_i
+ # 语言百分比
+ @languages_percent = Hash.new
+ for key in @project_languages_count.keys do
+ @languages_percent[key] = (@project_languages_count[key].to_f / @project_languages_count.values.sum).round(2)
+ end
+ @platform_languages_percent = Hash.new
+ for key in @platform_project_languages_count.keys do
+ @platform_languages_percent[key] = (@platform_project_languages_count[key].to_f / @platform_project_languages_count.values.sum).round(2)
+ end
+ # 各门语言分数
+ @each_language_score = Hash.new
+ for key in @project_languages_count.keys do
+ @each_language_score[key] = (60.0 + @project_languages_count[key] / (@project_languages_count[key] + 5.0) * 40.0).to_i
+ end
+ @platform_each_language_score = Hash.new
+ for key in @platform_project_languages_count.keys do
+ @platform_each_language_score[key] = (60.0 + @platform_project_languages_count[key] / (@platform_project_languages_count[key] + 5.0) * 40.0).to_i
+ end
+ else
+ # 影响力
+ @influence = (60.0 + @follow_count / (@follow_count + 20.0) * 40.0).to_i
+ @platform_influence = (60.0 + @platform_follow_count / (@platform_follow_count + 20.0) * 40.0).to_i
+
+ # 贡献度
+ @contribution = (60.0 + @pullrequest_count / (@pullrequest_count + 20.0) * 40.0).to_i
+ @platform_contribution = (60.0 + @platform_pullrequest_count / (@platform_pullrequest_count + 20.0) * 40.0).to_i
+
+ # 活跃度
+ @activity = (60.0 + @issues_count / (@issues_count + 80.0) * 40.0).to_i
+ @platform_activity = (60.0 + @platform_issues_count / (@platform_issues_count + 80.0) * 40.0).to_i
+
+ # 项目经验
+ @experience = 10 * @project_count + 5 * @fork_count + @project_watchers_count + @project_praises_count
+ @experience = (60.0 + @experience / (@experience + 100.0) * 40.0).to_i
+ @platform_experience = 10 * @platform_project_count + 5 * @platform_fork_count + @platform_project_watchers_count + @platform_project_praises_count
+ @platform_experience = (60.0 + @platform_experience / (@platform_experience + 100.0) * 40.0).to_i
+ # 语言能力
+ @language = (60.0 + @project_languages_count.length / (@project_languages_count.length + 5.0) * 40.0).to_i
+ @platform_language = (60.0 + @platform_project_languages_count.length / (@platform_project_languages_count.length + 5.0) * 40.0).to_i
+
+ # 语言百分比
+ @languages_percent = Hash.new
+ for key in @project_languages_count.keys do
+ @languages_percent[key] = (@project_languages_count[key].to_f / @project_languages_count.values.sum).round(2)
+ end
+ # @platform_languages_percent = Hash.new
+ # for key in @platform_project_languages_count.keys do
+ # @platform_languages_percent[key] = (@platform_project_languages_count[key].to_f / @platform_project_languages_count.values.sum).round(2)
+ # end
+ # 各门语言分数
+ @each_language_score = Hash.new
+ for key in @project_languages_count.keys do
+ @each_language_score[key] = (60.0 + @project_languages_count[key] / (@project_languages_count[key] + 5.0) * 40.0).to_i
+ end
+ # @platform_each_language_score = Hash.new
+ # for key in @platform_project_languages_count.keys do
+ # @platform_each_language_score[key] = (60.0 + @platform_project_languages_count[key] / (@platform_project_languages_count[key] + 5.0) * 40.0).to_i
+ # end
+ end
+
+ end
+
+ # 角色定位
+ def role
+ full_member_projects = observed_user.full_member_projects
+ owner_projects = filter_member_projects_by_role("Owner")
+ manager_projects = filter_member_projects_by_role("Manager").where.not(id: owner_projects.ids)
+ developer_projects = filter_member_projects_by_role("Developer").where.not(id: owner_projects.ids + manager_projects.ids)
+ reporter_projects = filter_member_projects_by_role("Reporter").where.not(id: owner_projects.ids + manager_projects.ids + developer_projects.ids)
+
+ @full_member_projects_count = full_member_projects.size
+ @owner_projects_count = owner_projects.size
+ @manager_projects_count = manager_projects.size
+ @developer_projects_count = developer_projects.size
+ @reporter_projects_count = reporter_projects.size
+
+ end
+
+ # 专业定位
+ def major
+ # 参与项目
+ join_normal_projects_sql = Project.members_projects(observed_user.id).to_sql
+ join_org_projects_sql = Project.joins(team_projects: [team: :team_users]).where(team_users: {user_id: observed_user.id}).to_sql
+ # 关注项目
+ star_projects_sql = Project.joins(:watchers).where(watchers: {watchable_type: "Project", user_id: observed_user.id}).to_sql
+ # fork项目
+ fork_projects_sql = Project.where(id: observed_user.fork_users.select(:id, :fork_project_id).pluck(:fork_project_id)).to_sql
+ major_projects = Project.from("( #{ join_normal_projects_sql} UNION #{ join_org_projects_sql } UNION #{ star_projects_sql } UNION #{fork_projects_sql}) AS projects").distinct
+ categories = ProjectCategory.joins(:projects).merge(Project.where(id: time_filter(major_projects, 'created_on'))).distinct.pluck(:name)
+ render :json => {categories: categories}
+ end
+
+ private
+ def time_filter(collection, time_field)
+ if params[:start_time].present? && params[:end_time].present?
+ return collection.where("#{time_field} > ? AND #{time_field} < ?", Time.at(params[:start_time].to_i).beginning_of_day, Time.at(params[:end_time].to_i).end_of_day)
+ else
+ return collection
+ end
+ rescue
+ return collection
+ end
+
+ def filter_member_projects_by_role(role)
+ case role
+ when 'Owner'
+ normal_projects_sql = Project.joins(members: :roles).where(user_id: observed_user.id).where(members: {user_id: observed_user.id}, roles: {name: 'Manager'}).to_sql
+ org_projects_sql = Project.joins(:owner, teams: :team_users).where(users: {type: 'Organization'}, teams: {authorize: "owner"}, team_users: {user_id: observed_user.id}).to_sql
+ when "Manager"
+ normal_projects_sql = Project.joins(members: :roles).where.not(user_id: observed_user.id).where(members: {user_id: observed_user.id}, roles: {name: 'Manager'}).to_sql
+ org_projects_sql = Project.joins(:owner, teams: :team_users).where(users: {type: 'Organization'}, teams: {authorize: "admin"}, team_users: {user_id: observed_user.id}).to_sql
+ when "Developer"
+ normal_projects_sql = Project.joins(members: :roles).where.not(user_id: observed_user.id).where(members: {user_id: observed_user.id}, roles: {name: 'Developer'}).to_sql
+ org_projects_sql = Project.joins(:owner, teams: :team_users).where(users: {type: 'Organization'}, teams: {authorize: "write"}, team_users: {user_id: observed_user.id}).to_sql
+ when "Reporter"
+ normal_projects_sql = Project.joins(members: :roles).where.not(user_id: observed_user.id).where(members: {user_id: observed_user.id}, roles: {name: 'Reporter'}).to_sql
+ org_projects_sql = Project.joins(:owner, teams: :team_users).where(users: {type: 'Organization'}, teams: {authorize: "read"}, team_users: {user_id: observed_user.id}).to_sql
+ end
+ return time_filter(Project.from("( #{normal_projects_sql} UNION #{org_projects_sql} ) AS projects").distinct, 'created_on')
+ end
+
+ def preload_develop_data
+ if params[:start_time].present? && params[:end_time].present?
+ # 用户被follow数量
+ @follow_count = time_filter(Watcher.where(watchable: observed_user), 'created_at').count
+ @platform_follow_count = time_filter(Watcher.where(watchable_type: 'User'), 'created_at').count
+ # 用户pr数量
+ @pullrequest_count = time_filter(PullRequest.where(user_id: observed_user.id), 'created_at').count
+ @platform_pullrequest_count = time_filter(PullRequest, 'created_at').count
+ # 用户issue数量
+ @issues_count = time_filter(Issue.where(author_id: observed_user.id), 'created_on').count
+ @platform_issues_count = time_filter(Issue, 'created_on').count
+ # 用户总项目数
+ @project_count = time_filter(Project.where(user_id:observed_user.id), 'created_on').count
+ @platform_project_count = time_filter(Project, 'created_on').count
+ # 用户项目被fork数量
+ @fork_count = time_filter(Project.where(user_id:observed_user.id), 'created_on').sum("forked_count")
+ @platform_fork_count = time_filter(Project, 'created_on').sum("forked_count")
+ # 用户项目关注数
+ @project_watchers_count = time_filter(Project.where(user_id: observed_user.id), 'created_on').sum("watchers_count")
+ @platform_project_watchers_count = time_filter(Project, 'created_on').sum("watchers_count")
+ # 用户项目点赞数
+ @project_praises_count = time_filter(Project.where(user_id: observed_user.id), 'created_on').sum("praises_count")
+ @platform_project_praises_count = time_filter(Project, 'created_on').sum("praises_count")
+ # 用户不同语言项目数量
+ @project_languages_count = time_filter(Project.where(user_id: observed_user.id), 'created_on').joins(:project_language).group("project_languages.name").count
+ @platform_project_languages_count = time_filter(Project, 'created_on').joins(:project_language).group("project_languages.name").count
+ else
+ @platform_result = Cache::V2::PlatformStatisticService.new.read
+ @user_result = Cache::V2::UserStatisticService.new(observed_user.id).read
+ # 用户被follow数量
+ @follow_count = @user_result["follow-count"].to_i
+ @platform_follow_count = @platform_result["follow-count"].to_i
+ # 用户pr数量
+ @pullrequest_count = @user_result["pullrequest-count"].to_i
+ @platform_pullrequest_count = @platform_result["pullrequest-count"].to_i
+ # 用户issue数量
+ @issues_count = @user_result["issue-count"].to_i
+ @platform_issues_count = @platform_result["issue-count"].to_i
+ # 用户总项目数
+ @project_count = @user_result["project-count"].to_i
+ @platform_project_count = @platform_result["project-count"].to_i
+ # 用户项目被fork数量
+ @fork_count = @user_result["fork-count"].to_i
+ @platform_fork_count = @platform_result["fork-count"].to_i
+ # 用户项目关注数
+ @project_watchers_count = @user_result["project-watcher-count"].to_i
+ @platform_project_watchers_count = @platform_result["project-watcher-count"].to_i
+ # 用户项目点赞数
+ @project_praises_count = @user_result["project-praise-count"].to_i
+ @platform_project_praises_count = @platform_result["project-praise-count"].to_i
+ # 用户不同语言项目数量
+ @project_languages_count = JSON.parse(@user_result["project-language"])
+ @platform_project_languages_count = JSON.parse(@platform_result["project-language"])
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/users/system_notification_histories_controller.rb b/app/controllers/users/system_notification_histories_controller.rb
new file mode 100644
index 000000000..70e91fbb9
--- /dev/null
+++ b/app/controllers/users/system_notification_histories_controller.rb
@@ -0,0 +1,15 @@
+class Users::SystemNotificationHistoriesController < Users::BaseController
+ before_action :private_user_resources!, only: [:create]
+ def create
+ @history = observed_user.system_notification_histories.new(system_notification_id: params[:system_notification_id])
+ if @history.save
+ render_ok
+ else
+ Rails.logger.info @history.errors.as_json
+ render_error(@history.errors.full_messages.join(","))
+ end
+ rescue Exception => e
+ uid_logger_error(e.message)
+ tip_exception(e.message)
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/users/template_message_settings_controller.rb b/app/controllers/users/template_message_settings_controller.rb
new file mode 100644
index 000000000..2234e7301
--- /dev/null
+++ b/app/controllers/users/template_message_settings_controller.rb
@@ -0,0 +1,36 @@
+class Users::TemplateMessageSettingsController < Users::BaseController
+ before_action :check_auth
+ before_action :get_current_setting
+
+ def current_setting
+
+ end
+
+ def update_setting
+ Rails.logger.info setting_params[:notification_body]
+ Rails.logger.info setting_params[:email_body]
+
+ @current_setting.notification_body = setting_params[:notification_body].to_hash
+ @current_setting.email_body = setting_params[:email_body].to_hash
+ return render_error("保存失败") unless @current_setting.save!
+ end
+
+ private
+ def check_auth
+ return render_forbidden unless current_user.admin? || observed_logged_user?
+ end
+
+ def get_current_setting
+ @current_setting = @_observed_user.user_template_message_setting
+ @current_setting = UserTemplateMessageSetting.build(@_observed_user.id) if @current_setting.nil?
+ end
+
+ def setting_params
+ params.require(:setting).permit(notification_body: {}, email_body: {})
+ end
+
+ def valid_setting_params
+ setting_params[:notification_body].keys.equal?(UserTemplateMessageSetting.init_notification_body.keys) && setting_params[:email_body].keys.equal?(UserTemplateMessageSetting.init_email_body)
+ end
+
+end
\ No newline at end of file
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 6b6b8fcb3..64ec13bf5 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -2,10 +2,11 @@ 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]
- before_action :check_user_exist, only: [:show, :homepage_info,:projects, :watch_users, :fan_users]
- before_action :require_login, only: %i[me list]
+ 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 :require_login, only: %i[me list sync_user_info]
before_action :connect_to_ci_db, only: [:get_user_info]
+ before_action :convert_image!, only: [:update, :update_image]
skip_before_action :check_sign, only: [:attachment_show]
def connect_to_ci_db(options={})
@@ -27,15 +28,32 @@ class UsersController < ApplicationController
def show
#待办事项,现在未做
- @undo_events = 0
+ if User.current.admin? || User.current.login == @user.login
+ @waiting_applied_messages = @user.applied_messages.waiting
+ @common_applied_transfer_projects = AppliedTransferProject.where(owner_id: @user.id).common + AppliedTransferProject.where(owner_id: Organization.joins(team_users: :team).where(team_users: {user_id: @user.id}, teams: {authorize: %w(admin owner)} )).common
+ @common_applied_projects = AppliedProject.where(project_id: @user.full_admin_projects).common
+ #@undo_events = @waiting_applied_messages.size + @common_applied_transfer_projects.size + @common_applied_projects.size
+ @undo_events = @common_applied_transfer_projects.size + @common_applied_projects.size
+ else
+ @waiting_applied_messages = AppliedMessage.none
+ @common_applied_transfer_projects = AppliedTransferProject.none
+ @common_applied_projects = AppliedProject.none
+ @undo_events = 0
+ end
#用户的组织数量
# @user_composes_count = @user.composes.size
@user_composes_count = 0
- @user_org_count = User.current.logged? ? @user.organizations.with_visibility(%w(common limited)).size + @user.organizations.with_visibility("privacy").joins(:organization_users).where(organization_users: {user_id: current_user.id}).size : @user.organizations.with_visibility("common").size
- user_projects = User.current.logged? && (User.current.admin? || User.current.login == @user.login) ? @user.projects : @user.projects.visible
+ user_organizations = User.current.logged? ? @user.organizations.with_visibility(%w(common limited)) + @user.organizations.with_visibility("privacy").joins(:team_users).where(team_users: {user_id: current_user.id}) : @user.organizations.with_visibility("common")
+ @user_org_count = user_organizations.size
+ normal_projects = Project.members_projects(@user.id).to_sql
+ org_projects = Project.joins(team_projects: [team: :team_users]).where(team_users: {user_id: @user.id}).to_sql
+ projects = Project.from("( #{ normal_projects} UNION #{ org_projects } ) AS projects").distinct
+ user_projects = User.current.logged? && (User.current.admin? || User.current.login == @user.login) ? projects : projects.visible
@projects_common_count = user_projects.common.size
@projects_mirrior_count = user_projects.mirror.size
@projects_sync_mirrior_count = user_projects.sync_mirror.size
+ # 为了缓存活跃用户的基本信息,后续删除
+ Cache::V2::OwnerCommonService.new(@user.id).read
end
def watch_users
@@ -50,16 +68,33 @@ class UsersController < ApplicationController
def fan_users
watchers = @user.watchers.includes(:user).order("watchers.created_at desc")
- watchers = watchers.joins(:user).where("LOWER(concat(users.lastname, users.firstname, users.login)) LIKE ?", "%#{params[:search].split(" ").join('|')}%") if params[:search].present?
-
+ watchers = watchers.joins(:user).merge(User.like(params[:search]))
@watchers_count = watchers.size
@watchers = paginate(watchers)
end
+ def hovercard
+ end
+
def update
- @user = User.find params[:id]
- @user.update!(user_params)
- render_ok
+ 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)) if user_params[:image].present?
+ @user.attributes = user_params.except(:image)
+ unless @user.save
+ render_error(-1, @user.errors.full_messages.join(", "))
+ end
+ end
+
+ 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))
+ return render_ok({message: '头像修改成功'})
+ rescue Exception => e
+ uid_logger_error(e.message)
+ render_error(-1, '头像修改失败!')
end
def me
@@ -70,6 +105,12 @@ class UsersController < ApplicationController
def get_user_info
begin
@user = current_user
+ begin
+ result = Notice::Read::CountService.call(current_user.id)
+ @message_unread_total = result.nil? ? 0 : result[2]["unread_notification"]
+ rescue
+ @message_unread_total = 0
+ end
# TODO 等消息上线再打开注释
#@tidding_count = unviewed_tiddings(current_user) if current_user.present?
rescue Exception => e
@@ -163,7 +204,7 @@ class UsersController < ApplicationController
def trustie_related_projects
projects = Project.includes(:owner, :members, :project_score).where(id: params[:ids]).order("updated_on desc")
projects_json = []
- domain_url = EduSetting.get('host_name') + '/projects'
+ domain_url = EduSetting.get('host_name')
if projects.present?
projects.each do |p|
project_url = "/#{p.owner.login}/#{p.identifier}"
@@ -232,17 +273,39 @@ class UsersController < ApplicationController
render_ok
end
+ def sync_user_info
+ user = User.find_by_login params[:login]
+ return render_forbidden unless user === current_user
+
+ sync_params = {
+ email: params[:email],
+ password: params[:password]
+ }
+
+ Users::UpdateInfoForm.new(sync_params.merge(login: params[:login])).validate!
+
+ interactor = Gitea::User::UpdateInteractor.call(user.login, sync_params)
+ if interactor.success?
+ user.update!(password: params[:password], mail: params[:email], status: User::STATUS_EDIT_INFO)
+ render_ok
+ else
+ render_error(interactor.error)
+ end
+ end
+
private
def load_user
@user = User.find_by_login(params[:id]) || User.find_by(id: params[:id])
end
def user_params
- params.require(:user).permit(:nickname, :lastname, :show_realname,:login,:mail,
+ params.require(:user).permit(:nickname, :image,
user_extension_attributes: [
:gender, :location, :location_city,
:occupation, :technical_title,
- :school_id, :department_id,:identity, :student_id, :description]
+ :school_id, :department_id, :province, :city,
+ :custom_department, :identity, :student_id, :description,
+ :show_email, :show_location, :show_department]
)
end
diff --git a/app/controllers/users_for_private_messages_controller.rb b/app/controllers/users_for_private_messages_controller.rb
index 610bf5d52..8f73bb36b 100644
--- a/app/controllers/users_for_private_messages_controller.rb
+++ b/app/controllers/users_for_private_messages_controller.rb
@@ -11,7 +11,7 @@ class UsersForPrivateMessagesController < ApplicationController
return
end
- users = users.where('LOWER(concat(lastname, firstname, nickname)) LIKE ?', "%#{keyword}%")
+ users = users.like(keyword)
@users = users.limit(10).includes(:user_extension)
end
diff --git a/app/controllers/version_releases_controller.rb b/app/controllers/version_releases_controller.rb
index 1b56236a4..2d7546a1c 100644
--- a/app/controllers/version_releases_controller.rb
+++ b/app/controllers/version_releases_controller.rb
@@ -1,14 +1,14 @@
class VersionReleasesController < ApplicationController
before_action :load_repository
before_action :set_user
- before_action :require_login, except: [:index]
- before_action :find_version , only: [:edit, :update, :destroy]
+ before_action :require_login, except: [:index, :show]
+ before_action :check_release_authorize, except: [:index, :show]
+ before_action :find_version , only: [:show, :edit, :update, :destroy]
def index
- version_releases = Gitea::Versions::ListService.new(@user.gitea_token, @user.try(:login), @repository.try(:identifier)).call
- @version_releases = version_releases
- @user_permission = current_user.present? && (current_user == @user || current_user.admin?)
- @forge_releases = @repository.version_releases.select(:id,:version_gid).includes(:attachments)
+ @version_releases = kaminari_paginate(@repository.version_releases.order(created_at: :desc))
+ @user_permission = current_user.present? && (@repository.project.all_developers.include?(current_user) || current_user.admin?)
+ @user_admin_permission = current_user.present? && (@repository.project.all_managers.include?(current_user) || current_user.admin?)
end
def new
@@ -22,6 +22,10 @@ class VersionReleasesController < ApplicationController
end
end
+ def show
+ # @release = Gitea::Versions::GetService.call(current_user.gitea_token, @user&.login, @repository&.identifier, @version&.version_gid)
+ end
+
def create
if params[:name].nil?
normal_status(-1, "名称不能为空")
@@ -37,13 +41,14 @@ class VersionReleasesController < ApplicationController
version_params = releases_params
version_release = VersionRelease.new(version_params.merge(user_id: current_user.id, repository_id: @repository.id))
if version_release.save!
- git_version_release = Gitea::Versions::CreateService.new(@user.gitea_token, @user.try(:login), @repository.try(:identifier), version_params).call
+ git_version_release = Gitea::Versions::CreateService.new(current_user.gitea_token, @user.try(:login), @repository.try(:identifier), version_params).call
if git_version_release
update_params = {
tarball_url: git_version_release["tarball_url"],
zipball_url: git_version_release["zipball_url"],
url: git_version_release["url"],
version_gid: git_version_release["id"],
+ sha: git_version_release["sha"]
}
version_release.update_attributes!(update_params)
version_release.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: "create")
@@ -81,7 +86,7 @@ class VersionReleasesController < ApplicationController
if @version.update_attributes!(version_params)
create_attachments(params[:attachment_ids], @version) if params[:attachment_ids].present?
- git_version_release = Gitea::Versions::UpdateService.new(@user.gitea_token, @user.try(:login), @repository.try(:identifier), version_params, @version.try(:version_gid)).call
+ git_version_release = Gitea::Versions::UpdateService.new(current_user.gitea_token, @user.try(:login), @repository.try(:identifier), version_params, @version.try(:version_gid)).call
unless git_version_release
raise Error, "更新失败"
end
@@ -102,7 +107,7 @@ class VersionReleasesController < ApplicationController
ActiveRecord::Base.transaction do
begin
if @version.destroy
- git_version_release = Gitea::Versions::DeleteService.new(@user.gitea_token, @user.try(:login), @repository.try(:identifier), @version.try(:version_gid)).call
+ git_version_release = Gitea::Versions::DeleteService.new(current_user.gitea_token, @user.try(:login), @repository.try(:identifier), @version.try(:version_gid)).call
if git_version_release.status == 204
normal_status(0, "删除成功")
@@ -157,4 +162,8 @@ class VersionReleasesController < ApplicationController
end
end
+ def check_release_authorize
+ return render_forbidden("您没有权限进行此操作.") unless current_user.admin? || @project.manager?(current_user)
+ end
+
end
diff --git a/app/controllers/versions_controller.rb b/app/controllers/versions_controller.rb
index 8b6dba9a5..084648c6d 100644
--- a/app/controllers/versions_controller.rb
+++ b/app/controllers/versions_controller.rb
@@ -1,13 +1,13 @@
class VersionsController < ApplicationController
before_action :require_login, except: [:index, :show]
+ before_action :require_profile_completed, only: [:create]
before_action :load_repository
+ before_action :check_menu_authorize
before_action :check_issue_permission, except: [:show, :index]
before_action :set_version, only: [:edit, :update, :destroy, :show,:update_status]
def index
@user_admin_or_member = current_user.present? && (current_user.admin || @project.member?(current_user))
- order_name = params[:order_name] || "created_on"
- order_type = params[:order_type] || "desc"
status = params[:status]
versions = @project.versions.version_includes
@open_versions_size = versions.where(status: "open")&.size
@@ -26,20 +26,13 @@ class VersionsController < ApplicationController
end
def show
- order_name = params[:order_name] || "created_on"
- order_type = params[:order_type] || "desc"
-
- version_issues = @version.issues.issue_includes
+ version_issues = @version.issues.issue_issue.issue_includes
status_type = params[:status_type] || "1"
# @close_issues_size = version_issues.where(status_id: 5).size
# @open_issues_size = version_issues.size - @close_issues_size
- if status_type.to_s == "1" #表示开启中的
- version_issues = version_issues.where.not(status_id: 5)
- else
- version_issues = version_issues.where(status_id: 5)
- end
+
version_issues = version_issues.where(author_id: params[:author_id]) if params[:author_id].present? && params[:author_id].to_s != "all"
version_issues = version_issues.where(assigned_to_id: params[:assigned_to_id]) if params[:assigned_to_id].present? && params[:assigned_to_id].to_s != "all"
version_issues = version_issues.where(tracker_id: params[:tracker_id]) if params[:tracker_id].present? && params[:tracker_id].to_s != "all"
@@ -51,10 +44,26 @@ class VersionsController < ApplicationController
version_issues = version_issues.joins(:issue_tags).where(issue_tags: {id: params[:issue_tag_id].to_i}) if params[:issue_tag_id].present? && params[:issue_tag_id].to_s != "all"
version_issues = version_issues.reorder("#{order_name} #{order_type}")
+ has_filter_params = (params[:author_id].present? && params[:author_id].to_s != "all") ||
+ (params[:assigned_to_id].present? && params[:assigned_to_id].to_s != "all") ||
+ (params[:tracker_id].present? && params[:tracker_id].to_s != "all") ||
+ (params[:status_id].present? && params[:status_id].to_s != "all") ||
+ (params[:priority_id].present? && params[:priority_id].to_s != "all") ||
+ (params[:fixed_version_id].present? && params[:fixed_version_id].to_s != "all") ||
+ (params[:done_ratio].present? && params[:done_ratio].to_s != "all") ||
+ (params[:issue_type].present? && params[:issue_type].to_s != "all") ||
+ (params[:issue_tag_id].present? && params[:issue_tag_id].to_s != "all")
+ @version_close_issues_size = has_filter_params ? version_issues.closed.size : @version.issues.issue_issue.issue_includes.closed.size
+ @version_issues_size = has_filter_params ? version_issues.size : @version.issues.issue_issue.issue_includes.size
+ if status_type.to_s == "1" #表示开启中的
+ version_issues = version_issues.where.not(status_id: 5)
+ else
+ version_issues = version_issues.where(status_id: 5)
+ end
@page = params[:page] || 1
@limit = params[:limit] || 15
- @version_issues_size = version_issues.size
+ # @version_issues_size = version_issues.size
@version_issues = version_issues.page(@page).per(@limit)
end
@@ -166,4 +175,16 @@ class VersionsController < ApplicationController
end
end
+ def order_name
+ Version.column_names.include?(params[:order_name]) ? params[:order_name] : 'created_on'
+ end
+
+ def order_type
+ %w(desc asc).include?(params[:order_type]) ? params[:order_type] : 'desc'
+ end
+
+ def check_menu_authorize
+ return render_not_found unless @project.has_menu_permission("versions")
+ end
+
end
diff --git a/app/controllers/watchers_controller.rb b/app/controllers/watchers_controller.rb
index 38f60ebae..aa3642616 100644
--- a/app/controllers/watchers_controller.rb
+++ b/app/controllers/watchers_controller.rb
@@ -1,5 +1,6 @@
class WatchersController < ApplicationController
before_action :require_login, except: %i[index]
+ before_action :require_profile_completed, only: [:follow]
# before_action :find_project_with_id
before_action :get_target
diff --git a/app/controllers/weapps/courses_controller.rb b/app/controllers/weapps/courses_controller.rb
index ccd5d1bcf..d7ac903ed 100644
--- a/app/controllers/weapps/courses_controller.rb
+++ b/app/controllers/weapps/courses_controller.rb
@@ -86,7 +86,7 @@ class Weapps::CoursesController < Weapps::BaseController
end
if search.present?
- @teacher_list = @teacher_list.joins(:user).where("LOWER(CONCAT(users.lastname, users.firstname)) like ?", "%#{search}%")
+ @teacher_list = @teacher_list.joins(:user).where("LOWER(CONCAT(users.lastname, users.firstname)) like ? OR users.nickname like ?", "%#{search}%", "%#{search}%")
end
@teacher_list_size = @teacher_list.size
@@ -127,8 +127,8 @@ class Weapps::CoursesController < Weapps::BaseController
@students = CourseMember.students(@course)
if search.present?
- @students = @students.joins(user: :user_extension).where("LOWER(CONCAT(users.lastname, users.firstname)) like ? or
- user_extensions.student_id like ?", "%#{search}%", "%#{search}%")
+ @students = @students.joins(user: :user_extension).where("LOWER(CONCAT(users.lastname, users.firstname)) like ? or users.nickname like ? or
+ user_extensions.student_id like ?", "%#{search}%", "%#{search}%", "%#{search}%")
end
if course_group_id.present?
diff --git a/app/controllers/zips_controller.rb b/app/controllers/zips_controller.rb
index 3ed1eb8b3..287891403 100644
--- a/app/controllers/zips_controller.rb
+++ b/app/controllers/zips_controller.rb
@@ -86,7 +86,7 @@ class ZipsController < ApplicationController
#搜索
if params[:search].present?
- @ex_users = @ex_users.joins(user: :user_extension).where("CONCAT(lastname, firstname) like ? OR student_id like ?", "%#{params[:search]}%", "%#{params[:search]}%")
+ @ex_users = @ex_users.joins(user: :user_extension).where("CONCAT(lastname, firstname) like ? OR nickname like ? OR student_id like ?", "%#{params[:search]}%", "%#{params[:search]}%", "%#{params[:search]}%")
end
default_ex_users_size = @ex_users&.size
@@ -130,8 +130,8 @@ class ZipsController < ApplicationController
end
unless params[:search].blank?
- @all_student_works = @all_student_works.joins(user: :user_extension).where("concat(lastname, firstname) like ?
- or student_id like ?", "%#{params[:search]}%", "%#{params[:search]}%")
+ @all_student_works = @all_student_works.joins(user: :user_extension).where("CONCAT(lastname, firstname) like ? or nickname like ?
+ or student_id like ?", "%#{params[:search]}%", "%#{params[:search]}%", "%#{params[:search]}%")
end
student_work_sizes = @all_student_works&.size
diff --git a/app/decorators/course_decorator.rb b/app/decorators/course_decorator.rb
deleted file mode 100644
index 9c3340bbf..000000000
--- a/app/decorators/course_decorator.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-module CourseDecorator
- def can_visited?
- is_public == 1 || User.current.admin_or_business? || User.current.member_of_course?(self)
- end
-end
\ No newline at end of file
diff --git a/app/decorators/ec_course_target_decorator.rb b/app/decorators/ec_course_target_decorator.rb
deleted file mode 100644
index 2965a8381..000000000
--- a/app/decorators/ec_course_target_decorator.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-module EcCourseTargetDecorator
-end
\ No newline at end of file
diff --git a/app/decorators/experience_decorator.rb b/app/decorators/experience_decorator.rb
deleted file mode 100644
index f50f479d7..000000000
--- a/app/decorators/experience_decorator.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-module ExperienceDecorator
- def container_type_text
- I18n.t("experience.container_type.#{container_type.to_s.underscore}")
- end
-
- def content
- case container_type.to_s.underscore
- when 'game' then
- game = Game.find_by(id: container_id)
- game.present? ? "通过实训“#{game.challenge.shixun.name}”的第#{game.challenge.position}关获得的奖励" : ''
- when 'shixun_publish' then
- shixun = Shixun.find_by(id: container_id)
- shixun.present? ? "发布实训“#{shixun.name}”获得的奖励" : ''
- end
- end
-end
\ No newline at end of file
diff --git a/app/decorators/grade_decorator.rb b/app/decorators/grade_decorator.rb
deleted file mode 100644
index 5e2b9deed..000000000
--- a/app/decorators/grade_decorator.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-module GradeDecorator
- def container_type_text
- I18n.t("grade.container_type.#{container_type.to_s.underscore}")
- end
-
- def content
- case container_type.to_s.underscore
- when 'avatar' then '用户首次上传头像获得的奖励'
- when 'phone' then '用户首次绑定手机号码获得的奖励'
- when 'mail' then '用户首次绑定邮箱获得的奖励'
- when 'attendance' then '用户每天签到获得的奖励'
- when 'account' then '新用户首次填写基本资料获得的奖励'
- when 'memo' then '发布的评论或者帖子获得平台奖励'
- when 'discusses' then '发布的评论获得平台奖励'
- when 'star' then '用户给实训评分获得的随机奖励'
- when 'feedback' then '反馈的问题获得平台奖励'
- when 'authentication' then '用户首次完成实名认证获得的奖励'
- when 'professional' then '用户首次完成职业认证获得的奖励'
- when 'answer' then
- game = Game.find_by(id: container_id)
- game.present? ? "查看实训“#{game.challenge.shixun.name}”第#{game.challenge.position}关的参考答案消耗的金币" : ''
- when 'game' then
- game = Game.find_by(id: container_id)
- game.present? ? "通过实训“#{game.challenge.shixun.name}”的第#{game.challenge.position}关获得的奖励" : ''
- when 'test_set' then
- game = Game.find_by(id: container_id)
- game.present? ? "查看实训“#{game.challenge.shixun.name}”的第#{game.challenge.position}关的隐藏测试集消耗的金币" : ''
- when 'shixun_publish' then
- shixun = Shixun.find_by(id: container_id)
- shixun.present? ? "发布实训“#{shixun.name}”获得的奖励" : ''
- when 'check_ta_answer' then
- game = Game.find_by(id: container_id)
- game.present? ? "查看实训“#{game.challenge.shixun.name}”第#{game.challenge.position}关的TA人解答消耗的金币" : ''
- when 'hack' then
- hack = Hack.find_by(id: container_id)
- hack.present? ? "完成了题目解答“#{hack.name}”,获得金币奖励:#{hack.score}" : ''
- end
- end
-end
\ No newline at end of file
diff --git a/app/decorators/library_decorator.rb b/app/decorators/library_decorator.rb
deleted file mode 100644
index 2768ad036..000000000
--- a/app/decorators/library_decorator.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-module LibraryDecorator
- extend ApplicationDecorator
-
- display_time_method :published_at, :created_at, :updated_at
-end
\ No newline at end of file
diff --git a/app/decorators/shixun_decorator.rb b/app/decorators/shixun_decorator.rb
deleted file mode 100644
index 4b7a0714a..000000000
--- a/app/decorators/shixun_decorator.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-module ShixunDecorator
- def human_status
- I18n.t("shixun.status.#{status}")
- end
-end
diff --git a/app/decorators/subject_decorator.rb b/app/decorators/subject_decorator.rb
deleted file mode 100644
index 7ba3277f7..000000000
--- a/app/decorators/subject_decorator.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-module SubjectDecorator
- def can_visited?
- published? || User.current.admin? || member?(User.current)
- end
-end
\ No newline at end of file
diff --git a/app/decorators/video_decorator.rb b/app/decorators/video_decorator.rb
deleted file mode 100644
index 904e78dbb..000000000
--- a/app/decorators/video_decorator.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-module VideoDecorator
- extend ApplicationDecorator
-
- display_time_method :published_at, :created_at, :updated_at
-end
\ No newline at end of file
diff --git a/app/docs/slate/source/api.html.md b/app/docs/slate/source/api.html.md
index a08823a4e..a846317c4 100644
--- a/app/docs/slate/source/api.html.md
+++ b/app/docs/slate/source/api.html.md
@@ -12,6 +12,7 @@ toc_footers:
includes:
- licenses
- gitignores
+ - public_keys
- users
- projects
- repositories
diff --git a/app/docs/slate/source/images/logo.png b/app/docs/slate/source/images/logo.png
index 30affed0b..4326948c1 100644
Binary files a/app/docs/slate/source/images/logo.png and b/app/docs/slate/source/images/logo.png differ
diff --git a/public/docs/images/logo-b38b63e6.png b/app/docs/slate/source/images/trustie_logo.png
similarity index 100%
rename from public/docs/images/logo-b38b63e6.png
rename to app/docs/slate/source/images/trustie_logo.png
diff --git a/app/docs/slate/source/includes/_projects.md b/app/docs/slate/source/includes/_projects.md
index 1fbbd0dab..d4899a710 100644
--- a/app/docs/slate/source/includes/_projects.md
+++ b/app/docs/slate/source/includes/_projects.md
@@ -1,5 +1,91 @@
# Projects
+## 申请加入项目
+申请加入项目
+
+> 示例:
+
+```shell
+curl -X POST http://localhost:3000/api/applied_projects.json
+```
+
+```javascript
+await octokit.request('POST /api/appliedr_projects.json')
+```
+
+### HTTP 请求
+`POST /api/applied_projects.json`
+
+### 请求参数
+参数 | 必选 | 默认 | 类型 | 字段说明
+--------- | ------- | ------- | -------- | ----------
+|applied_project.code |是| |string |邀请码 |
+|applied_project.role |否| |string |项目权限,reporter: 报告者, developer: 开发者,manager:管理员 |
+
+> 请求的JSON示例
+
+```json
+{
+ "applied_project": {
+ "code": "1una34",
+ "role": "developer"
+ }
+}
+```
+
+### 返回字段说明
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|id |int |申请id |
+|status |string |申请状态,canceled:取消,common:正在申请, accept:已接受,refuse:已拒绝|
+|time_ago |string |项目申请创建的时间 |
+|project.id |int |申请项目的id |
+|project.identifier |string |申请项目的标识 |
+|project.name |string |申请项目的名称 |
+|project.description |string |申请项目的描述 |
+|project.is_public |bool |申请项目是否公开 |
+|project.owner.id |bool |申请项目拥有者id |
+|project.owner.type |string |申请项目拥有者类型 |
+|project.owner.name |string |申请项目拥有者昵称 |
+|project.owner.login |string |申请项目拥有者标识 |
+|project.owner.image_url |string |申请项目拥有者头像 |
+|user.id |int |申请创建者的id |
+|user.type |string |申请创建者的类型 |
+|user.name |string |申请创建者的名称 |
+|user.login |string |申请创建者的标识 |
+|user.image_url |string |申请创建者头像 |
+> 返回的JSON示例:
+
+```json
+{
+ "project": {
+ "id": 74,
+ "identifier": "hehuisssjssjjsjs",
+ "name": "hehuisssjssjjsjs",
+ "description": "wwww",
+ "is_public": false,
+ "owner": {
+ "id": 10,
+ "type": "User",
+ "name": "testforge1",
+ "login": "testforge1",
+ "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png"
+ }
+ },
+ "user": {
+ "id": 6,
+ "type": "User",
+ "name": "何慧",
+ "login": "yystopf",
+ "image_url": "images/avatars/User/6?t=1622513134"
+ },
+ "id": 7,
+ "status": "common",
+ "created_at": "2021-06-09 16:41",
+ "time_ago": "1分钟前"
+}
+```
+
## 获取项目列表
获取项目列表,也可以更加相关条件过滤搜素
@@ -168,6 +254,55 @@ Remember — a happy kitten is an authenticated kitten!
+## 项目导航
+获取项目导航信息
+
+> 示例:
+
+```shell
+curl -X GET \
+http://localhost:3000/api/yystopf/ceshi/menu_list | jq
+```
+
+```javascript
+await octokit.request('GET /api/yystopf/ceshi/menu_list')
+```
+
+### HTTP 请求
+`GET api/:owner/:repo/menu_list`
+
+### 请求参数
+参数 | 必选 | 默认 | 类型 | 字段说明
+--------- | ------- | ------- | -------- | ----------
+owner |是| |string |用户登录名
+repo |是| |string |项目标识identifier
+
+### 返回字段说明
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+menu_name |string|导航名称, home:主页,code:代码库,issues:疑修,pulls:合并请求,devops:工作流,versions:里程碑,activity:动态,setting:仓库设置
+
+
+> 返回的JSON示例:
+
+```json
+[
+ {
+ "menu_name": "home"
+ },
+ {
+ "menu_name": "code"
+ },
+ {
+ "menu_name": "pulls"
+ },
+ {
+ "menu_name": "activity"
+ }
+]
+```
+
+
## 项目主页
获取项目主页信息
@@ -212,6 +347,84 @@ attachments |array|附件
Remember — a happy kitten is an authenticated kitten!
+## 项目模块信息
+项目模块信息
+
+> 示例:
+
+```shell
+curl -X GET \
+http://localhost:3000/api/yystopf/ceshi/project_units.json
+```
+
+```javascript
+await octokit.request('GET /api/yystopf/ceshi/project_units')
+```
+
+### HTTP 请求
+`GET /api/yystopf/ceshi/project_units`
+
+### 返回字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|type |string|模块名称 |
+
+> 返回的JSON示例:
+
+```json
+[
+ {
+ "type": "code"
+ },
+ {
+ "type": "pulls"
+ },
+ {
+ "type": "issues"
+ }
+]
+```
+
+## 更改项目模块展示
+更改项目模块展示
+
+> 示例:
+
+```shell
+curl -X POST \
+-H "accept: application/json" \
+-H "Content-Type: application/json" \
+-d "{ \"unit_typs\": [\"code\", \"pulls\"]}" \
+http://localhost:3000/api/yystopf/ceshi/project_units.json
+```
+
+```javascript
+await octokit.request('POST /api/yystopf/ceshi/project_units')
+```
+
+### HTTP 请求
+`POST /api/yystopf/ceshi/project_units`
+
+### 请求参数
+参数 | 必选 | 默认 | 类型 | 字段说明
+--------- | ------- | ------- | -------- | ----------
+|unit_types |是| |array | 项目模块内容, 支持以下参数:code:代码库,issues:疑修,pulls:合并请求,devops:工作流,versions:里程碑 |
+
+
+### 返回字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|status |int|返回状态, 0: 表示操作成功 |
+|message |string|返回信息说明|
+
+> 返回的JSON示例:
+
+```json
+{
+ "status": 0,
+ "message": "success"
+}
+```
## 创建项目
创建项目
@@ -399,3 +612,241 @@ await octokit.request('POST /api/jaser/jasder_test/forks.json')
"identifier": "newadm"
}
```
+
+## 用户管理的组织列表
+用户管理的组织列表
+
+> 示例:
+
+```shell
+curl -X GET \
+http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizations.json | jq
+```
+
+```javascript
+await octokit.request('GET /api/:owner/:repo/applied_transfer_projects/organizations')
+```
+
+### HTTP 请求
+`GET api/:owner/:repo/applied_transfer_projects/organizations`
+
+### 请求参数
+参数 | 必选 | 默认 | 类型 | 字段说明
+--------- | ------- | ------- | -------- | ----------
+owner |是| |string |用户登录名
+repo |是| |string |项目标识identifier
+
+### 返回字段说明
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+name |string|组织标识
+nickname |string|组织名称
+description|string|组织描述
+avatar_url|string|组织头像
+
+
+> 返回的JSON示例:
+
+```json
+{
+ "total_count": 3,
+ "organizations": [
+ {
+ "id": 9,
+ "name": "ceshi_org",
+ "nickname": "测试组织",
+ "description": "测试组织",
+ "avatar_url": "images/avatars/Organization/9?t=1612706073"
+ },
+ {
+ "id": 51,
+ "name": "ceshi",
+ "nickname": "测试组织哈哈哈",
+ "description": "23212312",
+ "avatar_url": "images/avatars/Organization/51?t=1618800723"
+ },
+ {
+ "id": 52,
+ "name": "ceshi1",
+ "nickname": "身份卡手动阀",
+ "description": "1231手动阀是的",
+ "avatar_url": "images/avatars/Organization/52?t=1618805056"
+ }
+ ]
+}
+```
+
+## 迁移项目
+迁移项目,edit接口is_transfering为true表示正在迁移
+
+> 示例:
+
+```shell
+curl -X POST http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects.json
+```
+
+```javascript
+await octokit.request('POST /api/:owner/:repo/applied_transfer_projects.json')
+```
+
+### HTTP 请求
+`POST /api/:owner/:repo/applied_transfer_projects.json`
+
+### 请求参数
+参数 | 必选 | 默认 | 类型 | 字段说明
+--------- | ------- | ------- | -------- | ----------
+|owner |是| |string |用户登录名 |
+|repo |是| |string |项目标识identifier |
+|owner_name|是| |string |迁移对象标识 |
+
+### 返回字段说明
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|id |int |项目id |
+|status |string |项目迁移状态,canceled:取消,common:正在迁移, accept:已接受,refuse:已拒绝|
+|time_ago |string |项目迁移创建的时间 |
+|project.id |int |迁移项目的id |
+|project.identifier |string |迁移项目的标识 |
+|project.name |string |迁移项目的名称 |
+|project.description |string |迁移项目的描述 |
+|project.is_public |bool |迁移项目是否公开 |
+|project.owner.id |bool |迁移项目拥有者id |
+|project.owner.type |string |迁移项目拥有者类型 |
+|project.owner.name |string |迁移项目拥有者昵称 |
+|project.owner.login |string |迁移项目拥有者标识 |
+|project.owner.image_url |string |迁移项目拥有者头像 |
+|user.id |int |迁移创建者的id |
+|user.type |string |迁移创建者的类型 |
+|user.name |string |迁移创建者的名称 |
+|user.login |string |迁移创建者的标识 |
+|user.image_url |string |迁移创建者头像 |
+|owner.id |int |迁移接受者的id |
+|owner.type |string |迁移接受者的类型 |
+|owner.name |string |迁移接受者的名称 |
+|owner.login |string |迁移接受者的标识 |
+|owner.image_url |string |迁移接受者头像 |
+> 返回的JSON示例:
+
+```json
+{
+ "project": {
+ "id": 86,
+ "identifier": "ceshi_repo1",
+ "name": "测试项目啊1",
+ "description": "二十多",
+ "is_public": true,
+ "owner": {
+ "id": 52,
+ "type": "Organization",
+ "name": "身份卡手动阀",
+ "login": "ceshi1",
+ "image_url": "images/avatars/Organization/52?t=1618805056"
+ }
+ },
+ "user": {
+ "id": 6,
+ "type": "User",
+ "name": "yystopf",
+ "login": "yystopf",
+ "image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png"
+ },
+ "owner": {
+ "id": 9,
+ "type": "Organization",
+ "name": "测试组织",
+ "login": "ceshi_org",
+ "image_url": "images/avatars/Organization/9?t=1612706073"
+ },
+ "id": 4,
+ "status": "common",
+ "created_at": "2021-04-26 09:54",
+ "time_ago": "1分钟前"
+}
+```
+
+## 取消迁移项目
+迁移项目,edit接口is_transfering为true表示正在迁移
+
+> 示例:
+
+```shell
+curl -X POST http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/cancel.json
+```
+
+```javascript
+await octokit.request('POST /api/:owner/:repo/applied_transfer_projects/cancel.json')
+```
+
+### HTTP 请求
+`POST /api/:owner/:repo/applied_transfer_projects/cancel.json`
+
+### 请求参数
+参数 | 必选 | 默认 | 类型 | 字段说明
+--------- | ------- | ------- | -------- | ----------
+|owner |是| |string |用户登录名 |
+|repo |是| |string |项目标识identifier |
+
+### 返回字段说明
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|id |int |迁移id |
+|status |string |迁移状态,canceled:取消,common:正在迁移, accept:已接受,refuse:已拒绝|
+|time_ago |string |迁移创建的时间 |
+|project.id |int |迁移项目的id |
+|project.identifier |string |迁移项目的标识 |
+|project.name |string |迁移项目的名称 |
+|project.description |string |迁移项目的描述 |
+|project.is_public |bool |迁移项目是否公开 |
+|project.owner.id |bool |迁移项目拥有者id |
+|project.owner.type |string |迁移项目拥有者类型 |
+|project.owner.name |string |迁移项目拥有者昵称 |
+|project.owner.login |string |迁移项目拥有者标识 |
+|project.owner.image_url |string |迁移项目拥有者头像 |
+|user.id |int |迁移创建者的id |
+|user.type |string |迁移创建者的类型 |
+|user.name |string |迁移创建者的名称 |
+|user.login |string |迁移创建者的标识 |
+|user.image_url |string |迁移创建者头像 |
+|owner.id |int |迁移接受者的id |
+|owner.type |string |迁移接受者的类型 |
+|owner.name |string |迁移接受者的名称 |
+|owner.login |string |迁移接受者的标识 |
+|owner.image_url |string |迁移接受者头像 |
+> 返回的JSON示例:
+
+```json
+{
+ "project": {
+ "id": 86,
+ "identifier": "ceshi_repo1",
+ "name": "测试项目啊1",
+ "description": "二十多",
+ "is_public": true,
+ "owner": {
+ "id": 52,
+ "type": "Organization",
+ "name": "身份卡手动阀",
+ "login": "ceshi1",
+ "image_url": "images/avatars/Organization/52?t=1618805056"
+ }
+ },
+ "user": {
+ "id": 6,
+ "type": "User",
+ "name": "yystopf",
+ "login": "yystopf",
+ "image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png"
+ },
+ "owner": {
+ "id": 9,
+ "type": "Organization",
+ "name": "测试组织",
+ "login": "ceshi_org",
+ "image_url": "images/avatars/Organization/9?t=1612706073"
+ },
+ "id": 4,
+ "status": "common",
+ "created_at": "2021-04-26 09:54",
+ "time_ago": "1分钟前"
+}
+```
\ No newline at end of file
diff --git a/app/docs/slate/source/includes/_public_keys.md b/app/docs/slate/source/includes/_public_keys.md
new file mode 100644
index 000000000..60d73c01a
--- /dev/null
+++ b/app/docs/slate/source/includes/_public_keys.md
@@ -0,0 +1,158 @@
+
+# PublicKeys
+
+## public_keys列表
+获取public_keys列表,支持分页
+
+> 示例:
+
+```shell
+curl -X GET \
+http://localhost:3000/api/public_keys.json
+```
+
+```javascript
+await octokit.request('GET /api/public_keys.json')
+```
+
+### HTTP 请求
+`GET api/public_keys.json`
+
+### 请求参数
+参数 | 必选 | 默认 | 类型 | 字段说明
+--------- | ------- | ------- | -------- | ----------
+page |否| 1 | int | 页码 |
+limit |否| 15 | int | 每页数量 |
+
+### 返回字段说明
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+total_count |int |总数 |
+public_keys.id |int |ID|
+public_keys.name |string|密钥标题|
+public_keys.content |string|密钥内容|
+public_keys.fingerprint |string|密钥标识|
+public_keys.created_time |string|密钥创建时间|
+
+
+> 返回的JSON示例:
+
+```json
+{
+ "total_count": 1,
+ "public_keys": [
+ {
+ "id": 16,
+ "name": "xxx",
+ "content": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDe5ETOTB5PcmcYJkIhfF7+mxmJQDCLg7/LnMoKHpKoo/jYUnFU9OjfsxVo3FTNUvh2475WXMAur5KsFoNKjK9+JHxvoXyJKmyVPWgXU/NRxQyaWPnPLPK8qPRF5ksJE6feBOqtsdxsvBiHs2r1NX/U26Ecnpr6avudD0cmyrEfbYMWbupLrhsd39dswPT73f3W5jc7B9Y47Ioiv8UOju3ABt1+kpuAjaaVC6VtUQoEFiZb1y33yBnyePya7dvFyApyD4ILyyIG2rtZWK7l53YFnwZDuFsTWjEEEQD0U4FBSFdH5wtwx0WQLMSNyTtaFBSG0kJ+uiQQIrxlvikcm63df7zbC3/rWLPsKgW122Zt966dcpFqiCiJNDKZPPw3qpg8TBL6X+qIZ+FxVEk/16/zScpyEfoxQp0GvgxI7hPLErmfkC5tMsib8MAXYBNyvJXna0vg/wOaNNIaI4SAH9Ksh3f/TtalYVjp6WxIwVBfnbq51WnmlnEXePtX6XjAGL+GbF2VQ1nv/IzrY09tNbTV6wQsrSIP3VDzYQxdJ1rdsVNMoJB0H2Pu0NdcSz53Wx45N+myD0QnE05ss+zDp5StY90OYsx2aCo6qAA8Qn2jUjdta7MQWwkPfKrta4tTQ0XbWMjx4/E1+l3J5liwZkl2XOGOwhfXdRsBjaEziZ18kQ== yystopf@163.com",
+ "fingerprint": "SHA256:cU8AK/+roqUUyiaYXIdS2Nj4+Rb2p6rqWSeRDc+aqKM",
+ "created_unix": 1626246596,
+ "created_time": "2021/07/14 15:09"
+ }
+ ]
+}
+```
+
+Success — a happy kitten is an authenticated kitten!
+
+
+## 创建public_key
+创建public_key
+
+> 示例:
+
+```shell
+curl -X POST \
+http://localhost:3000/api/public_keys.json
+```
+
+```javascript
+await octokit.request('POST /api/public_keys.json')
+```
+
+### HTTP 请求
+`POST api/public_keys.json`
+
+### 请求参数
+参数 | 必选 | 默认 | 类型 | 字段说明
+--------- | ------- | ------- | -------- | ----------
+key |是 | 否 | string | 密钥 |
+title |是 | 否 | string | 密钥标题 |
+
+> 请求的JSON示例:
+```json
+{
+ "public_key": {
+ "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDe5ETOTB5PcmcYJkIhfF7+mxmJQDCLg7/LnMoKHpKoo/jYUnFU9OjfsxVo3FTNUvh2475WXMAur5KsFoNKjK9+JHxvoXyJKmyVPWgXU/NRxQyaWPnPLPK8qPRF5ksJE6feBOqtsdxsvBiHs2r1NX/U26Ecnpr6avudD0cmyrEfbYMWbupLrhsd39dswPT73f3W5jc7B9Y47Ioiv8UOju3ABt1+kpuAjaaVC6VtUQoEFiZb1y33yBnyePya7dvFyApyD4ILyyIG2rtZWK7l53YFnwZDuFsTWjEEEQD0U4FBSFdH5wtwx0WQLMSNyTtaFBSG0kJ+uiQQIrxlvikcm63df7zbC3/rWLPsKgW122Zt966dcpFqiCiJNDKZPPw3qpg8TBL6X+qIZ+FxVEk/16/zScpyEfoxQp0GvgxI7hPLErmfkC5tMsib8MAXYBNyvJXna0vg/wOaNNIaI4SAH9Ksh3f/TtalYVjp6WxIwVBfnbq51WnmlnEXePtX6XjAGL+GbF2VQ1nv/IzrY09tNbTV6wQsrSIP3VDzYQxdJ1rdsVNMoJB0H2Pu0NdcSz53Wx45N+myD0QnE05ss+zDp5StY90OYsx2aCo6qAA8Qn2jUjdta7MQWwkPfKrta4tTQ0XbWMjx4/E1+l3J5liwZkl2XOGOwhfXdRsBjaEziZ18kQ== yystopf@163.com",
+ "title": "xxx"
+ }
+}
+```
+
+### 返回字段说明
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+total_count |int |总数 |
+id |int |ID|
+name |string|密钥标题|
+content |string|密钥内容|
+fingerprint |string|密钥标识|
+created_time |string|密钥创建时间|
+
+
+> 返回的JSON示例:
+
+```json
+{
+ "id": 17,
+ "name": "xxx",
+ "content": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDe5ETOTB5PcmcYJkIhfF7+mxmJQDCLg7/LnMoKHpKoo/jYUnFU9OjfsxVo3FTNUvh2475WXMAur5KsFoNKjK9+JHxvoXyJKmyVPWgXU/NRxQyaWPnPLPK8qPRF5ksJE6feBOqtsdxsvBiHs2r1NX/U26Ecnpr6avudD0cmyrEfbYMWbupLrhsd39dswPT73f3W5jc7B9Y47Ioiv8UOju3ABt1+kpuAjaaVC6VtUQoEFiZb1y33yBnyePya7dvFyApyD4ILyyIG2rtZWK7l53YFnwZDuFsTWjEEEQD0U4FBSFdH5wtwx0WQLMSNyTtaFBSG0kJ+uiQQIrxlvikcm63df7zbC3/rWLPsKgW122Zt966dcpFqiCiJNDKZPPw3qpg8TBL6X+qIZ+FxVEk/16/zScpyEfoxQp0GvgxI7hPLErmfkC5tMsib8MAXYBNyvJXna0vg/wOaNNIaI4SAH9Ksh3f/TtalYVjp6WxIwVBfnbq51WnmlnEXePtX6XjAGL+GbF2VQ1nv/IzrY09tNbTV6wQsrSIP3VDzYQxdJ1rdsVNMoJB0H2Pu0NdcSz53Wx45N+myD0QnE05ss+zDp5StY90OYsx2aCo6qAA8Qn2jUjdta7MQWwkPfKrta4tTQ0XbWMjx4/E1+l3J5liwZkl2XOGOwhfXdRsBjaEziZ18kQ== yystopf@163.com",
+ "fingerprint": "SHA256:cU8AK/+roqUUyiaYXIdS2Nj4+Rb2p6rqWSeRDc+aqKM",
+ "created_time": "2021/07/14 15:26"
+}
+```
+
+Success — a happy kitten is an authenticated kitten!
+
+
+
+## 删除public_key
+删除public_key
+
+> 示例:
+
+```shell
+curl -X DELETE \
+http://localhost:3000/api/public_keys/:id.json
+```
+
+```javascript
+await octokit.request('DELETE /api/public_keys/:id.json')
+```
+
+### HTTP 请求
+`DELETE api/public_keys/:id.json`
+
+### 请求参数
+参数 | 必选 | 默认 | 类型 | 字段说明
+--------- | ------- | ------- | -------- | ----------
+id |是 | 否 | int | 密钥ID |
+
+
+> 返回的JSON示例:
+
+```json
+{
+ "status": 0,
+ "message": "success"
+}
+```
+
+Success — a happy kitten is an authenticated kitten!
+
+
diff --git a/app/docs/slate/source/includes/_pulls.md b/app/docs/slate/source/includes/_pulls.md
index 1df50f6bf..b9ad3b0ba 100644
--- a/app/docs/slate/source/includes/_pulls.md
+++ b/app/docs/slate/source/includes/_pulls.md
@@ -1 +1,502 @@
# Pulls
+
+
+## Get a pull request
+获取合并请求详情接口
+
+> 示例:
+
+```shell
+curl -X GET http://localhost:3000/api/Jasder/gitlink/pulls/88.json
+```
+
+```javascript
+await octokit.request('GET /api/Jasder/gitlink/pulls/88.json')
+```
+
+### HTTP 请求
+`GET /api/:owner/:repo/pulls/:id.json`
+
+### 请求参数:
+参数 | 必选 | 默认 | 类型 | 字段说明
+--------- | ------- | ------- | -------- | ----------
+|owner |是| |string |用户登录名 |
+|repo |是| |string |项目标识identifier |
+|id |是| | integer | pull id值 |
+
+
+
+
+> 返回的JSON示例:
+
+```json
+{
+ "status": 0,
+ "message": "响应成功",
+ "project_name": "Gitlink",
+ "identifier": "forgeplus",
+ "project_identifier": "forgeplus",
+ "pr_time": "52分钟前",
+ "commits_count": 229,
+ "files_count": 328,
+ "comments_count": 0,
+ "comments_total_count": 0,
+ "pull_request": {
+ "id": 1189,
+ "base": "master",
+ "head": "develop",
+ "status": 0,
+ "fork_project_id": null,
+ "is_original": false,
+ "pull_request_staus": "open",
+ "fork_project_user": null,
+ "create_user": "jasder",
+ "mergeable": true,
+ "state": "open"
+ },
+ "issue": {
+ "id": 51888,
+ "subject": "FIx release v3.2.0",
+ "description": null,
+ "is_private": false,
+ "branch_name": null,
+ "project_author_name": "Gitlink",
+ "closed_on": "",
+ "created_at": "2021-10-12 15:51",
+ "assign_user_name": "victor",
+ "assign_user_login": "moshenglv",
+ "author_name": "段甲生",
+ "author_login": "jasder",
+ "author_picture": "images/avatars/User/36480?t=1615520120",
+ "issue_status": "新增",
+ "priority": "正常",
+ "version": null,
+ "issue_tags": null
+ },
+ "conflict_files": []
+}
+```
+
+## 获取pull request文件列表
+获取pull request文件列表
+
+> 示例:
+
+```shell
+curl -X GET \
+http://localhost:3000/api/Jason/gitlink/pulls/1/files.json
+```
+
+```javascript
+await octokit.request('GET /api/jasder/gitlink/pulls/1/files.json')
+```
+
+### HTTP 请求
+`GET /api/:owner/:repo/pulls/:id/files.json`
+
+### 请求参数:
+|参数名|必选|类型|说明|
+|-|-|-|-|
+|owner |是|string |用户登录名 |
+|repo |是|string |project's identifier |
+|id |是|int |pull request's id |
+
+
+### 返回字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|-|-|-|
+|files_count |int|文件更改的总数量|
+|total_addition |int|添加代码总行数|
+|total_deletion |int|删除代码总行数|
+|files |array||
+|-- sha |string|commit's sha value|
+|-- name |string|当前文件名|
+|-- old_name |string| 修改之前的文件名称,与name相同的话,说明文件名未更改|
+|-- addition |int|文件添加的行数|
+|-- deletion |int|文件删除的行数|
+|-- type |int|文件类型, 1: 表示该文件只添加了内容,2: 表示该文件内容有修改, 3: 表示文件被删除或者改文件只删除了内容|
+|-- isCreated |boolean|当前文件是否为新增文件, true: 是, false: 否|
+|-- isDeleted |boolean|当前文件是否被删除, true: 是,false: 否|
+|-- isBin |boolean|当前文件是否为二进制文件,true: 是,false: 否|
+|-- isLFSFile |boolean|当前文件是否为LFS文件,true: 是,false: 否|
+|-- isRenamed |boolean|当前文件是否被重命名,true: 是,false: 否|
+|-- sections |array||
+|---- fileName |string|文件名称|
+|---- lines |array||
+|------ leftIdx |string|文件变动之前所在行数|
+|------ rightIdx |string|文件更改后所在行数|
+|------ type |string|文件变更类型,1: 新增,2: 修改, 3: 删除, 4: diff统计信息|
+|------ content |string|文件变更的内容|
+|------ sectionInfo |object||
+|-------- path |string|文件相对仓库的路径|
+|-------- lastLeftIdx |int||
+|-------- lastRightIdx |int||
+|-------- leftHunkSize |int|文件变更之前的行数|
+|-------- rightHunkSize |int|文件变更之后的行数(及当前页面编辑器显示的总行数)|
+|-------- leftIdx |int|文件变更之前所在行数|
+|-------- rightIdx |int|文件变更之后所在行数(即:页面编辑器开始显示的行数)|
+
+
+
+> 返回的JSON示例:
+
+```json
+{
+ "files_count": 6,
+ "total_addition": 447,
+ "total_deletion": 0,
+ "files": [
+ {
+ "sha": "xefenisnii",
+ "name": "文件.txt",
+ "old_name": "文件.txt",
+ "index": 6,
+ "addition": 2,
+ "deletion": 0,
+ "type": 1,
+ "isCreated": true,
+ "isDeleted": false,
+ "isBin": false,
+ "isLFSFile": false,
+ "isRenamed": false,
+ "isSubmodule": false,
+ "sections": [
+ {
+ "fileName": "文件.txt",
+ "name": "",
+ "lines": [
+ {
+ "leftIdx": 0,
+ "rightIdx": 0,
+ "type": 4,
+ "content": "@@ -0,0 +1,2 @@",
+ "sectionInfo": {
+ "path": null,
+ "lastLeftIdx": null,
+ "lastRightIdx": null,
+ "leftIdx": 0,
+ "rightIdx": 0,
+ "leftHunkSize": null,
+ "rightHunkSize": null
+ }
+ },
+ {
+ "leftIdx": 0,
+ "rightIdx": 1,
+ "type": 2,
+ "content": "+用例图一致性更新",
+ "sectionInfo": null
+ },
+ {
+ "leftIdx": 0,
+ "rightIdx": 2,
+ "type": 2,
+ "content": "+工程文件直接上传会有文件缺失,现在压缩后上传",
+ "sectionInfo": null
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
+```
+
+
+## 获取pull request的commits列表
+获取pull request的commits列表
+
+> 示例:
+
+```shell
+curl -X GET http://localhost:3000/api/jasder/jasder_test/pulls/1/commits.json
+```
+
+```javascript
+await octokit.request('GET /api/jasder/jasder_test/pulls/1/commits.json')
+```
+
+### HTTP 请求
+`GET /api/:owner/:repo/pulls/:id/commits.json`
+
+### 请求参数:
+|参数名|必选|类型|说明|
+|-|-|-|-|
+|owner |是|string |用户登录名 |
+|repo |是|string |project's identifier |
+|id |是|int |pull request's id |
+
+
+### 返回字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|commits_count |int|commits总数量|
+|commits |array||
+|-- author |object|项目作者|
+|---- login |string|用户login|
+|---- name |string|用户姓名|
+|---- image_url |string|用户头像|
+|-- committer |object|commit提交用户|
+|---- login |string|用户login|
+|---- name |string|用户姓名|
+|---- image_url |string|用户头像|
+|-- timestamp |int|commit的unix时间戳|
+|-- time_from_now|string|commit’s 提交时间距当前时间的时间值|
+|-- message |string|commit说明信息|
+|-- sha |string|commit’s sha值|
+
+
+> 返回的JSON示例:
+
+```json
+{
+ "commits_count": 1,
+ "commits": [
+ {
+ "author": {
+ "id": 36480,
+ "login": "jasder",
+ "name": "段甲生",
+ "image_url": "avatars/User/b"
+ },
+ "committer": {
+ "id": 36480,
+ "login": "jasder",
+ "name": "段甲生",
+ "image_url": "avatars/User/b"
+ },
+ "timestamp": 1604382982,
+ "time_from_now": "3小时前",
+ "message": "add some file\n* Add the tag list page to the release page\n* Apply suggestions from code review\n* Add the tags list view\n* Add the delete tag way on ui\n* Not delete tag and clear message when delete a release\n",
+ "sha": "8f5faee0d3b3be1b8063e84da0c79dd75327b968"
+ }
+ ]
+}
+```
+
+## Compare two commits
+Compare two commits
+
+> 示例:
+
+```shell
+curl -X GET \
+http://localhost:3000/api/Jason/test-txt/compare/master...develop
+
+curl -X GET \
+http://localhost:3000/api/Jason/test-txt/compare/master...Jason/test-txt:develop
+```
+
+```javascript
+await octokit.request('GET /api/Jason/test-txt/compare/master...Jason/test-txt:develop')
+```
+
+### HTTP 请求
+`GET /api/:owner/:repo/compare/{base}...{head}.json`
+
+### 请求参数:
+|参数名|必选|类型|说明|
+|-|-|-|-|
+|owner |是|string |用户登录名 |
+|repo |是|string |project's identifier |
+|base |是|string |pull request's id |
+|head |是|string |pull request's id |
+
+
+### 返回字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|-|-|-|
+|commits_count |int|commits总数量|
+|commits |array||
+|-- author |object|项目作者|
+|---- login |string|用户login|
+|---- name |string|用户姓名|
+|---- image_url |string|用户头像|
+|-- committer |object|commit提交用户|
+|---- login |string|用户login|
+|---- name |string|用户姓名|
+|---- image_url |string|用户头像|
+|-- timestamp |int|commit的unix时间戳|
+|-- time_from_now|string|commit’s 提交时间距当前时间的时间值|
+|-- message |string|commit说明信息|
+|-- sha |string|commit’s sha值|
+|diff |object||
+|-- files_count |int|文件更改的总数量|
+|-- total_addition |int|添加代码总行数|
+|-- total_deletion |int|删除代码总行数|
+|-- files |Array||
+|-- sha |string|commit's sha |
+|-- name |string|当前文件名|
+|-- old_name |string| 修改之前的文件名称,与name相同的话,说明文件名未更改|
+|-- addition |int|文件添加的行数|
+|-- deletion |int|文件删除的行数|
+|-- type |int|文件类型, 1: 表示该文件只添加了内容,2: 表示该文件内容有修改, 3: 表示文件被删除或者改文件只删除了内容|
+|-- isCreated |boolean|当前文件是否为新增文件, true: 是, false: 否|
+|-- isDeleted |boolean|当前文件是否被删除, true: 是,false: 否|
+|-- isBin |boolean|当前文件是否为二进制文件,true: 是,false: 否|
+|-- isLFSFile |boolean|当前文件是否为LFS文件,true: 是,false: 否|
+|-- isRenamed |boolean|当前文件是否被重命名,true: 是,false: 否|
+|-- sections |array||
+|---- fileName |string|文件名称|
+|---- lines |array||
+|------ leftIdx |string|文件变动之前所在行数|
+|------ rightIdx |string|文件更改后所在行数|
+|------ type |string|文件变更类型,1: 内容未改动,2: 添加, 3: 删除, 4: diff统计信息|
+|------ content |string|文件变更的内容|
+|------ sectionInfo |object||
+|-------- path |string|文件相对仓库的路径|
+|-------- lastLeftIdx |int||
+|-------- lastRightIdx |int||
+|-------- leftHunkSize |int|文件变更之前的行数|
+|-------- rightHunkSize |int|文件变更之后的行数(及当前页面编辑器显示的总行数)|
+|-------- leftIdx |int|文件变更之前所在行数|
+|-------- rightIdx |int|文件变更之后所在行数|
+
+> 返回的JSON示例:
+
+```json
+{
+ "commits_count": 1,
+ "commits": [
+ {
+ "author": {
+ "id": 36480,
+ "login": "jasder",
+ "name": "段甲生",
+ "image_url": "avatars/User/b"
+ },
+ "committer": {
+ "id": 36480,
+ "login": "jasder",
+ "name": "段甲生",
+ "image_url": "avatars/User/b"
+ },
+ "timestamp": 1604382982,
+ "time_from_now": "4小时前",
+ "message": "add some file\n* Add the tag list page to the release page\n* Apply suggestions from code review\n* Add the tags list view\n* Add the delete tag way on ui\n* Not delete tag and clear message when delete a release\n",
+ "sha": "8f5faee0d3b3be1b8063e84da0c79dd75327b968"
+ }
+ ],
+ "diff": {
+ "files_count": 6,
+ "total_addition": 447,
+ "total_deletion": 0,
+ "files": [
+ {
+ "name": "build.go",
+ "old_name": "build.go",
+ "index": 1,
+ "addition": 33,
+ "deletion": 0,
+ "type": 1,
+ "isCreated": true,
+ "isDeleted": false,
+ "isBin": false,
+ "isLFSFile": false,
+ "isRenamed": false,
+ "isSubmodule": false,
+ "sections": [
+ {
+ "fileName": "build.go",
+ "name": "",
+ "lines": [
+ {
+ "leftIdx": 0,
+ "rightIdx": 0,
+ "type": 4,
+ "content": "@@ -0,0 +1,33 @@",
+ "sectionInfo": {
+ "path": "build.go",
+ "lastLeftIdx": 0,
+ "lastRightIdx": 0,
+ "leftIdx": 0,
+ "rightIdx": 1,
+ "leftHunkSize": 0,
+ "rightHunkSize": 33
+ }
+ },
+ {
+ "leftIdx": 0,
+ "rightIdx": 1,
+ "type": 2,
+ "content": "+// Copyright 2020 The Gitea Authors. All rights reserved.",
+ "sectionInfo": null
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+```
+
+
+## List pull requests
+获取合并请求列表
+
+> 示例:
+
+```shell
+curl -X GET http://localhost:3000/api/Jasder/gitlink/pulls.json
+```
+
+```javascript
+await octokit.request('GET /api/Jasder/gitlink/pulls.json')
+```
+
+### HTTP 请求
+`GET /api/:owner/:repo/pulls.json`
+
+### 请求参数:
+参数 | 必选 | 默认 | 类型 | 字段说明
+--------- | ------- | ------- | -------- | ----------
+|owner |是| |string |用户登录名 |
+|repo |是| |string |项目标识identifier |
+
+
+
+
+> 返回的JSON示例:
+
+```json
+{
+ "status": 0,
+ "message": "响应成功",
+ "open_count": 4,
+ "close_count": 51,
+ "merged_issues_size": 123,
+ "search_count": 4,
+ "limit": null,
+ "user_admin_or_member": true,
+ "user_admin_or_developer": true,
+ "project_name": "Gitlink",
+ "project_author_name": "Gitlink",
+ "issues": [
+ {
+ "pull_request_id": 1189,
+ "pull_request_status": 0,
+ "pull_request_head": "develop",
+ "pull_request_base": "master",
+ "pull_request_staus": "open",
+ "is_original": false,
+ "fork_project_id": null,
+ "fork_project_identifier": null,
+ "fork_project_user": null,
+ "id": 51888,
+ "name": "FIx release v3.2.0",
+ "pr_time": "59分钟前",
+ "assign_user_name": "victor",
+ "assign_user_login": "moshenglv",
+ "author_name": "段甲生",
+ "author_login": "jasder",
+ "avatar_url": "images/avatars/User/36480?t=1615520120",
+ "priority": "正常",
+ "version": null,
+ "journals_count": 0,
+ "issue_tags": null
+ }
+ ]
+}
+```
\ No newline at end of file
diff --git a/app/docs/slate/source/includes/_repositories.md b/app/docs/slate/source/includes/_repositories.md
index b57df90f6..a44d2b35d 100644
--- a/app/docs/slate/source/includes/_repositories.md
+++ b/app/docs/slate/source/includes/_repositories.md
@@ -97,6 +97,300 @@ await octokit.request('GET /api/jasder/jasder_test/simple.json')
}
```
+## 仓库详情(新版)
+仓库详情
+
+> 示例:
+
+```shell
+curl -X GET http://localhost:3000/api/yystopf/ceshi/detail.json
+```
+
+```javascript
+await octokit.request('GET /api/yystopf/ceshi/detail.json')
+```
+
+### HTTP 请求
+`GET /api/:owner/:repo/detail`
+
+### 请求参数:
+参数 | 必选 | 默认 | 类型 | 字段说明
+--------- | ------- | ------- | -------- | ----------
+|owner |是| |string |用户登录名 |
+|repo |是| |string |项目标识identifier |
+
+
+### 返回字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|content |string |仓库简介 |
+|website |string |仓库网址 |
+|readme |string |readme文件|
+|identifier |string |项目标识 |
+|name |string |项目名称 |
+|issues_count |int |项目issue数量|
+|pull_requests_count |int |项目合并请求数量|
+|project_identifier |int |项目标识|
+|praises_count |int |项目点赞数量|
+|forked_count |int |项目复刻数量|
+|watchers_count |int |项目关注数量|
+|versions_count |int |项目里程碑数量|
+|version_releases_count |int |项目发行版数量|
+|version_releasesed_count |int |项目发行版已发行数量|
+|permission |string |项目权限|
+|mirror_url |string |镜像地址|
+|mirror |bool |是否为镜像项目|
+|type |int |项目类型 0 普通项目 1 普通镜像项目 2 同步镜像项目|
+|open_devops |int |是否开启devops|
+|watched |bool |是否关注|
+|praised |bool |是否点赞|
+|status |int |项目状态|
+|forked_from_project_id |int |fork项目id|
+|fork_info |object |fork项目信息|
+|size |string |仓库大小|
+|ssh_url |string |项目ssh地址|
+|clone_url |string |项目克隆地址|
+|default_branch |string |仓库默认分支|
+|empty |bool |仓库是否为空|
+|full_name |string |仓库全称|
+|private |bool |仓库是否为私有项目|
+|license_name |string |许可证名称|
+|release_versions.list.name |string |项目issue数量|
+|release_versions.list.tag_name |string |发行版标签名称|
+|release_versions.list.created_at |string |发行版创建时间|
+|release_versions.total_count |int |发行版数量|
+|branches.list.name |string |分支名称|
+|branches.total_count |int |分支数量|
+|tags.list.name |string |标签名称|
+|tags.total_count |int |标签数量|
+|contributors.list.contributions|int |贡献数量|
+|contributors.list.login |string |贡献者登录名|
+|contributors.list.name |string |贡献者用户名称|
+|contributors.list.image_url |string |贡献者头像|
+|languages |object |项目语言占比|
+
+> 返回的JSON示例:
+
+```json
+{
+ "content": "仓库简介",
+ "website": "仓库网址",
+ "readme": {
+ "type": "file",
+ "encoding": "base64",
+ "size": 9,
+ "name": "README.md",
+ "path": "README.md",
+ "content": "# ceshi\n\n",
+ "sha": ""
+ },
+ "identifier": "ceshi",
+ "name": "测试项目",
+ "project_id": 2,
+ "repo_id": 2,
+ "issues_count": 0,
+ "pull_requests_count": 0,
+ "project_identifier": "ceshi",
+ "praises_count": 0,
+ "forked_count": 0,
+ "watchers_count": 0,
+ "versions_count": 0,
+ "version_releases_count": 0,
+ "version_releasesed_count": 0,
+ "permission": "Reporter",
+ "mirror_url": null,
+ "mirror": false,
+ "type": 0,
+ "open_devops": false,
+ "watched": false,
+ "praised": false,
+ "status": 1,
+ "forked_from_project_id": 1,
+ "fork_info": {
+ "fork_form_name": "测试项目",
+ "fork_project_user_login": "ceshi_org",
+ "fork_project_identifier": "ceshi",
+ "fork_project_user_name": "ceshi_org"
+ },
+ "size": "25.0 KB",
+ "ssh_url": "virus@localhost:yystopf/ceshi.git",
+ "clone_url": "http://localhost:10080/yystopf/ceshi.git",
+ "default_branch": "master",
+ "empty": false,
+ "full_name": "yystopf/ceshi",
+ "private": false,
+ "license_name": "gnu-javamail-exception",
+ "release_versions": {
+ "list": [
+ {
+ "id": 2,
+ "name": "vvvv",
+ "tag_name": "v1.1",
+ "created_at": "2019-07-18 10:16"
+ }
+ ],
+ "total_count": 1
+ },
+ "branches": {
+ "list": [
+ {
+ "name": "master"
+ }
+ ],
+ "total_count": 1
+ },
+ "tags": {
+ "list": [
+ {
+ "name": "v1.1"
+ },
+ {
+ "name": "v1.0"
+ }
+ ],
+ "total_count": 2
+ },
+ "contributors": {
+ "list": [
+ {
+ "contributions": 1,
+ "gid": 2,
+ "login": "yystopf",
+ "type": "User",
+ "name": "yystopf",
+ "image_url": "avatars/User/b"
+ }
+ ],
+ "total_count": 1
+ },
+ "languages": {
+ "HTML": "50.9%",
+ "Ruby": "25.6%",
+ "JavaScript": "21.4%",
+ "CSS": "1.3%",
+ "CoffeeScript": "0.7%",
+ "Shell": "0.1%"
+ }
+}
+```
+
+## 仓库标签列表
+仓库标签列表
+
+> 示例:
+
+```shell
+curl -X GET http://localhost:3000/api/yystopf/csfjkkj/tags.json
+```
+
+```javascript
+await octokit.request('GET /api/yystopf/csfjkkj/tags.json')
+```
+
+### HTTP 请求
+`GET /api/:owner/:repo/tags.json`
+
+### 请求参数:
+参数 | 必选 | 默认 | 类型 | 字段说明
+--------- | ------- | ------- | -------- | ----------
+|owner |是| |string |用户登录名 |
+|repo |是| |string |项目标识identifier |
+|page |否| 1 | integer | 页码 |
+|limit |否| 20| integer | 每页个数 |
+
+
+### 返回字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|id |int |标签id |
+|name |string|标签名称|
+|zipball_url |string|标签zip包下载地址|
+|tarball_url |string|标签tar包下载地址|
+|tagger |object|打标签的人|
+|time_ago |string|打标签的时间|
+|created_at_unix|string|打标签的时间戳|
+|message |string|标签信息|
+|commit |object|标签最后一个commit|
+|commit.sha |string|commit的id|
+|commit.message |string|commit的提交信息|
+|commit.time_ago|string|commit的提交时间|
+|commit.created_at_unix|string|commit的提交时间戳|
+|commit.committer|object|commit的提交者|
+|commit.author|object|commit的作者|
+
+
+> 返回的JSON示例:
+
+```json
+[
+ {
+ "name": "v2.0.0",
+ "id": "c7d0873ee41796d1a0e193063095ccf539a9bf31",
+ "zipball_url": "http://localhost:3000/api/yystopf/csfjkkj/archive/v2.0.0.zip",
+ "tarball_url": "http://localhost:3000/api/yystopf/csfjkkj/archive/v2.0.0.tar.gz",
+ "tagger": {
+ "id": 4,
+ "login": "testforge1",
+ "name": "testforge1",
+ "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png"
+ },
+ "time_ago": "1天前",
+ "created_at_unix": 1632376903,
+ "message": "jdfkls",
+ "commit": {
+ "sha": "08fe383f1e5ebe2e2a384a8ea3ee890a758c7cd7",
+ "message": "add\n",
+ "time_ago": "1天前",
+ "created_at_unix": 1632376186,
+ "committer": {
+ "id": 4,
+ "login": "testforge1",
+ "name": "testforge1",
+ "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png"
+ },
+ "author": {
+ "id": 4,
+ "login": "testforge1",
+ "name": "testforge1",
+ "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png"
+ }
+ }
+ },
+ {
+ "name": "v1.0.0",
+ "id": "12168ad39c3ef201a445a2db181a3e43d50e40dd",
+ "zipball_url": "http://localhost:3000/api/yystopf/csfjkkj/archive/v1.0.0.zip",
+ "tarball_url": "http://localhost:3000/api/yystopf/csfjkkj/archive/v1.0.0.tar.gz",
+ "tagger": {
+ "id": null,
+ "login": "viletyy",
+ "name": "viletyy",
+ "image_url": "system/lets/letter_avatars/2/V/39_141_222/120.png"
+ },
+ "time_ago": "10天前",
+ "created_at_unix": 1631588042,
+ "message": "dfks",
+ "commit": {
+ "sha": "5291b5e45a377c1f7710cc6647259887ed7aaccf",
+ "message": "ADD file via upload\n",
+ "time_ago": "21天前",
+ "created_at_unix": 1630648417,
+ "committer": {
+ "id": null,
+ "login": "yystopf",
+ "name": "yystopf",
+ "image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png"
+ },
+ "author": {
+ "id": null,
+ "login": "yystopf",
+ "name": "yystopf",
+ "image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png"
+ }
+ }
+ }
+]
+```
## 编辑仓库信息
编辑仓库信息
@@ -463,6 +757,78 @@ await octokit.request('GET /api/jasder/jasder_test/collaborators.json')
Success Data.
+## 获取仓库所有文件
+获取仓库所有文件
+
+> 示例:
+
+```shell
+curl -X GET \
+-d "ref=develop" \
+http://localhost:3000/api/yystopf/ceshi/files.json
+```
+
+```javascript
+await octokit.request('GET /api/yystopf/ceshi/files.json')
+```
+
+### HTTP 请求
+`GET /api/:owner/:repo/files`
+
+### 请求参数:
+参数 | 必选 | 默认 | 类型 | 字段说明
+--------- | ------- | ------- | -------- | ----------
+|owner |是| |string |用户登录名 |
+|repo |是| |string |项目标识identifier |
+|search |否| |string |文件搜索关键词 |
+|ref |是| |string |分支名,默认为仓库默认分支 |
+
+
+### 返回字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|name |string |文件名称|
+|path |string |文件路径|
+|sha |string |文件标识|
+|type |string |文件类型|
+|size |int |文件大小|
+|url |string |文件地址|
+
+
+> 返回的JSON示例:
+
+```json
+[
+ {
+ "name": ".gitignore",
+ "path": ".gitignore",
+ "sha": "f83922d01ae60f6e637a1a2b9f08871b4f87dfc8",
+ "type": "file",
+ "size": 63,
+ "url": "http://localhost:10080/api/v1/repos/yystopf/ceshi/contents/.gitignore?ref=master",
+ "html_url": "http://localhost:10080/yystopf/ceshi/src/branch/master/.gitignore"
+ },
+ {
+ "name": "LICENSE",
+ "path": "LICENSE",
+ "sha": "8f3b9ab0d08afd3a624d822e3971a2f42b3bc2b9",
+ "type": "file",
+ "size": 341,
+ "url": "http://localhost:10080/api/v1/repos/yystopf/ceshi/contents/LICENSE?ref=master",
+ "html_url": "http://localhost:10080/yystopf/ceshi/src/branch/master/LICENSE"
+ },
+ {
+ "name": "README.md",
+ "path": "README.md",
+ "sha": "1bc8a60ac6ddc876ebc4b60fc68991435bfad93e",
+ "type": "file",
+ "size": 9,
+ "url": "http://localhost:10080/api/v1/repos/yystopf/ceshi/contents/README.md?ref=master",
+ "html_url": "http://localhost:10080/yystopf/ceshi/src/branch/master/README.md"
+ }
+]
+```
+
## 获取仓库代码目录
获取仓库代码目录
@@ -619,3 +985,796 @@ await octokit.request('GET /api/jasder/jasder_test/sub_entries.json')
+
+## 获取仓库README文件
+获取仓库README文件
+
+> 示例:
+
+```shell
+curl -X GET \
+-d "ref=master" \
+-d "filepath=lib" \
+http://localhost:3000/api/yystopf/csfjkkj/readme.json
+```
+
+```javascript
+await octokit.request('GET /api/yystopf/csfjkkj/readme.json')
+```
+
+### HTTP 请求
+`GET /api/:owner/:repo/readme.json`
+
+### 请求参数:
+参数 | 必选 | 默认 | 类型 | 字段说明
+--------- | ------- | ------- | -------- | ----------
+|owner |是| |string |用户登录名 |
+|repo |是| |string |项目标识identifier |
+|ref |否| | string |分支名称、tag名称或是提交记录id,默认为默认分支 |
+|filepath |否| | string |子目录名称,默认为空 |
+
+### 返回字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|type |string|文件类型, file:文件,dir:文件目录
+|encoding |string |编码 |
+|size |int|文件夹或文件大小 单位B
+|name |string|文件夹或文件名称|
+|path |string|文件夹或文件相对路径|
+|content |string|文件内容
+|sha |string|文件commitid
+
+
+> 返回的JSON示例:
+
+```json
+{
+ "type": "file",
+ "encoding": "base64",
+ "size": 24,
+ "name": "README.md",
+ "path": "lib/README.md",
+ "content": "ZGZhc2RhZGpmIGRrZnNsCgpzZGZkZnMK",
+ "sha": "860962cd21c60b1a9e07d723080c87c32c18d44a"
+}
+```
+
+
+## 获取仓库贡献者
+获取仓库贡献者
+
+> 示例:
+
+```shell
+curl -X GET \
+-d "ref=master" \
+-d "filepath=lib" \
+http://localhost:3000/api/yystopf/csfjkkj/contributors.json
+```
+
+```javascript
+await octokit.request('GET /api/yystopf/csfjkkj/contributors.json')
+```
+
+### HTTP 请求
+`GET /api/:owner/:repo/contributors.json`
+
+### 请求参数:
+参数 | 必选 | 默认 | 类型 | 字段说明
+--------- | ------- | ------- | -------- | ----------
+|owner |是| |string |用户登录名 |
+|repo |是| |string |项目标识identifier |
+|ref |否| | string |分支名称、tag名称或是提交记录id,默认为整个仓库 |
+|filepath |否| | string |子目录名称,默认为空 |
+
+### 返回字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|total_count |integer|贡献者数量|
+|contributions |integer|贡献数量|
+|login |string |用户登录名 |
+|type |string|用户类型 |
+|name |string|用户昵称|
+|image_url |string|用户头像|
+
+
+> 返回的JSON示例:
+
+```json
+{
+ "contributors": [
+ {
+ "contributions": 5,
+ "login": "testforge2",
+ "type": "User",
+ "name": "testforge2",
+ "image_url": "system/lets/letter_avatars/2/T/236_177_85/120.png"
+ },
+ {
+ "contributions": 79,
+ "login": "yystopf",
+ "type": "User",
+ "name": "yystopf",
+ "image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png"
+ }
+ ],
+ "total_count": 2
+}
+```
+
+
+
+## 获取仓库webhooks列表
+获取仓库webhooks列表
+
+> 示例:
+
+```shell
+curl -X GET \
+http://localhost:3000/api/yystopf/ceshi/webhooks.json
+```
+
+```javascript
+await octokit.request('GET /api/yystopf/ceshi/webhooks.json')
+```
+
+### HTTP 请求
+`GET /api/:owner/:repo/webhooks.json`
+
+### 请求参数:
+参数 | 必选 | 默认 | 类型 | 字段说明
+--------- | ------- | ------- | -------- | ----------
+|owner |是| |string |用户登录名 |
+|repo |是| |string |项目标识identifier |
+
+
+### 返回字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|id |int |id |
+|url |string|地址|
+|http_method |string|请求方式|
+|is_active |bool |是否激活|
+|type |string|类型|
+|last_status |string|最后一次推送的状态|
+|create_time |string|创建时间|
+
+
+> 返回的JSON示例:
+
+```json
+{
+ "total_count": 4,
+ "webhooks": [
+ {
+ "id": 2,
+ "url": "https://oapi.dingtalk.com/robot/send?access_token=7e1e19d0eddb6a5e33c5c2c4e66f4c88f9437184b9ed2c2653194c6374c7d513",
+ "http_method": "",
+ "is_active": true,
+ "type": "dingtalk",
+ "last_status": "succeed",
+ "create_time": "2021-07-12 10:50:07"
+ },
+ {
+ "id": 3,
+ "url": "http://localhost:3000",
+ "http_method": "GET",
+ "is_active": true,
+ "type": "gitea",
+ "last_status": "succeed",
+ "create_time": "2021-07-26 10:03:45"
+ },
+ {
+ "id": 4,
+ "url": "http://localhost:10081",
+ "http_method": "POST",
+ "is_active": true,
+ "type": "gitea",
+ "last_status": "waiting",
+ "create_time": "2021-07-26 16:56:53"
+ },
+ {
+ "id": 5,
+ "url": "http://localhost:3001",
+ "http_method": "POST",
+ "is_active": true,
+ "type": "gitea",
+ "last_status": "fail",
+ "create_time": "2021-07-26 16:58:23"
+ }
+ ]
+}
+```
+
+
+## 获取仓库单个webhook
+获取仓库单个webhook
+
+> 示例:
+
+```shell
+curl -X GET \
+http://localhost:3000/api/yystopf/ceshi/webhooks/3/edit.json
+```
+
+```javascript
+await octokit.request('GET /api/yystopf/ceshi/webhooks/3/edit.json')
+```
+
+### HTTP 请求
+`GET /api/:owner/:repo/webhooks/:id/edit.json`
+
+### 请求参数:
+参数 | 必选 | 默认 | 类型 | 字段说明
+--------- | ------- | ------- | -------- | ----------
+|owner |是| |string |用户登录名 |
+|repo |是| |string |项目标识identifier |
+|id |是||integer|webhook ID|
+
+
+### 返回字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|id |int |id |
+|url |string|地址|
+|content_type |string|POST Content Type|
+|http_method |string|请求方式|
+|secret| |string|密钥|
+|is_active |bool |是否激活|
+|type |string|类型|
+|last_status |string|最后一次推送的状态, waiting 等待,fail 失败,succeed 成功|
+|branch_filter |string|分支过滤|
+|events |string|触发条件|
+|create_time |string|创建时间|
+
+
+参数| 含义|
+--------- | ------- | ------- |
+|create|创建分支或标签|
+|delete|分支或标签删除|
+|fork|仓库被fork|
+|push|git仓库推送|
+|issue|疑修已打开、已关闭、已重新打开或编辑|
+|issue_assign|疑修被指派|
+|issue_label|疑修标签被更新或删除|
+|issue_milestone|疑修被收入里程碑|
+|issue_comment|疑修评论|
+|pull_request|合并请求|
+|pull_request_assign|合并请求被指派|
+|pull_request_label|合并请求被贴上标签|
+|pull_request_milestone|合并请求被记录于里程碑中|
+|pull_request_comment|合并请求被评论|
+|pull_request_review_approved|合并请求被批准|
+|pull_request_review_rejected|合并请求被拒绝|
+|pull_request_review_comment|合并请求被提出审查意见|
+|pull_request_sync|合并请求被同步|
+|repository|创建或删除仓库|
+|release|版本发布|
+
+
+> 返回的JSON示例:
+
+```json
+{
+ "id": 3,
+ "http_method": "GET",
+ "content_type": "form",
+ "url": "http://localhost:3000",
+ "secret": "123456",
+ "last_status": "succeed",
+ "is_active": true,
+ "type": "gitea",
+ "create_time": "2021-07-26 10:03:45",
+ "branch_filter": "*",
+ "events": [
+ "create",
+ "delete",
+ "fork",
+ "issues",
+ "issue_assign",
+ "issue_label",
+ "issue_milestone",
+ "issue_comment",
+ "push",
+ "pull_request",
+ "pull_request_assign",
+ "pull_request_label",
+ "pull_request_milestone",
+ "pull_request_comment",
+ "pull_request_review",
+ "pull_request_sync",
+ "repository",
+ "release"
+ ]
+}
+```
+
+
+## 添加仓库webhook
+添加仓库webhook
+
+> 示例:
+
+```shell
+curl -X POST \
+http://localhost:3000/api/yystopf/ceshi/webhooks.json
+```
+
+```javascript
+await octokit.request('POST /api/yystopf/ceshi/webhooks.json')
+```
+
+### HTTP 请求
+`POST /api/:owner/:repo/webhooks.json`
+
+### 请求参数:
+参数 | 必选 | 默认 | 类型 | 字段说明
+--------- | ------- | ------- | -------- | ----------
+|owner |是| | string |用户登录名 |
+|repo |是| | string |项目标识identifier |
+|webhook.url |是| | string |目标url |
+|webhook.type |否| | string |类型|
+|webhook.http_method |是| | string | http方法, POST和GET |
+|webhook.content_type |是| | string | POST Content Type |
+|webhook.secret |否| | string |密钥文本|
+|webhook.active |是| | bool | 是否激活|
+|webhook.branch_filter|否| |string|分支过滤|
+|webhook.events |否| |array|触发事件|
+
+触发事件字段说明
+
+参数| 含义|
+--------- | ------- | ------- |
+|create|创建分支或标签|
+|delete|分支或标签删除|
+|fork|仓库被fork|
+|push|git仓库推送|
+|issue|疑修已打开、已关闭、已重新打开或编辑|
+|issue_assign|疑修被指派|
+|issue_label|疑修标签被更新或删除|
+|issue_milestone|疑修被收入里程碑|
+|issue_comment|疑修评论|
+|pull_request|合并请求|
+|pull_request_assign|合并请求被指派|
+|pull_request_label|合并请求被贴上标签|
+|pull_request_milestone|合并请求被记录于里程碑中|
+|pull_request_comment|合并请求被评论|
+|pull_request_review_approved|合并请求被批准|
+|pull_request_review_rejected|合并请求被拒绝|
+|pull_request_review_comment|合并请求被提出审查意见|
+|pull_request_sync|合并请求被同步|
+|repository|创建或删除仓库|
+|release|版本发布|
+
+
+> 请求的JSON示例:
+
+```json
+{
+ "active": true,
+ "content_type": "json",
+ "http_method": "GET",
+ "secret": "123456",
+ "url": "http://localhost:10000",
+ "branch_filter": "*",
+ "events": ["push"]
+}
+```
+
+### 返回字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|id |int |id |
+|url |string|地址|
+|content_type |string|POST Content Type|
+|is_active |bool |是否激活|
+|type |string|类型|
+|events | array|触发事件 |
+|create_time |string|创建时间|
+
+
+> 返回的JSON示例:
+
+```json
+{
+ "id": 18,
+ "type": "gitea",
+ "content_type": "json",
+ "url": "http://localhost:10000",
+ "events": [
+ "push"
+ ],
+ "active": true,
+ "create_time": "2021-07-26 18:53:43"
+}
+```
+
+
+## 更新仓库webhook
+更新仓库webhook
+
+> 示例:
+
+```shell
+curl -X PATCH \
+http://localhost:3000/api/yystopf/ceshi/webhooks/7.json
+```
+
+```javascript
+await octokit.request('PATCH /api/yystopf/ceshi/webhooks/7.json')
+```
+
+### HTTP 请求
+`PATCH /api/:owner/:repo/webhooks/:id.json`
+
+### 请求参数:
+参数 | 必选 | 默认 | 类型 | 字段说明
+--------- | ------- | ------- | -------- | ----------
+|owner |是| | string |用户登录名 |
+|repo |是| | string |项目标识identifier |
+|id |是| | string |webhook id |
+|webhook.url |是| | string |目标url |
+|webhook.type |否| | string |类型|
+|webhook.http_method |是| | string | http方法, POST和GET |
+|webhook.content_type |是| | string | POST Content Type |
+|webhook.secret |否| | string |密钥文本|
+|webhook.active |是| | bool | 是否激活|
+|webhook.branch_filter|否| |string|分支过滤|
+|webhook.events |否| |array|触发事件|
+
+触发事件字段说明
+
+参数| 含义|
+--------- | ------- | ------- |
+|create|创建分支或标签|
+|delete|分支或标签删除|
+|fork|仓库被fork|
+|push|git仓库推送|
+|issue|疑修已打开、已关闭、已重新打开或编辑|
+|issue_assign|疑修被指派|
+|issue_label|疑修标签被更新或删除|
+|issue_milestone|疑修被收入里程碑|
+|issue_comment|疑修评论|
+|pull_request|合并请求|
+|pull_request_assign|合并请求被指派|
+|pull_request_label|合并请求被贴上标签|
+|pull_request_milestone|合并请求被记录于里程碑中|
+|pull_request_comment|合并请求被评论|
+|pull_request_review_approved|合并请求被批准|
+|pull_request_review_rejected|合并请求被拒绝|
+|pull_request_review_comment|合并请求被提出审查意见|
+|pull_request_sync|合并请求被同步|
+|repository|创建或删除仓库|
+|release|版本发布|
+
+
+> 请求的JSON示例:
+
+```json
+{
+ "active": true,
+ "content_type": "json",
+ "http_method": "GET",
+ "secret": "123456",
+ "url": "http://localhost:10000",
+ "branch_filter": "*",
+ "events": ["push"]
+}
+```
+
+### 返回字段说明:
+
+> 返回的JSON示例:
+
+```json
+{
+ "status": 0,
+ "message": "success"
+}
+```
+
+
+
+## 删除仓库webhook
+删除仓库webhook
+
+> 示例:
+
+```shell
+curl -X DELETE \
+http://localhost:3000/api/yystopf/ceshi/webhooks/7.json
+```
+
+```javascript
+await octokit.request('DELETE /api/yystopf/ceshi/webhooks/7.json')
+```
+
+### HTTP 请求
+`DELETE /api/:owner/:repo/webhooks/:id.json`
+
+### 请求参数:
+参数 | 必选 | 默认 | 类型 | 字段说明
+--------- | ------- | ------- | -------- | ----------
+|owner |是| | string |用户登录名 |
+|repo |是| | string |项目标识identifier |
+|id |是| | string |webhook id |
+
+### 返回字段说明:
+
+> 返回的JSON示例:
+
+```json
+{
+ "status": 0,
+ "message": "success"
+}
+```
+
+
+## 获取仓库webhook的历史推送列表
+获取仓库webhook的历史推送列表
+
+> 示例:
+
+```shell
+curl -X GET \
+http://localhost:3000/api/yystopf/ceshi/webhooks/3/tasks.json
+```
+
+```javascript
+await octokit.request('GET /api/yystopf/ceshi/webhooks/3/tasks.json')
+```
+
+### HTTP 请求
+`GET /api/:owner/:repo/webhooks/:id/tasks.json`
+
+### 请求参数:
+参数 | 必选 | 默认 | 类型 | 字段说明
+--------- | ------- | ------- | -------- | ----------
+|owner |是| |string |用户登录名 |
+|repo |是| |string |项目标识identifier |
+|id |是| |integer |webhook ID|
+
+### 返回字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|id |int |id |
+|uuid |string|推送uuid|
+|type |string|类型|
+|is_succeed |bool|是否推送成功|
+|is_delivered |bool|是否完成推送|
+|payload_content |json|请求主体内容|
+|request_content |json|请求内容,头部等等|
+|reponse_content |json|响应内容,状态,头部,主体等等|
+|delivered_time |string|推送时间|
+
+
+> 返回的JSON示例:
+
+```json
+{
+ "total_count": 6,
+ "tasks": [
+ {
+ "id": 20,
+ "type": "gitea",
+ "uuid": "99aa2c23-6884-4c44-9020-5469320aa408",
+ "is_succeed": true,
+ "is_delivered": true,
+ "payload_content": {
+ "secret": "123456",
+ "ref": "refs/heads/master",
+ "before": "feb48e31362787a7620b53d4df3c4effddbb6f0b",
+ "after": "feb48e31362787a7620b53d4df3c4effddbb6f0b",
+ "compare_url": "",
+ "commits": [
+ {
+ "id": "feb48e31362787a7620b53d4df3c4effddbb6f0b",
+ "message": "fix\n",
+ "url": "http://localhost:10081/yystopf/ceshi/commit/feb48e31362787a7620b53d4df3c4effddbb6f0b",
+ "author": {
+ "name": "viletyy",
+ "email": "yystopf@163.com",
+ "username": "root"
+ },
+ "committer": {
+ "name": "viletyy",
+ "email": "yystopf@163.com",
+ "username": "root"
+ },
+ "verification": {
+ "verified": false,
+ "reason": "gpg.error.not_signed_commit",
+ "signature": "",
+ "signer": null,
+ "payload": ""
+ },
+ "timestamp": "2021-07-26T13:52:13+08:00",
+ "added": null,
+ "removed": null,
+ "modified": null
+ }
+ ],
+ "head_commit": null,
+ "repository": {
+ "id": 2,
+ "owner": {
+ "id": 3,
+ "login": "yystopf",
+ "full_name": "",
+ "email": "yystopf@forge.com",
+ "avatar_url": "http://localhost:10081/user/avatar/yystopf/-1",
+ "language": "zh-CN",
+ "is_admin": true,
+ "last_login": "2021-07-21T18:38:21+08:00",
+ "created": "2021-06-03T14:50:25+08:00",
+ "username": "yystopf"
+ },
+ "name": "ceshi",
+ "full_name": "yystopf/ceshi",
+ "description": "",
+ "empty": false,
+ "private": false,
+ "fork": false,
+ "template": false,
+ "parent": null,
+ "mirror": false,
+ "size": 3846,
+ "html_url": "http://localhost:10081/yystopf/ceshi",
+ "ssh_url": "virus@localhost:10081:yystopf/ceshi.git",
+ "clone_url": "http://localhost:10081/yystopf/ceshi.git",
+ "original_url": "",
+ "website": "",
+ "stars_count": 0,
+ "forks_count": 1,
+ "watchers_count": 1,
+ "open_issues_count": 0,
+ "open_pr_counter": 0,
+ "release_counter": 0,
+ "default_branch": "master",
+ "archived": false,
+ "created_at": "2021-06-03T15:15:30+08:00",
+ "updated_at": "2021-07-26T13:52:16+08:00",
+ "permissions": {
+ "admin": false,
+ "push": false,
+ "pull": false
+ },
+ "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,
+ "ignore_whitespace_conflicts": false,
+ "allow_merge_commits": true,
+ "allow_rebase": true,
+ "allow_rebase_explicit": true,
+ "allow_squash_merge": true,
+ "avatar_url": "",
+ "internal": false
+ },
+ "pusher": {
+ "id": 0,
+ "login": "yystopf",
+ "full_name": "",
+ "email": "yystopf@forge.com",
+ "avatar_url": "http://localhost:10081/user/avatar/yystopf/-1",
+ "language": "",
+ "is_admin": false,
+ "last_login": "0001-01-01T00:00:00Z",
+ "created": "2021-06-03T14:50:25+08:00",
+ "username": "yystopf"
+ },
+ "sender": {
+ "id": 0,
+ "login": "yystopf",
+ "full_name": "",
+ "email": "yystopf@forge.com",
+ "avatar_url": "http://localhost:10081/user/avatar/yystopf/-1",
+ "language": "",
+ "is_admin": false,
+ "last_login": "0001-01-01T00:00:00Z",
+ "created": "2021-06-03T14:50:25+08:00",
+ "username": "yystopf"
+ }
+ },
+ "request_content": {
+ "headers": {
+ "X-GitHub-Delivery": "99aa2c23-6884-4c44-9020-5469320aa408",
+ "X-GitHub-Event": "push",
+ "X-Gitea-Delivery": "99aa2c23-6884-4c44-9020-5469320aa408",
+ "X-Gitea-Event": "push",
+ "X-Gitea-Signature": "34a01edcd952ff6410ff6ebc946471161bde74aff86171f21621d2c2c4130f66",
+ "X-Gogs-Delivery": "99aa2c23-6884-4c44-9020-5469320aa408",
+ "X-Gogs-Event": "push",
+ "X-Gogs-Signature": "34a01edcd952ff6410ff6ebc946471161bde74aff86171f21621d2c2c4130f66"
+ }
+ },
+ "response_content": {
+ "status": 200,
+ "headers": {
+ "Cache-Control": "no-store, must-revalidate, private, max-age=0",
+ "Content-Length": "2556",
+ "Content-Type": "text/html; charset=utf-8",
+ "Referrer-Policy": "strict-origin-when-cross-origin",
+ "Set-Cookie": "__profilin=p%3Dt; path=/; HttpOnly",
+ "Vary": "Origin",
+ "X-Content-Type-Options": "nosniff",
+ "X-Download-Options": "noopen",
+ "X-Frame-Options": "SAMEORIGIN",
+ "X-Miniprofiler-Ids": "9ynvpncz5xm0rpgorb5y,hgggd9mv6lr4a9drcrlr,j7zqlx2vy5aji2vtgoba,f1ktsmh3jxvq0z2hf612,mih3dvgvlqhi3zy8lf2x,5k1qbkvbnru8mye9cest,tj6ern8w6awqf2zsimbr,9isaehvubivd52wo5p9v,1rzfhtq1nhuwbgy9p76g,z0xzidzyywna0y7a69m0,hzoklky92ycjqt42gi0s,y0ai7y0t28mcn8x0py2x,322il7nadinp51mw2r5m,m6dukftfsh6tjcxzp1gq,667wlqbytfwbrirnmma1,jcehj3dl8lkw8gk510cr",
+ "X-Miniprofiler-Original-Cache-Control": "max-age=0, private, must-revalidate",
+ "X-Permitted-Cross-Domain-Policies": "none",
+ "X-Request-Id": "08bff080-bbb5-4183-b845-81de3d47120a",
+ "X-Runtime": "0.394766",
+ "X-Xss-Protection": "1; mode=block"
+ },
+ "body": "
\n"
+ },
+ "delivered_time": "2021-07-28 11:47:29"
+ }
+ ]
+}
+```
+
+
+## 仓库webhook测试推送
+仓库webhook测试推送
+
+> 示例:
+
+```shell
+curl -X POST \
+http://localhost:3000/api/yystopf/ceshi/webhooks/3/test.json
+```
+
+```javascript
+await octokit.request('POST /api/yystopf/ceshi/webhooks/3/test.json')
+```
+
+### HTTP 请求
+`POST /api/:owner/:repo/webhooks/:id/test.json`
+
+### 请求参数:
+参数 | 必选 | 默认 | 类型 | 字段说明
+--------- | ------- | ------- | -------- | ----------
+|owner |是| | string |用户登录名 |
+|repo |是| | string |项目标识identifier |
+|id |是| | integer|webhook ID|
+
+
+
+
+### 返回字段说明:
+
+
+> 返回的JSON示例:
+
+```json
+{
+ "status": 0,
+ "message": "success"
+}
+```
+
\ No newline at end of file
diff --git a/app/docs/slate/source/includes/_users.md b/app/docs/slate/source/includes/_users.md
index 62a6391e1..f416d488d 100644
--- a/app/docs/slate/source/includes/_users.md
+++ b/app/docs/slate/source/includes/_users.md
@@ -1,3 +1,9 @@
+
# Users
## 获取当前登陆用户信息
@@ -40,3 +46,2163 @@ await octokit.request('GET /api/users/me.json')
+
+## 用户消息列表
+获取用户消息列表
+
+> 示例:
+
+```shell
+curl -X GET http://localhost:3000/api/users/:login/messages.json
+```
+
+```javascript
+await octokit.request('GET /api/users/:login/messages.json')
+```
+
+### HTTP 请求
+`GET api/users/yystopf/messages.json`
+
+### 请求字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|type | string | 消息类型,不传为所有消息,notification为系统消息,atme为@我消息|
+|status | integer | 是否已读,不传为所有消息,1为未读,2为已读 |
+|limit | integer | 每页个数 |
+|page | integer | 页码 |
+
+### 返回字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|total_count | integer | 消息总数 |
+|type | string | 消息类型 |
+|unread_notification | integer | 未读系统通知数量 |
+|unread_atme | integer | 未读@我数量 |
+|messages.id | integer | 消息id |
+|messages.status | integer | 消息是否已读,1为未读,2为已读 |
+|messages.content | string | 消息内容 |
+|messages.notification_url | string | 消息跳转地址 |
+|messages.source | string | 消息来源 |
+|messages.timeago | string | 消息时间 |
+|messages.type | string | 消息类型,notification为系统消息,atme为@我消息|
+|sender | object | 消息发送者 |
+
+#### 消息来源source字段说明
+类型|说明
+--------- | -----------
+|IssueAssigned | 有新指派给我的疑修 |
+|IssueAssignerExpire | 我负责的疑修截止日期到达最后一天 |
+|IssueAtme | 在疑修中@我 |
+|IssueChanged | 我创建或负责的疑修状态变更 |
+|IssueCreatorExpire | 我创建的疑修截止日期到达最后一天 |
+|IssueDeleted | 我创建或负责的疑修删除 |
+|IssueJournal | 我创建或负责的疑修有新的评论 |
+|LoginIpTip | 登录异常提示 |
+|OrganizationJoined | 账号被拉入组织 |
+|OrganizationLeft | 账号被移出组织 |
+|OrganizationRole | 账号组织权限变更 |
+|ProjectDeleted | 我关注的仓库被删除 |
+|ProjectFollowed | 我管理的仓库被关注 |
+|ProjectForked | 我管理的仓库被复刻 |
+|ProjectIssue | 我管理/关注的仓库有新的疑修 |
+|ProjectJoined | 账号被拉入项目 |
+|ProjectLeft | 账号被移出项目 |
+|ProjectMemberJoined | 我管理的仓库有成员加入 |
+|ProjectMemberLeft | 我管理的仓库有成员移出 |
+|ProjectMilestone | 我管理的仓库有新的里程碑 |
+|ProjectPraised | 我管理的仓库被点赞 |
+|ProjectPullRequest | 我管理/关注的仓库有新的合并请求 |
+|ProjectRole | 账号仓库权限变更 |
+|ProjectSettingChanged | 我管理的仓库项目设置被更改 |
+|ProjectTransfer | 我关注的仓库被转移 |
+|ProjectVersion | 我关注的仓库有新的发行版 |
+|PullRequestAssigned | 有新指派给我的合并请求 |
+|PullReuqestAtme | 在合并请求中@我 |
+|PullRequestChanged | 我创建或负责的合并请求状态变更 |
+|PullRequestClosed | 我创建或负责的合并请求被关闭 |
+|PullRequestJournal | 我创建或负责的合并请求有新的评论 |
+|PullRequestMerged | 我创建或负责的合并请求被合并 |
+
+
+> 返回的JSON示例:
+
+```json
+{
+ "total_count": 5,
+ "type": "",
+ "unread_notification": 3,
+ "unread_atme": 2,
+ "messages": [
+ {
+ "id": 1,
+ "status": 1,
+ "content": "Atme Message Content 1",
+ "notification_url": "http://www.baidu.com",
+ "source": "PullRequestAtme",
+ "time_ago": "1天前",
+ "type": "atme",
+ "sender": {
+ "id": 5,
+ "type": "User",
+ "name": "testforge2",
+ "login": "testforge2",
+ "image_url": "system/lets/letter_avatars/2/T/236_177_85/120.png"
+ }
+ },
+ {
+ "id": 2,
+ "status": 0,
+ "content": "Atme Message Content 2",
+ "notification_url": "http://www.baidu.com",
+ "source": "IssueAtme",
+ "time_ago": "1天前",
+ "type": "atme",
+ "sender": {
+ "id": 4,
+ "type": "User",
+ "name": "testforge1",
+ "login": "testforge1",
+ "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png"
+ }
+ },
+ {
+ "id": 3,
+ "status": 1,
+ "content": "Notification Message Content 1",
+ "notification_url": "http://www.baidu.com",
+ "source": "IssueDelete",
+ "time_ago": "1天前",
+ "type": "notification"
+ },
+ {
+ "id": 4,
+ "status": 0,
+ "content": "Notification Message Content 2",
+ "notification_url": "http://www.baidu.com",
+ "source": "IssueChanged",
+ "time_ago": "1天前",
+ "type": "notification"
+ },
+ {
+ "id": 5,
+ "status": 0,
+ "content": "Notification Message Content 3",
+ "notification_url": "http://www.baidu.com",
+ "source": "ProjectJoined",
+ "time_ago": "1天前",
+ "type": "notification"
+ }
+ ]
+}
+```
+
+
+## 用户阅读系统通知
+用户阅读系统通知
+
+> 示例:
+
+```shell
+curl -X POST http://localhost:3000/api/users/yystopf/system_notification_histories.json
+```
+
+```javascript
+await octokit.request('GET /api/users/:login/system_notification_histories.json')
+```
+
+### HTTP 请求
+`POST /api/users/:login/system_notification_histories.json`
+
+### 请求字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|system_notification_id |integer |阅读的系统通知id |
+
+> 返回的JSON示例:
+
+```json
+{
+ "status": 0,
+ "message": "success"
+}
+```
+
+## 发送消息
+发送消息, 目前只支持atme
+
+> 示例:
+
+```shell
+curl -X POST http://localhost:3000/api/users/:login/messages.json
+```
+
+```javascript
+await octokit.request('POST /api/users/:login/messages.json')
+```
+
+### HTTP 请求
+`POST api/users/yystopf/messages.json`
+
+### 请求字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|type | string | 消息类型 |
+|receivers_login | array | 需要发送消息的用户名数组|
+|atmeable_type | string | atme消息对象,是从哪里@我的,比如评论:Journal、疑修:Issue、合并请求:PullRequest |
+|atmeable_id | integer | atme消息对象id |
+
+> 请求的JSON示例:
+
+```json
+{
+ "type": "atme",
+ "receivers_login": ["yystopf", "testforge1"],
+ "atmeable_type": "Journal",
+ "atmeable_id": 67
+}
+```
+
+
+> 返回的JSON示例:
+
+```json
+{
+ "status": 0,
+ "message": "success"
+}
+```
+
+
+## 阅读消息
+阅读消息
+
+> 示例:
+
+```shell
+curl -X POST http://localhost:3000/api/users/:login/messages/read.json
+```
+
+```javascript
+await octokit.request('POST /api/users/:login/messages/read.json')
+```
+
+### HTTP 请求
+`POST api/users/yystopf/messages/read.json`
+
+### 请求字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|type | string | 消息类型,不传为所有消息,notification为系统消息,atme为@我消息|
+|ids | array | 消息id数组,包含-1则把所有未读消息标记为已读|
+
+> 返回的JSON示例:
+
+```json
+{
+ "status": 0,
+ "message": "success"
+}
+```
+
+
+## 删除消息
+删除消息
+
+> 示例:
+
+```shell
+curl -X DELETE http://localhost:3000/api/users/:login/messages.json
+```
+
+```javascript
+await octokit.request('DELETE /api/users/:login/messages.json')
+```
+
+### HTTP 请求
+`DELETE api/users/yystopf/messages.json`
+
+### 请求字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|type | string | 消息类型,atme为@我消息|
+|ids | array | 消息id数组,包含-1则把所有消息删除|
+
+> 返回的JSON示例:
+
+```json
+{
+ "status": 0,
+ "message": "success"
+}
+```
+
+
+
+## 更改用户信息
+更改用户信息
+
+> 示例:
+
+```shell
+curl -X PATCH/PUT http://localhost:3000/api/users/yystopf.json
+```
+
+```javascript
+await octokit.request('PATCH/PUT /api/users/:login.json')
+```
+
+### HTTP 请求
+`PATCH/PUT /api/users/:login.json`
+
+### 请求字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|user.nickname |string |用户昵称 |
+|user.image |base64/file |用户头像 |
+|user.user_extension_attributes.gender |int |性别, 0男 1女 |
+|user.user_extension_attributes.province |string |省份 |
+|user.user_extension_attributes.city |string |城市 |
+|user.user_extension_attributes.description |string |简介 |
+|user.user_extension_attributes.custom_department|string |单位名称 |
+|user.user_extension_attributes.technical_title |string |职业 |
+|user.user_extension_attributes.show_email |bool |是否展示邮箱 |
+|user.user_extension_attributes.show_location |bool |是否展示位置 |
+|user.user_extension_attributes.show_department |bool |是否展示公司 |
+
+> 请求的JSON示例:
+
+```json
+{
+ "user": {
+ "nickname": "xxx",
+ "user_extension_attributes": {
+ "gender": 0,
+ "province": "湖南",
+ "city": "长沙",
+ "description": "个性签名",
+ "custom_department": "湖南智擎科技有限公司",
+ }
+ }
+}
+```
+
+> 返回的JSON示例:
+
+```json
+{
+ "status": 0,
+ "message": "success"
+}
+```
+
+## 获取平台消息设置配置信息
+获取平台消息设置配置信息
+
+> 示例:
+
+```shell
+curl -X GET http://localhost:3000/api/template_message_settings.json
+```
+
+```javascript
+await octokit.request('GET /api/template_message_settings.json')
+```
+
+### HTTP 请求
+`GET /api/template_message_settings.json`
+
+### 返回字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|type |string |消息配置类型 |
+|type_name |string |消息配置类型含义|
+|total_settings_count |int |配置条数|
+|settings.name |string |配置名称|
+|settings.key |string |配置标识|
+|settings.notification_disabled |boolean |站内信设置是否禁用|
+|settings.email_disabled |boolean |邮件设置是否禁用|
+
+
+> 返回的JSON示例:
+
+```json
+{
+ "status": 0,
+ "message": "响应成功",
+ "setting_types": [
+ {
+ "type": "TemplateMessageSetting::Normal",
+ "type_name": "",
+ "total_settings_count": 3,
+ "settings": [
+ {
+ "name": "被拉入或移出组织",
+ "key": "Organization",
+ "notification_disabled": true,
+ "email_disabled": false
+ },
+ {
+ "name": "被拉入或移出项目",
+ "key": "Project",
+ "notification_disabled": true,
+ "email_disabled": false
+ },
+ {
+ "name": "有权限变更",
+ "key": "Permission",
+ "notification_disabled": true,
+ "email_disabled": false
+ }
+ ]
+ },
+ {
+ "type": "TemplateMessageSetting::CreateOrAssign",
+ "type_name": "我创建的或负责的",
+ "total_settings_count": 4,
+ "settings": [
+ {
+ "name": "疑修被指派",
+ "key": "IssueAssigned",
+ "notification_disabled": true,
+ "email_disabled": false
+ },
+ {
+ "name": "合并请求被指派",
+ "key": "PullRequestAssigned",
+ "notification_disabled": true,
+ "email_disabled": false
+ }
+ ]
+ },
+ {
+ "type": "TemplateMessageSetting::ManageProject",
+ "type_name": "我管理的仓库",
+ "total_settings_count": 4,
+ "settings": [
+ {
+ "name": "有新的疑修",
+ "key": "Issue",
+ "notification_disabled": true,
+ "email_disabled": false
+ },
+ {
+ "name": "有新的合并请求",
+ "key": "PullRequest",
+ "notification_disabled": true,
+ "email_disabled": false
+ },
+ {
+ "name": "有成员变动",
+ "key": "Member",
+ "notification_disabled": true,
+ "email_disabled": false
+ },
+ {
+ "name": "设置更改",
+ "key": "SettingChanged",
+ "notification_disabled": true,
+ "email_disabled": false
+ }
+ ]
+ }
+ ]
+}
+```
+
+
+## 获取用户消息设置配置信息
+获取用户消息设置配置信息
+
+> 示例:
+
+```shell
+curl -X GET http://localhost:3000/api/users/yystopf/template_message_settings.json
+```
+
+```javascript
+await octokit.request('GET /api/uses/yystopf/template_message_settings.json')
+```
+
+### HTTP 请求
+`GET /api/users/:user_id/template_message_settings.json`
+
+### 返回字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|notification_body |string |站内信配置 |
+|email_body |string |邮件配置|
+
+
+> 返回的JSON示例:
+
+```json
+{
+ "status": 0,
+ "message": "响应成功",
+ "user": {
+ "id": 2,
+ "type": "User",
+ "name": "heh",
+ "login": "yystopf",
+ "image_url": "system/lets/letter_avatars/2/H/188_239_142/120.png"
+ },
+ "notification_body": {
+ "CreateOrAssign::IssueAssigned": true,
+ "CreateOrAssign::PullRequestAssigned": true,
+ "ManageProject::Issue": true,
+ "ManageProject::PullRequest": true,
+ "ManageProject::Member": true,
+ "ManageProject::SettingChanged": true,
+ "Normal::Organization": true,
+ "Normal::Project": true,
+ "Normal::Permission": true
+ },
+ "email_body": {
+ "CreateOrAssign::IssueAssigned": false,
+ "CreateOrAssign::PullRequestAssigned": false,
+ "ManageProject::Issue": false,
+ "ManageProject::PullRequest": false,
+ "ManageProject::Member": false,
+ "ManageProject::SettingChanged": true,
+ "Normal::Organization": false,
+ "Normal::Project": true,
+ "Normal::Permission": false
+ }
+}
+```
+
+
+## 重新设置用户消息设置配置信息
+重新设置用户消息设置配置信息
+
+> 示例:
+
+```shell
+curl -X POST http://localhost:3000/api/users/yystopf/template_message_settings/update_setting.json
+```
+
+```javascript
+await octokit.request('POST /api/uses/yystopf/template_message_settings/update_setting.json')
+```
+
+### HTTP 请求
+`POST /api/users/:user_id/template_message_settings/update_setting.json`
+
+### 请求字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|notification_body |string |站内信配置 |
+|email_body |string |邮件配置|
+
+
+> 请求的JSON示例:
+
+```json
+{
+ "setting": {
+ "notification_body": {
+ "CreateOrAssign::IssueAssigned": true,
+ "CreateOrAssign::PullRequestAssigned": true,
+ "ManageProject::Issue": true,
+ "ManageProject::PullRequest": true,
+ "ManageProject::Member": true,
+ "ManageProject::SettingChanged": true,
+ "Normal::Organization": true,
+ "Normal::Project": true,
+ "Normal::Permission": true
+ },
+ "email_body": {
+ "CreateOrAssign::IssueAssigned": false,
+ "CreateOrAssign::PullRequestAssigned": false,
+ "ManageProject::Issue": false,
+ "ManageProject::PullRequest": false,
+ "ManageProject::Member": false,
+ "ManageProject::SettingChanged": true,
+ "Normal::Organization": false,
+ "Normal::Project": "t",
+ "Normal::Permission": false
+ }
+ }
+}
+```
+
+### 返回字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|notification_body |string |站内信配置 |
+|email_body |string |邮件配置|
+
+
+> 返回的JSON示例:
+
+```json
+{
+ "status": 0,
+ "message": "响应成功",
+ "user": {
+ "id": 2,
+ "type": "User",
+ "name": "heh",
+ "login": "yystopf",
+ "image_url": "system/lets/letter_avatars/2/H/188_239_142/120.png"
+ },
+ "notification_body": {
+ "CreateOrAssign::IssueAssigned": true,
+ "CreateOrAssign::PullRequestAssigned": true,
+ "ManageProject::Issue": true,
+ "ManageProject::PullRequest": true,
+ "ManageProject::Member": true,
+ "ManageProject::SettingChanged": true,
+ "Normal::Organization": true,
+ "Normal::Project": true,
+ "Normal::Permission": true
+ },
+ "email_body": {
+ "CreateOrAssign::IssueAssigned": false,
+ "CreateOrAssign::PullRequestAssigned": false,
+ "ManageProject::Issue": false,
+ "ManageProject::PullRequest": false,
+ "ManageProject::Member": false,
+ "ManageProject::SettingChanged": true,
+ "Normal::Organization": false,
+ "Normal::Project": true,
+ "Normal::Permission": false
+ }
+}
+```
+
+
+## 获取用户星标项目
+获取用户星标项目
+
+> 示例:
+
+```shell
+curl -X GET http://localhost:3000/api/users/yystopf/is_pinned_projects.json
+```
+
+```javascript
+await octokit.request('GET /api/users/:login/is_pinned_projects.json')
+```
+
+### HTTP 请求
+`GET api/users/:login/is_pinned_projects.json`
+
+### 返回字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|total_count |int |星标项目数量 |
+|identifier |string |项目标识 |
+|name |string |项目名称 |
+|description |string |项目描述 |
+|visits |int |项目访问数量|
+|praises_count |int |项目点赞数量|
+|watchers_count |int |项目关注数量|
+|issues_count |int |项目issue数量|
+|pull_requests_count |int |项目合并请求数量|
+|forked_count |int |项目复刻数量|
+|is_public |bool |项目是否公开|
+|mirror_url |string |镜像地址|
+|type |int |项目类型 0 普通项目 1 普通镜像项目 2 同步镜像项目|
+|time_ago |string |上次更新时间|
+|open_devops |int |是否开启devops|
+|forked_from_project_id |int |fork项目id|
+|platform |string |项目平台|
+|author.name |string |项目拥有者名称|
+|author.type |string |项目拥有者类型|
+|author.login |string |项目拥有者用户名|
+|author.image_url |string |项目拥有者头像|
+|category.name |string |项目分类名称|
+|language.name |string |项目语言名称|
+|position |int |项目排序|
+
+
+> 返回的JSON示例:
+
+```json
+{
+ "total_count": 1,
+ "projects": [
+ {
+ "id": 89,
+ "repo_id": 89,
+ "identifier": "monkey",
+ "name": "boke",
+ "description": "dkkd",
+ "visits": 4,
+ "praises_count": 0,
+ "watchers_count": 0,
+ "issues_count": 0,
+ "pull_requests_count": 0,
+ "forked_count": 0,
+ "is_public": true,
+ "mirror_url": "https://github.com/viletyy/monkey.git",
+ "type": 1,
+ "last_update_time": 1619685144,
+ "time_ago": "27天前",
+ "forked_from_project_id": null,
+ "open_devops": false,
+ "platform": "forge",
+ "author": {
+ "name": "测试组织",
+ "type": "Organization",
+ "login": "ceshi_org",
+ "image_url": "images/avatars/Organization/9?t=1612706073"
+ },
+ "category": {
+ "id": 3,
+ "name": "深度学习"
+ },
+ "language": {
+ "id": 2,
+ "name": "C"
+ }
+ }
+ ]
+}
+```
+
+
+## 用户添加星标项目
+用户添加星标项目
+
+> 示例:
+
+```shell
+curl -X POST http://localhost:3000/api/users/yystopf/is_pinned_projects/pin.json
+```
+
+```javascript
+await octokit.request('GET /api/users/:login/is_pinned_projects/pin.json')
+```
+
+### HTTP 请求
+`POST /api/users/:login/is_pinned_projects/pin.json`
+
+### 请求字段说明:
+#### 同时设定多个星标项目
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|is_pinned_project_ids |array |设定为星标项目的id |
+
+#### 只设定一个星标项目
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|is_pinned_project_id |integer |设定为星标项目的id |
+
+> 返回的JSON示例:
+
+```json
+{
+ "status": 0,
+ "message": "success"
+}
+```
+
+
+## 星标项目展示排序
+星标项目展示排序
+
+> 示例:
+
+```shell
+curl -X PATCH http://localhost:3000/api/users/yystopf/is_pinned_projects/11.json
+```
+
+```javascript
+await octokit.request('PATCH/PUT /api/users/:login/is_pinned_projects/:id.json')
+```
+
+### HTTP 请求
+`PATCH/PUT /api/users/:login/is_pinned_projects/:id.json`
+
+### 请求字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|pinned_projects.position |int |排序,数字越大排名越前 |
+
+> 请求的JSON示例:
+
+```json
+{
+ "pinned_project": {
+ "position": 1
+ }
+}
+```
+
+> 返回的JSON示例:
+
+```json
+{
+ "status": 0,
+ "message": "success"
+}
+```
+## 用户近期活动统计
+用户近期活动统计, 默认显示近一周的数据
+
+> 示例:
+
+```shell
+curl -X GET http://localhost:3000/api/users/yystopf/statistics/activity.json
+```
+
+```javascript
+await octokit.request('GET /api/users/:login/statistics/activity.json')
+```
+
+### HTTP 请求
+`GET /api/users/:login/statistics/activity.json`
+
+### 返回字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|dates |array |时间 |
+|issues_count |array |疑修数量|
+|pull_requests_count |array |合并请求数量|
+|commtis_count |array |贡献数量|
+
+
+> 返回的JSON示例:
+
+```json
+{
+ "dates": [
+ "2021.05.21",
+ "2021.05.22",
+ "2021.05.23",
+ "2021.05.24",
+ "2021.05.25",
+ "2021.05.26",
+ "2021.05.27",
+ "2021.05.28"
+ ],
+ "issues_count": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "pull_requests_count": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "commits_count": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+}
+```
+
+
+
+## 获取用户贡献度
+获取用户贡献度
+
+> 示例:
+
+```shell
+curl -X GET http://localhost:3000/api/users/yystopf/headmaps.json
+```
+
+```javascript
+await octokit.request('GET /api/users/:login/headmaps.json')
+```
+
+### HTTP 请求
+`GET api/users/:login/headmaps.json`
+
+
+### 请求字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|year |string |年份 |
+
+### 返回字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|total_contributions |int |所选时间内的总贡献度 |
+|headmaps.date |string|时间|
+|headmaps.contributions |int|贡献度|
+
+
+> 返回的JSON示例:
+
+```json
+{
+ "total_contributions": 139,
+ "headmaps": [
+ {
+ "date": "2021-02-07",
+ "contributions": 1
+ },
+ {
+ "date": "2021-02-21",
+ "contributions": 13
+ },
+ {
+ "date": "2021-02-25",
+ "contributions": 5
+ },
+ {
+ "date": "2021-03-01",
+ "contributions": 2
+ },
+ {
+ "date": "2021-03-04",
+ "contributions": 1
+ },
+ {
+ "date": "2021-03-15",
+ "contributions": 9
+ },
+ {
+ "date": "2021-03-22",
+ "contributions": 14
+ },
+ {
+ "date": "2021-03-24",
+ "contributions": 1
+ },
+ {
+ "date": "2021-03-30",
+ "contributions": 11
+ },
+ {
+ "date": "2021-04-06",
+ "contributions": 1
+ },
+ {
+ "date": "2021-04-12",
+ "contributions": 1
+ },
+ {
+ "date": "2021-04-13",
+ "contributions": 2
+ },
+ {
+ "date": "2021-04-19",
+ "contributions": 3
+ },
+ {
+ "date": "2021-04-23",
+ "contributions": 37
+ },
+ {
+ "date": "2021-04-25",
+ "contributions": 2
+ },
+ {
+ "date": "2021-04-26",
+ "contributions": 6
+ },
+ {
+ "date": "2021-04-28",
+ "contributions": 1
+ },
+ {
+ "date": "2021-04-29",
+ "contributions": 18
+ },
+ {
+ "date": "2021-04-30",
+ "contributions": 9
+ },
+ {
+ "date": "2021-05-04",
+ "contributions": 1
+ },
+ {
+ "date": "2021-05-06",
+ "contributions": 1
+ }
+ ]
+}
+```
+
+
+## 获取用户动态
+获取用户动态
+
+> 示例:
+
+```shell
+curl -X GET http://localhost:3000/api/users/yystopf/project_trends.json
+```
+
+```javascript
+await octokit.request('GET /api/users/:login/project_trends.json')
+```
+
+### HTTP 请求
+`GET api/users/:login/project_trends.json`
+
+
+### 请求字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|date |string |日期,格式: 2021-05-28 |
+
+### 返回字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|total_count |int |所选时间内的总动态数 |
+|project_trends.trend_type |string|动态类型,Issue:疑修,VersionRelease:版本发布,PullRequest:合并请求|
+|project_trends.action_type |string|操作类型|
+|project_trends.trend_id |integer|动态id|
+|project_trends.user_name |string|用户名称|
+|project_trends.user_login |string|用户用户名|
+|project_trends.user_avatar |string|用户头像|
+|project_trends.action_time |string|操作时间|
+|project_trends.name |string|动态标题|
+
+> 返回的JSON示例:
+
+```json
+{
+ "total_count": 16,
+ "project_trends": [
+ {
+ "id": 27,
+ "trend_type": "Issue",
+ "action_type": "创建了工单",
+ "trend_id": 18,
+ "user_name": "yystopf",
+ "user_login": "yystopf",
+ "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
+ "action_time": "21天前",
+ "name": "31213123123",
+ "issue_type": "1",
+ "status_id": 2,
+ "priority_id": 4,
+ "created_at": "2021-05-07 15:39",
+ "updated_at": "2021-05-27 15:42",
+ "assign_user_name": "yystopf",
+ "assign_user_login": "yystopf",
+ "issue_journal_size": 1,
+ "issue_journals": []
+ },
+ {
+ "id": 8,
+ "trend_type": "VersionRelease",
+ "action_type": "创建了版本发布",
+ "trend_id": 8,
+ "user_name": "yystopf",
+ "user_login": "yystopf",
+ "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
+ "action_time": "24天前",
+ "name": "heihei1",
+ "tag_name": "v1.0",
+ "target_commitish": "master",
+ "tarball_url": "http://localhost:10080/forgeceshiorg1/ceshi1/archive/v1.0.tar.gz",
+ "zipball_url": "http://localhost:10080/forgeceshiorg1/ceshi1/archive/v1.0.zip",
+ "url": "http://localhost:10080/api/v1/repos/forgeceshiorg1/ceshi1/releases/84",
+ "version_gid": "84",
+ "created_at": "2021-05-04 12:04"
+ },
+ {
+ "id": 25,
+ "trend_type": "PullRequest",
+ "action_type": "关闭了合并请求",
+ "trend_id": 14,
+ "user_name": "yystopf",
+ "user_login": "yystopf",
+ "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
+ "action_time": "28天前",
+ "name": "13",
+ "created_at": "2021-04-30 15:39"
+ },
+ {
+ "id": 24,
+ "trend_type": "PullRequest",
+ "action_type": "创建了合并请求",
+ "trend_id": 13,
+ "user_name": "yystopf",
+ "user_login": "yystopf",
+ "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
+ "action_time": "28天前",
+ "name": "211212",
+ "created_at": "2021-04-30 15:37"
+ },
+ {
+ "id": 23,
+ "trend_type": "PullRequest",
+ "action_type": "创建了合并请求",
+ "trend_id": 12,
+ "user_name": "yystopf",
+ "user_login": "yystopf",
+ "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
+ "action_time": "28天前",
+ "name": "奇偶哦iu",
+ "created_at": "2021-04-30 10:19"
+ },
+ {
+ "id": 22,
+ "trend_type": "PullRequest",
+ "action_type": "创建了合并请求",
+ "trend_id": 11,
+ "user_name": "yystopf",
+ "user_login": "yystopf",
+ "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
+ "action_time": "28天前",
+ "name": "2112123",
+ "created_at": "2021-04-29 18:46"
+ },
+ {
+ "id": 21,
+ "trend_type": "PullRequest",
+ "action_type": "关闭了合并请求",
+ "trend_id": 10,
+ "user_name": "yystopf",
+ "user_login": "yystopf",
+ "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
+ "action_time": "28天前",
+ "name": "23123",
+ "created_at": "2021-04-29 18:45"
+ },
+ {
+ "id": 20,
+ "trend_type": "PullRequest",
+ "action_type": "创建了合并请求",
+ "trend_id": 9,
+ "user_name": "yystopf",
+ "user_login": "yystopf",
+ "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
+ "action_time": "28天前",
+ "name": "33",
+ "created_at": "2021-04-29 18:37"
+ },
+ {
+ "id": 19,
+ "trend_type": "PullRequest",
+ "action_type": "关闭了合并请求",
+ "trend_id": 8,
+ "user_name": "yystopf",
+ "user_login": "yystopf",
+ "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
+ "action_time": "28天前",
+ "name": "gggg",
+ "created_at": "2021-04-29 17:51"
+ },
+ {
+ "id": 16,
+ "trend_type": "Issue",
+ "action_type": "创建了工单",
+ "trend_id": 8,
+ "user_name": "yystopf",
+ "user_login": "yystopf",
+ "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
+ "action_time": "1个月前",
+ "name": "hjhkj",
+ "issue_type": "1",
+ "status_id": 1,
+ "priority_id": 2,
+ "created_at": "2021-04-19 10:52",
+ "updated_at": "2021-04-19 10:52",
+ "assign_user_name": null,
+ "assign_user_login": null,
+ "issue_journal_size": 0,
+ "issue_journals": []
+ },
+ {
+ "id": 7,
+ "trend_type": "VersionRelease",
+ "action_type": "创建了版本发布",
+ "trend_id": 7,
+ "user_name": "yystopf",
+ "user_login": "yystopf",
+ "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
+ "action_time": "1个月前",
+ "name": "v3.0.1",
+ "tag_name": "v3.0.1",
+ "target_commitish": "master",
+ "tarball_url": "http://localhost:10080/yystopf/ceshirepo1/archive/v3.0.1.tar.gz",
+ "zipball_url": "http://localhost:10080/yystopf/ceshirepo1/archive/v3.0.1.zip",
+ "url": "http://localhost:10080/api/v1/repos/yystopf/ceshirepo1/releases/78",
+ "version_gid": "78",
+ "created_at": "2021-03-30 15:51"
+ },
+ {
+ "id": 6,
+ "trend_type": "VersionRelease",
+ "action_type": "创建了版本发布",
+ "trend_id": 6,
+ "user_name": "yystopf",
+ "user_login": "yystopf",
+ "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
+ "action_time": "1个月前",
+ "name": "v3.0.0",
+ "tag_name": "v3.0.0",
+ "target_commitish": "master",
+ "tarball_url": "http://localhost:10080/yystopf/ceshirepo1/archive/v3.0.0.tar.gz",
+ "zipball_url": "http://localhost:10080/yystopf/ceshirepo1/archive/v3.0.0.zip",
+ "url": "http://localhost:10080/api/v1/repos/yystopf/ceshirepo1/releases/77",
+ "version_gid": "77",
+ "created_at": "2021-03-30 15:33"
+ },
+ {
+ "id": 5,
+ "trend_type": "VersionRelease",
+ "action_type": "创建了版本发布",
+ "trend_id": 5,
+ "user_name": "yystopf",
+ "user_login": "yystopf",
+ "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
+ "action_time": "1个月前",
+ "name": "v1.0.0",
+ "tag_name": "v1.0.0",
+ "target_commitish": "master",
+ "tarball_url": "http://localhost:10080/yystopf/ceshirepo1/archive/v1.0.0.tar.gz",
+ "zipball_url": "http://localhost:10080/yystopf/ceshirepo1/archive/v1.0.0.zip",
+ "url": "http://localhost:10080/api/v1/repos/yystopf/ceshirepo1/releases/76",
+ "version_gid": "76",
+ "created_at": "2021-03-30 15:27"
+ },
+ {
+ "id": 2,
+ "trend_type": "VersionRelease",
+ "action_type": "创建了版本发布",
+ "trend_id": 2,
+ "user_name": "yystopf",
+ "user_login": "yystopf",
+ "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
+ "action_time": "2个月前",
+ "name": "vvvv",
+ "tag_name": "v1.1",
+ "target_commitish": "dev",
+ "tarball_url": "http://localhost:10080/yystopf/virus_blog/archive/v1.1.tar.gz",
+ "zipball_url": "http://localhost:10080/yystopf/virus_blog/archive/v1.1.zip",
+ "url": "http://localhost:10080/api/v1/repos/yystopf/virus_blog/releases/6",
+ "version_gid": "6",
+ "created_at": "2021-03-15 14:18"
+ },
+ {
+ "id": 2,
+ "trend_type": "PullRequest",
+ "action_type": "创建了合并请求",
+ "trend_id": 2,
+ "user_name": "yystopf",
+ "user_login": "yystopf",
+ "user_avatar": "system/lets/letter_avatars/2/Y/241_125_89/120.png",
+ "action_time": "3个月前",
+ "name": "444",
+ "created_at": "2021-02-25 17:31"
+ }
+ ]
+}
+```
+
+
+## 用户开发能力
+用户开发能力, 默认为所有时间下的开发能力
+
+> 示例:
+
+```shell
+curl -X GET http://localhost:3000/api/users/yystopf/statistics/develop.json
+```
+
+```javascript
+await octokit.request('GET /api/users/:login/statistics/develop.json')
+```
+
+### HTTP 请求
+`GET /api/users/:login/statistics/develop.json`
+
+
+### 请求字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|start_time |integer |时间戳,开始时间,格式:1621526400|
+|end_time |integer |时间戳,结束时间,格式:1622131200|
+
+### 返回字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|influence |int |影响力 |
+|contribution |int |贡献度|
+|activity |int |活跃度|
+|experience |int |项目经验|
+|language |int |语言能力|
+|languages_percent |float |语言百分比|
+|each_language_score |int |各门语言分数|
+
+
+> 返回的JSON示例:
+
+```json
+{
+ "platform": {
+ "influence": 61,
+ "contribution": 75,
+ "activity": 66,
+ "experience": 95,
+ "language": 87,
+ "languages_percent": {
+ "CSS": 0.03,
+ "C#": 0.13,
+ "Ruby": 0.04,
+ "Go": 0.05,
+ "C": 0.19,
+ "Java": 0.34,
+ "Python": 0.09,
+ "C+": 0.01,
+ "C++": 0.11,
+ "Scala": 0.01,
+ "HTML": 0.01
+ },
+ "each_language_score": {
+ "CSS": 71,
+ "C#": 86,
+ "Ruby": 75,
+ "Go": 77,
+ "C": 90,
+ "Java": 93,
+ "Python": 83,
+ "C+": 66,
+ "C++": 85,
+ "Scala": 66,
+ "HTML": 66
+ }
+ },
+ "user": {
+ "influence": 60,
+ "contribution": 72,
+ "activity": 65,
+ "experience": 88,
+ "language": 84,
+ "languages_percent": {
+ "C": 0.25,
+ "C#": 0.33,
+ "C++": 0.13,
+ "CSS": 0.08,
+ "Go": 0.04,
+ "HTML": 0.04,
+ "Java": 0.04,
+ "Ruby": 0.08
+ },
+ "each_language_score": {
+ "C": 81,
+ "C#": 84,
+ "C++": 75,
+ "CSS": 71,
+ "Go": 66,
+ "HTML": 66,
+ "Java": 66,
+ "Ruby": 71
+ }
+ }
+}
+```
+
+
+## 用户角色定位
+用户角色定位,默认显示所有时间下的角色定位数据
+
+> 示例:
+
+```shell
+curl -X GET http://localhost:3000/api/users/yystopf/statistics/role.json
+```
+
+```javascript
+await octokit.request('GET /api/users/:login/statistics/role.json')
+```
+
+### HTTP 请求
+`GET /api/users/:login/statistics/role.json`
+
+
+### 请求字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|start_time |integer |时间戳,开始时间,格式:1621526400|
+|end_time |integer |时间戳,结束时间,格式:1622131200|
+
+### 返回字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|total_projects_count |int |用户所有的项目数量 |
+|role.object.count |int |用户该语言下的项目数量|
+|role.object.percent |float |用户该语言下的项目占比|
+
+> 返回的JSON示例:
+
+```json
+{
+ "total_projects_count": 27,
+ "role": {
+ "owner": {
+ "count": 24,
+ "percent": 0.89
+ },
+ "manager": {
+ "count": 1,
+ "percent": 0.04
+ },
+ "developer": {
+ "count": 2,
+ "percent": 0.07
+ },
+ "reporter": {
+ "count": 0,
+ "percent": 0.0
+ }
+ }
+}
+```
+
+
+## 用户专业定位
+用户专业定位,默认显示所有时间下的专业定位数据
+
+> 示例:
+
+```shell
+curl -X GET http://localhost:3000/api/users/yystopf/statistics/major.json
+```
+
+```javascript
+await octokit.request('GET /api/users/:login/statistics/major.json')
+```
+
+### HTTP 请求
+`GET /api/users/:login/statistics/major.json`
+
+
+### 请求字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|start_time |integer |时间戳,开始时间,格式:1621526400|
+|end_time |integer |时间戳,结束时间,格式:1622131200|
+
+### 返回字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|categories |int |用户项目分类 |
+
+> 返回的JSON示例:
+
+```json
+{
+ "categories": [
+ "大数据",
+ "机器学习",
+ "深度学习",
+ "人工智能",
+ "智慧医疗",
+ "云计算"
+ ]
+}
+```
+
+
+## 待办事项-用户通知信息
+待办事项-用户通知信息
+
+> 示例:
+
+```shell
+curl -X GET http://localhost:3000/api/users/yystopf/applied_messages.json
+```
+
+```javascript
+await octokit.request('GET /api/users/:login/applied_messages.json')
+```
+
+### HTTP 请求
+`GET /api/users/:login/applied_messages.json`
+
+### 请求字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|login |string |用户标识 |
+
+### 返回字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|applied |object |通知主体 |
+|applied.id |int |通知主体的迁移id |
+|applied.status |string |通知主体的迁移状态,canceled:取消,common:正在迁移, accept:已接受,refuse:已拒绝|
+|applied.time_ago |string |通知主体的迁移创建的时间 |
+|applied.project.id |int |通知主体的迁移项目的id |
+|applied.project.identifier |string |通知主体的迁移项目的标识 |
+|applied.project.name |string |通知主体的迁移项目的名称 |
+|applied.project.description |string |通知主体的迁移项目的描述 |
+|applied.project.is_public |bool |通知主体的迁移项目是否公开 |
+|applied.project.owner.id |bool |通知主体的迁移项目拥有者id |
+|applied.project.owner.type |string |通知主体的迁移项目拥有者类型 |
+|applied.project.owner.name |string |通知主体的迁移项目拥有者昵称 |
+|applied.project.owner.login |string |通知主体的迁移项目拥有者标识 |
+|applied.project.owner.image_url |string |通知主体的迁移项目拥有者头像 |
+|applied.user.id |int |通知主体的迁移创建者的id |
+|applied.user.type |string |通知主体的迁移创建者的类型 |
+|applied.user.name |string |通知主体的迁移创建者的名称 |
+|applied.user.login |string |通知主体的迁移创建者的标识 |
+|applied.user.image_url |string |通知主体的迁移创建者头像 |
+|applied_user.id |int |通知发起者的id |
+|applied_user.type |string |通知发起者的类型 |
+|applied_user.name |string |通知发起者的名称 |
+|applied_user.login |string |通知发起者的标识 |
+|applied_user.image_url |string |通知发起者头像 |
+|applied_type |string |通知类型 |
+|name |string | 通知内容 |
+|viewed |string|是否已读,waiting:未读,viewed:已读|
+|status |string|通知状态, canceled:已取消,common: 正常,successed:成功,failure:失败|
+|time_ago |string|通知时间|
+
+
+> 返回的JSON示例:
+
+```json
+{
+ "total_count": 5,
+ "applied_messages": [
+ {
+ "applied": {
+ "project": {
+ "id": 74,
+ "identifier": "hehuisssjssjjsjs",
+ "name": "hehuisssjssjjsjs",
+ "description": "wwww",
+ "is_public": false,
+ "owner": {
+ "id": 10,
+ "type": "User",
+ "name": "testforge1",
+ "login": "testforge1",
+ "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png"
+ }
+ },
+ "user": {
+ "id": 6,
+ "type": "User",
+ "name": "何慧",
+ "login": "yystopf",
+ "image_url": "images/avatars/User/6?t=1622513134"
+ },
+ "id": 6,
+ "status": "accepted",
+ "created_at": "2021-06-09 16:34",
+ "time_ago": "1分钟前"
+ },
+ "applied_user": {
+ "id": 6,
+ "type": "User",
+ "name": "何慧",
+ "login": "yystopf",
+ "image_url": "images/avatars/User/6?t=1622513134"
+ },
+ "applied_type": "AppliedProject",
+ "name": "已通过你加入【hehuisssjssjjsjs】仓库的申请。",
+ "viewed": "waiting",
+ "status": "successed",
+ "created_at": "2021-06-09 16:34",
+ "time_ago": "1分钟前"
+ },
+ {
+ "applied": {
+ "project": {
+ "id": 86,
+ "identifier": "ceshi_repo1",
+ "name": "测试项目啊1",
+ "description": "二十多",
+ "is_public": true,
+ "owner": {
+ "id": 52,
+ "type": "Organization",
+ "name": "身份卡手动阀",
+ "login": "ceshi1",
+ "image_url": "images/avatars/Organization/52?t=1618805056"
+ }
+ },
+ "user": {
+ "id": 6,
+ "type": "User",
+ "name": "yystopf",
+ "login": "yystopf",
+ "image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png"
+ },
+ "owner": {
+ "id": 9,
+ "type": "Organization",
+ "name": "测试组织",
+ "login": "ceshi_org",
+ "image_url": "images/avatars/Organization/9?t=1612706073"
+ },
+ "id": 4,
+ "status": "common",
+ "created_at": "2021-04-26 09:54",
+ "time_ago": "35分钟前"
+ },
+ "applied_user": {
+ "id": 6,
+ "type": "User",
+ "name": "yystopf",
+ "login": "yystopf",
+ "image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png"
+ },
+ "applied_type": "AppliedTransferProject",
+ "name": "正在将【测试项目啊1】仓库转移给【测试组织】",
+ "viewed": "viewed",
+ "status": "common",
+ "created_at": "2021-04-26 09:54",
+ "time_ago": "35分钟前"
+ },
+ ...
+ ]
+}
+```
+
+## 待办事项-接受仓库
+待办事项-接受仓库
+
+> 示例:
+
+```shell
+curl -X GET http://localhost:3000/api/users/yystopf/applied_transfer_projects.json
+```
+
+```javascript
+await octokit.request('GET /api/users/:login/applied_transfer_projects.json')
+```
+
+### HTTP 请求
+`GET /api/users/:login/applied_transfer_projects.json`
+
+### 请求字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|login |string |用户标识 |
+
+### 返回字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|id |int |迁移id |
+|status |string |迁移状态,canceled:取消,common:正在迁移, accept:已接受,refuse:已拒绝|
+|time_ago |string |迁移创建的时间 |
+|project.id |int |迁移项目的id |
+|project.identifier |string |迁移项目的标识 |
+|project.name |string |迁移项目的名称 |
+|project.description |string |迁移项目的描述 |
+|project.is_public |bool |迁移项目是否公开 |
+|project.owner.id |bool |迁移项目拥有者id |
+|project.owner.type |string |迁移项目拥有者类型 |
+|project.owner.name |string |迁移项目拥有者昵称 |
+|project.owner.login |string |迁移项目拥有者标识 |
+|project.owner.image_url |string |迁移项目拥有者头像 |
+|user.id |int |迁移创建者的id |
+|user.type |string |迁移创建者的类型 |
+|user.name |string |迁移创建者的名称 |
+|user.login |string |迁移创建者的标识 |
+|user.image_url |string |迁移创建者头像 |
+|owner.id |int |迁移接受者的id |
+|owner.type |string |迁移接受者的类型 |
+|owner.name |string |迁移接受者的名称 |
+|owner.login |string |迁移接受者的标识 |
+|owner.image_url |string |迁移接受者头像 |
+
+
+> 返回的JSON示例:
+
+```json
+{
+ "total_count": 4,
+ "applied_transfer_projects": [
+ {
+ "project": {
+ "id": 86,
+ "identifier": "ceshi_repo1",
+ "name": "测试项目啊1",
+ "description": "二十多",
+ "is_public": true,
+ "owner": {
+ "id": 52,
+ "type": "Organization",
+ "name": "身份卡手动阀",
+ "login": "ceshi1",
+ "image_url": "images/avatars/Organization/52?t=1618805056"
+ }
+ },
+ "user": {
+ "id": 6,
+ "type": "User",
+ "name": "yystopf",
+ "login": "yystopf",
+ "image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png"
+ },
+ "owner": {
+ "id": 52,
+ "type": "Organization",
+ "name": "身份卡手动阀",
+ "login": "ceshi1",
+ "image_url": "images/avatars/Organization/52?t=1618805056"
+ },
+ "id": 1,
+ "status": "canceled",
+ "created_at": "2021-04-25 18:06",
+ "time_ago": "16小时前"
+ },
+ ...
+ ]
+}
+```
+
+## 用户接受迁移
+用户接受迁移
+
+> 示例:
+
+```shell
+curl -X POST http://localhost:3000/api/users/yystopf/applied_transfer_projects/2/accept.json
+```
+
+```javascript
+await octokit.request('GET /api/users/:login/applied_transfer_projects/:id/accept.json')
+```
+
+### HTTP 请求
+`GET /api/users/:login/applied_transfer_projects/:id/accept.json`
+
+### 请求字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|login |string |用户标识 |
+|id |int |迁移id |
+
+### 返回字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|id |int |迁移id |
+|status |string |迁移状态,canceled:取消,common:正在迁移, accept:已接受,refuse:已拒绝|
+|time_ago |string |迁移创建的时间 |
+|project.id |int |迁移项目的id |
+|project.identifier |string |迁移项目的标识 |
+|project.name |string |迁移项目的名称 |
+|project.description |string |迁移项目的描述 |
+|project.is_public |bool |迁移项目是否公开 |
+|project.owner.id |bool |迁移项目拥有者id |
+|project.owner.type |string |迁移项目拥有者类型 |
+|project.owner.name |string |迁移项目拥有者昵称 |
+|project.owner.login |string |迁移项目拥有者标识 |
+|project.owner.image_url |string |迁移项目拥有者头像 |
+|user.id |int |迁移创建者的id |
+|user.type |string |迁移创建者的类型 |
+|user.name |string |迁移创建者的名称 |
+|user.login |string |迁移创建者的标识 |
+|user.image_url |string |迁移创建者头像 |
+|owner.id |int |迁移接受者的id |
+|owner.type |string |迁移接受者的类型 |
+|owner.name |string |迁移接受者的名称 |
+|owner.login |string |迁移接受者的标识 |
+|owner.image_url |string |迁移接受者头像 |
+
+
+> 返回的JSON示例:
+
+```json
+{
+ "project": {
+ "id": 86,
+ "identifier": "ceshi_repo1",
+ "name": "测试项目啊1",
+ "description": "二十多",
+ "is_public": true,
+ "owner": {
+ "id": 52,
+ "type": "Organization",
+ "name": "身份卡手动阀",
+ "login": "ceshi1",
+ "image_url": "images/avatars/Organization/52?t=1618805056"
+ }
+ },
+ "user": {
+ "id": 6,
+ "type": "User",
+ "name": "yystopf",
+ "login": "yystopf",
+ "image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png"
+ },
+ "owner": {
+ "id": 52,
+ "type": "Organization",
+ "name": "身份卡手动阀",
+ "login": "ceshi1",
+ "image_url": "images/avatars/Organization/52?t=1618805056"
+ },
+ "id": 1,
+ "status": "canceled",
+ "created_at": "2021-04-25 18:06",
+ "time_ago": "16小时前"
+}
+```
+
+## 用户拒绝迁移
+用户拒绝迁移
+
+> 示例:
+
+```shell
+curl -X POST http://localhost:3000/api/users/yystopf/applied_transfer_projects/2/refuse.json
+```
+
+```javascript
+await octokit.request('GET /api/users/:login/applied_transfer_projects/:id/refuse.json')
+```
+
+### HTTP 请求
+`GET /api/users/:login/applied_transfer_projects/:id/refuse.json`
+
+### 请求字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|login |string |用户标识 |
+|id |int |迁移id |
+
+### 返回字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|id |int |迁移id |
+|status |string |迁移状态,canceled:取消,common:正在迁移, accept:已接受,refuse:已拒绝|
+|time_ago |string |迁移创建的时间 |
+|project.id |int |迁移项目的id |
+|project.identifier |string |迁移项目的标识 |
+|project.name |string |迁移项目的名称 |
+|project.description |string |迁移项目的描述 |
+|project.is_public |bool |迁移项目是否公开 |
+|project.owner.id |bool |迁移项目拥有者id |
+|project.owner.type |string |迁移项目拥有者类型 |
+|project.owner.name |string |迁移项目拥有者昵称 |
+|project.owner.login |string |迁移项目拥有者标识 |
+|project.owner.image_url |string |迁移项目拥有者头像 |
+|user.id |int |迁移创建者的id |
+|user.type |string |迁移创建者的类型 |
+|user.name |string |迁移创建者的名称 |
+|user.login |string |迁移创建者的标识 |
+|user.image_url |string |迁移创建者头像 |
+|owner.id |int |迁移接受者的id |
+|owner.type |string |迁移接受者的类型 |
+|owner.name |string |迁移接受者的名称 |
+|owner.login |string |迁移接受者的标识 |
+|owner.image_url |string |迁移接受者头像 |
+
+
+> 返回的JSON示例:
+
+```json
+{
+ "project": {
+ "id": 86,
+ "identifier": "ceshi_repo1",
+ "name": "测试项目啊1",
+ "description": "二十多",
+ "is_public": true,
+ "owner": {
+ "id": 52,
+ "type": "Organization",
+ "name": "身份卡手动阀",
+ "login": "ceshi1",
+ "image_url": "images/avatars/Organization/52?t=1618805056"
+ }
+ },
+ "user": {
+ "id": 6,
+ "type": "User",
+ "name": "yystopf",
+ "login": "yystopf",
+ "image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png"
+ },
+ "owner": {
+ "id": 52,
+ "type": "Organization",
+ "name": "身份卡手动阀",
+ "login": "ceshi1",
+ "image_url": "images/avatars/Organization/52?t=1618805056"
+ },
+ "id": 1,
+ "status": "canceled",
+ "created_at": "2021-04-25 18:06",
+ "time_ago": "16小时前"
+}
+```
+
+
+## 待办事项-项目申请
+待办事项-项目申请
+
+> 示例:
+
+```shell
+curl -X GET http://localhost:3000/api/users/yystopf/applied_projects.json
+```
+
+```javascript
+await octokit.request('GET /api/users/:login/applied_projects.json')
+```
+
+### HTTP 请求
+`GET /api/users/:login/applied_projects.json`
+
+### 请求字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|login |string |用户标识 |
+
+### 返回字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|id |int |申请id |
+|status |string |申请状态,canceled:取消,common:正在申请, accept:已接受,refuse:已拒绝|
+|time_ago |string |申请创建的时间 |
+|project.id |int |申请项目的id |
+|project.identifier |string |申请项目的标识 |
+|project.name |string |申请项目的名称 |
+|project.description |string |申请项目的描述 |
+|project.is_public |bool |申请项目是否公开 |
+|project.owner.id |bool |申请项目拥有者id |
+|project.owner.type |string |申请项目拥有者类型 |
+|project.owner.name |string |申请项目拥有者昵称 |
+|project.owner.login |string |申请项目拥有者标识 |
+|project.owner.image_url |string |申请项目拥有者头像 |
+|user.id |int |申请创建者的id |
+|user.type |string |申请创建者的类型 |
+|user.name |string |申请创建者的名称 |
+|user.login |string |申请创建者的标识 |
+|user.image_url |string |申请创建者头像 |
+
+
+> 返回的JSON示例:
+
+```json
+{
+ "total_count": 4,
+ "applied_transfer_projects": [
+ {
+ "project": {
+ "id": 74,
+ "identifier": "hehuisssjssjjsjs",
+ "name": "hehuisssjssjjsjs",
+ "description": "wwww",
+ "is_public": false,
+ "owner": {
+ "id": 10,
+ "type": "User",
+ "name": "testforge1",
+ "login": "testforge1",
+ "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png"
+ }
+ },
+ "user": {
+ "id": 6,
+ "type": "User",
+ "name": "何慧",
+ "login": "yystopf",
+ "image_url": "images/avatars/User/6?t=1622513134"
+ },
+ "id": 7,
+ "status": "common",
+ "created_at": "2021-06-09 16:41",
+ "time_ago": "7分钟前"
+ },
+ ...
+ ]
+}
+```
+
+## 用户接受申请
+用户接受申请
+
+> 示例:
+
+```shell
+curl -X POST http://localhost:3000/api/users/yystopf/applied_projects/2/accept.json
+```
+
+```javascript
+await octokit.request('GET /api/users/:login/applied_projects/:id/accept.json')
+```
+
+### HTTP 请求
+`GET /api/users/:login/applied_projects/:id/accept.json`
+
+### 请求字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|login |string |用户标识 |
+|id |int |申请id |
+
+### 返回字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|id |int |申请id |
+|status |string |申请状态,canceled:取消,common:正在申请, accept:已接受,refuse:已拒绝|
+|time_ago |string |申请创建的时间 |
+|project.id |int |申请项目的id |
+|project.identifier |string |申请项目的标识 |
+|project.name |string |申请项目的名称 |
+|project.description |string |申请项目的描述 |
+|project.is_public |bool |申请项目是否公开 |
+|project.owner.id |bool |申请项目拥有者id |
+|project.owner.type |string |申请项目拥有者类型 |
+|project.owner.name |string |申请项目拥有者昵称 |
+|project.owner.login |string |申请项目拥有者标识 |
+|project.owner.image_url |string |申请项目拥有者头像 |
+|user.id |int |申请创建者的id |
+|user.type |string |申请创建者的类型 |
+|user.name |string |申请创建者的名称 |
+|user.login |string |申请创建者的标识 |
+|user.image_url |string |申请创建者头像 |
+
+
+> 返回的JSON示例:
+
+```json
+{
+ "project": {
+ "id": 74,
+ "identifier": "hehuisssjssjjsjs",
+ "name": "hehuisssjssjjsjs",
+ "description": "wwww",
+ "is_public": false,
+ "owner": {
+ "id": 10,
+ "type": "User",
+ "name": "testforge1",
+ "login": "testforge1",
+ "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png"
+ }
+ },
+ "user": {
+ "id": 6,
+ "type": "User",
+ "name": "何慧",
+ "login": "yystopf",
+ "image_url": "images/avatars/User/6?t=1622513134"
+ },
+ "id": 7,
+ "status": "accept",
+ "created_at": "2021-06-09 16:41",
+ "time_ago": "7分钟前"
+}
+```
+
+## 用户拒绝申请
+用户拒绝申请
+
+> 示例:
+
+```shell
+curl -X POST http://localhost:3000/api/users/yystopf/applied_projects/2/refuse.json
+```
+
+```javascript
+await octokit.request('GET /api/users/:login/applied_projects/:id/refuse.json')
+```
+
+### HTTP 请求
+`GET /api/users/:login/applied_projects/:id/refuse.json`
+
+### 请求字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|login |string |用户标识 |
+|id |int |申请id |
+
+### 返回字段说明:
+参数 | 类型 | 字段说明
+--------- | ----------- | -----------
+|id |int |申请id |
+|status |string |申请状态,canceled:取消,common:正在申请, accept:已接受,refuse:已拒绝|
+|time_ago |string |申请创建的时间 |
+|project.id |int |申请项目的id |
+|project.identifier |string |申请项目的标识 |
+|project.name |string |申请项目的名称 |
+|project.description |string |申请项目的描述 |
+|project.is_public |bool |申请项目是否公开 |
+|project.owner.id |bool |申请项目拥有者id |
+|project.owner.type |string |申请项目拥有者类型 |
+|project.owner.name |string |申请项目拥有者昵称 |
+|project.owner.login |string |申请项目拥有者标识 |
+|project.owner.image_url |string |申请项目拥有者头像 |
+|user.id |int |申请创建者的id |
+|user.type |string |申请创建者的类型 |
+|user.name |string |申请创建者的名称 |
+|user.login |string |申请创建者的标识 |
+|user.image_url |string |申请创建者头像 |
+
+> 返回的JSON示例:
+
+```json
+{
+ "project": {
+ "id": 74,
+ "identifier": "hehuisssjssjjsjs",
+ "name": "hehuisssjssjjsjs",
+ "description": "wwww",
+ "is_public": false,
+ "owner": {
+ "id": 10,
+ "type": "User",
+ "name": "testforge1",
+ "login": "testforge1",
+ "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png"
+ }
+ },
+ "user": {
+ "id": 6,
+ "type": "User",
+ "name": "何慧",
+ "login": "yystopf",
+ "image_url": "images/avatars/User/6?t=1622513134"
+ },
+ "id": 7,
+ "status": "accept",
+ "created_at": "2021-06-09 16:41",
+ "time_ago": "7分钟前"
+}
+```
\ No newline at end of file
diff --git a/app/docs/slate/source/layouts/layout.erb b/app/docs/slate/source/layouts/layout.erb
index c125d66a9..2f89bd2db 100644
--- a/app/docs/slate/source/layouts/layout.erb
+++ b/app/docs/slate/source/layouts/layout.erb
@@ -63,7 +63,7 @@ under the License.
- <%= image_tag "logo.png", class: 'logo' %>
+ <%= image_tag "logo.png", class: 'logo ' %>
<% if language_tabs.any? %>
<% language_tabs.each do |lang| %>
diff --git a/app/docs/slate/source/stylesheets/screen.css.scss b/app/docs/slate/source/stylesheets/screen.css.scss
index 10fd3ef5e..3e408b893 100644
--- a/app/docs/slate/source/stylesheets/screen.css.scss
+++ b/app/docs/slate/source/stylesheets/screen.css.scss
@@ -77,6 +77,7 @@ html, body {
margin: 15px auto;
max-width: 100%;
margin-bottom: $logo-margin;
+ max-width: 80%;
}
&>.search {
diff --git a/app/forms/add_school_apply_form.rb b/app/forms/add_school_apply_form.rb
deleted file mode 100644
index f98e8b05b..000000000
--- a/app/forms/add_school_apply_form.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-class AddSchoolApplyForm
- include ActiveModel::Model
-
- attr_accessor :name, :province, :city, :address, :remarks
-
- validates :name, presence: true
- # validates :province, presence: true
- # validates :city, presence: true
- # validates :address, presence: true
-end
\ No newline at end of file
diff --git a/app/forms/apply_shixun_mirror_form.rb b/app/forms/apply_shixun_mirror_form.rb
deleted file mode 100644
index 4f6b738e2..000000000
--- a/app/forms/apply_shixun_mirror_form.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-class ApplyShixunMirrorForm
- include ActiveModel::Model
-
- attr_accessor :language, :runtime, :run_method, :attachment_id
-
- validates :language, presence: true
- validates :runtime, presence: true
- validates :run_method, presence: true
- validates :attachment_id, presence: true, numericality: { only_integer: true }
-
- validate :ensure_attachment_presence
- def ensure_attachment_presence
- return unless attachment_id
-
- if attachment.blank?
- errors.add(:attachment_id, :attachment_not_exist)
- end
- end
-
- def attachment
- @attachment ||= Attachment.find_by_id(attachment_id)
- end
-
- def to_json
- { language: language, runtime: runtime, run_method: run_method, attachment_id: attachment_id }.to_json
- end
-end
\ No newline at end of file
diff --git a/app/forms/base_form.rb b/app/forms/base_form.rb
index 015332d82..be1c1e1a1 100644
--- a/app/forms/base_form.rb
+++ b/app/forms/base_form.rb
@@ -2,18 +2,52 @@ class BaseForm
include ActiveModel::Model
def check_project_category(project_category_id)
- raise "project_category_id参数值无效." if (ProjectCategory.find_by_id project_category_id).blank?
+ unless project_category_id == ''
+ raise "project_category_id参数值无效." if project_category_id && !ProjectCategory.exists?(project_category_id)
+ end
end
def check_project_language(project_language_id)
- raise "project_language_id参数值无效." if (ProjectLanguage.find_by_id project_language_id).blank?
+ unless project_language_id == ''
+ raise "project_language_id参数值无效." if project_language_id && !ProjectLanguage.exists?(project_language_id)
+ end
end
def check_repository_name(user_id, repository_name)
- raise "仓库名称已被使用." if Repository.where(user_id: user_id, identifier: repository_name.strip).exists?
+ check_reversed_keyword(repository_name)
+ raise "项目标识已被使用." if Repository.where(user_id: user_id, identifier: repository_name.strip).exists?
end
def check_project_name(user_id, project_name)
raise "项目名称已被使用." if Project.where(user_id: user_id, name: project_name.strip).exists?
end
+
+ def check_reversed_keyword(repository_name)
+ raise "项目标识已被占用." if ReversedKeyword.check_exists?(repository_name)
+ end
+
+ def check_password(password)
+ password = strip(password)
+ raise PasswordFormatError, "密码8~16位密码,支持字母数字和符号" unless password =~ CustomRegexp::PASSWORD
+ end
+
+ def check_password_confirmation(password, password_confirmation)
+ password = strip(password)
+ password_confirmation = strip(password_confirmation)
+
+ raise PasswordFormatError, "确认密码为8~16位密码,支持字母数字和符号" unless password_confirmation =~ CustomRegexp::PASSWORD
+ raise PasswordConfirmationError, "两次输入的密码不一致" unless password == password_confirmation
+ end
+
+ def check_verifi_code(verifi_code, code)
+ code = strip(code)
+ # return if code == "123123" # TODO 万能验证码,用于测试
+ raise VerifiCodeError, "验证码已失效" if !verifi_code&.effective?
+ raise VerifiCodeError, "验证码不正确" if verifi_code&.code != code
+ end
+
+ private
+ def strip(str)
+ str.to_s.strip.presence
+ end
end
diff --git a/app/forms/examination_banks/save_exam_form.rb b/app/forms/examination_banks/save_exam_form.rb
deleted file mode 100644
index d066300be..000000000
--- a/app/forms/examination_banks/save_exam_form.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-class ExaminationBanks::SaveExamForm
- include ActiveModel::Model
-
- attr_accessor :discipline_id, :sub_discipline_id, :difficulty, :name, :duration, :tag_discipline_id
-
- validates :discipline_id, presence: true
- validates :sub_discipline_id, presence: true
- validates :difficulty, presence: true, inclusion: {in: 1..3}, numericality: { only_integer: true }
- validates :name, presence: true, length: { maximum: 60, too_long: "不能超过60个字符" }
- validate :validate_duration
-
- def validate_duration
- raise '时长应为大于0的整数' if duration.present? && duration.to_i < 1
- end
-end
\ No newline at end of file
diff --git a/app/forms/examination_intelligent_settings/save_exam_form.rb b/app/forms/examination_intelligent_settings/save_exam_form.rb
deleted file mode 100644
index ec4f17ad7..000000000
--- a/app/forms/examination_intelligent_settings/save_exam_form.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-class ExaminationIntelligentSettings::SaveExamForm
- include ActiveModel::Model
-
- attr_accessor :name, :duration
-
- validates :name, presence: true, length: { maximum: 60 }
- validate :validate_duration
-
- def validate_duration
- raise '时长应为大于0的整数' if duration.present? && duration.to_i < 1
- end
-end
\ No newline at end of file
diff --git a/app/forms/examination_intelligent_settings/save_exam_setting_form.rb b/app/forms/examination_intelligent_settings/save_exam_setting_form.rb
deleted file mode 100644
index bbfb9eee8..000000000
--- a/app/forms/examination_intelligent_settings/save_exam_setting_form.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-class ExaminationIntelligentSettings::SaveExamSettingForm
- include ActiveModel::Model
-
- attr_accessor :discipline_id, :sub_discipline_id, :source, :difficulty, :tag_discipline_id, :question_settings
-
- validates :discipline_id, presence: true
- validates :sub_discipline_id, presence: true
- validates :source, presence: true
- validates :difficulty, presence: true, inclusion: {in: 1..3}, numericality: { only_integer: true }
- validates :question_settings, presence: true
-end
\ No newline at end of file
diff --git a/app/forms/issues/create_form.rb b/app/forms/issues/create_form.rb
new file mode 100644
index 000000000..602775ff4
--- /dev/null
+++ b/app/forms/issues/create_form.rb
@@ -0,0 +1,11 @@
+class Issues::CreateForm
+ include ActiveModel::Model
+
+ attr_accessor :subject
+
+ validates :subject, presence: { message: "不能为空" }
+
+ validates :subject, length: { maximum: 200, too_long: "不能超过200个字符" }
+
+
+end
diff --git a/app/forms/issues/update_form.rb b/app/forms/issues/update_form.rb
new file mode 100644
index 000000000..64acdfb5c
--- /dev/null
+++ b/app/forms/issues/update_form.rb
@@ -0,0 +1,10 @@
+class Issues::UpdateForm
+ include ActiveModel::Model
+
+ attr_accessor :subject
+
+ validates :subject, presence: { message: "不能为空" }
+
+ validates :subject, length: { maximum: 200, too_long: "不能超过200个字符" }
+
+end
\ No newline at end of file
diff --git a/app/forms/notice/write/create_atme_form.rb b/app/forms/notice/write/create_atme_form.rb
new file mode 100644
index 000000000..5ac3acc25
--- /dev/null
+++ b/app/forms/notice/write/create_atme_form.rb
@@ -0,0 +1,23 @@
+class Notice::Write::CreateAtmeForm
+ include ActiveModel::Model
+
+ attr_accessor :receivers_login, :atmeable_type, :atmeable_id
+
+ validate :check_receivers
+
+ def check_receivers
+ receivers_login.each do |login|
+ receiver = User.find_by(login: login) || User.find_by_id(login)
+ raise 'receivers_login值无效.' unless receiver.present?
+ end
+ end
+
+ def check_atmeable
+ begin
+ raise 'atmeable对象无效.' unless atmeable_type.constantize.find_by_id(atmeable_id).present?
+ rescue => exception
+ raise 'atmeable对象无效.'
+ end
+ end
+
+end
\ No newline at end of file
diff --git a/app/forms/organizations/create_form.rb b/app/forms/organizations/create_form.rb
new file mode 100644
index 000000000..2163f477e
--- /dev/null
+++ b/app/forms/organizations/create_form.rb
@@ -0,0 +1,18 @@
+class Organizations::CreateForm < BaseForm
+ NAME_REGEX = /^(?!_)(?!.*?_$)[a-zA-Z0-9_-]+$/ #只含有数字、字母、下划线不能以下划线开头和结尾
+ attr_accessor :name, :description, :website, :location, :repo_admin_change_team_access, :visibility, :max_repo_creation, :nickname, :original_name
+
+ validates :name, :nickname, :visibility, presence: true
+ validates :name, :nickname, length: { maximum: 100 }
+ validates :location, length: { maximum: 50 }
+ validates :description, length: { maximum: 200 }
+ validates :name, format: { with: NAME_REGEX, multiline: true, message: "只能含有数字、字母、下划线且不能以下划线开头和结尾" }
+
+ validate do
+ check_name(name) unless name.blank? || name == original_name
+ end
+
+ def check_name(name)
+ raise "组织账号已被使用." if Owner.where(login: name.strip).exists?
+ end
+end
diff --git a/app/forms/organizations/create_team_form.rb b/app/forms/organizations/create_team_form.rb
new file mode 100644
index 000000000..6d816b0ed
--- /dev/null
+++ b/app/forms/organizations/create_team_form.rb
@@ -0,0 +1,10 @@
+class Organizations::CreateTeamForm < BaseForm
+ NAME_REGEX = /^(?!_)(?!.*?_$)[a-zA-Z0-9_-]+$/ #只含有数字、字母、下划线不能以下划线开头和结尾
+ attr_accessor :name, :nickname, :description, :authorize, :includes_all_project, :can_create_org_project, :unit_types
+
+ validates :name, :nickname, presence: true
+ validates :name, :nickname, length: { maximum: 100 }
+ validates :description, length: { maximum: 200 }
+ validates :name, format: { with: NAME_REGEX, multiline: true, message: "只能含有数字、字母、下划线且不能以下划线开头和结尾" }
+
+end
diff --git a/app/forms/projects/create_form.rb b/app/forms/projects/create_form.rb
index 2838996b6..c51c2c60f 100644
--- a/app/forms/projects/create_form.rb
+++ b/app/forms/projects/create_form.rb
@@ -1,16 +1,20 @@
class Projects::CreateForm < BaseForm
- REPOSITORY_NAME_REGEX = /^(?!_)(?!.*?_$)[a-zA-Z0-9_-]+$/ #只含有数字、字母、下划线不能以下划线开头和结尾
attr_accessor :user_id, :name, :description, :repository_name, :project_category_id,
:project_language_id, :ignore_id, :license_id, :private, :owner
+
+ validates :user_id, :name, :repository_name, presence: true
+ validates :repository_name, format: { with: CustomRegexp::REPOSITORY_NAME_REGEX, multiline: true, message: "只能含有数字、字母、下划线且不能以下划线开头和结尾" }
- validates :user_id, :name, :description,:repository_name,
- :project_category_id, :project_language_id, presence: true
- validates :repository_name, format: { with: REPOSITORY_NAME_REGEX, multiline: true, message: "只能含有数字、字母、下划线且不能以下划线开头和结尾" }
+ validates :name, length: { maximum: 50 }
+ validates :repository_name, length: { maximum: 100 }
+ validates :description, length: { maximum: 200 }
validate :check_ignore, :check_license, :check_owner, :check_max_repo_creation
validate do
check_project_category(project_category_id)
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?
end
def check_license
@@ -22,13 +26,14 @@ class Projects::CreateForm < BaseForm
end
def check_owner
- @owner = Owner.find_by(id: user_id)
- raise "user_id值无效." if user_id && owner.blank?
+ @project_owner = Owner.find_by(id: user_id)
+ raise "user_id值无效." if user_id && @project_owner.blank?
end
def check_max_repo_creation
- return unless owner.is_a?(Organization)
- return if owner.max_repo_creation <= -1
- raise "已超过组织设置最大仓库数" if owner.max_repo_creation == owner.num_projects
+ return unless @project_owner.is_a?(Organization)
+ return if @project_owner.max_repo_creation <= -1
+
+ raise "已超过组织设置最大仓库数" if @project_owner.max_repo_creation == @project_owner.num_projects
end
end
diff --git a/app/forms/projects/migrate_form.rb b/app/forms/projects/migrate_form.rb
index 51116b2b5..ccd854478 100644
--- a/app/forms/projects/migrate_form.rb
+++ b/app/forms/projects/migrate_form.rb
@@ -1,12 +1,13 @@
class Projects::MigrateForm < BaseForm
- REPOSITORY_NAME_REGEX = /^(?!_)(?!.*?_$)[a-zA-Z0-9_-]+$/ #只含有数字、字母、下划线不能以下划线开头和结尾
- URL_REGEX = /\A(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?\z/i
+ attr_accessor :user_id, :name, :repository_name, :project_category_id, :description,
+ :project_language_id, :clone_addr, :private, :is_mirror, :auth_username, :auth_password, :owner
- attr_accessor :user_id, :name, :description, :repository_name, :project_category_id, :project_language_id, :clone_addr, :private, :is_mirror, :auth_username, :auth_password, :owner
-
- validates :user_id, :name, :description,:repository_name, :project_category_id, :project_language_id, presence: true
- validates :repository_name, format: { with: REPOSITORY_NAME_REGEX, multiline: true, message: "只能含有数字、字母、下划线且不能以下划线开头和结尾" }
- validates :clone_addr, format: { with: URL_REGEX, multiline: true, message: "地址格式不正确" }
+ validates :user_id, :name, :repository_name, :clone_addr, presence: true
+ validates :repository_name, format: { with: CustomRegexp::REPOSITORY_NAME_REGEX, multiline: true, message: "只能含有数字、字母、下划线且不能以下划线开头和结尾" }
+ validates :clone_addr, format: { with: CustomRegexp::URL_REGEX, multiline: true, message: "地址格式不正确" }
+ validates :name, length: { maximum: 50 }
+ validates :repository_name, length: { maximum: 100 }
+ validates :description, length: { maximum: 200 }
validate do
check_project_name(user_id, name) unless name.blank?
check_repository_name(user_id, repository_name) unless repository_name.blank?
diff --git a/app/forms/projects/update_form.rb b/app/forms/projects/update_form.rb
index 4696beaa3..3048bc079 100644
--- a/app/forms/projects/update_form.rb
+++ b/app/forms/projects/update_form.rb
@@ -1,4 +1,15 @@
class Projects::UpdateForm < BaseForm
- attr_reader :name, :description, :repository_name, :project_category_id
+ attr_accessor :name, :description, :project_category_id, :project_language_id, :private, :identifier, :user_id, :project_identifier
+ validates :name, presence: true
+ validates :name, length: { maximum: 50 }
+ validates :description, length: { maximum: 200 }
+ validates :identifier, format: { with: CustomRegexp::REPOSITORY_NAME_REGEX, multiline: true, message: "只能含有数字、字母、下划线且不能以下划线开头和结尾" }
+
+ validate do
+ check_project_category(project_category_id)
+ check_project_language(project_language_id)
+
+ check_repository_name(user_id, identifier) unless identifier.blank? || identifier == project_identifier
+ end
end
diff --git a/app/forms/projects/webhooks/create_form.rb b/app/forms/projects/webhooks/create_form.rb
new file mode 100644
index 000000000..75b07b1bd
--- /dev/null
+++ b/app/forms/projects/webhooks/create_form.rb
@@ -0,0 +1,8 @@
+class Projects::Webhooks::CreateForm < BaseForm
+ attr_accessor :type, :url, :http_method, :content_type, :secret, :events, :active, :branch_filter
+
+ validates :url, format: { with: URI::regexp(%w[http https]), message: "请输入正确的地址" }
+ validates :active, inclusion: {in: [true, false]}
+ validates :http_method, inclusion: { in: %w(POST GET), message: "请输入正确的请求方式"}
+ validates :content_type, inclusion: { in: %w(json form), message: "请输入正确的Content Type"}
+end
\ No newline at end of file
diff --git a/app/forms/register/base_form.rb b/app/forms/register/base_form.rb
new file mode 100644
index 000000000..df5abd871
--- /dev/null
+++ b/app/forms/register/base_form.rb
@@ -0,0 +1,51 @@
+module Register
+ class BaseForm < ::BaseForm
+ include ActiveModel::Model
+
+ Error = Class.new(StandardError)
+ EmailError = Class.new(Error)
+ LoginError = Class.new(Error)
+ PhoneError = Class.new(Error)
+ PasswordFormatError = Class.new(Error)
+ VerifiCodeError = Class.new(Error)
+
+ private
+ def check_login(login)
+ login = strip(login)
+ raise LoginError, "登录名格式有误" unless login =~ CustomRegexp::LOGIN
+
+ login_exist = Owner.exists?(login: login) || ReversedKeyword.check_exists?(login)
+ raise LoginError, '登录名已被使用' if login_exist
+ end
+
+ def check_mail(mail)
+ mail = strip(mail)
+ raise EmailError, "邮件格式有误" unless mail =~ CustomRegexp::EMAIL
+
+ mail_exist = Owner.exists?(mail: mail)
+ raise EmailError, '邮箱已被使用' if mail_exist
+ end
+
+ def check_phone(phone)
+ phone = strip(phone)
+ raise PhoneError, "手机号格式有误" unless phone =~ CustomRegexp::PHONE
+
+ phone_exist = Owner.exists?(phone: phone)
+ raise PhoneError, '手机号已被使用' if phone_exist
+ end
+
+ def check_password(password)
+ password = strip(password)
+ raise PasswordFormatError, "8~16位密码,支持字母数字和符号" unless password =~ CustomRegexp::PASSWORD
+ end
+
+ def check_verifi_code(verifi_code, code)
+ code = strip(code)
+ # return if code == "123123" # TODO 万能验证码,用于测试
+
+ raise VerifiCodeError, "验证码不正确" if verifi_code&.code != code
+ raise VerifiCodeError, "验证码已失效" if !verifi_code&.effective?
+ end
+
+ end
+end
diff --git a/app/forms/register/check_columns_form.rb b/app/forms/register/check_columns_form.rb
new file mode 100644
index 000000000..20c5b1e89
--- /dev/null
+++ b/app/forms/register/check_columns_form.rb
@@ -0,0 +1,19 @@
+module Register
+ class CheckColumnsForm < Register::BaseForm
+ attr_accessor :type, :value
+
+ validates :type, presence: true, numericality: true
+ validates :value, presence: true
+ validate :check!
+
+ def check!
+ # params[:type] 为事件类型 1:登录名(login) 2:email(邮箱) 3:phone(手机号)
+ case strip(type).to_i
+ when 1 then check_login(strip(value))
+ when 2 then check_mail(strip(value))
+ when 3 then check_phone(strip(value))
+ else raise("type值无效")
+ end
+ end
+ end
+end
diff --git a/app/forms/register/form.rb b/app/forms/register/form.rb
new file mode 100644
index 000000000..bad7a23e6
--- /dev/null
+++ b/app/forms/register/form.rb
@@ -0,0 +1,27 @@
+module Register
+ class Form < Register::BaseForm
+ # login 登陆方式,支持邮箱、登陆、手机号等
+ # namespace 用户空间地址
+ # type: 1:手机号注册;2:邮箱注册
+ attr_accessor :login, :namespace, :password, :code, :type
+
+ validates :login, :code, :password, :namespace, presence: true
+ validate :check!
+
+ def check!
+ Rails.logger.info "Register::Form params: code: #{code}; login: #{login}; namespace: #{namespace}; password: #{password}; type: #{type}"
+ db_verifi_code =
+ if type == 1
+ check_phone(login)
+ VerificationCode.where(phone: login, code: code, code_type: 1).last
+ elsif type == 0
+ check_mail(login)
+ VerificationCode.where(email: login, code: code, code_type: 8).last
+ end
+
+ check_login(namespace)
+ check_verifi_code(db_verifi_code, code)
+ check_password(password)
+ end
+ end
+end
diff --git a/app/forms/register/remote_form.rb b/app/forms/register/remote_form.rb
new file mode 100644
index 000000000..59a5fbc82
--- /dev/null
+++ b/app/forms/register/remote_form.rb
@@ -0,0 +1,16 @@
+module Register
+ class RemoteForm < Register::BaseForm
+ # login 登陆方式,支持邮箱、登陆、手机号等
+ attr_accessor :username, :email, :password, :platform
+
+ validates :username, :email, :password, presence: true
+ validate :check!
+
+ def check!
+ Rails.logger.info "Register::RemoteForm params: username: #{username}; email: #{email}; password: #{password}; platform: #{platform}"
+ check_login(username)
+ check_mail(email)
+ check_password(password)
+ end
+ end
+end
diff --git a/app/forms/users/login_form.rb b/app/forms/users/login_form.rb
new file mode 100644
index 000000000..8ae917e0f
--- /dev/null
+++ b/app/forms/users/login_form.rb
@@ -0,0 +1,8 @@
+class Users::LoginForm
+ include ActiveModel::Model
+
+ attr_accessor :password, :login
+
+ validates :login, presence: true
+ validates :password, presence: true, length: { minimum: 6, maximum: 16 }, format: { with: CustomRegexp::LOGIN_PASSWORD, message: "6~16位,支持字母数字和符号" }
+end
\ No newline at end of file
diff --git a/app/forms/users/update_info_form.rb b/app/forms/users/update_info_form.rb
new file mode 100644
index 000000000..088db06d3
--- /dev/null
+++ b/app/forms/users/update_info_form.rb
@@ -0,0 +1,9 @@
+class Users::UpdateInfoForm
+ include ActiveModel::Model
+
+ attr_accessor :email, :password, :login
+
+ validates :email, presence: true, format: { with: CustomRegexp::EMAIL }
+ validates :password, presence: true
+ validates :login, presence: true
+end
diff --git a/app/forms/weapps/create_course_form.rb b/app/forms/weapps/create_course_form.rb
deleted file mode 100644
index 64d0a506f..000000000
--- a/app/forms/weapps/create_course_form.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-class Weapps::CreateCourseForm
- include ActiveModel::Model
-
- attr_accessor :course
- attr_accessor :name, :course_list_name, :credit, :course_module_types, :end_date
-
- validates :name, presence: true
- validates :course_list_name, presence: true
-
- validate :course_name_prefix
- validate :check_course_modules
-
- def course_name_prefix
- raise '课堂名称应以课程名称开头' unless name.index(course_list_name) && name.index(course_list_name) == 0
- end
-
- def check_course_modules
- raise '请至少添加一个课堂模块' if course_module_types.blank?
- end
-end
\ No newline at end of file
diff --git a/app/forms/weapps/update_course_form.rb b/app/forms/weapps/update_course_form.rb
deleted file mode 100644
index 60509dd1d..000000000
--- a/app/forms/weapps/update_course_form.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-class Weapps::UpdateCourseForm
- include ActiveModel::Model
-
- attr_accessor :course
- attr_accessor :name, :course_list_name, :credit, :end_date
-
- validates :name, presence: true
- validates :course_list_name, presence: true
-
- validate :course_name_prefix
-
- def course_name_prefix
- raise '课堂名称应以课程名称开头' unless name.index(course_list_name) && name.index(course_list_name) == 0
- end
-end
\ No newline at end of file
diff --git a/app/helpers/admins/faqs_helper.rb b/app/helpers/admins/faqs_helper.rb
new file mode 100644
index 000000000..0316d44fd
--- /dev/null
+++ b/app/helpers/admins/faqs_helper.rb
@@ -0,0 +1,2 @@
+module Admins::FaqsHelper
+end
diff --git a/app/helpers/admins/projects_helper.rb b/app/helpers/admins/projects_helper.rb
new file mode 100644
index 000000000..36d9d6f5a
--- /dev/null
+++ b/app/helpers/admins/projects_helper.rb
@@ -0,0 +1,14 @@
+module Admins::ProjectsHelper
+
+ def link_to_project(project)
+ owner = project.owner
+
+ if owner.is_a?(User)
+ link_to(project.owner&.real_name, "/#{project&.owner&.login}", target: '_blank')
+ elsif owner.is_a?(Organization)
+ link_to(project.owner&.real_name, "/organize/#{project&.owner&.login}", target: '_blank')
+ else
+ ""
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 3eba473f3..5ad37d73b 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -1,6 +1,6 @@
# 所有的方法请按首字母的顺序依次列出
module ApplicationHelper
- include Educoder::I18n
+ include Gitlink::I18n
include GitHelper
ONE_MINUTE = 60 * 1000
@@ -35,12 +35,6 @@ module ApplicationHelper
course.course_modules.find_by(module_type: "graduation").try(:id)
end
- # 是否关注
- # from_user_id为被关注的用户
- def follow?(from_user_id, user_id)
- Watcher.where(watchable_type: 'Principal', watchable_id: from_user_id, user_id: user_id).exists?
- end
-
# git用户
# git用户命名规则:login+"@educoder.net"
def git_username(email)
@@ -144,13 +138,12 @@ module ApplicationHelper
if File.exist?(disk_filename(source&.class, source&.id))
ctime = File.ctime(disk_filename(source.class, source.id)).to_i
if %w(User Organization).include?(source.class.to_s)
- File.join(relative_path, ["#{source.class}", "#{source.id}"]) + "?t=#{ctime}"
+ File.join("images", relative_path, ["#{source.class}", "#{source.id}"]) + "?t=#{ctime}"
else
File.join("images/avatars", ["#{source.class}", "#{source.id}"]) + "?t=#{ctime}"
end
elsif source.class.to_s == 'User'
- str = source.user_extension.try(:gender).to_i == 0 ? "boy.jpg" : "girl.jpg"
- File.join(relative_path, "#{source.class}", str)
+ source.get_letter_avatar_url
elsif source.class.to_s == 'Subject'
File.join("images","educoder", "index", "subject", "subject#{rand(17)}.jpg")
elsif source.class.to_s == 'Shixun'
@@ -445,8 +438,40 @@ module ApplicationHelper
User.find_by_login login
end
+ def find_user_by_login_and_mail(login, mail)
+ User.find_by(login: login, mail: mail)
+ end
+
+ def find_user_by_gitea_uid(gitea_uid)
+ User.find_by(gitea_uid: gitea_uid)
+ end
+
+ def find_user_in_redis_cache(login, email)
+ $redis_cache.hgetall("v2-owner-common:#{login}-#{email}")
+ end
+
+ def find_user_in_redis_cache_by_id(id)
+ $redis_cache.hgetall("v2-owner-common:#{id}")
+ end
+
def render_base64_decoded(str)
return nil if str.blank?
Base64.decode64 str
end
+
+ def render_admin_statistics_item
+ url = Rails.application.config_for(:configuration)["admin_statistics_url"]
+
+ return if url.blank?
+ content_tag(:li) do
+ sidebar_item(url, "数据统计", icon: 'bar-chart', controller: 'root')
+ end
+ end
+
+ # 1 手机类型;0 邮箱类型
+ # 注意新版的login是自动名生成的
+ def phone_mail_type value
+ value =~ /^1\d{10}$/ ? 1 : 0
+ end
+
end
diff --git a/app/helpers/avatar_helper.rb b/app/helpers/avatar_helper.rb
new file mode 100644
index 000000000..b703e1b4e
--- /dev/null
+++ b/app/helpers/avatar_helper.rb
@@ -0,0 +1,26 @@
+module AvatarHelper
+ def relative_path
+ "avatars"
+ end
+
+ def storage_path
+ File.join(Rails.root, "public", "images", relative_path)
+ end
+
+ def disk_filename(source_type,source_id,image_file=nil)
+ File.join(storage_path, "#{source_type}", "#{source_id}")
+ end
+
+ def url_to_avatar(source)
+ if File.exist?(disk_filename(source&.class, source&.id))
+ ctime = File.ctime(disk_filename(source.class, source.id)).to_i
+ if %w(User Organization).include?(source.class.to_s)
+ File.join("images", relative_path, ["#{source.class}", "#{source.id}"]) + "?t=#{ctime}"
+ else
+ File.join("images/avatars", ["#{source.class}", "#{source.id}"]) + "?t=#{ctime}"
+ end
+ elsif source.class.to_s == 'User'
+ source.get_letter_avatar_url
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/helpers/boards_helper.rb b/app/helpers/boards_helper.rb
deleted file mode 100644
index e66bdaf6b..000000000
--- a/app/helpers/boards_helper.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-module BoardsHelper
-end
diff --git a/app/helpers/challenges_helper.rb b/app/helpers/challenges_helper.rb
deleted file mode 100644
index fc0101dff..000000000
--- a/app/helpers/challenges_helper.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-module ChallengesHelper
-
- def match_begin_symbol str
- str.gsub(/\A\r/, "\r\r")
- end
-
-
-
-
-end
diff --git a/app/helpers/course_groups_helper.rb b/app/helpers/course_groups_helper.rb
deleted file mode 100644
index 061c39dd5..000000000
--- a/app/helpers/course_groups_helper.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-module CourseGroupsHelper
-end
diff --git a/app/helpers/course_modules_helper.rb b/app/helpers/course_modules_helper.rb
deleted file mode 100644
index 4de7a3826..000000000
--- a/app/helpers/course_modules_helper.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-module CourseModulesHelper
-end
diff --git a/app/helpers/course_second_categories_helper.rb b/app/helpers/course_second_categories_helper.rb
deleted file mode 100644
index 7ed9aa1e9..000000000
--- a/app/helpers/course_second_categories_helper.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-module CourseSecondCategoriesHelper
-end
diff --git a/app/helpers/course_stages_helper.rb b/app/helpers/course_stages_helper.rb
deleted file mode 100644
index 7ebb68d9a..000000000
--- a/app/helpers/course_stages_helper.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-module CourseStagesHelper
-end
diff --git a/app/helpers/courses_helper.rb b/app/helpers/courses_helper.rb
deleted file mode 100644
index f0d58adbb..000000000
--- a/app/helpers/courses_helper.rb
+++ /dev/null
@@ -1,298 +0,0 @@
-module CoursesHelper
-
- def member_manager group, teachers
- str = ""
- members = teachers.select{|teacher| teacher.teacher_course_groups.pluck(:course_group_id).include?(group.id) || teacher.teacher_course_groups.size == 0}
- str = members.uniq.size == teachers.size ? "全部教师" : members.map{|member| member.user.real_name}.join("、")
- str
- # teachers.each do |member|
- # if member.teacher_course_groups.exists?(course_group_id: group.id) || member.teacher_course_groups.size == 0
- # str << member.user.real_name
- # end
- # end
- end
-
- def edit_auth group, teachers
- User.current.admin_or_business? ||
- teachers.select{|teacher| teacher.user_id == User.current.id &&
- (teacher.teacher_course_groups.pluck(:course_group_id).include?(group.id) || teacher.teacher_course_groups.size == 0)}.size > 0
- end
-
- # 是否有切换为学生的入口
- def switch_student_role is_teacher, course, user
- is_teacher && course.course_members.where(user_id: user.id, role: %i(STUDENT)).exists?
- end
-
- # 是否有切换为教师的入口
- def switch_teacher_role is_student, course, user
- is_student && course.course_members.where(user_id: user.id, role: %i(CREATOR PROFESSOR)).exists?
- end
-
- # 是否有切换为助教的入口
- def switch_assistant_role is_student, course, user
- is_student && course.course_members.where(user_id: user.id, role: %i(ASSISTANT_PROFESSOR)).exists?
- end
-
- # 课堂结束天数
- def course_end_date end_date
- if end_date.present?
- curr = Time.new
- date = ((Date.parse(end_date.to_s) - Date.parse(curr.to_s)).to_i)
- date > 0 ? "#{date}天后" : ""
- end
- end
-
- # 课堂模块的url
- def module_url mod, course
- return nil if mod.blank? or course.blank?
- case mod.module_type
- when "announcement"
- "/courses/#{course.id}/informs"
- when "online_learning"
- "/courses/#{course.id}/online_learning"
- when "shixun_homework"
- "/courses/#{course.id}/shixun_homeworks/#{mod.id}"
- when "common_homework"
- "/courses/#{course.id}/common_homeworks/#{mod.id}"
- when "group_homework"
- "/courses/#{course.id}/group_homeworks/#{mod.id}"
- when "graduation"
- "/courses/#{course.id}/graduation_topics/#{mod.id}"
- when "exercise"
- "/courses/#{course.id}/exercises/#{mod.id}"
- when "poll"
- "/courses/#{course.id}/polls/#{mod.id}"
- when "attachment"
- "/courses/#{course.id}/files/#{mod.id}"
- when "board"
- course_board = course.course_board
- "/courses/#{course.id}/boards/#{course_board.id}"
- when "course_group"
- "/courses/#{course.id}/course_groups"
- when "statistics"
- "/courses/#{course.id}/statistics"
- when "video"
- "/courses/#{course.id}/course_videos"
- end
- end
-
- # 子目录对应的url
- def category_url category, course
- case category.category_type
- when "shixun_homework"
- "/courses/#{course.id}/shixun_homework/#{category.id}"
- when "graduation"
- if category.name == "毕设选题"
- "/courses/#{course.id}/graduation_topics/#{category.course_module_id}"
- else
- "/courses/#{course.id}/graduation_tasks/#{category.course_module_id}"
- end
- when "attachment"
- "/courses/#{course.id}/file/#{category.id}"
- end
- end
-
- # 子目录下的任务数
- def category_task_count course, category, user
- case category.category_type
- when "shixun_homework"
- get_homework_commons_count(course, 4, category.id)
- when "graduation"
- if category.name == "毕设选题"
- course.graduation_topics_count
- else
- course.graduation_tasks_count
- end
- when "attachment"
- get_attachment_count(course, category.id)
- end
- end
-
- # 课堂模块的任务数
- def course_task_count(course, module_type)
- case module_type
- when "shixun_homework"
- get_homework_commons_count(course, 4, 0)
- when "common_homework"
- get_homework_commons_count(course, 1, 0)
- when "group_homework"
- get_homework_commons_count(course, 3, 0)
- when "graduation"
- 0
- when "exercise"
- course.exercises_count
- when "poll"
- course.polls_count
- when "attachment"
- get_attachment_count(course, 0)
- when "board"
- course_board = course.course_board
- course_board.present? ? course_board.messages.size : 0
- when "course_group"
- course.course_groups_count
- when "announcement"
- course.informs.count
- when "online_learning"
- course.shixuns.count
- when "video"
- course.course_videos.count + course.live_links.count
- end
- end
-
- # 当前用户可见的课堂作业,type指定作业类型, category_id指定二级目录
- def visible_homework course, user, type, category_id=0
- if user.teacher_of_course?(course)
- homeworks = course.homework_commons.where("homework_type = #{type} and course_second_category_id = #{category_id}")
- elsif user.member_of_course?(course)
- member = course.course_members.find_by(user_id: user.id, role: 4)
- if member.try(:course_group_id).to_i == 0
- homeworks = course.homework_commons.where("homework_commons.homework_type = #{type} and publish_time <= '#{Time.now}'
- and unified_setting = 1 and course_second_category_id = #{category_id}")
- else
- not_homework_ids = course.homework_group_settings.where("course_group_id = #{member.try(:course_group_id)} and
- (publish_time > '#{Time.now}' or publish_time is null)").pluck(:homework_common_id)
- # not_homework_ids = not_homework_ids.blank? ? "(-1)" : "(" + not_homework_ids.map(&:homework_common_id).join(",") + ")"
- homeworks = course.homework_commons.where.not(id: not_homework_ids).where("homework_commons.homework_type = #{type} and publish_time <= '#{Time.now}'
- and course_second_category_id = #{category_id}")
- end
- else
- homeworks = course.homework_commons.where("homework_type = #{type} and publish_time <= '#{Time.now}' and unified_setting = 1
- and course_second_category_id = #{category_id}")
- end
- homeworks
- end
-
- # 当前用户可见的课堂试卷
- def visible_exercise course, user
- if user.teacher_of_course?(course)
- exercises = course.exercises
- elsif user.member_of_course?(course)
- member = course.course_members.find_by(user_id: user.id, role: 4)
- if member.try(:course_group_id).to_i == 0
- exercises = course.exercises.where("publish_time <= '#{Time.now}' and unified_setting = 1")
- else
- not_exercise_ids = course.exercise_group_settings.where("course_group_id = #{member.try(:course_group_id)} and
- (publish_time > '#{Time.now}' or publish_time is null)").pluck(:exercise_id)
- exercises = course.exercises.where.not(id: not_exercise_ids).where("publish_time <= '#{Time.now}'")
- end
- else
- exercises = course.exercises.where("publish_time <= '#{Time.now}' and unified_setting = 1")
- end
- exercises
- end
-
- # 当前用户可见的课堂问卷
- def visible_poll course, user
- if user.teacher_of_course?(course)
- polls = course.polls
- elsif user.member_of_course?(course)
- member = course.course_members.find_by(user_id: user.id, role: 4)
- if member.try(:course_group_id).to_i == 0
- polls = course.polls.where("publish_time <= '#{Time.now}' and unified_setting = 1")
- else
- not_poll_ids = course.poll_group_settings.where("course_group_id = #{member.try(:course_group_id)} and
- (publish_time > '#{Time.now}' or publish_time is null)").pluck(:poll_id)
- polls = course.polls.where.not(id: not_poll_ids).where("publish_time <= '#{Time.now}'")
- end
- else
- polls = course.polls.where("publish_time <= '#{Time.now}' and unified_setting = 1")
- end
- polls
- end
-
- # 当前用户可见的课堂资源,category_id指定资源的目录
- def visible_attachment course, user, category_id=0
- result = []
- course.attachments.where(course_second_category_id: category_id).each do |attachment|
- if attachment.unified_setting
- if attachment.is_public == 1 && attachment.is_publish == 1 || user == attachment.author || user.teacher_of_course?(course) || (user.member_of_course?(course) && attachment.is_publish == 1)
- result << attachment
- end
- else
- if attachment.is_public == 1 && attachment.is_publish == 1 && !user.member_of_course?(course) || user == attachment.author || user.teacher_of_course?(course)
- result << attachment
- elsif user.member_of_course?(course) && attachment.is_publish == 1
- member = course.course_members.find_by(user_id: user.id, role: 4)
- if member.try(:course_group_id).to_i == 0 && attachment.unified_setting
- result << attachment
- elsif attachment.attachment_group_settings.where("course_group_id = #{member.try(:course_group_id)} and publish_time > '#{Time.now}'").count == 0
- result << attachment
- end
- end
- end
- end
- result
- end
-
- # 获取课堂的资源数
- def get_attachment_count(course, category_id)
- category_id.to_i == 0 ? course.attachments.size : course.attachments.where(course_second_category_id: category_id).size
- end
-
- # 获取课堂的作业数
- def get_homework_commons_count(course, type, category_id)
- category_id == 0 ? HomeworkCommon.where(course_id: course.id, homework_type: type).size :
- HomeworkCommon.where(course_id: course.id, homework_type: type, course_second_category_id: category_id).size
- end
-
-
- # 获取课堂的任务数(作业数+试卷数+问卷数)
- def get_tasks_count(course)
- course.homework_commons_count + course.exercises_count + course.polls_count
- end
-
- # 当前用户可见的毕设任务
- def visible_graduation_task course, user
- if user.teacher_of_course?(course)
- tasks = course.graduation_tasks
- else
- tasks = course.graduation_tasks.where("publish_time <= '#{Time.now}'")
- end
- tasks
- end
-
- # 分班情况
- def course_group_info course, user_id
- course_group_ids = course.group_course_power(user_id)
- course_groups =
- if course_group_ids.present?
- course.course_groups.where(id: course_group_ids).includes(:course_members)
- else
- course.course_groups.includes(:course_members)
- end
- group_info = []
- if !course_groups.blank?
- course_groups.each do |group|
- group_info << {course_group_id: group.id, group_group_name: group.name, count: group.course_members_count}
- end
-
- none_group_count = course.students.where(course_group_id: 0).size
- group_info << {course_group_id: 0, group_group_name: "未分班", count: none_group_count} if none_group_count > 0 && !course_group_ids.present?
- end
-
- return group_info
- end
-
- def left_group_info course
- group_info = []
- if course.course_groups_count > 0
- none_group_count = course.students.where(course_group_id: 0).size
- group_info << {category_id: 0, category_name: "未分班", position: course.course_groups.pluck(:position).max.to_i + 1,
- category_count: none_group_count, category_type: false,
- second_category_url: "/courses/#{@course.id}/course_groups/0"}
- course.course_groups.each do |course_group|
- group_info << {category_id: course_group.id, category_name: course_group.name, position: course_group.position,
- category_count: course_group.course_members_count, category_type: false,
- second_category_url: "/courses/#{@course.id}/course_groups/#{course_group.id}"}
- end
- end
- group_info
- end
-
- def last_subject_shixun course, myshixuns
- myshixun = myshixuns.sort{|x,y| y[:updated_at] <=> x[:updated_at] }.first
- return "" unless myshixun
- stage_shixun = course.course_stage_shixuns.where(shixun_id: myshixun.shixun_id).take
- progress = stage_shixun&.course_stage&.position.to_s + "-" + stage_shixun&.position.to_s + " " + myshixun.shixun&.name
- end
-end
diff --git a/app/helpers/discusses_helper.rb b/app/helpers/discusses_helper.rb
deleted file mode 100644
index c686fcada..000000000
--- a/app/helpers/discusses_helper.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-module DiscussesHelper
-end
diff --git a/app/helpers/edu_datas_helper.rb b/app/helpers/edu_datas_helper.rb
deleted file mode 100644
index 1a8584d15..000000000
--- a/app/helpers/edu_datas_helper.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-module EduDatasHelper
-end
diff --git a/app/helpers/graduation_tasks_helper.rb b/app/helpers/graduation_tasks_helper.rb
deleted file mode 100644
index 2bc059a55..000000000
--- a/app/helpers/graduation_tasks_helper.rb
+++ /dev/null
@@ -1,150 +0,0 @@
-module GraduationTasksHelper
- include CoursesHelper
- # 教师评阅
- def teacher_comment task, user_id
- [{ id: 0 ,name: "未评", count: task.uncomment_count(user_id)}, {id: 1, name: "已评", count: task.comment_count(user_id)}]
- end
-
- # 作品状态
- def task_status task, user_id
- [{id: 0, name: "未提交", count: task.unfinished_count(user_id)},
- {id: 1, name: "按时提交", count: task.finished_count(user_id)},
- {id: 2, name: "延时提交", count: task.delay_finished_count(user_id)}]
- end
-
- # 交叉评阅
- def cross_comment task, user_id
- if task.cross_comment && task.status >= 3
- [{id: 1, name: "只看我的交叉评阅", count: task.graduation_work_comment_assignations.myself(user_id).count}]
- else
- []
- end
- end
-
- def task_curr_status task, course
- result = {}
- status = []
- time = ""
-
- if course.try(:is_end)
- status << "已结束"
- time = course.end_date.present? ? course.end_date.strftime("%Y-%m-%d") : ""
- else
- if task.status > 1 && task.allow_late && (task.late_time.nil? || task.late_time > Time.now)
- status << "补交中"
- end
-
- case task.status
- when 0
- status << "未发布"
- time = task.publish_time.present? ? "将于 #{format_time(task.publish_time)} 发布" : "创建于#{time_from_now(task.created_at)}"
- when 1
- if task.end_time && task.end_time >= Time.now
- status << "提交中"
- time = how_much_time(task.end_time)
- end
- when 2
- status << "评阅中"
- time = task.comment_time.present? ? how_much_time(task.comment_time) : course.end_date.present? ? how_much_time(course.end_date.end_of_day) : ""
- when 3
- status << "交叉评阅中"
- time = course.end_date.present? ? how_much_time(course.end_date.end_of_day) : ""
- end
-
- status << "未开启补交" if (!task.allow_late && task.status != 0) #6.11 -hs 新增status不等于0
-
- # 如果还在补交阶段则显示补交结束时间
- if task.status > 1 && task.allow_late && task.late_time && task.late_time > Time.now
- time = how_much_time(task.late_time)
- end
- end
-
- result[:status] = status
- result[:time] = time
- result
- end
-
- # 作品数统计:type: 1 已提交 0 未提交
- def grduationwork_count task, type
- works = task.graduation_works
- type == 1 ? works.select{|work| work.work_status != 0}.size : works.select{|work| work.work_status == 0}.size
- end
-
- # 普通/分组 作业作品状态数组
- def graduation_work_status task, user_id, course
- status = []
- work = task.graduation_works.find_by(user_id: user_id)
-
- work = work || GraduationWork.create(graduation_task_id: task.id, user_id: user_id)
- late_time = task.late_time || course.end_date
-
- if course.is_end && work && work.work_status > 0
- status << "查看作品"
- elsif !course.is_end
- if task.publish_time && task.publish_time < Time.now
- # 作业未截止时
- if task.end_time > Time.now
- if task.task_type == 2 && task.base_on_project
- if work.project_id.nil? || work.project_id == 0
- status << "创建项目"
- status << "关联项目"
- elsif work.work_status == 0
- status << "取消关联"
- status << "提交作品"
- else
- status << "修改作品"
- end
- else
- if work.work_status == 0
- status << "提交作品"
- else
- status << "修改作品"
- end
- end
-
- # 补交阶段
- elsif task.allow_late && (late_time.nil? || late_time > Time.now)
- if task.task_type == 2 && task.base_on_project
- if work.project_id.nil? || work.project_id == 0
- status << "创建项目"
- status << "关联项目"
- elsif work.work_status == 0
- status << "取消关联"
- status << "补交作品"
- else
- status << "补交附件"
- status << "查看作品"
- end
- else
- if work.work_status == 0
- status << "补交作品"
- else
- status << "补交附件"
- status << "查看作品"
- end
- end
-
- # 匿评阶段
- elsif work.work_status != 0
- status << "查看作品"
- end
- end
- end
- end
-
- # 阶段剩余时间
- def task_left_time task
- if task.publish_time && task.publish_time < Time.now
- if task.end_time > Time.now
- status = "剩余提交时间"
- time = "#{how_much_time(task.end_time)}"
- else
- if task.allow_late && task.late_time && task.late_time >= Time.now
- status = "剩余补交时间"
- time = "#{how_much_time(task.late_time)}"
- end
- end
- end
- {status: status, time: time}
- end
-end
diff --git a/app/helpers/graduation_topics_helper.rb b/app/helpers/graduation_topics_helper.rb
deleted file mode 100644
index ae15d201e..000000000
--- a/app/helpers/graduation_topics_helper.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-module GraduationTopicsHelper
-
- # 课题类型
- def topic_type
- [{id: 1, name: "设计"}, {id: 2, name: "论文"}, {id: 3, name: "创作"}]
- end
-
- # 课程来源
- def topic_source
- [{id: 1, name: "生产/社会实际"}, {id: 2, name:"结合科研"}, {id: 3, name: "其它"}]
- end
-
- # 课题性质1
- def topic_property_first
- [{id: 1, name: "真题"}, {id: 2, name:"模拟题"}]
- end
-
- # 课题性质2
- def topic_property_second
- [{id: 1, name: "纵向课题"}, {id: 2, name:"横向课题"}, {id: 3, name: "自选"}]
- end
-
- # 课题重复
- def topic_repeat
- [{id: 1, name: "新题"}, {id: 2, name:"往届题,有新要求"}, {id: 3, name: "往届题,无新要求"}]
- end
-
-
-
-end
diff --git a/app/helpers/graduation_works_helper.rb b/app/helpers/graduation_works_helper.rb
deleted file mode 100644
index 577efa797..000000000
--- a/app/helpers/graduation_works_helper.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-module GraduationWorksHelper
- include GraduationTasksHelper
-
- # 作品最终成绩
- # 参数: work作品, current_user用户,course_identity用户在课堂的身份
- def work_final_score work, current_user, course_identity
- work_score =
- if work.work_score.nil?
- "--"
- else
- if work.check_score_power? current_user, course_identity
- format("%.1f", work.work_score < 0 ? 0 : work.work_score.round(1))
- else
- "**"
- end
- end
- # work_score 最终成绩; late_penalty 迟交扣分; final_score 最终评分
- {username: work.user.full_name, login: work.user.login, work_score: work_score, final_score: work.final_score}
- end
-end
diff --git a/app/helpers/hack_user_lastest_codes_helper.rb b/app/helpers/hack_user_lastest_codes_helper.rb
deleted file mode 100644
index 13350a8f6..000000000
--- a/app/helpers/hack_user_lastest_codes_helper.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-module HackUserLastestCodesHelper
-end
diff --git a/app/helpers/hacks_helper.rb b/app/helpers/hacks_helper.rb
deleted file mode 100644
index 13f5ac76d..000000000
--- a/app/helpers/hacks_helper.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-module HacksHelper
-end
diff --git a/app/helpers/helps/faqs_helper.rb b/app/helpers/helps/faqs_helper.rb
new file mode 100644
index 000000000..68b8279f7
--- /dev/null
+++ b/app/helpers/helps/faqs_helper.rb
@@ -0,0 +1,2 @@
+module Helps::FaqsHelper
+end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 629df117c..a67c2792c 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -1,4 +1,9 @@
module ProjectsHelper
+ def menu_hash_by_name(name)
+ {
+ menu_name: name
+ }
+ end
def render_zh_project_type(project_type)
case project_type
@@ -16,6 +21,10 @@ module ProjectsHelper
[base_url, archive_repositories_path(owner&.login, repository, "#{archive}.tar.gz")].join
end
+ def render_download_file_url(owner, repository, filepath, ref)
+ [base_url, "/api/#{owner&.login}/#{repository.identifier}/raw?filepath=#{filepath}&ref=#{ref}"].join
+ end
+
def render_http_url(project)
[gitea_domain, project.owner.login, "#{project.identifier}.git"].join('/')
end
@@ -29,15 +38,15 @@ module ProjectsHelper
end
def json_response(project, user)
- # repo = project.repository
- repo = Repository.includes(:mirror).select(:id, :mirror_url).find_by(project: project)
+ repo = Repository.includes(:mirror).select(:id, :is_mirror, :mirror_url, :source_clone_url).find_by(project: project)
tmp_json = {}
unless project.common?
tmp_json = tmp_json.merge({
+ is_mirror: repo.is_mirror ? true : false,
mirror_status: repo.mirror_status,
mirror_num: repo.mirror_num,
- mirror_url: repo.mirror_url,
+ mirror_url: repo.remote_mirror_url,
first_sync: repo.first_sync?
})
end
@@ -53,7 +62,11 @@ module ProjectsHelper
author: render_owner(project),
is_secret: project.is_secret,
is_member: project.member?(user.id),
- user_apply_signatures: project.apply_signatures.with_user_id(user.id).collect{|s| {id: s.id, status: s.status}}
+ user_apply_signatures: project.apply_signatures.with_user_id(user.id).collect{|s| {id: s.id, status: s.status}},
+ project_category_id: project.project_category_id,
+ project_language_id: project.project_language_id,
+ license_id: project.license_id,
+ ignore_id: project.ignore_id
}).compact
render json: tmp_json
@@ -88,8 +101,4 @@ module ProjectsHelper
def render_educoder_avatar_url(project_educoder)
[Rails.application.config_for(:configuration)['educoder']['cdn_url'], project_educoder&.image_url].join('/')
end
-
- def render_avatar_url(owner)
- ['images', url_to_avatar(owner)].join('/')
- end
end
diff --git a/app/helpers/repositories_helper.rb b/app/helpers/repositories_helper.rb
index 07646c07a..c69241858 100644
--- a/app/helpers/repositories_helper.rb
+++ b/app/helpers/repositories_helper.rb
@@ -1,22 +1,21 @@
module RepositoriesHelper
def render_permission(user, project)
return "Admin" if user&.admin?
- return "Owner" if user === project.owner
project.get_premission(user)
end
def render_decode64_content(str)
return nil if str.blank?
- Base64.decode64(str).force_encoding("UTF-8")
+ Base64.decode64(str).force_encoding("UTF-8").encode("UTF-8", invalid: :replace)
end
def download_type(str)
- default_type = %w(xlsx xls ppt pptx pdf zip 7z rar exe pdb obj idb png jpg gif tif psd svg RData rdata doc docx mpp vsdx dot)
- default_type.include?(str&.downcase)
+ default_type = %w(xlsx xls ppt pptx pdf zip 7z rar exe pdb obj idb RData rdata doc docx mpp vsdx dot otf eot ttf woff woff2 mp4 mov wmv flv mpeg avi avchd webm mkv)
+ default_type.include?(str&.downcase) || str.blank?
end
def image_type?(str)
- default_type = %w(png jpg gif tif psd svg)
+ default_type = %w(png jpg gif tif psd svg bmp webp jpeg ico psd)
default_type.include?(str&.downcase)
end
@@ -27,40 +26,62 @@ module RepositoriesHelper
end
def render_commit_author(author_json)
- return nil if author_json.blank?
- find_user_by_login author_json['login']
+ return nil if author_json.blank? || (author_json["id"].blank? && author_json['name'].blank?)
+ if author_json["id"].present?
+ return find_user_by_gitea_uid author_json['id']
+ end
+ if author_json["id"].nil? && (author_json["name"].present? && author_json["email"].present?)
+ return find_user_by_login_and_mail(author_json['name'], author_json["email"])
+ end
end
- def readme_render_decode64_content(str, path)
- return nil if str.blank?
- content = Base64.decode64(str).force_encoding('UTF-8')
-
- c_regex = /\!\[.*?\]\((.*?)\)/
- src_regex = /src=\"(.*?)\"/
- ss = content.to_s.scan(c_regex)
- ss_src = content.to_s.scan(src_regex)
- total_images = ss + ss_src
- if total_images.length > 0
- total_images.each do |s|
- image_title = /\"(.*?)\"/
- r_content = s[0]
- remove_title = r_content.to_s.scan(image_title)
- if remove_title.length > 0
- r_content = r_content.gsub(/#{remove_title[0]}/, "").strip
- end
- if r_content.include?("?")
- new_r_content = r_content + "&raw=true"
- else
- new_r_content = r_content + "?raw=true"
- end
- unless r_content.include?("http://") || r_content.include?("https://") || r_content.include?("mailto:")
- new_r_content = "#{path}" + new_r_content
- end
- content = content.gsub(/#{r_content}/, new_r_content)
- end
+ def render_cache_commit_author(author_json)
+ Rails.logger.info author_json['Email']
+ if author_json["name"].present? && author_json["email"].present?
+ return find_user_in_redis_cache(author_json['name'], author_json['email'])
end
+ if author_json["Name"].present? && author_json["Email"].present?
+ return find_user_in_redis_cache(author_json['Name'], author_json['Email'])
+ end
+ end
- return content
+ def readme_render_decode64_content(str, owner, repo, ref)
+ return nil if str.blank?
+ begin
+ content = Base64.decode64(str).force_encoding('UTF-8')
+
+ c_regex = /\!\[.*?\]\((.*?)\)/
+ src_regex = /src=\"(.*?)\"/
+ ss = content.to_s.scan(c_regex)
+ ss_src = content.to_s.scan(src_regex)
+ total_images = ss + ss_src
+ if total_images.length > 0
+ total_images.each do |s|
+ image_title = /\"(.*?)\"/
+ r_content = s[0]
+ remove_title = r_content.to_s.scan(image_title)
+ if remove_title.length > 0
+ r_content = r_content.gsub(/#{remove_title[0]}/, "").strip
+ end
+ # if r_content.include?("?")
+ # new_r_content = r_content + "&raw=true"
+ # else
+ # new_r_content = r_content + "?raw=true"
+ # end
+ new_r_content = r_content
+
+ unless r_content.include?("http://") || r_content.include?("https://") || r_content.include?("mailto:")
+ # new_r_content = "#{path}" + new_r_content
+ new_r_content = [base_url, "/api/#{owner&.login}/#{repo.identifier}/raw?filepath=#{r_content}&ref=#{ref}"].join
+ end
+ content = content.gsub(/#{r_content}/, new_r_content)
+ end
+ end
+
+ return content
+ rescue
+ return str
+ end
end
# unix_time values for example: 1604382982
@@ -75,12 +96,53 @@ module RepositoriesHelper
def decode64_content(entry, owner, repo, ref, path=nil)
if is_readme?(entry['type'], entry['name'])
- content = Gitea::Repository::Entries::GetService.call(owner, repo.identifier, entry['path'], ref: ref)['content']
- readme_render_decode64_content(content, path)
+ content = Gitea::Repository::Entries::GetService.call(owner, repo.identifier, URI.escape(entry['path']), ref: ref)['content']
+ readme_render_decode64_content(content, owner, repo, ref)
else
- file_type = entry['name'].to_s.split(".").last
- return entry['content'] if download_type(file_type)
+ file_type = File.extname(entry['name'].to_s)[1..-1]
+ if image_type?(file_type)
+ return entry['content'].nil? ? Gitea::Repository::Entries::GetService.call(owner, repo.identifier, URI.escape(entry['path']), ref: ref)['content'] : entry['content']
+ end
+ if download_type(file_type)
+ return entry['content']
+ end
render_decode64_content(entry['content'])
end
end
+
+ def base64_to_image(path, content)
+ # generate to https://git.trusite.net/pawm36ozq/-/raw/branch/master/entrn.png"
+ content = Base64.decode64(content)
+ File.open(path, 'wb') { |f| f.write(content) }
+ end
+
+ def render_download_image_url(dir_path, file_path, content)
+ full_path = file_path.starts_with?("/") ? [dir_path, file_path].join("") : [dir_path, file_path].join("/")
+ file_name = full_path.split("/")[-1]
+ # 用户名/项目标识/文件路径
+ dir_path = generate_dir_path(full_path.split("/"+file_name)[0])
+
+ file_path = [dir_path, file_name].join('/')
+
+ puts "##### render_download_image_url file_path: #{file_path}"
+ base64_to_image(file_path, content)
+ file_path = file_path[6..-1]
+ File.join(base_url, file_path)
+ end
+
+ def generate_dir_path(dir_path)
+ # tmp_dir_path
+ # eg: jasder/forgeplus/raw/branch/ref
+ dir_path = ["public", tmp_dir, dir_path].join('/')
+ puts "#### dir_path: #{dir_path}"
+ unless Dir.exists?(dir_path)
+ FileUtils.mkdir_p(dir_path) ##不成功这里会抛异常
+ end
+ dir_path
+ end
+
+ def tmp_dir
+ "repo"
+ end
+
end
diff --git a/app/helpers/tag_chosen_helper.rb b/app/helpers/tag_chosen_helper.rb
index afd51170d..dcf3c457c 100644
--- a/app/helpers/tag_chosen_helper.rb
+++ b/app/helpers/tag_chosen_helper.rb
@@ -20,10 +20,21 @@ module TagChosenHelper
"done_ratio": render_complete_percentage,
"issue_tag": render_issue_tags(project),
"issue_type": render_issue_species,
- "all_issues": all_issues
+ "all_issues": all_issues,
+ "branches": render_branches(project)
}
end
+ def render_branches(project)
+ if project.educoder?
+ return ['master']
+ else
+ branches = Gitea::Repository::Branches::ListNameService.call(project&.owner, project.identifier)
+ return branches.collect{|i| i["name"] if i.is_a?(Hash)} if branches.is_a?(Array)
+ return branches["branch_name"] if branches.is_a?(Hash)
+ end
+ end
+
def render_cache_trackers
cache_key = "all_trackers/#{Tracker.maximum('id')}"
@@ -94,7 +105,7 @@ module TagChosenHelper
def render_issue_tags(project)
# project.issue_tags.last&.cache_key
- cache_key = "all_issue_tags/#{project.issue_tags.maximum('updated_at')}"
+ cache_key = "project-#{project.id}/all_issue_tags/size-#{project.issue_tags.size}/#{project.issue_tags.maximum('updated_at')}"
Rails.cache.fetch(cache_key) do
project.issue_tags.select(:id, :name, :color).collect do |event|
@@ -109,7 +120,7 @@ module TagChosenHelper
end
def render_cache_milestones(project)
- cache_key = "all_milestones/#{project.versions.maximum('updated_on')}"
+ cache_key = "project-#{project.id}/all_milestones/size-#{project.versions.size}/#{project.versions.maximum('updated_on')}"
Rails.cache.fetch(cache_key) do
project.versions.select(:id, :name, :status).collect do |event|
@@ -124,14 +135,13 @@ module TagChosenHelper
end
def render_cache_collaborators(project)
- cache_key = "all_collaborators/#{project.members.maximum('created_on')}"
-
+ cache_key = "project-#{project.id}/all_collaborators/size-#{project.all_collaborators.size}/#{project.all_collaborators.maximum('updated_on')}"
Rails.cache.fetch(cache_key) do
- project.members.includes(:user).collect do |event|
+ project.all_collaborators.order(created_on: :desc).collect do |user|
{
- id: event.user&.id,
- name: event.user&.show_real_name,
- avatar_url: url_to_avatar(event.user),
+ id: user&.id,
+ name: user&.show_real_name,
+ avatar_url: url_to_avatar(user),
is_chosen: '0'
}
end
@@ -171,15 +181,13 @@ module TagChosenHelper
# depended_issues_id = @depended_issues_id
end
- project_members = project.members_user_infos
project_members_info = [] #指派给
- project_members.includes(user: :user_extension).each do |member|
- user = member&.user
+ project.all_collaborators.includes(:user_extension).each do |user|
if user
real_name = user.try(:show_real_name)
user_id = user.id
is_chosen = ((user.id.to_s == issue_info[0].to_s) ? "1" : "0")
- member_info = {id: user_id, name: real_name,avatar_url: url_to_avatar(user),is_chosen: is_chosen}
+ member_info = {id: user_id, name: real_name,avatar_url: url_to_avatar(user), permission: project.get_premission(user), is_chosen: is_chosen}
project_members_info.push(member_info)
end
end
diff --git a/app/helpers/trustie_hacks_helper.rb b/app/helpers/trustie_hacks_helper.rb
deleted file mode 100644
index 3ebe3a9cc..000000000
--- a/app/helpers/trustie_hacks_helper.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-module TrustieHacksHelper
-end
diff --git a/app/helpers/weapps/courses_helper.rb b/app/helpers/weapps/courses_helper.rb
deleted file mode 100644
index 580536ec6..000000000
--- a/app/helpers/weapps/courses_helper.rb
+++ /dev/null
@@ -1,69 +0,0 @@
-module Weapps::CoursesHelper
- require 'chinese_pinyin'
-
- def teacher_list teachers, user_course_identity
- data = []
- teachers.each do |teacher|
- if teacher.user.present?
- teacher_user = teacher.user
- name = teacher_user.real_name
- role = teacher.role == "CREATOR" ? "管理员" : teacher.role == "PROFESSOR" ? "教师" : "助教"
- member_roles = user_course_identity < Course::ASSISTANT_PROFESSOR ? teacher_user.course_role(teacher.course) : []
- item = {name: name, course_member_id: teacher.id, login: teacher_user.login, user_id: teacher.user_id, role: role,
- school: teacher_user.school_name, image_url: url_to_avatar(teacher_user), member_roles: member_roles}
- pinyin = Pinyin.t(name.strip, splitter: '')
- first_char = pinyin[0]
- letter = first_letter first_char
- if data.pluck(:letter).include?(letter)
- data.select{|a|a[:letter]==letter}.first[:items] << item
- else
- data << {letter: letter, items: [item]}
- end
- end
- end
- # data = data.sort do |a, b|
- # [a[:letter]] <=> [b[:letter]]
- # end
- # data.push(data.shift) if data.select{|a|a[:letter]=='#'}.first.present? # '#'排在最后
- return data
- end
-
-
- def student_list students, excellent, user_course_identity
- data = []
- students.each do |student|
- if student.user.present?
- student_user = student.user
- name = student_user.real_name
- phone = excellent ? "" : student_user.hidden_phone
- member_roles = user_course_identity < Course::ASSISTANT_PROFESSOR ? student_user.course_role(student.course) : []
- item = {name: name, course_member_id: student.id, login: student_user.login, user_id: student.user_id,
- student_id: student_user.student_id, image_url: url_to_avatar(student_user), phone: phone, member_roles: member_roles}
- pinyin = Pinyin.t(name.strip, splitter: '')
- first_char = pinyin[0]
- letter = first_letter first_char
- if data.pluck(:letter).include?(letter)
- data.select{|a|a[:letter]==letter}.first[:items] << item
- else
- data << {letter: letter, items: [item]}
- end
- end
- end
- # data = data.sort do |a, b|
- # [a[:letter]] <=> [b[:letter]]
- # end
- # data.push(data.shift) if data.select{|a|a[:letter]=='#'}.first.present? # '#'排在最后
- return data
- end
-
- def first_letter char
- if char.ord >= 97 && char.ord <= 122
- letter = (char.ord - 32).chr.to_s
- elsif char.ord >= 65 && char.ord <= 90
- letter = char
- else
- letter = '#'
- end
- letter
- end
-end
\ No newline at end of file
diff --git a/app/imports/admins/import_course_member_excel.rb b/app/imports/admins/import_course_member_excel.rb
deleted file mode 100644
index ddd3b01a1..000000000
--- a/app/imports/admins/import_course_member_excel.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-class Admins::ImportCourseMemberExcel < BaseImportXlsx
- Data = Struct.new(:student_id, :name, :course_id, :role, :course_group_name, :school_id)
-
- def read_each(&block)
- sheet.each_row_streaming(pad_cells: true, offset: 1) do |row|
- data = row.map(&method(:cell_value))[0..5]
- block.call Data.new(*data)
- end
- end
-
- private
-
- def check_sheet_valid!
- raise_import_error('请按照模板格式导入') if sheet.row(1).size != 6
- end
-
- def cell_value(obj)
- obj&.cell_value&.to_s&.strip
- end
-end
diff --git a/app/interactors/projects/add_member_interactor.rb b/app/interactors/projects/add_member_interactor.rb
index 810facfae..5fd541553 100644
--- a/app/interactors/projects/add_member_interactor.rb
+++ b/app/interactors/projects/add_member_interactor.rb
@@ -24,7 +24,7 @@ module Projects
ActiveRecord::Base.transaction do
gitea_result = Gitea::Repository::Members::AddService.new(owner, project.identifier, collaborator.login, permission).call
if gitea_result.status == 204
- project.add_member!(collaborator.id, set_member_role, is_apply_signature)
+ project.add_member!(collaborator.id, role_name, is_apply_signature)
end
fail!(nil)
end
@@ -39,13 +39,19 @@ module Projects
@error = error
end
- def set_member_role
- @role ||=
- case @permission
- when "write" then "Developer"
- when "read" then "Reporter"
- else "Developer"
- end
+ def role_name
+ case permission
+ when 'read'
+ 'Reporter'
+ when 'write'
+ 'Developer'
+ when 'admin'
+ 'Manager'
+ when 'owner'
+ 'Manager'
+ else
+ 'Reporter'
+ end
end
end
diff --git a/app/jobs/batch_publish_video_notify_job.rb b/app/jobs/batch_publish_video_notify_job.rb
deleted file mode 100644
index 01390dc2d..000000000
--- a/app/jobs/batch_publish_video_notify_job.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-# 批量发布视频 消息任务
-class BatchPublishVideoNotifyJob < ApplicationJob
- queue_as :notify
-
- def perform(user_id, video_ids)
- user = User.find_by(id: user_id)
- return if user.blank?
-
- attrs = %i[user_id trigger_user_id container_id container_type tiding_type status created_at updated_at]
-
- same_attrs = {
- user_id: 1,
- trigger_user_id: user.id,
- container_type: 'Video',
- tiding_type: 'Apply', status: 0
- }
- Tiding.bulk_insert(*attrs) do |worker|
- user.videos.where(id: video_ids).each do |video|
- worker.add same_attrs.merge(container_id: video.id)
- end
- end
- end
-end
diff --git a/app/jobs/broadcast_mirror_repo_msg_job.rb b/app/jobs/broadcast_mirror_repo_msg_job.rb
index 6372ae2e3..4dee6894a 100644
--- a/app/jobs/broadcast_mirror_repo_msg_job.rb
+++ b/app/jobs/broadcast_mirror_repo_msg_job.rb
@@ -18,12 +18,30 @@ class BroadcastMirrorRepoMsgJob < ApplicationJob
id: project.id,
type: project.numerical_for_project_type
}
+ # 新增失败重试机制, 重试三次
+ result = broadcast(project, json_data)
+
+ if result == 0
+ count = 3
+ while count > 0
+ sleep 3.seconds
+ result = broadcast(project, json_data)
+ if result > 0
+ break
+ end
+ count -= 1
+ end
+ end
+ end
+
+ def broadcast(project, json_data)
puts "############ broadcast start.......... "
puts "############ broadcast channel_name: channel_room_#{project.id}"
puts "############ broadcast project data: #{json_data} "
cable_result = ActionCable.server.broadcast "channel_room_#{project.id}", project: json_data
- puts "############ broadcast result: #{cable_result == 1 ? 'successed' : 'failed'} "
+ puts "############ broadcast result: #{cable_result > 0 ? 'successed' : 'failed'} "
+ return cable_result
end
end
diff --git a/app/jobs/cache_async_clear_job.rb b/app/jobs/cache_async_clear_job.rb
new file mode 100644
index 000000000..651dfaf41
--- /dev/null
+++ b/app/jobs/cache_async_clear_job.rb
@@ -0,0 +1,12 @@
+class CacheAsyncClearJob < ApplicationJob
+ queue_as :cache
+
+ def perform(type, id=nil)
+ case type
+ when "project_common_service"
+ Cache::V2::ProjectCommonService.new(id).clear
+ when "owner_common_service"
+ Cache::V2::OwnnerCommonService.new(id).clear
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/jobs/cache_async_reset_job.rb b/app/jobs/cache_async_reset_job.rb
new file mode 100644
index 000000000..0df0f0fd4
--- /dev/null
+++ b/app/jobs/cache_async_reset_job.rb
@@ -0,0 +1,16 @@
+class CacheAsyncResetJob < ApplicationJob
+ queue_as :cache
+
+ def perform(type, id=nil)
+ case type
+ when "platform_statistic_service"
+ Cache::V2::PlatformStatisticService.new.reset
+ when "project_common_service"
+ Cache::V2::ProjectCommonService.new(id).reset
+ when "owner_common_service"
+ Cache::V2::OwnnerCommonService.new(id).reset
+ when "user_statistic_service"
+ Cache::V2::UserStatisticService.new(id).reset
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/jobs/cache_async_set_job.rb b/app/jobs/cache_async_set_job.rb
new file mode 100644
index 000000000..9c7015d42
--- /dev/null
+++ b/app/jobs/cache_async_set_job.rb
@@ -0,0 +1,16 @@
+class CacheAsyncSetJob < ApplicationJob
+ queue_as :cache
+
+ def perform(type, params={}, id=nil)
+ case type
+ when "platform_statistic_service"
+ Cache::V2::PlatformStatisticService.new(params).call
+ when "project_common_service"
+ Cache::V2::ProjectCommonService.new(id, params).call
+ when "owner_common_service"
+ Cache::V2::OwnnerCommonService.new(id, params).call
+ when "user_statistic_service"
+ Cache::V2::UserStatisticService.new(id, params).call
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/jobs/course_add_student_create_works_job.rb b/app/jobs/course_add_student_create_works_job.rb
deleted file mode 100644
index fec28f395..000000000
--- a/app/jobs/course_add_student_create_works_job.rb
+++ /dev/null
@@ -1,67 +0,0 @@
-# 学生加入课堂时创建相关任务作品
-class CourseAddStudentCreateWorksJob < ApplicationJob
- queue_as :default
-
- def perform(course_id, student_ids)
- course = Course.find_by(id: course_id)
- return if course.blank?
-
- # 如果之前存在相关作品,则更新is_delete字段
- student_works = StudentWork.joins(:homework_common).where(user_id: student_ids, homework_commons: {course_id: course.id})
- student_works.update_all(is_delete: 0)
-
- exercise_users = ExerciseUser.joins(:exercise).where(user_id: student_ids, exercises: {course_id: course.id})
- exercise_users.update_all(is_delete: 0)
-
- poll_users = PollUser.joins(:poll).where(user_id: student_ids, polls: {course_id: course.id})
- poll_users.update_all(is_delete: 0)
-
- graduation_works = course.graduation_works.where(user_id: student_ids)
- graduation_works.update_all(is_delete: 0)
-
- attrs = %i[homework_common_id user_id created_at updated_at]
-
- StudentWork.bulk_insert(*attrs) do |worker|
- student_ids.each do |user_id|
- same_attrs = {user_id: user_id}
- course.homework_commons.where(homework_type: %i[normal group practice]).each do |homework|
- next if StudentWork.where(user_id: user_id, homework_common_id: homework.id).any?
- worker.add same_attrs.merge(homework_common_id: homework.id)
- end
- end
- end
-
- attrs = %i[exercise_id user_id created_at updated_at]
- ExerciseUser.bulk_insert(*attrs) do |worker|
- student_ids.each do |user_id|
- same_attrs = {user_id: user_id}
- course.exercises.each do |exercise|
- next if ExerciseUser.where(user_id: user_id, exercise_id: exercise.id).any?
- worker.add same_attrs.merge(exercise_id: exercise.id)
- end
- end
- end
-
- attrs = %i[poll_id user_id created_at updated_at]
- PollUser.bulk_insert(*attrs) do |worker|
- student_ids.each do |user_id|
- same_attrs = {user_id: user_id}
- course.polls.each do |poll|
- next if PollUser.where(user_id: user_id, poll_id: poll.id).any?
- worker.add same_attrs.merge(poll_id: poll.id)
- end
- end
- end
-
- attrs = %i[graduation_task_id user_id course_id created_at updated_at]
- GraduationWork.bulk_insert(*attrs) do |worker|
- student_ids.each do |user_id|
- same_attrs = {user_id: user_id, course_id: course.id}
- course.graduation_tasks.each do |task|
- next if GraduationWork.where(user_id: user_id, graduation_task_id: task.id).any?
- worker.add same_attrs.merge(graduation_task_id: task.id)
- end
- end
- end
- end
-end
diff --git a/app/jobs/course_delete_student_delete_works_job.rb b/app/jobs/course_delete_student_delete_works_job.rb
deleted file mode 100644
index a84608b2c..000000000
--- a/app/jobs/course_delete_student_delete_works_job.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-class CourseDeleteStudentDeleteWorksJob < ApplicationJob
- queue_as :default
-
- def perform(course_id, student_ids)
- course = Course.find_by(id: course_id)
- return if course.blank?
-
- student_works = StudentWork.joins(:homework_common).where(user_id: student_ids, homework_commons: {course_id: course.id})
- student_works.update_all(is_delete: 1)
-
- exercise_users = ExerciseUser.joins(:exercise).where(user_id: student_ids, exercises: {course_id: course.id})
- exercise_users.update_all(is_delete: 1)
-
- poll_users = PollUser.joins(:poll).where(user_id: student_ids, polls: {course_id: course.id})
- poll_users.update_all(is_delete: 1)
-
- course.graduation_works.where(user_id: student_ids).update_all(is_delete: 1)
- end
-end
diff --git a/app/jobs/course_delete_student_notify_job.rb b/app/jobs/course_delete_student_notify_job.rb
deleted file mode 100644
index 898fc97c9..000000000
--- a/app/jobs/course_delete_student_notify_job.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-# 删除课堂用户
-class CourseDeleteStudentNotifyJob < ApplicationJob
- queue_as :notify
-
- def perform(course_id, student_ids, trigger_user_id)
- course = Course.find_by(id: course_id)
- return if course.blank?
-
- attrs = %i[user_id trigger_user_id container_id container_type belong_container_id
- belong_container_type tiding_type created_at updated_at]
-
- same_attrs = {
- trigger_user_id: trigger_user_id, container_id: course.id, container_type: 'DeleteCourseMember',
- belong_container_id: course.id, belong_container_type: 'Course', tiding_type: 'System'
- }
- Tiding.bulk_insert(*attrs) do |worker|
- student_ids.each do |user_id|
- worker.add same_attrs.merge(user_id: user_id)
- end
- end
- end
-end
diff --git a/app/jobs/create_diff_record_job.rb b/app/jobs/create_diff_record_job.rb
deleted file mode 100644
index fbe8cbff2..000000000
--- a/app/jobs/create_diff_record_job.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-class CreateDiffRecordJob < ApplicationJob
- queue_as :default
-
- def perform(user_id, obj_id, obj_klass, column_name, before, after)
- user = User.find_by(id: user_id)
- obj = obj_klass.constantize.find_by(id: obj_id)
-
- return if user.blank? || obj.blank?
-
- CreateDiffRecordService.call(user, obj, column_name, before, after)
- end
-end
\ No newline at end of file
diff --git a/app/jobs/delay_expired_issue_job.rb b/app/jobs/delay_expired_issue_job.rb
new file mode 100644
index 000000000..bf0e1d2e4
--- /dev/null
+++ b/app/jobs/delay_expired_issue_job.rb
@@ -0,0 +1,11 @@
+class DelayExpiredIssueJob < ApplicationJob
+ queue_as :message
+
+ def perform
+ Issue.where(due_date: Date.today + 1.days).find_each do |issue|
+ SendTemplateMessageJob.perform_later('IssueAssignerExpire', issue.id) if Site.has_notice_menu?
+ SendTemplateMessageJob.perform_later('IssueCreatorExpire', issue.id) if Site.has_notice_menu?
+ end
+ end
+
+end
\ No newline at end of file
diff --git a/app/jobs/delete_department_notify_job.rb b/app/jobs/delete_department_notify_job.rb
deleted file mode 100644
index 1da5e2e85..000000000
--- a/app/jobs/delete_department_notify_job.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-# 删除部门 消息通知
-class DeleteDepartmentNotifyJob < ApplicationJob
- queue_as :notify
-
- def perform(department_id, operator_id, user_ids)
- department = Department.unscoped.find_by(id: department_id)
- return if department.blank? || user_ids.blank?
-
- attrs = %i[ user_id trigger_user_id container_id container_type tiding_type status created_at updated_at]
-
- same_attrs = {
- trigger_user_id: operator_id, container_id: department.id, container_type: 'Department',
- status: 4, tiding_type: 'System'
- }
- Tiding.bulk_insert(*attrs) do |worker|
- user_ids.each do |user_id|
- worker.add same_attrs.merge(user_id: user_id)
- end
- end
- end
-end
diff --git a/app/jobs/exercise_publish_notify_job.rb b/app/jobs/exercise_publish_notify_job.rb
deleted file mode 100644
index 9c43b1978..000000000
--- a/app/jobs/exercise_publish_notify_job.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-# 试卷发布 消息通知
-class ExercisePublishNotifyJob < ApplicationJob
- queue_as :notify
-
- def perform(exercise_id, group_ids)
- exercise = Exercise.find_by(id: exercise_id)
- return if exercise.blank?
- user = exercise.user
- course = exercise.course
-
- if group_ids.present?
- students = course.students.where(course_group_id: group_ids)
- subquery = course.teacher_course_groups.where(course_group_id: group_ids).select(:course_member_id)
- teachers = course.teachers.where(id: subquery)
- else
- students = course.students
- teachers = course.teachers
- end
-
- attrs = %i[
- user_id trigger_user_id container_id container_type parent_container_id parent_container_type
- belong_container_id belong_container_type viewed tiding_type created_at updated_at
- ]
-
- same_attrs = {
- trigger_user_id: user.id, container_id: exercise.id, container_type: 'Exercise',
- parent_container_id: exercise.id, parent_container_type: 'ExercisePublish',
- belong_container_id: exercise.course_id, belong_container_type: 'Course',
- viewed: 0, tiding_type: 'Exercise'
- }
- Tiding.bulk_insert(*attrs) do |worker|
- teacher_ids = teachers.pluck(:user_id)
- unless exercise.tidings.exists?(parent_container_type: 'ExercisePublish', user_id: teacher_ids)
- teacher_ids.each do |user_id|
- worker.add same_attrs.merge(user_id: user_id)
- end
- end
-
- students.pluck(:user_id).each do |user_id|
- worker.add same_attrs.merge(user_id: user_id)
- end
- end
- end
-end
diff --git a/app/jobs/get_aliyun_video_info_job.rb b/app/jobs/get_aliyun_video_info_job.rb
deleted file mode 100644
index d93186458..000000000
--- a/app/jobs/get_aliyun_video_info_job.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# 获取阿里云视频信息
-class GetAliyunVideoInfoJob < ApplicationJob
- queue_as :default
-
- def perform(vod_video_id)
- video = Video.find_by(uuid: vod_video_id)
- return if video.blank? || video.vod_uploading?
-
- result = AliyunVod::Service.get_play_info(video.uuid)
- cover_url = result.dig('VideoBase', 'CoverURL')
- file_url = (result.dig('PlayInfoList', 'PlayInfo') || []).first&.[]('PlayURL')
-
- video.cover_url = cover_url if cover_url.present? && video.cover_url.blank?
- video.file_url = file_url if file_url.present?
- video.save!
- end
-end
\ No newline at end of file
diff --git a/app/jobs/graduation_task_cross_comment_job.rb b/app/jobs/graduation_task_cross_comment_job.rb
deleted file mode 100644
index a2d181b50..000000000
--- a/app/jobs/graduation_task_cross_comment_job.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-# 毕设任务的交叉评阅分配
-class GraduationTaskCrossCommentJob < ApplicationJob
- queue_as :default
-
- def perform(graduation_task_id)
- task = GraduationTask.find_by(id: graduation_task_id)
- return if task.blank?
-
- task.graduation_task_group_assignations.includes(:graduation_group, :graduation_work).each do |assignation|
- graduation_group = assignation.graduation_group
- work = assignation.graduation_work
- if graduation_group.present? && work.present?
- member_ids = graduation_group.course_members.pluck(:user_id).uniq
- member_ids.each do |user_id|
- unless work.graduation_work_comment_assignations.exists?(user_id: user_id)
- work.graduation_work_comment_assignations << GraduationWorkCommentAssignation.new(user_id: user_id, graduation_task_id: task.id)
- end
- end
- end
- end
- end
-end
diff --git a/app/jobs/graduation_task_publish_notify_job.rb b/app/jobs/graduation_task_publish_notify_job.rb
deleted file mode 100644
index 84049fe90..000000000
--- a/app/jobs/graduation_task_publish_notify_job.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-# 任务发布 消息通知
-class GraduationTaskPublishNotifyJob < ApplicationJob
- queue_as :notify
-
- def perform(graduation_task_id)
- task = GraduationTask.find_by(id: graduation_task_id)
- return if task.blank?
- course = task.course
- return if course.blank?
-
- attrs = %i[
- user_id trigger_user_id container_id container_type parent_container_id parent_container_type
- belong_container_id belong_container_type viewed tiding_type created_at updated_at
- ]
-
- same_attrs = {
- trigger_user_id: task.user_id, container_id: task.id, container_type: 'GraduationTask',
- parent_container_id: task.id, parent_container_type: 'TaskPublish',
- belong_container_id: task.course_id, belong_container_type: 'Course',
- viewed: 0, tiding_type: 'GraduationTask'
- }
- Tiding.bulk_insert(*attrs) do |worker|
- course.course_members.pluck(:user_id).uniq.each do |user_id|
- worker.add same_attrs.merge(user_id: user_id)
- end
- end
- end
-end
diff --git a/app/jobs/migrate_remote_repository_job.rb b/app/jobs/migrate_remote_repository_job.rb
index 03727908d..488141910 100644
--- a/app/jobs/migrate_remote_repository_job.rb
+++ b/app/jobs/migrate_remote_repository_job.rb
@@ -6,12 +6,16 @@ class MigrateRemoteRepositoryJob < ApplicationJob
return if repo.blank?
puts "############ MigrateRemoteRepositoryJob starting ... ############"
-
+ params.except!(:auth_password, :auth_username) if params[:auth_username].nil?
gitea_repository = Gitea::Repository::MigrateService.new(token, params).call
- if gitea_repository
- repo&.project&.update_columns(gpid: gitea_repository["id"])
+ puts "#gitea_repository#{gitea_repository}"
+ if gitea_repository[0]==201
+ repo&.project&.update_columns(gpid: gitea_repository[2]["id"])
repo&.mirror&.succeeded!
puts "############ mirror status: #{repo.mirror.status} ############"
+ else
+ repo&.mirror&.failed!
end
+ BroadcastMirrorRepoMsgJob.perform_later(repo.id) unless repo&.mirror.waiting?
end
end
diff --git a/app/jobs/reset_platform_cache_job.rb b/app/jobs/reset_platform_cache_job.rb
new file mode 100644
index 000000000..bdc5b6aaa
--- /dev/null
+++ b/app/jobs/reset_platform_cache_job.rb
@@ -0,0 +1,14 @@
+class ResetPlatformCacheJob < ApplicationJob
+ queue_as :cache
+
+ def perform
+ Cache::PlatformFollowCountService.new.reset
+ Cache::PlatformIssueCountService.new.reset
+ Cache::PlatformProjectCountService.new.reset
+ Cache::PlatformProjectForkCountService.new.reset
+ Cache::PlatformProjectLanguagesCountService.new.reset
+ Cache::PlatformProjectPraisesCountService.new.reset
+ Cache::PlatformProjectWatchersCountService.new.reset
+ Cache::PlatformPullrequestCountService.new.reset
+ end
+end
\ No newline at end of file
diff --git a/app/jobs/reset_user_cache_job.rb b/app/jobs/reset_user_cache_job.rb
new file mode 100644
index 000000000..7b2c5c63b
--- /dev/null
+++ b/app/jobs/reset_user_cache_job.rb
@@ -0,0 +1,15 @@
+class ResetUserCacheJob < ApplicationJob
+ queue_as :cache
+
+ def perform(user)
+ return if user.nil?
+ Cache::UserFollowCountService.new(user).reset
+ Cache::UserIssueCountService.new(user).reset
+ Cache::UserProjectCountService.new(user).reset
+ Cache::UserProjectForkCountService.new(user).reset
+ Cache::UserProjectLanguagesCountService.new(user).reset
+ Cache::UserProjectPraisesCountService.new(user).reset
+ Cache::UserProjectWatchersCountService.new(user).reset
+ Cache::UserPullrequestCountService.new(user).reset
+ end
+end
\ No newline at end of file
diff --git a/app/jobs/resubmit_student_work_notify_job.rb b/app/jobs/resubmit_student_work_notify_job.rb
deleted file mode 100644
index 1a67aa3ad..000000000
--- a/app/jobs/resubmit_student_work_notify_job.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-class ResubmitStudentWorkNotifyJob < ApplicationJob
- queue_as :notify
-
- def perform(homework_id, student_ids)
- homework = HomeworkCommon.find_by(id: homework_id)
- return if homework.blank? || student_ids.blank?
- course = homework.course
-
- attrs = %i[user_id trigger_user_id container_id container_type parent_container_id parent_container_type
- belong_container_id belong_container_type tiding_type viewed created_at updated_at]
-
- same_attrs = {
- container_type: 'ResubmitStudentWork', parent_container_id: homework.id, parent_container_type: 'HomeworkCommon',
- belong_container_id: course.id, belong_container_type: 'Course', tiding_type: 'HomeworkCommon', viewed: 0
- }
- Tiding.bulk_insert(*attrs) do |worker|
- student_ids.each do |user_id|
- next unless User.exists?(id: user_id)
-
- work = homework.student_works.find_by(user_id: user_id)
- next if work.blank?
- score_user_ids = work.student_works_scores.where.not(score: nil).where(reviewer_role: [1, 2]).pluck(user_id).uniq
- next if score_user_ids.blank?
-
- attrs = same_attrs.merge(trigger_user_id: user_id, container_id: work.id)
-
- score_user_ids.each do |user_id|
- worker.add attrs.merge(user_id: user_id)
- end
- end
- end
- end
-end
diff --git a/app/jobs/send_join_project_applied_message_job.rb b/app/jobs/send_join_project_applied_message_job.rb
new file mode 100644
index 000000000..557878ede
--- /dev/null
+++ b/app/jobs/send_join_project_applied_message_job.rb
@@ -0,0 +1,27 @@
+class SendJoinProjectAppliedMessageJob < ApplicationJob
+ queue_as :default
+
+ def perform(applied_project, applied_user, message_status)
+ project = applied_project.project
+ return unless project.present?
+ return unless applied_user.present?
+ return unless applied_project.user.present?
+ AppliedMessage.find_or_create_by!(user_id: applied_project.user_id,
+ applied: applied_project,
+ status: message_status,
+ name: build_name(project.name, message_status),
+ applied_user_id: applied_user.id,
+ project_id: project.id)
+ end
+
+ private
+ def build_name(repo_name, message_status, applied_name="")
+ case message_status
+ when 'successed'
+ return "已通过你加入【#{repo_name}】仓库的申请。"
+ when 'failure'
+ return "已拒绝你加入【#{repo_name}】仓库的申请。"
+ end
+ ""
+ end
+end
\ No newline at end of file
diff --git a/app/jobs/send_template_message_job.rb b/app/jobs/send_template_message_job.rb
new file mode 100644
index 000000000..f17755420
--- /dev/null
+++ b/app/jobs/send_template_message_job.rb
@@ -0,0 +1,295 @@
+class SendTemplateMessageJob < ApplicationJob
+ queue_as :message
+
+ def perform(source, *args)
+ Rails.logger.info "SendTemplateMessageJob [args] #{args}"
+ case source
+ when 'FollowTip'
+ watcher_id = args[0]
+ watcher = Watcher.find_by_id(watcher_id)
+ return unless watcher.present?
+ receivers = User.where(id: watcher.watchable_id)
+ followeder = User.find_by_id(watcher.user_id)
+ receivers_string, content, notification_url = MessageTemplate::FollowedTip.get_message_content(receivers, followeder)
+ Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {watcher_id: watcher.id})
+ when 'IssueAssigned'
+ 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)
+ 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|
+ receivers_email_string, email_title, email_content = MessageTemplate::IssueAssigned.get_email_message_content(receiver, operator, issue)
+ Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content)
+ end
+ when 'IssueAssignerExpire'
+ issue_id = args[0]
+ issue = Issue.find_by_id(issue_id)
+ return unless issue.present?
+ receivers = User.where(id: issue&.assigned_to_id)
+ receivers_string, content, notification_url = MessageTemplate::IssueAssignerExpire.get_message_content(receivers, issue)
+ Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {issue_id: issue.id})
+ when 'IssueAtme'
+ receivers, operator_id, issue_id = args[0], args[1], args[2]
+ operator = User.find_by_id(operator_id)
+ issue = Issue.find_by_id(issue_id)
+ return unless operator.present? && issue.present?
+ # receivers = receivers.where.not(id: operator&.id)
+ receivers = User.where(id: receivers)
+ receivers_string, content, notification_url = MessageTemplate::IssueAtme.get_message_content(receivers, operator, issue)
+ Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, issue_id: issue.id}, 2, operator_id)
+ when 'IssueChanged'
+ operator_id, issue_id, change_params = args[0], args[1], args[2]
+ 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_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|
+ receivers_email_string, email_title, email_content = MessageTemplate::IssueChanged.get_email_message_content(receiver, operator, issue, change_params)
+ Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content)
+ end
+ when 'IssueCreatorExpire'
+ issue_id = args[0]
+ issue = Issue.find_by_id(issue_id)
+ return unless issue.present?
+ receivers = User.where(id: issue&.author_id)
+ receivers_string, content, notification_url = MessageTemplate::IssueCreatorExpire.get_message_content(receivers, issue)
+ Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {issue_id: issue.id})
+ when 'IssueDeleted'
+ 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)
+ 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|
+ receivers_email_string, email_title, email_content = MessageTemplate::IssueDeleted.get_email_message_content(receiver, operator, issue_title)
+ Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content)
+ end
+ when 'OrganizationJoined'
+ user_id, organization_id = args[0], args[1]
+ user = User.find_by_id(user_id)
+ organization = Organization.find_by_id(organization_id)
+ return unless user.present? && organization.present?
+ receivers = User.where(id: user.id)
+ receivers_string, content, notification_url = MessageTemplate::OrganizationJoined.get_message_content(receivers, organization)
+ Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {user_id: user.id, organization_id: organization.id})
+ receivers.find_each do |receiver|
+ receivers_email_string, email_title, email_content = MessageTemplate::OrganizationJoined.get_email_message_content(receiver, organization)
+ Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content)
+ end
+ when 'OrganizationLeft'
+ user_id, organization_id = args[0], args[1]
+ user = User.find_by_id(user_id)
+ organization = Organization.find_by_id(organization_id)
+ return unless user.present? && organization.present?
+ receivers = User.where(id: user.id)
+ receivers_string, content, notification_url = MessageTemplate::OrganizationLeft.get_message_content(receivers, organization)
+ Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {user_id: user.id, organization_id: organization.id})
+ receivers.find_each do |receiver|
+ receivers_email_string, email_title, email_content = MessageTemplate::OrganizationLeft.get_email_message_content(receiver, organization)
+ Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content)
+ end
+ when 'ProjectIssue'
+ 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? && issue&.project.present?
+ managers = issue&.project&.all_managers.where.not(id: operator&.id)
+ followers = User.none # TODO
+ receivers_string, content, notification_url = MessageTemplate::ProjectIssue.get_message_content(managers, followers, operator, issue)
+ Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, issue_id: issue.id})
+ managers.find_each do |receiver|
+ receivers_email_string, email_title, email_content = MessageTemplate::ProjectIssue.get_email_message_content(receiver, true, operator, issue)
+ Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content)
+ end
+ followers.find_each do |receiver|
+ receivers_email_string, email_title, email_content = MessageTemplate::ProjectIssue.get_email_message_content(receiver, false, operator, issue)
+ Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content)
+ end
+ when 'ProjectJoined'
+ operator_id, user_id, project_id = args[0], args[1], args[2]
+ operator = User.find_by_id(operator_id)
+ user = User.find_by_id(user_id)
+ project = Project.find_by_id(project_id)
+ return unless operator.present? && user.present? && project.present?
+ receivers = User.where(id: user.id).where.not(id: operator&.id)
+ receivers_string, content, notification_url = MessageTemplate::ProjectJoined.get_message_content(receivers, project)
+ Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, user_id: user.id, project_id: project.id})
+ receivers.find_each do |receiver|
+ receivers_email_string, email_title, email_content = MessageTemplate::ProjectJoined.get_email_message_content(receiver, project)
+ Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content)
+ end
+ when 'ProjectLeft'
+ operator_id, user_id, project_id = args[0], args[1], args[2]
+ operator = User.find_by_id(operator_id)
+ user = User.find_by_id(user_id)
+ project = Project.find_by_id(project_id)
+ return unless operator.present? && user.present? && project.present?
+ receivers = User.where(id: user.id).where.not(id: operator&.id)
+ receivers_string, content, notification_url = MessageTemplate::ProjectLeft.get_message_content(receivers, project)
+ Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, user_id: user.id, project_id: project.id})
+ receivers.find_each do |receiver|
+ receivers_email_string, email_title, email_content = MessageTemplate::ProjectLeft.get_email_message_content(receiver, project)
+ Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content)
+ end
+ when 'ProjectMemberJoined'
+ operator_id, user_id, project_id = args[0], args[1], args[2]
+ operator = User.find_by_id(operator_id)
+ user = User.find_by_id(user_id)
+ project = Project.find_by_id(project_id)
+ return unless operator.present? && user.present? && project.present?
+ receivers = project&.all_managers.where.not(id: [operator&.id, user&.id])
+ receivers_string, content, notification_url = MessageTemplate::ProjectMemberJoined.get_message_content(receivers, user, project)
+ Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, user_id: user.id, project_id: project.id})
+ receivers.find_each do |receiver|
+ receivers_email_string, email_title, email_content = MessageTemplate::ProjectMemberJoined.get_email_message_content(receiver, user, project)
+ Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content)
+ end
+ when 'ProjectMemberLeft'
+ operator_id, user_id, project_id = args[0], args[1], args[2]
+ operator = User.find_by_id(operator_id)
+ user = User.find_by_id(user_id)
+ project = Project.find_by_id(project_id)
+ return unless operator.present? && user.present? && project.present?
+ receivers = project&.all_managers.where.not(id: [operator&.id, user&.id])
+ receivers_string, content, notification_url = MessageTemplate::ProjectMemberLeft.get_message_content(receivers, user, project)
+ Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, user_id: user.id, project_id: project.id})
+ receivers.find_each do |receiver|
+ receivers_email_string, email_title, email_content = MessageTemplate::ProjectMemberLeft.get_email_message_content(receiver, user, project)
+ Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content)
+ end
+ when 'ProjectPullRequest'
+ operator_id, pull_request_id = args[0], args[1]
+ operator = User.find_by_id(operator_id)
+ pull_request = PullRequest.find_by_id(pull_request_id)
+ return unless operator.present? && pull_request.present? && pull_request&.project.present?
+ managers = pull_request&.project&.all_managers.where.not(id: operator&.id)
+ followers = User.none # TODO
+ receivers_string, content, notification_url = MessageTemplate::ProjectPullRequest.get_message_content(managers, followers, operator, pull_request)
+ Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, pull_request_id: pull_request.id})
+ managers.find_each do |receiver|
+ receivers_email_string, email_title, email_content = MessageTemplate::ProjectPullRequest.get_email_message_content(receiver, true, operator, pull_request)
+ Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content)
+ end
+ followers.find_each do |receiver|
+ receivers_email_string, email_title, email_content = MessageTemplate::ProjectPullRequest.get_email_message_content(receiver, false, operator, pull_request)
+ Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content)
+ end
+ when 'ProjectRole'
+ operator_id, user_id, project_id, role = args[0], args[1], args[2], args[3]
+ operator = User.find_by_id(operator_id)
+ user = User.find_by_id(user_id)
+ project = Project.find_by_id(project_id)
+ return unless operator.present? && user.present? && project.present?
+ receivers = User.where(id: user.id).where.not(id: operator&.id)
+ receivers_string, content, notification_url = MessageTemplate::ProjectRole.get_message_content(receivers, project, role)
+ Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, user_id: user.id, project_id: project.id, role: role})
+ receivers.find_each do |receiver|
+ receivers_email_string, email_title, email_content = MessageTemplate::ProjectRole.get_email_message_content(receiver, project, role)
+ Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content)
+ end
+ when 'ProjectSettingChanged'
+ operator_id, project_id, change_params = args[0], args[1], args[2]
+ operator = User.find_by_id(operator_id)
+ project = Project.find_by_id(project_id)
+ return unless operator.present? && project.present?
+ receivers = project.all_managers.where.not(id: operator&.id)
+ receivers_string, content, notification_url = MessageTemplate::ProjectSettingChanged.get_message_content(receivers, operator, project, change_params.symbolize_keys)
+ Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, project_id: project.id, change_params: change_params.symbolize_keys})
+ receivers.find_each do |receiver|
+ receivers_email_string, email_title, email_content = MessageTemplate::ProjectSettingChanged.get_email_message_content(receiver, operator, project, change_params.symbolize_keys)
+ Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content)
+ end
+ when 'PullRequestAssigned'
+ operator_id, pull_request_id = args[0], args[1]
+ operator = User.find_by_id(operator_id)
+ pull_request = PullRequest.find_by_id(pull_request_id)
+ issue = Issue.find_by_id(pull_request&.issue_id)
+ return unless operator.present? && pull_request.present? && issue.present?
+ receivers = User.where(id: issue&.assigned_to_id).where.not(id: operator&.id)
+ receivers_string, content, notification_url = MessageTemplate::PullRequestAssigned.get_message_content(receivers, operator, pull_request)
+ Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, pull_request_id: pull_request.id})
+ receivers.find_each do |receiver|
+ receivers_email_string, email_title, email_content = MessageTemplate::PullRequestAssigned.get_email_message_content(receiver, operator, pull_request)
+ Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content)
+ end
+ when 'PullRequestAtme'
+ receivers, operator_id, pull_request_id = args[0], args[1], args[2]
+ operator = User.find_by_id(operator_id)
+ pull_request = PullRequest.find_by_id(pull_request_id)
+ return unless operator.present? && pull_request.present?
+ # receivers = receivers.where.not(id: operator&.id)
+ receivers = User.where(id: receivers)
+ receivers_string, content, notification_url = MessageTemplate::PullRequestAtme.get_message_content(receivers, operator, pull_request)
+ Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, pull_request_id: pull_request.id}, 2, operator_id)
+ when 'PullRequestChanged'
+ operator_id, pull_request_id, change_params = args[0], args[1], args[2]
+ operator = User.find_by_id(operator_id)
+ pull_request = PullRequest.find_by_id(pull_request_id)
+ issue = Issue.find_by_id(pull_request&.issue_id)
+ return unless operator.present? && pull_request.present? && issue.present?
+ receivers = User.where(id: [issue&.assigned_to_id, pull_request&.user_id]).where.not(id: operator&.id)
+ receivers_string, content, notification_url = MessageTemplate::PullRequestChanged.get_message_content(receivers, operator, pull_request, change_params.symbolize_keys)
+ Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, pull_request_id: pull_request.id, change_params: change_params})
+ receivers.find_each do |receiver|
+ receivers_email_string, email_title, email_content = MessageTemplate::PullRequestChanged.get_email_message_content(receiver, operator, pull_request, change_params.symbolize_keys)
+ Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content)
+ end
+ when 'PullRequestClosed'
+ operator_id, pull_request_id = args[0], args[1]
+ operator = User.find_by_id(operator_id)
+ pull_request = PullRequest.find_by_id(pull_request_id)
+ return unless operator.present? && pull_request.present?
+ receivers = User.where(id: [pull_request&.issue&.assigned_to_id, pull_request&.user_id]).where.not(id: operator&.id)
+ receivers_string, content, notification_url = MessageTemplate::PullRequestClosed.get_message_content(receivers, operator, pull_request)
+ Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, pull_request_id: pull_request.id})
+ receivers.find_each do |receiver|
+ receivers_email_string, email_title, email_content = MessageTemplate::PullRequestClosed.get_email_message_content(receiver, operator, pull_request)
+ Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content)
+ end
+ when 'PullRequestMerged'
+ operator_id, pull_request_id = args[0], args[1]
+ operator = User.find_by_id(operator_id)
+ pull_request = PullRequest.find_by_id(pull_request_id)
+ return unless operator.present? && pull_request.present?
+ receivers = User.where(id: [pull_request&.issue&.assigned_to_id, pull_request&.user_id]).where.not(id: operator&.id)
+ receivers_string, content, notification_url = MessageTemplate::PullRequestMerged.get_message_content(receivers, operator, pull_request)
+ Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, pull_request_id: pull_request.id})
+ receivers.find_each do |receiver|
+ receivers_email_string, email_title, email_content = MessageTemplate::PullRequestMerged.get_email_message_content(receiver, operator, pull_request)
+ Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content)
+ end
+ when 'TeamJoined'
+ user_id, organization_id, team_id = args[0], args[1], args[2]
+ user = User.find_by_id(user_id)
+ organization = Organization.find_by_id(organization_id)
+ team = Team.find_by_id(team_id)
+ return unless user.present? && organization.present? && team.present?
+ receivers = User.where(id: user.id)
+ receivers_string, content, notification_url = MessageTemplate::TeamJoined.get_message_content(receivers, organization, team)
+ Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {user_id: user.id, organization_id: organization.id, team_id: team.id})
+ receivers.find_each do |receiver|
+ receivers_email_string, email_title, email_content = MessageTemplate::TeamJoined.get_email_message_content(receiver, organization, team)
+ Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content)
+ end
+ when 'TeamLeft'
+ user_id, organization_id, team_id = args[0], args[1], args[2]
+ user = User.find_by_id(user_id)
+ organization = Organization.find_by_id(organization_id)
+ team = Team.find_by_id(team_id)
+ return unless user.present? && organization.present? && team.present?
+ receivers = User.where(id: user.id)
+ receivers_string, content, notification_url = MessageTemplate::TeamLeft.get_message_content(receivers, organization, team)
+ Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {user_id: user.id, organization_id: organization.id, team_id: team.id})
+ receivers.find_each do |receiver|
+ receivers_email_string, email_title, email_content = MessageTemplate::TeamLeft.get_email_message_content(receiver, organization, team)
+ Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content)
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/jobs/send_transfer_project_applied_message_job.rb b/app/jobs/send_transfer_project_applied_message_job.rb
new file mode 100644
index 000000000..92ea306d1
--- /dev/null
+++ b/app/jobs/send_transfer_project_applied_message_job.rb
@@ -0,0 +1,47 @@
+class SendTransferProjectAppliedMessageJob < ApplicationJob
+ queue_as :default
+
+ def perform(applied_transfer_project, applied_user, message_status)
+ project = applied_transfer_project.project
+ owner = project.owner
+ return unless project.present?
+ return unless owner.present?
+ if owner.is_a?(Organization)
+ receivers = project.managers + owner.team_users.joins(:team).where(teams: {authorize: %w(owner admin)})
+ else
+ receivers = project.managers
+ end
+ receivers.each do |rec|
+ next if applied_user.id == rec.user_id # 自己不要给自己发通知
+ AppliedMessage.create!(user_id: rec.user_id,
+ applied: applied_transfer_project,
+ status: message_status,
+ name: build_name(project.name, applied_transfer_project&.owner&.real_name, message_status, applied_user&.real_name),
+ applied_user_id: applied_user.id,
+ project_id: project.id)
+ end
+ if message_status == 'successed' # 如果转移成功,给转移发起者发通知已转移成功
+ AppliedMessage.find_or_create_by!(user_id: applied_transfer_project.user_id,
+ applied: applied_transfer_project,
+ status: message_status,
+ name: build_name(project.name, applied_transfer_project&.owner&.real_name, message_status),
+ applied_user_id: applied_user.id,
+ project_id: project.id)
+ end
+ end
+
+ private
+ def build_name(repo_name, owner_name, message_status, applied_name="")
+ case message_status
+ when 'canceled'
+ return "取消转移【#{repo_name}】仓库"
+ when 'common'
+ return "正在将【#{repo_name}】仓库转移给【#{owner_name}】"
+ when 'successed'
+ return "【#{repo_name}】仓库成功转移给【#{owner_name}】"
+ when 'failure'
+ return "拒绝转移【#{repo_name}】仓库"
+ end
+ ""
+ end
+end
\ No newline at end of file
diff --git a/app/jobs/sync_mirrored_repository_job.rb b/app/jobs/sync_mirrored_repository_job.rb
index 1d28dd03a..63abbfd80 100644
--- a/app/jobs/sync_mirrored_repository_job.rb
+++ b/app/jobs/sync_mirrored_repository_job.rb
@@ -5,7 +5,28 @@ class SyncMirroredRepositoryJob < ApplicationJob
repo = Repository.find_by(id: repo_id)
current_user = User.find_by(id: user_id)
return if repo.blank? || current_user.blank?
- result = Gitea::Repository::SyncMirroredService.new(repo.user.login, repo.identifier, token: current_user.gitea_token).call
- repo&.mirror&.set_status! if result[:status] == 200 && repo.mirror.present?
+
+ # TODO
+ # 先同步镜像库
+ if repo.config_accelerator?
+ puts "[gitea-accelerator]: ###### 镜像库开始同步 ######"
+ result = Gitea::Accelerator::SyncMirroredService.call(repo.identifier)
+ puts "[gitea-accelerator]: ###### 镜像库同步状态为 #{result[:status]}"
+
+ # TODO 暂时解决从镜像库镜像动作时间先执行的问题
+ # 避免了Gitea::Repository::SyncMirroredService先执行后,加速器Gitea::Accelerator::SyncMirroredService
+ # 再执行的导致Gitea::Repository::SyncMirroredService执行镜像不是最新代码的问题
+ sleep 3.seconds
+ end
+
+ sync_common!(repo, current_user)
end
+
+ def sync_common!(repo, user)
+ result = Gitea::Repository::SyncMirroredService.call(repo.owner.login,
+ repo.identifier, token: user.gitea_token)
+ repo&.mirror.set_status! if result[:status] === 200
+ BroadcastMirrorRepoMsgJob.perform_later(repo.id) unless repo&.mirror.waiting?
+ end
+
end
diff --git a/app/libs/custom_regexp.rb b/app/libs/custom_regexp.rb
index 4aa6fac2d..25c3ae988 100644
--- a/app/libs/custom_regexp.rb
+++ b/app/libs/custom_regexp.rb
@@ -1,9 +1,15 @@
module CustomRegexp
PHONE = /1\d{10}/
EMAIL = /\A[a-zA-Z0-9]+([._\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+\z/
+ LOGIN = /^(?!_)(?!.*?_$)[a-zA-Z0-9_-]+$/ #只含有数字、字母、下划线不能以下划线开头和结尾
LASTNAME = /\A[a-zA-Z0-9\u4e00-\u9fa5]+\z/
NICKNAME = /\A[\u4e00-\u9fa5_a-zA-Z0-9]+\z/
PASSWORD = /\A[a-z_A-Z0-9\-\.!@#\$%\\\^&\*\)\(\+=\{\}\[\]\/",'_<>~\·`\?:;|]{8,16}\z/
+ LOGIN_PASSWORD = /\A[a-z_A-Z0-9\-\.!@#\$%\\\^&\*\)\(\+=\{\}\[\]\/",'_<>~\·`\?:;|]{6,16}\z/
URL = /\Ahttps?:\/\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;]+[-A-Za-z0-9+&@#\/%=~_|]\z/
IP = /^((\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.){3}(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])$/
+
+ URL_REGEX = /\A(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?\z/i
+ REPOSITORY_NAME_REGEX = /^(?!_)(?!.*?_$)[a-zA-Z0-9_-]+$/ #只含有数字、字母、下划线不能以下划线开头和结尾
+
end
diff --git a/app/libs/notice.rb b/app/libs/notice.rb
new file mode 100644
index 000000000..93d5cb42d
--- /dev/null
+++ b/app/libs/notice.rb
@@ -0,0 +1,21 @@
+module Notice
+ class << self
+ def notice_config
+ notice_config = {}
+
+ begin
+ config = Rails.application.config_for(:configuration).symbolize_keys!
+ notice_config = config[:notice].symbolize_keys!
+ raise 'notice config missing' if notice_config.blank?
+ rescue => exception
+ raise ex if Rails.env.production?
+
+ puts %Q{\033[33m [warning] gitea config or configuration.yml missing,
+ please add it or execute 'cp config/configuration.yml.example config/configuration.yml' \033[0m}
+ notice_config = {}
+ end
+
+ notice_config
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/libs/util.rb b/app/libs/util.rb
index 1db9460f0..691ed7cab 100644
--- a/app/libs/util.rb
+++ b/app/libs/util.rb
@@ -29,6 +29,7 @@ module Util
file.write(io)
end
end
+ true
end
def download_file(url, save_path)
diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb
index 6e957b4df..acd34fbbd 100644
--- a/app/mailers/user_mailer.rb
+++ b/app/mailers/user_mailer.rb
@@ -1,17 +1,11 @@
class UserMailer < ApplicationMailer
# 注意:这个地方一定要和你的邮箱服务域名一致
- default from: 'educoder@trustie.org'
+ default from: 'notification@trustie.org'
# 用户注册验证码
def register_email(mail, code)
@code = code
- mail(to: mail, subject: '验证你的电子邮件')
+ mail(to: mail, subject: 'Gitink | 注册验证码')
end
- # 课堂讨论区的邮件通知
- def course_message_email(mail, message_id)
- @message = Message.find_by(id: message_id)
- @course = @message&.board&.course
- mail(to: mail, subject: '课堂发布了新的帖子') if @message.present? && @course.present?
- end
end
diff --git a/app/models/application_record.rb b/app/models/application_record.rb
index 888c65423..77e5fe2db 100644
--- a/app/models/application_record.rb
+++ b/app/models/application_record.rb
@@ -16,4 +16,12 @@ class ApplicationRecord < ActiveRecord::Base
def allow_sync_to_trustie?
Rails.env.production? && EduSetting.get('host_name') == 'https://www.educoder.net'
end
+
+ def self.strip_param(key)
+ key.to_s.strip.presence
+ end
+
+ def strip_param(key)
+ key.to_s.strip.presence
+ end
end
diff --git a/app/models/applied_message.rb b/app/models/applied_message.rb
index 8098e6e07..01f5fa7e5 100644
--- a/app/models/applied_message.rb
+++ b/app/models/applied_message.rb
@@ -1,23 +1,36 @@
# == Schema Information
#
-# Table name: applied_messages
+# Table name: forge_applied_messages
#
# id :integer not null, primary key
# user_id :integer
-# applied_id :integer
# applied_type :string(255)
+# applied_id :integer
# viewed :integer default("0")
# status :integer default("0")
-# created_at :datetime not null
-# updated_at :datetime not null
# name :string(255)
# applied_user_id :integer
# role :integer
# project_id :integer
+# created_at :datetime not null
+# updated_at :datetime not null
+#
+# Indexes
+#
+# index_forge_applied_messages_on_applied_type_and_applied_id (applied_type,applied_id)
+# index_forge_applied_messages_on_applied_user_id (applied_user_id)
+# index_forge_applied_messages_on_project_id (project_id)
+# index_forge_applied_messages_on_user_id (user_id)
#
class AppliedMessage < ApplicationRecord
+ self.table_name = 'forge_applied_messages'
belongs_to :user
belongs_to :applied, polymorphic: true
+ belongs_to :project
+ belongs_to :applied_user, class_name: 'User'
+
+ enum viewed: {waiting: 0, viewed: 1}
+ enum status: {canceled: -1, common: 0, successed: 1, failure: 2} # -1 已取消 0 正在操作 1 操作成功 2 操作失败
end
diff --git a/app/models/applied_project.rb b/app/models/applied_project.rb
index 68095d0ea..ea7ca6eee 100644
--- a/app/models/applied_project.rb
+++ b/app/models/applied_project.rb
@@ -1,20 +1,30 @@
# == Schema Information
#
-# Table name: applied_projects
+# Table name: forge_applied_projects
#
# id :integer not null, primary key
-# project_id :integer not null
-# user_id :integer not null
+# project_id :integer
+# user_id :integer
# role :integer default("0")
# status :integer default("0")
+# created_at :datetime not null
+# updated_at :datetime not null
+#
+# Indexes
+#
+# index_forge_applied_projects_on_project_id (project_id)
+# index_forge_applied_projects_on_user_id (user_id)
#
class AppliedProject < ApplicationRecord
+ self.table_name = "forge_applied_projects"
belongs_to :user
belongs_to :project
has_many :applied_messages, as: :applied, dependent: :destroy
- has_many :forge_activities, as: :forge_act, dependent: :destroy
+ # has_many :forge_activities, as: :forge_act, dependent: :destroy
+
+ enum role: {manager: 3, developer: 4, reporter: 5}
+ enum status: {canceled: -1, common: 0, accepted: 1, refused: 2} # -1 已取消 0 待操作 1 已接收 2 已拒绝
- scope :pending, -> { where(status: 0) }
end
diff --git a/app/models/applied_transfer_project.rb b/app/models/applied_transfer_project.rb
new file mode 100644
index 000000000..d0e0e5c44
--- /dev/null
+++ b/app/models/applied_transfer_project.rb
@@ -0,0 +1,28 @@
+# == Schema Information
+#
+# Table name: applied_transfer_projects
+#
+# id :integer not null, primary key
+# project_id :integer
+# owner_id :integer
+# user_id :integer
+# status :integer default("0")
+# created_at :datetime not null
+# updated_at :datetime not null
+#
+# Indexes
+#
+# index_applied_transfer_projects_on_owner_id (owner_id)
+# index_applied_transfer_projects_on_project_id (project_id)
+# index_applied_transfer_projects_on_user_id (user_id)
+#
+
+class AppliedTransferProject < ApplicationRecord
+ belongs_to :project
+ belongs_to :user # 操作者
+ belongs_to :owner # 接收个人或组织
+
+ has_many :applied_messages, as: :applied, dependent: :destroy
+
+ enum status: {canceled: -1, common: 0, accepted: 1, refused: 2} # -1 已取消 0 待操作 1 已接收 2 已拒绝
+end
diff --git a/app/models/attachment.rb b/app/models/attachment.rb
index d1e6402f9..e66b8f62d 100644
--- a/app/models/attachment.rb
+++ b/app/models/attachment.rb
@@ -17,7 +17,7 @@
# disk_directory :string(255)
# attachtype :integer default("1")
# is_public :integer default("1")
-# copy_from :integer
+# copy_from :string(255)
# quotes :integer default("0")
# is_publish :integer default("1")
# publish_time :datetime
@@ -26,17 +26,19 @@
# cloud_url :string(255) default("")
# course_second_category_id :integer default("0")
# delay_publish :boolean default("0")
+# link :string(255)
+# clone_id :integer
#
# Indexes
#
# index_attachments_on_author_id (author_id)
+# index_attachments_on_clone_id (clone_id)
# index_attachments_on_container_id_and_container_type (container_id,container_type)
-# index_attachments_on_course_second_category_id (course_second_category_id)
# index_attachments_on_created_on (created_on)
-# index_attachments_on_is_public (is_public)
-# index_attachments_on_quotes (quotes)
#
+
+
class Attachment < ApplicationRecord
include BaseModel
include Publicable
@@ -51,7 +53,7 @@ class Attachment < ApplicationRecord
# 二级目录
# belongs_to :course_second_category, optional: true
- scope :by_filename_or_user_name, -> (keywords) { joins(:author).where("filename like :search or LOWER(concat(users.lastname, users.firstname)) LIKE :search",
+ scope :by_filename_or_user_name, -> (keywords) { joins(:author).where("filename like :search or LOWER(CONCAT(users.lastname, users.firstname)) LIKE :search OR users.nickname LIKE :search",
:search => "%#{keywords.split(" ").join('|')}%") unless keywords.blank? }
scope :by_keywords, -> (keywords) { where("filename LIKE ?", "%#{keywords.split(" ").join('|')}%") unless keywords.blank? }
scope :ordered, -> (opts = {}) { order("#{opts[:sort_type]} #{opts[:sort] == 1 ? 'asc': 'desc'}") }
diff --git a/app/models/chart_rule.rb b/app/models/chart_rule.rb
index a0b344e07..26c7f8084 100644
--- a/app/models/chart_rule.rb
+++ b/app/models/chart_rule.rb
@@ -1,3 +1,21 @@
+# == Schema Information
+#
+# Table name: chart_rules
+#
+# id :integer not null, primary key
+# rule_type :string(255)
+# content :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# competition_id :integer
+# competition_stage_id :integer
+#
+# Indexes
+#
+# index_chart_rules_on_competition_id (competition_id)
+# index_chart_rules_on_competition_stage_id (competition_stage_id)
+#
+
class ChartRule < ApplicationRecord
validates :content, length: { maximum: 1000, too_long: "不能超过1000个字符" }
diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb
index 2309f05d4..536e51cf6 100644
--- a/app/models/ci/stage.rb
+++ b/app/models/ci/stage.rb
@@ -1,3 +1,22 @@
+# == Schema Information
+#
+# Table name: stages
+#
+# id :integer not null
+# subject_id :integer
+# name :string(255)
+# description :text(65535)
+# user_id :integer
+# position :integer
+# created_at :datetime not null
+# updated_at :datetime not null
+# shixuns_count :integer default("0")
+#
+# Indexes
+#
+# index_stages_on_subject_id (subject_id)
+#
+
class Ci::Stage < Ci::RemoteBase
self.primary_key = 'stage_id'
diff --git a/app/models/ci/user.rb b/app/models/ci/user.rb
index cd6246753..e4a4d0623 100644
--- a/app/models/ci/user.rb
+++ b/app/models/ci/user.rb
@@ -39,13 +39,15 @@
# business :boolean default("0")
# profile_completed :boolean default("0")
# laboratory_id :integer
-# platform :string(255) default("0")
-# gitea_token :string(255)
-# gitea_uid :integer
# is_shixun_marker :boolean default("0")
+# admin_visitable :boolean default("0")
+# collaborator :boolean default("0")
+# gitea_uid :integer
# is_sync_pwd :boolean default("1")
# watchers_count :integer default("0")
# devops_step :integer default("0")
+# gitea_token :string(255)
+# platform :string(255)
#
# Indexes
#
@@ -53,8 +55,9 @@
# index_users_on_homepage_engineer (homepage_engineer)
# index_users_on_homepage_teacher (homepage_teacher)
# index_users_on_laboratory_id (laboratory_id)
-# index_users_on_login (login)
-# index_users_on_mail (mail)
+# index_users_on_login (login) UNIQUE
+# index_users_on_mail (mail) UNIQUE
+# index_users_on_phone (phone) UNIQUE
# index_users_on_type (type)
#
diff --git a/app/models/concerns/.keep b/app/models/concerns/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/app/models/concerns/dcodes.rb b/app/models/concerns/dcodes.rb
new file mode 100644
index 000000000..6c2ff13b0
--- /dev/null
+++ b/app/models/concerns/dcodes.rb
@@ -0,0 +1,28 @@
+module Dcodes
+ DCODES = %W(1 2 3 4 5 6 7 8 9 a b c f e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)
+
+ extend ActiveSupport::Concern
+
+ def generate_dcode(field, num, pre='')
+ code = DCODES.sample(num).join
+ while self.class.exists?("#{field}": pre+code) do
+ code = DCODES.sample(num).join
+ end
+ code
+ end
+
+ def init_project_invite_code
+ while Project.where(invite_code: nil).size > 0 do
+ projects = Project.where(invite_code: nil).limit(1000)
+ set_sql = ""
+ projects.each do |p|
+ set_sql += "WHEN #{p.id} THEN '#{DCODES.sample(6).join}' "
+ end
+ sql = "UPDATE projects SET invite_code = CASE id "+ set_sql+ "END WHERE id IN(#{projects.ids.join(",")})"
+ Project.connection.execute(sql)
+ end
+ repeat_codes = Project.group(:invite_code).count.select{|k,v| v>1}
+ Project.where(invite_code: repeat_code.keys).update_all(invite_code: nil)
+ end
+
+end
\ No newline at end of file
diff --git a/app/models/concerns/project_operable.rb b/app/models/concerns/project_operable.rb
index a638e8e44..35d6d296e 100644
--- a/app/models/concerns/project_operable.rb
+++ b/app/models/concerns/project_operable.rb
@@ -9,16 +9,49 @@ module ProjectOperable
has_many :reporters, -> { joins(:roles).where(roles: { name: 'Reporter' }) }, class_name: 'Member'
has_many :writable_members, -> { joins(:roles).where.not(roles: {name: 'Reporter'}) }, class_name: 'Member'
has_many :team_projects, dependent: :destroy
+ has_many :teams, through: :team_projects, source: :team
+ end
+
+ def set_owner_permission(creator)
+ return unless owner.is_a?(Organization)
+ owner.build_permit_team_projects!(id)
+ # 避免自己创建的项目,却无法拥有访问权,因为该用户所在团队暂未获得项目访问权
+ return if creator.nil? || owner.is_owner?(creator.id)
+ add_member!(creator.id, "Manager")
end
def add_member!(user_id, role_name='Developer', is_apply_signature=false)
- member = members.create!(user_id: user_id, is_apply_signature: is_apply_signature)
+ if self.owner.is_a?(Organization)
+ case role_name
+ when 'Manager'
+ team = self.owner.teams.admin.take
+ team = team.nil? ? Team.build(self.user_id, 'admin', '管理员', '', 'admin', false, false) : team
+ TeamProject.build(self.user_id, team.id, self.id)
+ OrganizationUser.build(self.user_id, user_id)
+ team_user = TeamUser.build(self.user_id, user_id, team.id)
+ when 'Developer'
+ team = self.owner.teams.write.take
+ team = team.nil? ? Team.build(self.user_id, 'developer', '开发者', '', 'write', false, false) : team
+ TeamProject.build(self.user_id, team.id, self.id)
+ OrganizationUser.build(self.user_id, user_id)
+ team_user = TeamUser.build(self.user_id, user_id, team.id)
+ when 'Reporter'
+ team = self.owner.teams.read.take
+ team = team.nil? ? Team.build(self.user_id, 'reporter', '报告者', '', 'read', false, false) : team
+ TeamProject.build(self.user_id, team.id, self.id)
+ OrganizationUser.build(self.user_id, user_id)
+ team_user = TeamUser.build(self.user_id, user_id, team.id)
+ end
+ end
+ member = members.create!(user_id: user_id, team_user_id: team_user&.id, is_apply_signature: is_apply_signature)
set_developer_role(member, role_name)
end
def remove_member!(user_id)
member = members.find_by(user_id: user_id)
member.destroy! if member && self.user_id != user_id
+ team_user = TeamUser.find_by_id(member&.team_user_id)
+ team_user.destroy! if team_user
end
def member?(user_id)
@@ -38,6 +71,28 @@ module ProjectOperable
def change_member_role!(user_id, role)
member = self.member(user_id)
+ if self.owner.is_a?(Organization) && member.team_user.present?
+ case role&.name
+ when 'Manager'
+ team = self.owner.teams.admin.take
+ team = team.nil? ? Team.build(self.user_id, 'admin', '管理员', '', 'admin', false, false) : team
+ TeamProject.build(self.user_id, team.id, self.id)
+ OrganizationUser.build(self.user_id, user_id)
+ team_user = member.team_user.update(team_id: team&.id)
+ when 'Developer'
+ team = self.owner.teams.write.take
+ team = team.nil? ? Team.build(self.user_id, 'developer', '开发者', '', 'write', false, false) : team
+ TeamProject.build(self.user_id, team.id, self.id)
+ OrganizationUser.build(self.user_id, user_id)
+ team_user = member.team_user.update(team_id: team&.id)
+ when 'Reporter'
+ team = self.owner.teams.read.take
+ team = team.nil? ? Team.build(self.user_id, 'reporter', '报告者', '', 'read', false, false) : team
+ TeamProject.build(self.user_id, team.id, self.id)
+ OrganizationUser.build(self.user_id, user_id)
+ team_user = member.team_user.update(team_id: team&.id)
+ end
+ end
member.member_roles.last.update_attributes!(role: role)
end
@@ -56,7 +111,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_admin?(user.id)
+ managers.exists?(user_id: user.id) || owner.is_owner?(user.id) || owner.is_only_admin?(user.id)
else
false
end
@@ -67,7 +122,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_write?(user.id)
+ developers.exists?(user_id: user.id) || owner.is_only_write?(user.id)
else
false
end
@@ -78,15 +133,40 @@ module ProjectOperable
if owner.is_a?(User)
reporters.exists?(user_id: user.id)
elsif owner.is_a?(Organization)
- reporters.exists?(user_id: user.id) || owner.is_read?(user.id)
+ reporters.exists?(user_id: user.id) || owner.is_only_read?(user.id)
else
false
end
end
+ def operator?(user)
+ user.admin? || (member?(user.id) && !reporter?(user))
+ end
+
def set_developer_role(member, role_name)
role = Role.find_by(name: role_name)
member.member_roles.create!(role: role)
end
+ def has_menu_permission(unit_type)
+ self.project_units.where(unit_type: unit_type).exists?
+ end
+
+ def all_collaborators
+ member_sql = User.joins(members: :roles).where(members: {project_id: self.id}, roles: {name: %w(Manager Developer Reporter)}).to_sql
+ team_user_sql = User.joins(teams: :team_projects).where(team_projects: {project_id: self.id}).to_sql
+ return User.from("( #{ member_sql } UNION #{ team_user_sql } ) AS users").distinct
+ end
+
+ def all_developers
+ member_sql = User.joins(members: :roles).where(members: {project_id: self.id}, roles: {name: %w(Manager Developer)}).to_sql
+ team_user_sql = User.joins(teams: :team_projects).where(teams: {authorize: %w(owner admin write)}, team_projects: {project_id: self.id}).to_sql
+ return User.from("( #{ member_sql } UNION #{ team_user_sql } ) AS users").distinct
+ end
+
+ def all_managers
+ member_sql = User.joins(members: :roles).where(members: {project_id: self.id}, roles: {name: %w(Manager)}).to_sql
+ team_user_sql = User.joins(teams: :team_projects).where(teams: {authorize: %w(owner admin)},team_projects: {project_id: self.id}).to_sql
+ return User.from("( #{ member_sql} UNION #{ team_user_sql } ) AS users").distinct
+ end
end
diff --git a/app/models/concerns/watchable.rb b/app/models/concerns/watchable.rb
index 4c52cf99a..008d382f3 100644
--- a/app/models/concerns/watchable.rb
+++ b/app/models/concerns/watchable.rb
@@ -6,6 +6,7 @@ module Watchable
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)
@@ -21,6 +22,24 @@ module Watchable
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
diff --git a/app/models/discuss.rb b/app/models/discuss.rb
index a4c833b55..1a2efc606 100644
--- a/app/models/discuss.rb
+++ b/app/models/discuss.rb
@@ -1,3 +1,38 @@
+# == Schema Information
+#
+# Table name: discusses
+#
+# id :integer not null, primary key
+# user_id :integer
+# dis_type :string(255)
+# dis_id :integer
+# content :text(65535)
+# parent_id :integer
+# root_id :integer
+# praise_count :integer
+# created_at :datetime not null
+# updated_at :datetime not null
+# challenge_id :integer
+# reward :integer
+# hidden :boolean default("0")
+# last_reply_id :integer
+# position :integer
+# praises_count :integer default("0")
+# sticky :boolean default("0")
+# course_sticky :boolean default("0")
+# course_hidden :boolean default("0")
+# copy_message_id :integer
+#
+# Indexes
+#
+# index_discusses_on_challenge_id (challenge_id)
+# index_discusses_on_copy_message_id (copy_message_id)
+# index_discusses_on_course_hidden (course_hidden)
+# index_discusses_on_course_sticky (course_sticky)
+# index_discusses_on_dis_id_and_dis_type (dis_id,dis_type)
+# index_discusses_on_user_id (user_id)
+#
+
class Discuss < ApplicationRecord
default_scope { order(created_at: :desc) }
diff --git a/app/models/edu_setting.rb b/app/models/edu_setting.rb
index 6968b008e..22575ff15 100644
--- a/app/models/edu_setting.rb
+++ b/app/models/edu_setting.rb
@@ -1,22 +1,24 @@
-# == Schema Information
-#
-# Table name: edu_settings
-#
-# id :integer not null, primary key
-# name :string(255)
-# value :string(255)
-# created_at :datetime not null
-# updated_at :datetime not null
-# description :string(255)
-#
-# Indexes
-#
-# index_edu_settings_on_name (name) UNIQUE
-#
-
+# == Schema Information
+#
+# Table name: edu_settings
+#
+# id :integer not null, primary key
+# name :string(255)
+# value :string(255)
+# created_at :datetime not null
+# updated_at :datetime not null
+# description :string(255)
+#
+# Indexes
+#
+# index_edu_settings_on_name (name) UNIQUE
+#
+
class EduSetting < ApplicationRecord
after_commit :expire_value_cache
+ scope :by_search, -> (keyword){ where("name LIKE :keyword OR value LIKE :keyword", keyword: "%#{strip_param(keyword)}%") unless strip_param(keyword).blank? }
+
def value_cache_key
self.class.value_cache_key(name)
end
diff --git a/app/models/faq.rb b/app/models/faq.rb
new file mode 100644
index 000000000..5965de036
--- /dev/null
+++ b/app/models/faq.rb
@@ -0,0 +1,20 @@
+# == Schema Information
+#
+# Table name: faqs
+#
+# id :integer not null, primary key
+# question :string(255)
+# url :string(255)
+# created_at :datetime not null
+# updated_at :datetime not null
+#
+
+class Faq < ApplicationRecord
+
+ validates :question, presence: true,length: { maximum: 100, too_long: "最多%{count}个字符" }
+ validates :url, format: { with: CustomRegexp::URL, multiline: true, message: "格式不正确" }
+
+ scope :select_without_id, -> { select(:question, :url) }
+ scope :search_question, ->(keyword) { where("question LIKE ?", "%#{keyword}%") unless keyword.blank?}
+
+end
diff --git a/app/models/forge_activity.rb b/app/models/forge_activity.rb
index 77103d0ff..b9f11c5a8 100644
--- a/app/models/forge_activity.rb
+++ b/app/models/forge_activity.rb
@@ -1,5 +1,24 @@
+# == Schema Information
+#
+# Table name: forge_activities
+#
+# id :integer not null, primary key
+# user_id :integer
+# project_id :integer
+# forge_act_id :integer
+# forge_act_type :string(255)
+# org_id :integer
+# created_at :datetime not null
+# updated_at :datetime not null
+#
+# Indexes
+#
+# forge_act_index (project_id,forge_act_id,created_at,forge_act_type)
+# index_forge_activities_on_forge_act_id (forge_act_id)
+#
+
class ForgeActivity < ApplicationRecord
belongs_to :user
belongs_to :project
belongs_to :forge_act, polymorphic: true
-end
\ No newline at end of file
+end
diff --git a/app/models/fork_user.rb b/app/models/fork_user.rb
index d0915f152..bddf8f75c 100644
--- a/app/models/fork_user.rb
+++ b/app/models/fork_user.rb
@@ -20,4 +20,30 @@ class ForkUser < ApplicationRecord
belongs_to :user
belongs_to :fork_project, class_name: 'Project', foreign_key: :fork_project_id
+ after_create :incre_project_common, :incre_user_statistic, :incre_platform_statistic
+ after_destroy :decre_project_common, :decre_user_statistic, :decre_platform_statistic
+
+ def incre_project_common
+ CacheAsyncSetJob.perform_later("project_common_service", {forks: 1}, self.project_id)
+ end
+
+ def decre_project_common
+ CacheAsyncSetJob.perform_later("project_common_service", {forks: -1}, self.project_id)
+ end
+
+ def incre_user_statistic
+ CacheAsyncSetJob.perform_later("user_statistic_service", {fork_count: 1}, self.project&.user_id)
+ end
+
+ def decre_user_statistic
+ CacheAsyncSetJob.perform_later("user_statistic_service", {fork_count: -1}, self.project&.user_id)
+ end
+
+ def incre_platform_statistic
+ CacheAsyncSetJob.perform_later("platform_statistic_service", {fork_count: 1})
+ end
+
+ def decre_platform_statistic
+ CacheAsyncSetJob.perform_later("platform_statistic_service", {fork_count: -1})
+ end
end
diff --git a/app/models/gitea/base.rb b/app/models/gitea/base.rb
index d14249bf6..652fd777d 100644
--- a/app/models/gitea/base.rb
+++ b/app/models/gitea/base.rb
@@ -1,4 +1,6 @@
-class Gitea::Base < Gitea::Database
- self.abstract_class = true
-
+class Gitea::Base < ApplicationRecord
+ db_config = Rails.configuration.database_configuration[Rails.env]["gitea_server"]
+ raise 'gitea database config missing' if db_config.blank?
+
+ establish_connection db_config
end
diff --git a/app/models/gitea/public_key.rb b/app/models/gitea/public_key.rb
new file mode 100644
index 000000000..bb2192358
--- /dev/null
+++ b/app/models/gitea/public_key.rb
@@ -0,0 +1,9 @@
+class Gitea::PublicKey < Gitea::Base
+ self.inheritance_column = nil # FIX The single-table inheritance mechanism failed
+ # establish_connection :gitea_db
+
+ self.table_name = "public_key"
+
+ belongs_to :user, class_name: '::User', primary_key: :gitea_uid, foreign_key: :owner_id, optional: true
+
+end
diff --git a/app/models/gitea/pull.rb b/app/models/gitea/pull.rb
new file mode 100644
index 000000000..591e36c42
--- /dev/null
+++ b/app/models/gitea/pull.rb
@@ -0,0 +1,44 @@
+# == Schema Information
+#
+# Table name: pull_request
+#
+# id :integer not null, primary key
+# type :integer
+# status :integer
+# conflicted_files :text(65535)
+# commits_ahead :integer
+# commits_behind :integer
+# changed_protected_files :text(65535)
+# issue_id :integer
+# index :integer
+# head_repo_id :integer
+# base_repo_id :integer
+# head_branch :string(255)
+# base_branch :string(255)
+# merge_base :string(40)
+# has_merged :boolean
+# merged_commit_id :string(40)
+# merger_id :integer
+# merged_unix :integer
+#
+# Indexes
+#
+# IDX_pull_request_base_repo_id (base_repo_id)
+# IDX_pull_request_has_merged (has_merged)
+# IDX_pull_request_head_repo_id (head_repo_id)
+# IDX_pull_request_issue_id (issue_id)
+# IDX_pull_request_merged_unix (merged_unix)
+# IDX_pull_request_merger_id (merger_id)
+#
+
+class Gitea::Pull < Gitea::Base
+ self.inheritance_column = nil # FIX The single-table inheritance mechanism failed
+ # establish_connection :gitea_db
+
+ self.table_name = "pull_request"
+
+ serialize :conflicted_files, Array
+
+ belongs_to :pull_request, class_name: '::PullRequest', foreign_key: :id, primary_key: :gitea_number, optional: true
+
+end
diff --git a/app/models/gitea/webhook.rb b/app/models/gitea/webhook.rb
new file mode 100644
index 000000000..f60f56788
--- /dev/null
+++ b/app/models/gitea/webhook.rb
@@ -0,0 +1,13 @@
+class Gitea::Webhook < Gitea::Base
+ serialize :events, JSON
+ self.inheritance_column = nil
+
+ self.table_name = 'webhook'
+
+ has_many :tasks, class_name: "Gitea::WebhookTask", foreign_key: :hook_id
+ belongs_to :project, class_name: "::Project", primary_key: :gpid, foreign_key: :repo_id, optional: true
+
+ enum hook_task_type: {gogs: 1, slack: 2, gitea: 3, discord: 4, dingtalk: 5, telegram: 6, msteams: 7, feishu: 8, matrix: 9}
+ enum last_status: {waiting: 0, succeed: 1, fail: 2}
+ enum content_type: {json: 1, form: 2}
+end
\ No newline at end of file
diff --git a/app/models/gitea/webhook_task.rb b/app/models/gitea/webhook_task.rb
new file mode 100644
index 000000000..325352c69
--- /dev/null
+++ b/app/models/gitea/webhook_task.rb
@@ -0,0 +1,18 @@
+class Gitea::WebhookTask < Gitea::Base
+ serialize :payload_content, JSON
+ serialize :request_content, JSON
+
+ self.inheritance_column = nil
+
+ self.table_name = 'hook_task'
+
+ belongs_to :webhook, class_name: "Gitea::Webhook", foreign_key: :hook_id
+
+ enum type: {gogs: 1, slack: 2, gitea: 3, discord: 4, dingtalk: 5, telegram: 6, msteams: 7, feishu: 8, matrix: 9}
+
+ def response_content_json
+ JSON.parse(response_content)
+ rescue
+ {}
+ end
+end
\ No newline at end of file
diff --git a/app/models/help.rb b/app/models/help.rb
index 2b8bf5813..5a4c480b4 100644
--- a/app/models/help.rb
+++ b/app/models/help.rb
@@ -1,3 +1,17 @@
+# == Schema Information
+#
+# Table name: helps
+#
+# id :integer not null, primary key
+# created_at :datetime not null
+# updated_at :datetime not null
+# about_us :text(65535)
+# agreement :text(65535)
+# status :text(65535)
+# help_center :text(65535)
+# join_us :text(65535)
+#
+
class Help < ApplicationRecord
-end
\ No newline at end of file
+end
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 3595c7541..bb0f5c008 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -11,7 +11,7 @@
# category_id :integer
# status_id :integer not null
# assigned_to_id :integer
-# priority_id :integer not null
+# priority_id :integer
# fixed_version_id :integer
# author_id :integer not null
# created_on :datetime
@@ -51,7 +51,7 @@
class Issue < ApplicationRecord
#issue_type 1为普通,2为悬赏
include DunCheckAble
- belongs_to :project, :counter_cache => true
+ belongs_to :project, counter_cache: true, touch: true
belongs_to :tracker,optional: true
has_many :project_trends, as: :trend, dependent: :destroy
has_one :pull_request
@@ -74,10 +74,34 @@ class Issue < ApplicationRecord
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)}
+ after_create :incre_project_common, :incre_user_statistic, :incre_platform_statistic
after_update :change_versions_count
- after_destroy :update_closed_issues_count_in_project!
+ after_destroy :update_closed_issues_count_in_project!, :decre_project_common, :decre_user_statistic, :decre_platform_statistic
+ def incre_project_common
+ CacheAsyncSetJob.perform_later("project_common_service", {issues: 1}, self.project_id)
+ end
+
+ def decre_project_common
+ CacheAsyncSetJob.perform_later("project_common_service", {issues: -1}, self.project_id)
+ end
+
+ def incre_user_statistic
+ CacheAsyncSetJob.perform_later("user_statistic_service", {issue_count: 1}, self.author_id)
+ end
+
+ def decre_user_statistic
+ CacheAsyncSetJob.perform_later("user_statistic_service", {issue_count: -1}, self.author_id)
+ end
+
+ def incre_platform_statistic
+ CacheAsyncSetJob.perform_later("platform_statistic_service", {issue_count: 1})
+ end
+
+ def decre_platform_statistic
+ CacheAsyncSetJob.perform_later("platform_statistic_service", {issue_count: -1})
+ end
def get_assign_user
User&.find_by_id(self.assigned_to_id) if self.assigned_to_id.present?
@@ -130,6 +154,10 @@ class Issue < ApplicationRecord
end
end
+ def is_collaborators?
+ self.assigned_to_id.present? ? self.project.member?(self.assigned_to_id) : false
+ end
+
def get_issue_tags_name
if issue_tags.present?
issue_tags.select(:name).uniq.pluck(:name).join(",")
diff --git a/app/models/laboratory.rb b/app/models/laboratory.rb
index 1cf5c4adf..675cb525b 100644
--- a/app/models/laboratory.rb
+++ b/app/models/laboratory.rb
@@ -10,6 +10,12 @@
# sync_course :boolean default("0")
# sync_subject :boolean default("0")
# sync_shixun :boolean default("0")
+# is_local :boolean default("0")
+#
+# Indexes
+#
+# index_laboratories_on_identifier (identifier) UNIQUE
+# index_laboratories_on_school_id (school_id)
#
class Laboratory < ApplicationRecord
@@ -69,74 +75,6 @@ class Laboratory < ApplicationRecord
RequestStore.store[:current_laboratory] ||= User.anonymous
end
- def shixuns
- if main_site?
- not_shixun_ids = Shixun.joins(:laboratory_shixuns).where("laboratory_shixuns.laboratory_id != #{Laboratory.current.id}")
- Shixun.where.not(id: not_shixun_ids.pluck(:shixun_id))
- elsif sync_shixun
- laboratory_shixun_ids = laboratory_shixuns.pluck(:shixun_id)
- school_shixun_ids = Shixun.joins("join user_extensions on shixuns.user_id=user_extensions.user_id").where(user_extensions: { school_id: school_id }).pluck(:id)
- shixun_ids = laboratory_shixun_ids + school_shixun_ids
- Shixun.where(id: shixun_ids.uniq)
- else
- Shixun.joins(:laboratory_shixuns).where(laboratory_shixuns: { laboratory_id: id })
- end
- end
-
- def subjects
- if main_site?
- not_subject_ids = Subject.joins(:laboratory_subjects).where("laboratory_subjects.laboratory_id != #{Laboratory.current.id}")
- Subject.where.not(id: not_subject_ids.pluck(:subject_id))
- elsif sync_subject
- laboratory_subject_ids = laboratory_subjects.pluck(:subject_id)
- school_subject_ids = Subject.joins("join user_extensions on subjects.user_id=user_extensions.user_id").where(user_extensions: { school_id: school_id }).pluck(:id)
- subject_ids = laboratory_subject_ids + school_subject_ids
- Subject.where(id: subject_ids.uniq)
- else
- Subject.joins(:laboratory_subjects).where(laboratory_subjects: { laboratory_id: id })
- end
- end
-
- def all_courses
- main_site? || !sync_course ? courses : courses.or(Course.where(school_id: school_id))
- end
-
- def shixun_repertoires
- where_sql = ShixunTagRepertoire.where("shixun_tag_repertoires.tag_repertoire_id = tag_repertoires.id")
-
- # 云上实验室过滤
- unless main_site?
- where_sql = where_sql.joins("JOIN laboratory_shixuns ls ON ls.shixun_id = shixun_tag_repertoires.shixun_id "\
- "AND ls.laboratory_id = #{id}")
- end
- where_sql = where_sql.select('1').to_sql
- tags = TagRepertoire.where("EXISTS(#{where_sql})").distinct.includes(sub_repertoire: :repertoire)
-
- tags_map = tags.group_by(&:sub_repertoire)
- sub_reps_map = tags_map.keys.group_by(&:repertoire)
-
- sub_reps_map.keys.sort_by(&:updated_at).reverse.map do |repertoire|
- repertoire_hash = repertoire.as_json(only: %i[id name])
- repertoire_hash[:sub_repertoires] =
- sub_reps_map[repertoire].sort_by(&:updated_at).reverse.map do |sub_repertoire|
- sub_repertoire_hash = sub_repertoire.as_json(only: %i[id name])
- sub_repertoire_hash[:tags] = tags_map[sub_repertoire].sort_by(&:updated_at).reverse.map { |tag| tag.as_json(only: %i[id name]) }
- sub_repertoire_hash
- end
- repertoire_hash
- end
- end
-
- def subject_repertoires
- exist_sql = Subject.where('subjects.repertoire_id = repertoires.id')
-
- unless main_site?
- exist_sql = exist_sql.joins(:laboratory_subjects).where(laboratory_subjects: { laboratory_id: id })
- end
-
- Repertoire.where("EXISTS(#{exist_sql.select('1').to_sql})").order(updated_at: :desc).distinct
- end
-
# 是否为主站
def main_site?
id == 1
diff --git a/app/models/laboratory_setting.rb b/app/models/laboratory_setting.rb
index 5013dd547..61c677def 100644
--- a/app/models/laboratory_setting.rb
+++ b/app/models/laboratory_setting.rb
@@ -6,6 +6,10 @@
# laboratory_id :integer
# config :text(65535)
#
+# Indexes
+#
+# index_laboratory_settings_on_laboratory_id (laboratory_id)
+#
class LaboratorySetting < ApplicationRecord
belongs_to :laboratory
diff --git a/app/models/license.rb b/app/models/license.rb
index 72414e4cb..2baebb4fc 100644
--- a/app/models/license.rb
+++ b/app/models/license.rb
@@ -7,7 +7,6 @@
# content :text(65535)
# created_at :datetime not null
# updated_at :datetime not null
-# is_secret :boolean default("0")
#
class License < ApplicationRecord
diff --git a/app/models/live_link.rb b/app/models/live_link.rb
index 9c6531f87..c1c0bf750 100644
--- a/app/models/live_link.rb
+++ b/app/models/live_link.rb
@@ -1,3 +1,27 @@
+# == Schema Information
+#
+# Table name: live_links
+#
+# id :integer not null, primary key
+# course_id :integer
+# user_id :integer
+# url :string(255)
+# description :text(65535)
+# on_status :boolean default("0")
+# created_at :datetime not null
+# updated_at :datetime not null
+# course_name :string(255)
+# platform :string(255)
+# live_time :datetime
+# duration :integer
+# position :integer
+#
+# Indexes
+#
+# index_live_links_on_course_id (course_id)
+# index_live_links_on_user_id (user_id)
+#
+
class LiveLink < ApplicationRecord
# belongs_to :course
belongs_to :user
diff --git a/app/models/member.rb b/app/models/member.rb
index 408710a03..521f939c5 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -11,24 +11,26 @@
# course_group_id :integer default("0")
# is_collect :integer default("1")
# graduation_group_id :integer default("0")
-# is_apply_signature :boolean default("0")
+# team_user_id :integer
#
# Indexes
#
# index_members_on_course_id (course_id)
# index_members_on_project_id (project_id)
+# index_members_on_team_user_id (team_user_id)
# index_members_on_user_id (user_id)
# index_members_on_user_id_and_project_id (user_id,project_id,course_id) UNIQUE
#
-class Member < ApplicationRecord
- belongs_to :user
- # belongs_to :course, optional: true
- belongs_to :project, optional: true
-
- has_many :member_roles, dependent: :destroy
- has_many :roles, through: :member_roles
-
- validates :user_id, :project_id, presence: true
-
-end
+class Member < ApplicationRecord
+ belongs_to :user
+ # belongs_to :course, optional: true
+ belongs_to :project, optional: true
+ belongs_to :team_user, optional: true
+
+ has_many :member_roles, dependent: :destroy
+ has_many :roles, through: :member_roles
+
+ validates :user_id, :project_id, presence: true
+
+end
diff --git a/app/models/message_detail.rb b/app/models/message_detail.rb
index 51f4b17a9..c009b4744 100644
--- a/app/models/message_detail.rb
+++ b/app/models/message_detail.rb
@@ -1,3 +1,18 @@
+# == Schema Information
+#
+# Table name: message_details
+#
+# id :integer not null, primary key
+# content :text(4294967295)
+# message_id :integer
+# created_at :datetime not null
+# updated_at :datetime not null
+#
+# Indexes
+#
+# index_message_details_on_message_id (message_id)
+#
+
class MessageDetail < ApplicationRecord
# belongs_to :message, :touch => true
validates :content, length: { maximum: 10000, too_long: "内容不能超过10000个字符" }
diff --git a/app/models/message_template.rb b/app/models/message_template.rb
new file mode 100644
index 000000000..c9812e511
--- /dev/null
+++ b/app/models/message_template.rb
@@ -0,0 +1,108 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+class MessageTemplate < ApplicationRecord
+
+ def self.build_init_data
+ self.create(type: 'MessageTemplate::FollowedTip', sys_notice: '
{nickname} 关注了你', notification_url: '{baseurl}/{login}')
+ email_html = File.read("#{email_template_html_dir}/issue_assigned.html")
+ self.create(type: 'MessageTemplate::IssueAssigned', sys_notice: '{nickname1}在
{nickname2}/{repository} 指派给你一个疑修:
{title} ', notification_url: '{baseurl}/{owner}/{identifier}/issues/{id}', email: email_html, email_title: 'GitLink: {nickname1} 在 {nickname2}/{repository} 指派给你一个疑修')
+ self.create(type: 'MessageTemplate::IssueAssignerExpire', sys_notice: '您负责的疑修
{title} 已临近截止日期,请尽快处理', notification_url: '{baseurl}/{owner}/{identifier}/issues/{id}')
+ self.create(type: 'MessageTemplate::IssueAtme', sys_notice: '
{nickname} 在疑修
{title} 中@我', notification_url: '{baseurl}/{owner}/{identifier}/issues/{id}')
+ email_html = File.read("#{email_template_html_dir}/issue_changed.html")
+ self.create(type: 'MessageTemplate::IssueChanged', sys_notice: '在项目
{nickname2}/{repository} 的疑修
{title} 中:{ifassigner}{nickname1}将负责人从
{assigner1} 修改为
{assigner2} {endassigner}{ifstatus}{nickname1}将状态从
{status1} 修改为
{status2} {endstatus}{iftracker}{nickname1}将类型从
{tracker1} 修改为
{tracker2} {endtracker}{ifpriority}{nickname1}将优先级从
{priority1} 修改为
{priority2} {endpriority}{ifmilestone}{nickname1}将里程碑从
{milestone1} 修改为
{milestone2} {endmilestone}{iftag}{nickname1}将标记从
{tag1} 修改为
{tag2} {endtag}{ifdoneratio}{nickname1}将完成度从
{doneratio1} 修改为
{doneratio2} {enddoneratio}{ifbranch}{nickname1}将指定分支从
{branch1} 修改为
{branch2} {endbranch}{ifstartdate}{nickname1}将开始日期从
{startdate1} 修改为
{startdate2} {endstartdate}{ifduedate}{nickname1}将结束日期从
{duedate1} 修改为
{duedate2} {endduedate}', email: email_html, email_title: 'GitLink: 疑修 {title} 有状态变更', notification_url: '{baseurl}/{owner}/{identifier}/issues/{id}')
+ self.create(type: 'MessageTemplate::IssueCreatorExpire', sys_notice: '您发布的疑修
{title} 已临近截止日期,请尽快处理', notification_url: '{baseurl}/{owner}/{identifier}/issues/{id}')
+ email_html = File.read("#{email_template_html_dir}/issue_deleted.html")
+ self.create(type: 'MessageTemplate::IssueDeleted', sys_notice: '{nickname}已将疑修
{title} 删除', email: email_html, email_title: 'GitLink: 疑修 {title} 有状态变更', notification_url: '')
+ self.create(type: 'MessageTemplate::IssueJournal', sys_notice: '{nickname}评论疑修{title}:
{notes} ', notification_url: '{baseurl}/{owner}/{identifier}/issues/{id}')
+ self.create(type: 'MessageTemplate::LoginIpTip', sys_notice: '您的账号{nickname}于{login_time)在非常用的IP地址{ip}登录,如非本人操作,请立即修改密码', notification_url: '')
+ email_html = File.read("#{email_template_html_dir}/organization_joined.html")
+ self.create(type: 'MessageTemplate::OrganizationJoined', sys_notice: '你已加入
{organization} 组织', notification_url: '{baseurl}/{login}', email: email_html, email_title: 'GitLink: 你已加入 {organization} 组织')
+ email_html = File.read("#{email_template_html_dir}/organization_left.html")
+ self.create(type: 'MessageTemplate::OrganizationLeft', sys_notice: '你已被移出
{organization} 组织', notification_url: '', email: email_html, email_title: 'GitLink: 你已被移出 {organization} 组织')
+ self.create(type: 'MessageTemplate::ProjectDeleted', sys_notice: '你关注的仓库{nickname}/{repository}已被删除', notification_url: '')
+ self.create(type: 'MessageTemplate::ProjectFollowed', sys_notice: '
{nickname} 关注了你管理的仓库', notification_url: '{baseurl}/{login}')
+ self.create(type: 'MessageTemplate::ProjectForked', sys_notice: '
{nickname1} 复刻了你管理的仓库{nickname1}/{repository1}到{nickname2}/{repository2}', notification_url: '{baseurl}/{owner}/{identifier}')
+ email_html = File.read("#{email_template_html_dir}/project_issue.html")
+ self.create(type: 'MessageTemplate::ProjectIssue', sys_notice: '{nickname1}在
{nickname2}/{repository} 新建疑修:
{title} ', notification_url: '{baseurl}/{owner}/{identifier}/issues/{id}', email: email_html, email_title: 'GitLink: {nickname1} 在 {nickname2}/{repository} 新建了一个疑修')
+ email_html = File.read("#{email_template_html_dir}/project_joined.html")
+ self.create(type: 'MessageTemplate::ProjectJoined', sys_notice: '你已加入
{repository} 项目', notification_url: '{baseurl}/{owner}/{identifier}', email: email_html, email_title: 'GitLink: 你已加入 {repository} 项目')
+ email_html = File.read("#{email_template_html_dir}/project_left.html")
+ self.create(type: 'MessageTemplate::ProjectLeft', sys_notice: '你已被移出
{repository} 项目', notification_url: '', email: email_html, email_title: 'GitLink: 你已被移出 {repository} 项目')
+ email_html = File.read("#{email_template_html_dir}/project_member_joined.html")
+ self.create(type: 'MessageTemplate::ProjectMemberJoined', sys_notice: '
{nickname1} 已加入项目
{nickname2}/{repository} ', notification_url: '{baseurl}/{owner}/{identifier}', email: email_html, email_title: 'GitLink: {nickname1} 已加入项目 {nickname2}/{repository}')
+ email_html = File.read("#{email_template_html_dir}/project_member_left.html")
+ self.create(type: 'MessageTemplate::ProjectMemberLeft', sys_notice: '
{nickname1} 已被移出项目
{nickname2}/{repository} ', notification_url: '{baseurl}/{owner}/{identifier}', email: email_html, email_title: 'GitLink: {nickname1} 已被移出项目 {nickname2}/{repository}')
+ self.create(type: 'MessageTemplate::ProjectMilestone', sys_notice: '{nickname1}在
{nickname2}/{repository} 创建了一个里程碑:
{title} ', notification_url: '{baseurl}/{owner}/{identifier}/milestones/{id}')
+ self.create(type: 'MessageTemplate::ProjectPraised', sys_notice: '
{nickname} 点赞了你管理的仓库', notification_url: '{baseurl}/{login}')
+ email_html = File.read("#{email_template_html_dir}/project_pull_request.html")
+ self.create(type: 'MessageTemplate::ProjectPullRequest', sys_notice: '{nickname1}在
{nickname2}/{repository} 提交了一个合并请求:
{title} ', notification_url: '{baseurl}/{owner}/{identifier}/pulls/{id}', email: email_html, email_title: 'GitLink: {nickname1} 在 {nickname2}/{repository} 提交了一个合并请求')
+ email_html = File.read("#{email_template_html_dir}/project_role.html")
+ self.create(type: 'MessageTemplate::ProjectRole', sys_notice: '仓库
{nickname}/{repository} 已把你的角色改为
{role} ', email: email_html, email_title: 'GitLink: 在 {nickname}/{repository} 项目你的账号有权限变更', notification_url: '{baseurl}/{owner}/{identifier}')
+ email_html = File.read("#{email_template_html_dir}/project_setting_changed.html")
+ self.create(type: 'MessageTemplate::ProjectSettingChanged', sys_notice: '{nickname1}更改了
{nickname2}/{repository} 仓库设置:{ifname}更改项目名称为"
{name} "{endname}{ifidentifier}更改项目标识为"
{identifier} "{endidentifier}{ifdescription}更改项目简介为"
{description} "{enddescription}{ifcategory}更改项目类别为"
{category} "{endcategory}{iflanguage}更改项目语言为"
{language} "{endlanguage}{ifpermission}将仓库设为"
{permission} "{endpermission}{ifnavbar}将项目导航更改为"
{navbar} "{endnavbar}', notification_url: '{baseurl}/{owner}/{identifier}/settings', email: email_html, email_title: 'GitLink: 您管理的仓库 {nickname2}/{repository} 仓库设置已被更改')
+ self.create(type: 'MessageTemplate::ProjectTransfer', sys_notice: '你关注的仓库{nickname1}/{repository1}已被转移至{nickname2}/{repository2}', notification_url: '{baseurl}/{owner}/{identifier}')
+ self.create(type: 'MessageTemplate::ProjectVersion', sys_notice: '{nickname1}在
{nickname2}/{repository} 创建了发行版:
{title} ', notification_url: '{baseurl}/{owner}/{identifier}/releases')
+ email_html = File.read("#{email_template_html_dir}/pull_request_assigned.html")
+ self.create(type: 'MessageTemplate::PullRequestAssigned', sys_notice: '{nickname1}在
{nickname2}/{repository} 指派给你一个合并请求:
{title}', notification_url: '{baseurl}/{owner}/{identifier}/pulls/{id}', email: email_html, email_title: 'GitLink: {nickname1} 在 {nickname2}/{repository} 指派给你一个合并请求')
+ self.create(type: 'MessageTemplate::PullRequestAtme', sys_notice: '{nickname} 在合并请求 {title} 中@我', notification_url: '{baseurl}/{owner}/{identifier}/pulls/{id}')
+ email_html = File.read("#{email_template_html_dir}/pull_request_changed.html")
+ self.create(type: 'MessageTemplate::PullRequestChanged', sys_notice: '在项目{nickname2}/{repository}的合并请求 {title} 中:{ifassigner}{nickname1}将审查成员从 {assigner1} 修改为 {assigner2} {endassigner}{ifmilestone}{nickname1}将里程碑从 {milestone1} 修改为 {milestone2} {endmilestone}{iftag}{nickname1}将标记从 {tag1} 修改为 {tag2} {endtag}{ifpriority}{nickname1}将优先级从 {priority1} 修改为 {priority2} {endpriority}', email: email_html, email_title: 'GitLink: 合并请求 {title} 有状态变更', notification_url: '{baseurl}/{owner}/{identifier}/pulls/{id}')
+ email_html = File.read("#{email_template_html_dir}/pull_request_closed.html")
+ self.create(type: 'MessageTemplate::PullRequestClosed', sys_notice: '你提交的合并请求:{title} 被拒绝 ', email: email_html, email_title: 'GitLink: 合并请求 {title} 有状态变更', notification_url: '')
+ self.create(type: 'MessageTemplate::PullRequestJournal', sys_notice: '{nickname}评论合并请求{title}:{notes} ', notification_url: '{baseurl}/{owner}/{identifier}/pulls/{id}')
+ email_html = File.read("#{email_template_html_dir}/pull_request_merged.html")
+ self.create(type: 'MessageTemplate::PullRequestMerged', sys_notice: '你提交的合并请求:{title} 已通过 ', email: email_html, email_title: 'GitLink: 合并请求 {title} 有状态变更', notification_url: '{baseurl}/{owner}/{identifier}/pulls/{id}')
+ email_html = File.read("#{email_template_html_dir}/team_joined.html")
+ self.create(type: 'MessageTemplate::TeamJoined', sys_notice: '你已被拉入组织 {organization} 的 {team} 团队,拥有{role} 权限', email: email_html, email_title: 'GitLink: 在 {organization} 组织你的账号有权限变更', notification_url: '{baseurl}/{login}')
+ email_html = File.read("#{email_template_html_dir}/team_left.html")
+ self.create(type: 'MessageTemplate::TeamLeft', sys_notice: '你已被移出组织 {organization} 的 {team} 团队', email: email_html, email_title: 'GitLink: 在 {organization} 组织你的账号有权限变更', notification_url: '{baseurl}/{login}')
+ end
+
+ def self.sys_notice
+ self.last&.sys_notice
+ end
+
+ def self.email
+ self.last&.email
+ end
+
+ def self.email_title
+ self.last&.email_title
+ end
+
+ def self.notification_url
+ self.last&.notification_url.gsub('{baseurl}', base_url)
+ end
+
+ def self.base_url
+ Rails.application.config_for(:configuration)['platform_url']
+ end
+
+ def self.receivers_string(receivers)
+ receivers.pluck(:id).join(",")
+ end
+
+ def self.receivers_email_string(receivers)
+ receivers.pluck(:mail).join(",")
+ end
+
+ def self.email_template_html_dir
+ "#{Rails.root}/public/message_template"
+ end
+
+ def simple_type
+ self.type.split("::")[-1]
+ end
+end
diff --git a/app/models/message_template/followed_tip.rb b/app/models/message_template/followed_tip.rb
new file mode 100644
index 000000000..6231db595
--- /dev/null
+++ b/app/models/message_template/followed_tip.rb
@@ -0,0 +1,24 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# 被关注提示
+class MessageTemplate::FollowedTip < MessageTemplate
+
+ # MessageTemplate::FollowedTip.get_message_content(User.where(login: 'yystopf'), User.last)
+ def self.get_message_content(receivers, followeder)
+ return receivers_string(receivers), sys_notice.gsub('{nickname}', followeder&.real_name), notification_url.gsub('{login}', followeder.login)
+ rescue
+ return '', '', ''
+ end
+end
diff --git a/app/models/message_template/issue_assigned.rb b/app/models/message_template/issue_assigned.rb
new file mode 100644
index 000000000..7ce7f10c2
--- /dev/null
+++ b/app/models/message_template/issue_assigned.rb
@@ -0,0 +1,65 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# 有新指派给我的疑修
+class MessageTemplate::IssueAssigned < MessageTemplate
+
+ # MessageTemplate::IssueAssigned.get_message_content(User.where(login: 'yystopf'), User.last, Issue.last)
+ def self.get_message_content(receivers, operator, issue)
+ receivers.each do |receiver|
+ if receiver.user_template_message_setting.present?
+ receivers = receivers.where.not(id: receiver.id) unless receiver.user_template_message_setting.notification_body["Normal::IssueAssigned"]
+ end
+ end
+ return '', '', '' if receivers.blank?
+ 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)
+ return receivers_string(receivers), content, url
+ rescue => e
+ Rails.logger.info("MessageTemplate::IssueAssigned.get_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+
+ def self.get_email_message_content(receiver, operator, issue)
+ if receiver.user_template_message_setting.present?
+ return '', '', '' unless receiver.user_template_message_setting.email_body["Normal::IssueAssigned"]
+ project = issue&.project
+ owner = project&.owner
+ title = email_title
+ title.gsub!('{nickname1}', operator&.real_name)
+ title.gsub!('{nickname2}', owner&.real_name)
+ title.gsub!('{repository}', project&.name)
+ content = email
+ content.gsub!('{receiver}', receiver&.real_name)
+ content.gsub!('{nickname1}', operator&.real_name)
+ content.gsub!('{login1}', operator&.login)
+ content.gsub!('{nickname2}', owner&.real_name)
+ content.gsub!('{login2}', owner&.login)
+ content.gsub!('{identifier}', project&.identifier)
+ content.gsub!('{repository}', project&.name)
+ content.gsub!('{baseurl}', base_url)
+ content.gsub!('{title}', issue&.subject)
+ content.gsub!('{id}', issue&.id.to_s)
+
+ return receiver&.mail, title, content
+ else
+ return '', '', ''
+ end
+ rescue => e
+ Rails.logger.info("MessageTemplate::IssueAssigned.get_email_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+end
diff --git a/app/models/message_template/issue_assigner_expire.rb b/app/models/message_template/issue_assigner_expire.rb
new file mode 100644
index 000000000..610c51175
--- /dev/null
+++ b/app/models/message_template/issue_assigner_expire.rb
@@ -0,0 +1,29 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# 我负责的疑修截止日期到达最后一天
+class MessageTemplate::IssueAssignerExpire < MessageTemplate
+
+ # MessageTemplate::IssueAssignerExpire.get_message_content(User.where(login: 'yystopf'), Issue.last)
+ def self.get_message_content(receivers, issue)
+ 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)
+ return receivers_string(receivers), content, url
+ rescue => e
+ Rails.logger.info("MessageTemplate::IssueAssignerExpire.get_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+end
diff --git a/app/models/message_template/issue_atme.rb b/app/models/message_template/issue_atme.rb
new file mode 100644
index 000000000..04eb132e4
--- /dev/null
+++ b/app/models/message_template/issue_atme.rb
@@ -0,0 +1,29 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# 在疑修中@我
+class MessageTemplate::IssueAtme < MessageTemplate
+
+ # MessageTemplate::IssueAtme.get_message_content(User.where(login: 'yystopf'), User.last, Issue.last)
+ def self.get_message_content(receivers, operator, issue)
+ 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)
+ return receivers_string(receivers), content, url
+ rescue => e
+ Rails.logger.info("MessageTemplate::IssueAtme.get_message_content [ERROR] #{e}")
+ return 0, '', ''
+ end
+end
diff --git a/app/models/message_template/issue_changed.rb b/app/models/message_template/issue_changed.rb
new file mode 100644
index 000000000..4d9e132cb
--- /dev/null
+++ b/app/models/message_template/issue_changed.rb
@@ -0,0 +1,372 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# 我创建或负责的疑修状态变更
+class MessageTemplate::IssueChanged < MessageTemplate
+
+ # MessageTemplate::IssueChanged.get_message_content(User.where(login: 'yystopf'), User.last, Issue.last, {status_id: [1, 2], assigned_to_id: [nil, 203], tracker_id: [4, 3], priority_id: [2, 4], fixed_version_id: [nil, 5], due_date: ['', '2021-09-11'], done_ratio: [0, 40], issue_tags_value: ["", "7"], branch_name: ["", "master"]})
+ def self.get_message_content(receivers, operator, issue, change_params)
+ receivers.each do |receiver|
+ if receiver.user_template_message_setting.present?
+ receivers = receivers.where.not(id: receiver.id) unless receiver.user_template_message_setting.notification_body["CreateOrAssign::IssueChanged"]
+ end
+ end
+ return '', '', '' if receivers.blank?
+ return '', '', '' if change_params.blank?
+ 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)
+ 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])
+ if change_count > 1
+ content.sub!('{ifassigner}', ' ')
+ else
+ content.sub!('{ifassigner}', '')
+ end
+ content.sub!('{endassigner}', '')
+ content.gsub!('{assigner1}', assigner1.present? ? assigner1&.real_name : '未指派成员')
+ content.gsub!('{assigner2}', assigner2.present? ? assigner2&.real_name : '未指派成员')
+ else
+ content.gsub!(/({ifassigner})(.*)({endassigner})/, '')
+ end
+ # 疑修状态修改
+ if change_params[:status_id].present?
+ status1 = IssueStatus.find_by_id(change_params[:status_id][0])
+ status2 = IssueStatus.find_by_id(change_params[:status_id][1])
+ if change_count > 1
+ content.sub!('{ifstatus}', ' ')
+ else
+ content.sub!('{ifstatus}', '')
+ end
+ content.sub!('{endstatus}', '')
+ content.gsub!('{status1}', status1&.name)
+ content.gsub!('{status2}', status2&.name)
+ else
+ content.gsub!(/({ifstatus})(.*)({endstatus})/, '')
+ end
+ # 疑修类型修改
+ if change_params[:tracker_id].present?
+ tracker1 = Tracker.find_by_id(change_params[:tracker_id][0])
+ tracker2 = Tracker.find_by_id(change_params[:tracker_id][1])
+ if change_count > 1
+ content.sub!('{iftracker}', ' ')
+ else
+ content.sub!('{iftracker}', '')
+ end
+ content.sub!('{endtracker}', '')
+ content.gsub!('{tracker1}', tracker1&.name)
+ content.gsub!('{tracker2}', tracker2&.name)
+ else
+ content.gsub!(/({iftracker})(.*)({endtracker})/, '')
+ end
+ # 疑修里程碑修改
+ if change_params[:fixed_version_id].present?
+ fix_version1 = Version.find_by_id(change_params[:fixed_version_id][0])
+ fix_version2 = Version.find_by_id(change_params[:fixed_version_id][1])
+ if change_count > 1
+ content.sub!('{ifmilestone}', ' ')
+ else
+ content.sub!('{ifmilestone}', '')
+ end
+ content.sub!('{endmilestone}', '')
+ content.gsub!('{milestone1}', fix_version1.present? ? fix_version1&.name : '未选择里程碑')
+ content.gsub!('{milestone2}', fix_version2.present? ? fix_version2&.name : '未选择里程碑')
+ else
+ content.gsub!(/({ifmilestone})(.*)({endmilestone})/, '')
+ end
+ # 疑修标记修改
+ if change_params[:issue_tags_value].present?
+ issue_tags1 = IssueTag.where(id: change_params[:issue_tags_value][0]).distinct
+ issue_tags2 = IssueTag.where(id: change_params[:issue_tags_value][1]).distinct
+ tag1 = issue_tags1.pluck(:name).join(",").blank? ? '未选择标记' : issue_tags1.pluck(:name).join(",")
+ tag2 = issue_tags2.pluck(:name).join(",").blank? ? '未选择标记' : issue_tags2.pluck(:name).join(",")
+ if change_count > 1
+ content.sub!('{iftag}', ' ')
+ else
+ content.sub!('{iftag}', '')
+ end
+ content.sub!('{endtag}', '')
+ content.gsub!('{tag1}', tag1)
+ content.gsub!('{tag2}', tag2)
+ else
+ content.gsub!(/({iftag})(.*)({endtag})()/, '')
+ end
+ # 疑修优先级修改
+ if change_params[:priority_id].present?
+ priority1 = IssuePriority.find_by_id(change_params[:priority_id][0])
+ priority2 = IssuePriority.find_by_id(change_params[:priority_id][1])
+ if change_count > 1
+ content.sub!('{ifpriority}', ' ')
+ else
+ content.sub!('{ifpriority}', '')
+ end
+ content.sub!('{endpriority}', '')
+ content.gsub!('{priority1}', priority1&.name)
+ content.gsub!('{priority2}', priority2&.name)
+ else
+ content.gsub!(/({ifpriority})(.*)({endpriority})/, '')
+ end
+ # 疑修完成度修改
+ if change_params[:done_ratio].present?
+ doneratio1 = change_params[:done_ratio][0]
+ doneratio2 = change_params[:done_ratio][1]
+ if change_count > 1
+ content.sub!('{ifdoneratio}', ' ')
+ else
+ content.sub!('{ifdoneratio}', '')
+ end
+ content.sub!('{enddoneratio}', '')
+ content.gsub!('{doneratio1}', "#{doneratio1}%")
+ content.gsub!('{doneratio2}', "#{doneratio2}%")
+ else
+ content.gsub!(/({ifdoneratio})(.*)({enddoneratio})/, '')
+ end
+ # 疑修指定分支修改
+ if change_params[:branch_name].present?
+ branch1 = change_params[:branch_name][0].blank? ? '分支未指定' : change_params[:branch_name][0]
+ branch2 = change_params[:branch_name][1].blank? ? '分支未指定' : change_params[:branch_name][1]
+ if change_count > 1
+ content.sub!('{ifbranch}', ' ')
+ else
+ content.sub!('{ifbranch}', '')
+ end
+ content.sub!('{endbranch}', '')
+ content.gsub!('{branch1}', branch1)
+ content.gsub!('{branch2}', branch2)
+ else
+ content.gsub!(/({ifbranch})(.*)({endbranch})/, '')
+ end
+ # 疑修开始日期修改
+ if change_params[:start_date].present?
+ startdate1 = change_params[:start_date][0].blank? ? "未选择开始日期" : change_params[:start_date][0]
+ startdate2 = change_params[:start_date][1].blank? ? "未选择开始日期" : change_params[:start_date][1]
+ if change_count > 1
+ content.sub!('{ifstartdate}', ' ')
+ else
+ content.sub!('{ifstartdate}', '')
+ end
+ content.sub!('{endstartdate}', '')
+ content.gsub!('{startdate1}', startdate1 )
+ content.gsub!('{startdate2}', startdate2)
+ else
+ content.gsub!(/({ifstartdate})(.*)({endstartdate})/, '')
+ end
+ # 疑修结束日期修改
+ if change_params[:due_date].present?
+ duedate1 = change_params[:due_date][0].blank? ? '未选择结束日期' : change_params[:due_date][0]
+ duedate2 = change_params[:due_date][1].blank? ? '未选择结束日期' : change_params[:due_date][1]
+ if change_count > 1
+ content.sub!('{ifduedate}', ' ')
+ else
+ content.sub!('{ifduedate}', '')
+ end
+ content.sub!('{endduedate}', '')
+ content.gsub!('{duedate1}', duedate1)
+ content.gsub!('{duedate2}', duedate2)
+ else
+ content.gsub!(/({ifduedate})(.*)({endduedate})/, '')
+ end
+ return receivers_string(receivers), content, url
+ rescue => e
+ Rails.logger.info("MessageTemplate::IssueAssigned.get_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+
+ def self.get_email_message_content(receiver, operator, issue, change_params)
+ return '', '', '' if change_params.blank?
+ if receiver.user_template_message_setting.present?
+ return '', '', '' unless receiver.user_template_message_setting.email_body["CreateOrAssign::IssueChanged"]
+ project = issue&.project
+ owner = project&.owner
+ title = email_title
+ title.gsub!('{title}', issue&.subject)
+ content = email
+ content.gsub!('{receiver}', receiver&.real_name)
+ content.gsub!('{baseurl}', base_url)
+ content.gsub!('{login1}', operator&.login)
+ content.gsub!('{nickname1}', operator&.real_name)
+ content.gsub!('{login2}', owner&.login)
+ content.gsub!('{nickname2}', owner&.real_name)
+ content.gsub!('{identifier}', project&.identifier)
+ content.gsub!('{repository}', project&.name)
+ content.gsub!('{title}', issue&.subject)
+ content.gsub!('{id}', issue&.id.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])
+ if change_count > 1
+ content.sub!('{ifassigner}', ' ')
+ else
+ content.sub!('{ifassigner}', '')
+ end
+ content.sub!('{endassigner}', '')
+ content.gsub!('{assigner1}', assigner1.present? ? assigner1&.real_name : '未指派成员')
+ content.gsub!('{assigner2}', assigner2.present? ? assigner2&.real_name : '未指派成员')
+ else
+ content.gsub!(/({ifassigner})(.*)({endassigner})/, '')
+ end
+ # 疑修状态修改
+ if change_params[:status_id].present?
+ status1 = IssueStatus.find_by_id(change_params[:status_id][0])
+ status2 = IssueStatus.find_by_id(change_params[:status_id][1])
+ if change_count > 1
+ content.sub!('{ifstatus}', ' ')
+ else
+ content.sub!('{ifstatus}', '')
+ end
+ content.sub!('{endstatus}', '')
+ content.gsub!('{status1}', status1&.name)
+ content.gsub!('{status2}', status2&.name)
+ else
+ content.gsub!(/({ifstatus})(.*)({endstatus})/, '')
+ end
+ # 疑修类型修改
+ if change_params[:tracker_id].present?
+ tracker1 = Tracker.find_by_id(change_params[:tracker_id][0])
+ tracker2 = Tracker.find_by_id(change_params[:tracker_id][1])
+ if change_count > 1
+ content.sub!('{iftracker}', ' ')
+ else
+ content.sub!('{iftracker}', '')
+ end
+ content.sub!('{endtracker}', '')
+ content.gsub!('{tracker1}', tracker1&.name)
+ content.gsub!('{tracker2}', tracker2&.name)
+ else
+ content.gsub!(/({iftracker})(.*)({endtracker})/, '')
+ end
+ # 疑修里程碑修改
+ if change_params[:fixed_version_id].present?
+ fix_version1 = Version.find_by_id(change_params[:fixed_version_id][0])
+ fix_version2 = Version.find_by_id(change_params[:fixed_version_id][1])
+ if change_count > 1
+ content.sub!('{ifmilestone}', ' ')
+ else
+ content.sub!('{ifmilestone}', '')
+ end
+ content.sub!('{endmilestone}', '')
+ content.gsub!('{milestone1}', fix_version1.present? ? fix_version1&.name : '未选择里程碑')
+ content.gsub!('{milestone2}', fix_version2.present? ? fix_version2&.name : '未选择里程碑')
+ else
+ content.gsub!(/({ifmilestone})(.*)({endmilestone})/, '')
+ end
+ # 疑修标记修改
+ if change_params[:issue_tags_value].present?
+ issue_tags1 = IssueTag.where(id: change_params[:issue_tags_value][0]).distinct
+ issue_tags2 = IssueTag.where(id: change_params[:issue_tags_value][1]).distinct
+ tag1 = issue_tags1.pluck(:name).join(",").blank? ? '未选择标记' : issue_tags1.pluck(:name).join(",")
+ tag2 = issue_tags2.pluck(:name).join(",").blank? ? '未选择标记' : issue_tags2.pluck(:name).join(",")
+ if change_count > 1
+ content.sub!('{iftag}', ' ')
+ else
+ content.sub!('{iftag}', '')
+ end
+ content.sub!('{endtag}', '')
+ content.gsub!('{tag1}', tag1)
+ content.gsub!('{tag2}', tag2)
+ else
+ content.gsub!(/({iftag})(.*)({endtag})()/, '')
+ end
+ # 疑修优先级修改
+ if change_params[:priority_id].present?
+ priority1 = IssuePriority.find_by_id(change_params[:priority_id][0])
+ priority2 = IssuePriority.find_by_id(change_params[:priority_id][1])
+ if change_count > 1
+ content.sub!('{ifpriority}', ' ')
+ else
+ content.sub!('{ifpriority}', '')
+ end
+ content.sub!('{endpriority}', '')
+ content.gsub!('{priority1}', priority1&.name)
+ content.gsub!('{priority2}', priority2&.name)
+ else
+ content.gsub!(/({ifpriority})(.*)({endpriority})/, '')
+ end
+ # 疑修完成度修改
+ if change_params[:done_ratio].present?
+ doneratio1 = change_params[:done_ratio][0]
+ doneratio2 = change_params[:done_ratio][1]
+ if change_count > 1
+ content.sub!('{ifdoneratio}', ' ')
+ else
+ content.sub!('{ifdoneratio}', '')
+ end
+ content.sub!('{enddoneratio}', '')
+ content.gsub!('{doneratio1}', "#{doneratio1}%")
+ content.gsub!('{doneratio2}', "#{doneratio2}%")
+ else
+ content.gsub!(/({ifdoneratio})(.*)({enddoneratio})/, '')
+ end
+ # 疑修指定分支修改
+ if change_params[:branch_name].present?
+ branch1 = change_params[:branch_name][0].blank? ? '分支未指定' : change_params[:branch_name][0]
+ branch2 = change_params[:branch_name][1].blank? ? '分支未指定' : change_params[:branch_name][1]
+ if change_count > 1
+ content.sub!('{ifbranch}', ' ')
+ else
+ content.sub!('{ifbranch}', '')
+ end
+ content.sub!('{endbranch}', '')
+ content.gsub!('{branch1}', branch1)
+ content.gsub!('{branch2}', branch2)
+ else
+ content.gsub!(/({ifbranch})(.*)({endbranch})/, '')
+ end
+ # 疑修开始日期修改
+ if change_params[:start_date].present?
+ startdate1 = change_params[:start_date][0].blank? ? "未选择开始日期" : change_params[:start_date][0]
+ startdate2 = change_params[:start_date][1].blank? ? "未选择开始日期" : change_params[:start_date][1]
+ if change_count > 1
+ content.sub!('{ifstartdate}', ' ')
+ else
+ content.sub!('{ifstartdate}', '')
+ end
+ content.sub!('{endstartdate}', '')
+ content.gsub!('{startdate1}', startdate1 )
+ content.gsub!('{startdate2}', startdate2)
+ else
+ content.gsub!(/({ifstartdate})(.*)({endstartdate})/, '')
+ end
+ # 疑修结束日期修改
+ if change_params[:due_date].present?
+ duedate1 = change_params[:due_date][0].blank? ? '未选择结束日期' : change_params[:due_date][0]
+ duedate2 = change_params[:due_date][1].blank? ? '未选择结束日期' : change_params[:due_date][1]
+ if change_count > 1
+ content.sub!('{ifduedate}', ' ')
+ else
+ content.sub!('{ifduedate}', '')
+ end
+ content.sub!('{endduedate}', '')
+ content.gsub!('{duedate1}', duedate1)
+ content.gsub!('{duedate2}', duedate2)
+ else
+ content.gsub!(/({ifduedate})(.*)({endduedate})/, '')
+ end
+
+ return receiver&.mail, title, content
+ else
+ return '', '', ''
+ end
+
+ rescue => e
+ Rails.logger.info("MessageTemplate::IssueChanged.get_email_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+end
diff --git a/app/models/message_template/issue_creator_expire.rb b/app/models/message_template/issue_creator_expire.rb
new file mode 100644
index 000000000..475c05ce0
--- /dev/null
+++ b/app/models/message_template/issue_creator_expire.rb
@@ -0,0 +1,29 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# 我创建的疑修截止日期到达最后一天
+class MessageTemplate::IssueCreatorExpire < MessageTemplate
+
+ # MessageTemplate::IssueCreatorExpire.get_message_content(User.where(login: 'yystopf'), Issue.last)
+ def self.get_message_content(receivers, issue)
+ 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)
+ return receivers_string(receivers), content, url
+ rescue => e
+ Rails.logger.info("MessageTemplate::IssueAssignerExpire.get_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+end
diff --git a/app/models/message_template/issue_deleted.rb b/app/models/message_template/issue_deleted.rb
new file mode 100644
index 000000000..a3eae75de
--- /dev/null
+++ b/app/models/message_template/issue_deleted.rb
@@ -0,0 +1,53 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# 我创建或负责的疑修删除
+class MessageTemplate::IssueDeleted < MessageTemplate
+
+ # MessageTemplate::IssueDeleted.get_message_content(User.where(login: 'yystopf'), User.last, "hahah")
+ def self.get_message_content(receivers, operator, issue_title)
+ receivers.each do |receiver|
+ if receiver.user_template_message_setting.present?
+ receivers = receivers.where.not(id: receiver.id) unless receiver.user_template_message_setting.notification_body["CreateOrAssign::IssueChanged"]
+ end
+ end
+ return '', '', '' if receivers.blank?
+ content = sys_notice.gsub('{nickname}', operator&.real_name).gsub('{title}', issue_title)
+ return receivers_string(receivers), content, notification_url
+ rescue => e
+ Rails.logger.info("MessageTemplate::IssueDeleted.get_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+
+ def self.get_email_message_content(receiver, operator, issue_title)
+ if receiver.user_template_message_setting.present?
+ return '', '', '' unless receiver.user_template_message_setting.email_body["CreateOrAssign::IssueChanged"]
+ title = email_title
+ title.gsub!('{title}', issue_title)
+ content = email
+ content.gsub!('{receiver}', receiver&.real_name)
+ content.gsub!('{nickname}', operator&.real_name)
+ content.gsub!('{login}', operator&.login)
+ content.gsub!('{baseurl}', base_url)
+ content.gsub!('{title}', issue_title)
+
+ return receiver&.mail, title, content
+ else
+ return '', '', ''
+ end
+ rescue => e
+ Rails.logger.info("MessageTemplate::IssueDeleted.get_email_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+end
diff --git a/app/models/message_template/issue_journal.rb b/app/models/message_template/issue_journal.rb
new file mode 100644
index 000000000..3286776ae
--- /dev/null
+++ b/app/models/message_template/issue_journal.rb
@@ -0,0 +1,25 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# TODO 我创建或负责的疑修有新的评论
+class MessageTemplate::IssueJournal < MessageTemplate
+
+ # MessageTemplate::IssueJournal.get_message_content(User.where(login: 'yystopf'))
+ def self.get_message_content(receivers)
+ return receivers_string(receivers), content, url
+ rescue => e
+ Rails.logger.info("MessageTemplate::IssueJournal.get_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+end
diff --git a/app/models/message_template/login_ip_tip.rb b/app/models/message_template/login_ip_tip.rb
new file mode 100644
index 000000000..5c7caa936
--- /dev/null
+++ b/app/models/message_template/login_ip_tip.rb
@@ -0,0 +1,25 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# TODO 登录异常提示
+class MessageTemplate::LoginIpTip < MessageTemplate
+
+ # MessageTemplate::LoginIpTip.get_message_content(User.where(login: 'yystopf'))
+ def self.get_message_content(receivers)
+ return receivers_string(receivers), content, url
+ rescue => e
+ Rails.logger.info("MessageTemplate::LoginIpTip.get_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+end
diff --git a/app/models/message_template/organization_joined.rb b/app/models/message_template/organization_joined.rb
new file mode 100644
index 000000000..30f91ac98
--- /dev/null
+++ b/app/models/message_template/organization_joined.rb
@@ -0,0 +1,54 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# 账号被拉入组织
+class MessageTemplate::OrganizationJoined < MessageTemplate
+
+ # MessageTemplate::OrganizationJoined.get_message_content(User.where(login: 'yystopf'), Organization.last)
+ def self.get_message_content(receivers, organization)
+ receivers.each do |receiver|
+ if receiver.user_template_message_setting.present?
+ receivers = receivers.where.not(id: receiver.id) unless receiver.user_template_message_setting.notification_body["Normal::Organization"]
+ end
+ end
+ return '', '', '' if receivers.blank?
+ content = sys_notice.gsub('{organization}', organization&.real_name)
+ url = notification_url.gsub('{login}', organization&.name)
+ return receivers_string(receivers), content, url
+ rescue => e
+ Rails.logger.info("MessageTemplate::OrganizationJoined.get_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+
+ def self.get_email_message_content(receiver, organization)
+ if receiver.user_template_message_setting.present?
+ return '', '', '' unless receiver.user_template_message_setting.email_body["Normal::Organization"]
+ title = email_title
+ title.gsub!('{organization}', organization&.real_name)
+ content = email
+ content.gsub!('{receiver}', receiver&.real_name)
+ content.gsub!('{baseurl}', base_url)
+ content.gsub!('{login}', organization&.login)
+ content.gsub!('{organization}', organization&.real_name)
+
+ return receiver&.mail, title, content
+ else
+ return '', '', ''
+ end
+
+ rescue => e
+ Rails.logger.info("MessageTemplate::OrganizationJoined.get_email_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+end
diff --git a/app/models/message_template/organization_left.rb b/app/models/message_template/organization_left.rb
new file mode 100644
index 000000000..50a771385
--- /dev/null
+++ b/app/models/message_template/organization_left.rb
@@ -0,0 +1,54 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# 账号被移出组织
+class MessageTemplate::OrganizationLeft < MessageTemplate
+
+ # MessageTemplate::OrganizationLeft.get_message_content(User.where(login: 'yystopf'), Organization.last)
+ def self.get_message_content(receivers, organization)
+ receivers.each do |receiver|
+ if receiver.user_template_message_setting.present?
+ receivers = receivers.where.not(id: receiver.id) unless receiver.user_template_message_setting.notification_body["Normal::Organization"]
+ end
+ end
+ return '', '', '' if receivers.blank?
+ content = sys_notice.gsub('{organization}', organization&.real_name)
+ url = notification_url.gsub('{login}', organization&.name)
+ return receivers_string(receivers), content, url
+ rescue => e
+ Rails.logger.info("MessageTemplate::OrganizationLeft.get_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+
+ def self.get_email_message_content(receiver, organization)
+ if receiver.user_template_message_setting.present?
+ return '', '', '' unless receiver.user_template_message_setting.email_body["Normal::Organization"]
+ title = email_title
+ title.gsub!('{organization}', organization&.real_name)
+ content = email
+ content.gsub!('{receiver}', receiver&.real_name)
+ content.gsub!('{baseurl}', base_url)
+ content.gsub!('{login}', organization&.login)
+ content.gsub!('{organization}', organization&.real_name)
+
+ return receiver&.mail, title, content
+ else
+ return '', '', ''
+ end
+
+ rescue => e
+ Rails.logger.info("MessageTemplate::OrganizationLeft.get_email_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+end
diff --git a/app/models/message_template/organization_role.rb b/app/models/message_template/organization_role.rb
new file mode 100644
index 000000000..207712956
--- /dev/null
+++ b/app/models/message_template/organization_role.rb
@@ -0,0 +1,56 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# 账号组织权限变更
+class MessageTemplate::OrganizationRole < MessageTemplate
+
+ # MessageTemplate::OrganizationRole.get_message_content(User.where(login: 'yystopf'), Organization.last, '管理员')
+ def self.get_message_content(receivers, organization, role)
+ receivers.each do |receiver|
+ if receiver.user_template_message_setting.present?
+ receivers = receivers.where.not(id: receiver.id) unless receiver.user_template_message_setting.notification_body["Normal::Permission"]
+ end
+ end
+ return '', '', '' if receivers.blank?
+ content = sys_notice.gsub('{organization}', organization&.real_name).gsub('{role}', role)
+ url = notification_url.gsub('{login}', organization&.login)
+ return receivers_string(receivers), content, url
+ rescue => e
+ Rails.logger.info("MessageTemplate::OrganizationRole.get_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+
+ def self.get_email_message_content(receiver, organization, role)
+ if receiver.user_template_message_setting.present?
+ return '', '', '' unless receiver.user_template_message_setting.email_body["Normal::Permission"]
+ title = email_title
+ title.gsub!('{organization}', organization&.real_name)
+ title.gsub!('{role}', role)
+ content = email
+ content.gsub!('{receiver}', receiver&.real_name)
+ content.gsub!('{baseurl}', base_url)
+ content.gsub!('{login}', organization&.login)
+ content.gsub!('{organization}', organization&.real_name)
+ content.gsub!('{role}', role)
+
+ return receiver&.mail, title, content
+ else
+ return '', '', ''
+ end
+
+ rescue => e
+ Rails.logger.info("MessageTemplate::OrganizationRole.get_email_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+end
diff --git a/app/models/message_template/project_deleted.rb b/app/models/message_template/project_deleted.rb
new file mode 100644
index 000000000..aa45e818e
--- /dev/null
+++ b/app/models/message_template/project_deleted.rb
@@ -0,0 +1,25 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# TODO 我关注的仓库被删除
+class MessageTemplate::ProjectDeleted < MessageTemplate
+
+ # MessageTemplate::ProjectDeleted.get_message_content(User.where(login: 'yystopf'))
+ def self.get_message_content(receivers)
+ return receivers_string(receivers), content, url
+ rescue => e
+ Rails.logger.info("MessageTemplate::ProjectDeleted.get_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+end
diff --git a/app/models/message_template/project_followed.rb b/app/models/message_template/project_followed.rb
new file mode 100644
index 000000000..e9dd9a2a8
--- /dev/null
+++ b/app/models/message_template/project_followed.rb
@@ -0,0 +1,25 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# TODO 我管理的仓库被关注
+class MessageTemplate::ProjectFollowed < MessageTemplate
+
+ # MessageTemplate::ProjectFollowed.get_message_content(User.where(login: 'yystopf'))
+ def self.get_message_content(receivers)
+ return receivers_string(receivers), content, url
+ rescue => e
+ Rails.logger.info("MessageTemplate::ProjectFollowed.get_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+end
diff --git a/app/models/message_template/project_forked.rb b/app/models/message_template/project_forked.rb
new file mode 100644
index 000000000..7cd17222d
--- /dev/null
+++ b/app/models/message_template/project_forked.rb
@@ -0,0 +1,25 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# TODO 我管理的仓库被复刻
+class MessageTemplate::ProjectForked < MessageTemplate
+
+ # MessageTemplate::ProjectForked.get_message_content(User.where(login: 'yystopf'))
+ def self.get_message_content(receivers)
+ return receivers_string(receivers), content, url
+ rescue => e
+ Rails.logger.info("MessageTemplate::ProjectForked.get_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+end
diff --git a/app/models/message_template/project_issue.rb b/app/models/message_template/project_issue.rb
new file mode 100644
index 000000000..5904a4285
--- /dev/null
+++ b/app/models/message_template/project_issue.rb
@@ -0,0 +1,69 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# TODO 我管理/关注的仓库有新的疑修
+class MessageTemplate::ProjectIssue < MessageTemplate
+
+ # MessageTemplate::ProjectIssue.get_message_content(User.where(login: 'yystopf'), User.where(login: 'forgetest1'), User.last, Issue.last)
+ def self.get_message_content(managers, followers, operator, issue)
+ managers.each do |receiver|
+ if receiver.user_template_message_setting.present?
+ managers = managers.where.not(id: receiver.id) unless receiver.user_template_message_setting.notification_body["ManageProject::Issue"]
+ end
+ end
+ project = issue&.project
+ owner = project&.owner
+ 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)
+
+ return receivers_string(receivers), content, url
+ rescue => e
+ Rails.logger.info("MessageTemplate::ProjectIssue.get_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+
+ def self.get_email_message_content(receiver, is_manager, operator, issue)
+ if receiver.user_template_message_setting.present? && is_manager
+ return '', '', '' unless receiver.user_template_message_setting.email_body["ManageProject::Issue"]
+ project = issue&.project
+ owner = project&.owner
+ title = email_title
+ title.gsub!('{nickname1}', operator&.real_name)
+ title.gsub!('{nickname2}', owner&.real_name)
+ title.gsub!('{repository}', project&.name)
+
+ content = email
+ content.gsub!('{receiver}', receiver&.real_name)
+ content.gsub!('{baseurl}', base_url)
+ content.gsub!('{login1}', operator&.login)
+ content.gsub!('{nickname1}', operator&.real_name)
+ content.gsub!('{nickname2}', owner&.real_name)
+ content.gsub!('{repository}', project&.name)
+ content.gsub!('{login2}', owner&.login)
+ content.gsub!('{identifier}', project&.identifier)
+ content.gsub!('{id}', issue&.id.to_s)
+ content.gsub!('{title}', issue&.subject)
+
+ return receiver&.mail, title, content
+ else
+ return '', '', ''
+ end
+
+ rescue => e
+ Rails.logger.info("MessageTemplate::ProjectIssue.get_email_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+end
diff --git a/app/models/message_template/project_joined.rb b/app/models/message_template/project_joined.rb
new file mode 100644
index 000000000..c2d23fbd2
--- /dev/null
+++ b/app/models/message_template/project_joined.rb
@@ -0,0 +1,57 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# 账号被拉入项目
+class MessageTemplate::ProjectJoined < MessageTemplate
+
+ # MessageTemplate::ProjectJoined.get_message_content(User.where(login: 'yystopf'), Project.last)
+ def self.get_message_content(receivers, project)
+ receivers.each do |receiver|
+ if receiver.user_template_message_setting.present?
+ receivers = receivers.where.not(id: receiver.id) unless receiver.user_template_message_setting.notification_body["Normal::Project"]
+ end
+ end
+ return '', '', '' if receivers.blank?
+ content = sys_notice.gsub('{repository}', project&.name)
+ url = notification_url.gsub('{owner}', project&.owner&.login).gsub('{identifier}', project&.identifier)
+ return receivers_string(receivers), content, url
+ rescue => e
+ Rails.logger.info("MessageTemplate::ProjectJoined.get_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+
+ def self.get_email_message_content(receiver, project)
+ if receiver.user_template_message_setting.present?
+ return '', '', '' unless receiver.user_template_message_setting.email_body["Normal::Project"]
+ title = email_title
+ title.gsub!('{repository}', project&.name)
+
+ content = email
+ content.gsub!('{receiver}', receiver&.real_name)
+ content.gsub!('{baseurl}', base_url)
+ content.gsub!('{login}', project&.owner&.login)
+ content.gsub!('{identifier}', project&.identifier)
+ content.gsub!('{nickname}', project&.owner&.real_name)
+ content.gsub!('{repository}', project&.name)
+
+ return receiver&.mail, title, content
+ else
+ return '', '', ''
+ end
+
+ rescue => e
+ Rails.logger.info("MessageTemplate::ProjectJoined.get_email_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+end
diff --git a/app/models/message_template/project_left.rb b/app/models/message_template/project_left.rb
new file mode 100644
index 000000000..6f5bafa02
--- /dev/null
+++ b/app/models/message_template/project_left.rb
@@ -0,0 +1,56 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# 账号被移出项目
+class MessageTemplate::ProjectLeft < MessageTemplate
+
+ # MessageTemplate::ProjectLeft.get_message_content(User.where(login: 'yystopf'), Project.last)
+ def self.get_message_content(receivers, project)
+ receivers.each do |receiver|
+ if receiver.user_template_message_setting.present?
+ receivers = receivers.where.not(id: receiver.id) unless receiver.user_template_message_setting.notification_body["Normal::Project"]
+ end
+ end
+ content = sys_notice.gsub('{repository}', project&.name)
+ url = notification_url.gsub('{owner}', project&.owner&.login).gsub('{identifier}', project&.identifier)
+ return receivers_string(receivers), content, url
+ rescue => e
+ Rails.logger.info("MessageTemplate::ProjectLeft.get_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+
+ def self.get_email_message_content(receiver, project)
+ if receiver.user_template_message_setting.present?
+ return '', '', '' unless receiver.user_template_message_setting.email_body["Normal::Project"]
+ title = email_title
+ title.gsub!('{repository}', project&.name)
+
+ content = email
+ content.gsub!('{receiver}', receiver&.real_name)
+ content.gsub!('{baseurl}', base_url)
+ content.gsub!('{login}', project&.owner&.login)
+ content.gsub!('{identifier}', project&.identifier)
+ content.gsub!('{nickname}', project&.owner&.real_name)
+ content.gsub!('{repository}', project&.name)
+
+ return receiver&.mail, title, content
+ else
+ return '', '', ''
+ end
+
+ rescue => e
+ Rails.logger.info("MessageTemplate::ProjectLeft.get_email_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+end
diff --git a/app/models/message_template/project_member_joined.rb b/app/models/message_template/project_member_joined.rb
new file mode 100644
index 000000000..e9b6b5d51
--- /dev/null
+++ b/app/models/message_template/project_member_joined.rb
@@ -0,0 +1,60 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# 我管理的仓库有成员加入
+class MessageTemplate::ProjectMemberJoined < MessageTemplate
+
+ # MessageTemplate::ProjectMemberJoined.get_message_content(User.where(login: 'yystopf'))
+ def self.get_message_content(receivers, user, project)
+ receivers.each do |receiver|
+ if receiver.user_template_message_setting.present?
+ receivers = receivers.where.not(id: receiver.id) unless receiver.user_template_message_setting.notification_body["ManageProject::Member"]
+ end
+ end
+ return '', '', '' if receivers.blank?
+ content = sys_notice.gsub('{nickname1}', user&.real_name).gsub('{nickname2}', project&.owner&.real_name).gsub('{repository}', project&.name)
+ url = notification_url.gsub('{owner}', project&.owner&.login).gsub('{identifier}', project&.identifier)
+ return receivers_string(receivers), content, url
+ rescue => e
+ Rails.logger.info("MessageTemplate::ProjectMemberJoined.get_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+
+ def self.get_email_message_content(receiver, user, project)
+ if receiver.user_template_message_setting.present?
+ return '', '', '' unless receiver.user_template_message_setting.email_body["ManageProject::Member"]
+ title = email_title
+ title.gsub!('{nickname1}', user&.real_name)
+ title.gsub!('{nickname2}', project&.owner&.real_name)
+ title.gsub!('{repository}', project&.name)
+
+ content = email
+ content.gsub!('{receiver}', receiver&.real_name)
+ content.gsub!('{baseurl}', base_url)
+ content.gsub!('{login1}', user&.login)
+ content.gsub!('{login2}', project&.owner&.login)
+ content.gsub!('{identifier}', project&.identifier)
+ content.gsub!('{nickname1}', user&.real_name)
+ content.gsub!('{nickname2}', project&.owner&.real_name)
+ content.gsub!('{repository}', project&.name)
+
+ return receiver&.mail, title, content
+ else
+ return '', '', ''
+ end
+ rescue => e
+ Rails.logger.info("MessageTemplate::ProjectMemberJoined.get_email_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+end
diff --git a/app/models/message_template/project_member_left.rb b/app/models/message_template/project_member_left.rb
new file mode 100644
index 000000000..23d4ef5f3
--- /dev/null
+++ b/app/models/message_template/project_member_left.rb
@@ -0,0 +1,61 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# 我管理的仓库有成员移出
+class MessageTemplate::ProjectMemberLeft < MessageTemplate
+
+ # MessageTemplate::ProjectMemberLeft.get_message_content(User.where(login: 'yystopf'), User.last, Project.last)
+ def self.get_message_content(receivers, user, project)
+ receivers.each do |receiver|
+ if receiver.user_template_message_setting.present?
+ receivers = receivers.where.not(id: receiver.id) unless receiver.user_template_message_setting.notification_body["ManageProject::Member"]
+ end
+ end
+ return '', '', '' if receivers.blank?
+ content = sys_notice.gsub('{nickname1}', user&.real_name).gsub('{nickname2}', project&.owner&.real_name).gsub('{repository}', project&.name)
+ url = notification_url.gsub('{owner}', project&.owner&.login).gsub('{identifier}', project&.identifier)
+ return receivers_string(receivers), content, url
+ rescue => e
+ Rails.logger.info("MessageTemplate::ProjectMemberLeft.get_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+
+ def self.get_email_message_content(receiver, user, project)
+ if receiver.user_template_message_setting.present?
+ return '', '', '' unless receiver.user_template_message_setting.email_body["ManageProject::Member"]
+ title = email_title
+ title.gsub!('{nickname1}', user&.real_name)
+ title.gsub!('{nickname2}', project&.owner&.real_name)
+ title.gsub!('{repository}', project&.name)
+
+ content = email
+ content.gsub!('{receiver}', receiver&.real_name)
+ content.gsub!('{baseurl}', base_url)
+ content.gsub!('{login1}', user&.login)
+ content.gsub!('{login2}', project&.owner&.login)
+ content.gsub!('{identifier}', project&.identifier)
+ content.gsub!('{nickname1}', user&.real_name)
+ content.gsub!('{nickname2}', project&.owner&.real_name)
+ content.gsub!('{repository}', project&.name)
+
+ return receiver&.mail, title, content
+ else
+ return '', '', ''
+ end
+
+ rescue => e
+ Rails.logger.info("MessageTemplate::ProjectMemberLeft.get_email_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+end
diff --git a/app/models/message_template/project_milestone.rb b/app/models/message_template/project_milestone.rb
new file mode 100644
index 000000000..14f992cdd
--- /dev/null
+++ b/app/models/message_template/project_milestone.rb
@@ -0,0 +1,25 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# TODO 我管理的仓库有新的里程碑
+class MessageTemplate::ProjectMilestone < MessageTemplate
+
+ # MessageTemplate::ProjectMilestone.get_message_content(User.where(login: 'yystopf'))
+ def self.get_message_content(receivers)
+ return receivers_string(receivers), content, url
+ rescue => e
+ Rails.logger.info("MessageTemplate::ProjectMilestone.get_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+end
diff --git a/app/models/message_template/project_praised.rb b/app/models/message_template/project_praised.rb
new file mode 100644
index 000000000..e6acee6f5
--- /dev/null
+++ b/app/models/message_template/project_praised.rb
@@ -0,0 +1,25 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# TODO 我管理的仓库被点赞
+class MessageTemplate::ProjectPraised < MessageTemplate
+
+ # MessageTemplate::ProjectPraised.get_message_content(User.where(login: 'yystopf'))
+ def self.get_message_content(receivers)
+ return receivers_string(receivers), content, url
+ rescue => e
+ Rails.logger.info("MessageTemplate::ProjectPraised.get_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+end
diff --git a/app/models/message_template/project_pull_request.rb b/app/models/message_template/project_pull_request.rb
new file mode 100644
index 000000000..40c700b29
--- /dev/null
+++ b/app/models/message_template/project_pull_request.rb
@@ -0,0 +1,68 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# TODO 我管理/关注的仓库有新的合并请求
+class MessageTemplate::ProjectPullRequest < MessageTemplate
+
+ # MessageTemplate::ProjectPullRequest.get_message_content(User.where(login: 'yystopf'), User.where(login: 'testforge2'), User.last, PullRequest.last)
+ def self.get_message_content(managers, followers, operator, pull_request)
+ managers.each do |receiver|
+ if receiver.user_template_message_setting.present?
+ managers = managers.where.not(id: receiver.id) unless receiver.user_template_message_setting.notification_body["ManageProject::PullRequest"]
+ end
+ end
+ project = pull_request&.project
+ owner = project&.owner
+ 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}', pull_request&.title)
+ url = notification_url.gsub('{owner}', owner&.login).gsub('{identifier}', project&.identifier).gsub('{id}', pull_request&.id.to_s)
+
+ return receivers_string(receivers), content, url
+ rescue => e
+ Rails.logger.info("MessageTemplate::ProjectPullRequest.get_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+
+ def self.get_email_message_content(receiver, is_manager, operator, pull_request)
+ if receiver.user_template_message_setting.present? && is_manager
+ return '', '', '' unless receiver.user_template_message_setting.email_body["ManageProject::PullRequest"]
+ project = pull_request&.project
+ owner = project&.owner
+ title = email_title
+ title.gsub!('{nickname1}', operator&.real_name)
+ title.gsub!('{nickname2}', owner&.real_name)
+ title.gsub!('{repository}', project&.name)
+
+ content = email
+ content.gsub!('{receiver}', receiver&.real_name)
+ content.gsub!('{baseurl}', base_url)
+ content.gsub!('{login1}', operator&.login)
+ content.gsub!('{nickname1}', operator&.real_name)
+ content.gsub!('{nickname2}', owner&.real_name)
+ content.gsub!('{repository}', project&.name)
+ content.gsub!('{login2}', owner&.login)
+ content.gsub!('{identifier}', project&.identifier)
+ content.gsub!('{id}', pull_request&.id.to_s)
+ content.gsub!('{title}', pull_request&.title)
+
+ return receiver&.mail, title, content
+ else
+ return '', '', ''
+ end
+ rescue => e
+ Rails.logger.info("MessageTemplate::ProjectPullRequest.get_email_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+end
diff --git a/app/models/message_template/project_role.rb b/app/models/message_template/project_role.rb
new file mode 100644
index 000000000..f7f4940df
--- /dev/null
+++ b/app/models/message_template/project_role.rb
@@ -0,0 +1,59 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# 账号仓库权限变更
+class MessageTemplate::ProjectRole < MessageTemplate
+
+ # MessageTemplate::ProjectRole.get_message_content(User.where(login: 'yystopf'), Project.last, '管理员')
+ def self.get_message_content(receivers, project, role)
+ receivers.each do |receiver|
+ if receiver.user_template_message_setting.present?
+ receivers = receivers.where.not(id: receiver.id) unless receiver.user_template_message_setting.notification_body["Normal::Permission"]
+ end
+ end
+ return '', '', '' if receivers.blank?
+ content = sys_notice.gsub('{nickname}', project&.owner&.real_name).gsub('{repository}', project&.name).gsub('{role}', role)
+ url = notification_url.gsub('{owner}', project&.owner&.login).gsub('{identifier}', project&.identifier)
+ return receivers_string(receivers), content, url
+ rescue => e
+ Rails.logger.info("MessageTemplate::ProjectRole.get_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+
+ def self.get_email_message_content(receiver, project, role)
+ if receiver.user_template_message_setting.present?
+ return '', '', '' unless receiver.user_template_message_setting.email_body["Normal::Permission"]
+ title = email_title
+ title.gsub!('{repository}', project&.name)
+ title.gsub!('{role}', role)
+ title.gsub!('{nickname}', project&.owner&.real_name)
+ content = email
+ content.gsub!('{receiver}', receiver&.real_name)
+ content.gsub!('{baseurl}', base_url)
+ content.gsub!('{login}', project&.owner&.login)
+ content.gsub!('{nickname}', project&.owner&.real_name)
+ content.gsub!('{identifier}', project&.identifier)
+ content.gsub!('{repository}', project&.name)
+ content.gsub!('{role}', role)
+
+ return receiver&.mail, title, content
+ else
+ return '', '', ''
+ end
+
+ rescue => e
+ Rails.logger.info("MessageTemplate::ProjectRole.get_email_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+end
diff --git a/app/models/message_template/project_setting_changed.rb b/app/models/message_template/project_setting_changed.rb
new file mode 100644
index 000000000..49cf7821a
--- /dev/null
+++ b/app/models/message_template/project_setting_changed.rb
@@ -0,0 +1,312 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# 我管理的仓库项目设置被更改
+class MessageTemplate::ProjectSettingChanged < MessageTemplate
+
+ # MessageTemplate::ProjectSettingChanged.get_message_content(User.where(login: 'yystopf'), User.last, Project.last, {description: '测试修改项目简介', category: '大数据', language: 'Ruby', permission: '公有', navbar: '疑修, 合并请求'})
+ def self.get_message_content(receivers, operator, project, change_params)
+ receivers.each do |receiver|
+ if receiver.user_template_message_setting.present?
+ receivers = receivers.where.not(id: receiver.id) unless receiver.user_template_message_setting.notification_body["ManageProject::SettingChanged"]
+ end
+ end
+ return '', '', '' if receivers.blank?
+ return '', '', '' if change_params.blank?
+ owner = project&.owner
+ content = sys_notice.gsub('{nickname1}', operator&.real_name).gsub('{nickname2}', owner&.real_name).gsub('{repository}', project&.name)
+ url = notification_url.gsub('{owner}', owner&.login).gsub('{identifier}', project&.identifier)
+ change_count = change_params.keys.size
+ # 项目名称更改
+ if change_params[:name].present?
+ if change_count > 1
+ content.sub!('{ifname}', ' ')
+ else
+ content.sub!('{ifname}', '')
+ end
+ content.sub!('{endname}', '')
+ content.gsub!('{name}', change_params[:name][1])
+ else
+ content.gsub!(/({ifname})(.*)({endname})/, '')
+ end
+ # 项目标识更改
+ if change_params[:identifier].present?
+ if change_count > 1
+ content.sub!('{ifidentifier}', ' ')
+ else
+ content.sub!('{ifidentifier}', '')
+ end
+ content.sub!('{endidentifier}', '')
+ content.gsub!('{identifier}', change_params[:identifier][1])
+ else
+ content.gsub!(/({ifidentifier})(.*)({endidentifier})/, '')
+ end
+ # 项目简介更改
+ if change_params[:description].present?
+ if change_params[:description][1].blank?
+ if change_count > 1
+ content.gsub!(/({ifdescription})(.*)({enddescription})/, ' 删除了项目简介')
+ else
+ content.gsub!(/({ifdescription})(.*)({enddescription})/, '删除了项目简介')
+ end
+ else
+ if change_count > 1
+ content.sub!('{ifdescription}', ' ')
+ else
+ content.sub!('{ifdescription}', '')
+ end
+ content.sub!('{enddescription}', '')
+ content.gsub!('{description}', change_params[:description][1])
+ end
+ else
+ content.gsub!(/({ifdescription})(.*)({enddescription})/, '')
+ end
+ # 项目类别更改
+ if change_params[:project_category_id].present?
+ category = ProjectCategory.find_by_id(change_params[:project_category_id][1])
+ if category.present?
+ if change_count > 1
+ content.sub!('{ifcategory}', ' ')
+ else
+ content.sub!('{ifcategory}', '')
+ end
+ content.sub!('{endcategory}', '')
+ content.gsub!('{category}', category&.name)
+ else
+ if change_count > 1
+ content.gsub!(/({ifcategory})(.*)({endcategory})/, ' 删除了项目类别')
+ else
+ content.gsub!(/({ifcategory})(.*)({endcategory})/, '删除了项目类别')
+ end
+ end
+ else
+ content.gsub!(/({ifcategory})(.*)({endcategory})/, '')
+ end
+ # 项目语言更改
+ if change_params[:project_language_id].present?
+ language = ProjectLanguage.find_by_id(change_params[:project_language_id][1])
+ if language.present?
+ if change_count > 1
+ content.sub!('{iflanguage}', ' ')
+ else
+ content.sub!('{iflanguage}', '')
+ end
+ content.sub!('{endlanguage}', '')
+ content.gsub!('{language}', language&.name)
+ else
+ if change_count > 1
+ content.gsub!(/({iflanguage})(.*)({endlanguage})/, ' 删除了项目语言')
+ else
+ content.gsub!(/({iflanguage})(.*)({endlanguage})/, '删除了项目语言')
+ end
+ end
+ else
+ content.gsub!(/({iflanguage})(.*)({endlanguage})/, '')
+ end
+ # 项目公私有更改
+ if change_params[:is_public].present?
+ permission = change_params[:is_public][1] ? '公有' : '私有'
+ if change_count > 1
+ content.sub!('{ifpermission}', ' ')
+ else
+ content.sub!('{ifpermission}', '')
+ end
+ content.sub!('{endpermission}', '')
+ content.gsub!('{permission}', permission)
+ else
+ content.gsub!(/({ifpermission})(.*)({endpermission})/, '')
+ end
+ # 项目导航更改
+ if change_params[:navbar].present?
+ unit_types = project.project_units.order(unit_type: :asc).pluck(:unit_type)
+ unit_types.delete('code')
+ unit_types.unshift('代码库')
+ unit_types.unshift('主页')
+ unit_types.append('动态')
+ navbar = unit_types.join(',')
+ navbar.gsub!('issues', '疑修')
+ navbar.gsub!('pulls', '合并请求')
+ navbar.gsub!('wiki', 'Wiki')
+ navbar.gsub!('devops', '工作流')
+ navbar.gsub!('versions', '里程碑')
+ navbar.gsub!('resources', '资源库')
+ if change_count > 1
+ content.sub!('{ifnavbar}', ' ')
+ else
+ content.sub!('{ifnavbar}', '')
+ end
+ content.sub!('{endnavbar}', '')
+ content.gsub!('{navbar}', navbar)
+ else
+ content.gsub!(/({ifnavbar})(.*)({endnavbar})/, '')
+ end
+
+ return receivers_string(receivers), content, url
+ rescue => e
+ Rails.logger.info("MessageTemplate::ProjectSettingChanged.get_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+
+ def self.get_email_message_content(receiver, operator, project, change_params)
+ return '', '', '' if change_params.blank?
+ if receiver.user_template_message_setting.present?
+ return '', '', '' unless receiver.user_template_message_setting.email_body["ManageProject::SettingChanged"]
+ owner = project&.owner
+ title = email_title
+ title.gsub!('{nickname2}', owner&.real_name)
+ title.gsub!('{repository}', project&.name)
+
+ content = email
+ content.gsub!('{receiver}', receiver&.real_name)
+ content.gsub!('{baseurl}', base_url)
+ content.gsub!('{login1}', operator&.login)
+ content.gsub!('{nickname1}', operator&.real_name)
+ content.gsub!('{login2}', owner&.login)
+ content.gsub!('{nickname2}', owner&.real_name)
+ content.gsub!('{identifier}', project&.identifier)
+ content.gsub!('{repository}', project&.name)
+ change_count = change_params.keys.size
+ # 项目名称更改
+ if change_params[:name].present?
+ if change_count > 1
+ content.sub!('{ifname}', ' ')
+ else
+ content.sub!('{ifname}', '')
+ end
+ content.sub!('{endname}', '')
+ content.gsub!('{name}', change_params[:name][1])
+ else
+ content.gsub!(/({ifname})(.*)({endname})/, '')
+ end
+ # 项目标识更改
+ if change_params[:identifier].present?
+ if change_count > 1
+ content.sub!('{ifidentifier}', ' ')
+ else
+ content.sub!('{ifidentifier}', '')
+ end
+ content.sub!('{endidentifier}', '')
+ content.gsub!('{identifier}', change_params[:identifier][1])
+ else
+ content.gsub!(/({ifidentifier})(.*)({endidentifier})/, '')
+ end
+ # 项目简介更改
+ if change_params[:description].present?
+ if change_params[:description][1].blank?
+ if change_count > 1
+ content.gsub!(/({ifdescription})(.*)({enddescription})/, ' 删除了项目简介')
+ else
+ content.gsub!(/({ifdescription})(.*)({enddescription})/, '删除了项目简介')
+ end
+ else
+ if change_count > 1
+ content.sub!('{ifdescription}', ' ')
+ else
+ content.sub!('{ifdescription}', '')
+ end
+ content.sub!('{enddescription}', '')
+ content.gsub!('{description}', change_params[:description][1])
+ end
+ else
+ content.gsub!(/({ifdescription})(.*)({enddescription})/, '')
+ end
+ # 项目类别更改
+ if change_params[:project_category_id].present?
+ category = ProjectCategory.find_by_id(change_params[:project_category_id][1])
+ if category.present?
+ if change_count > 1
+ content.sub!('{ifcategory}', ' ')
+ else
+ content.sub!('{ifcategory}', '')
+ end
+ content.sub!('{endcategory}', '')
+ content.gsub!('{category}', category&.name)
+ else
+ if change_count > 1
+ content.gsub!(/({ifcategory})(.*)({endcategory})/, ' 删除了项目类别')
+ else
+ content.gsub!(/({ifcategory})(.*)({endcategory})/, '删除了项目类别')
+ end
+ end
+ else
+ content.gsub!(/({ifcategory})(.*)({endcategory})/, '')
+ end
+ # 项目语言更改
+ if change_params[:project_language_id].present?
+ language = ProjectLanguage.find_by_id(change_params[:project_language_id][1])
+ if language.present?
+ if change_count > 1
+ content.sub!('{iflanguage}', ' ')
+ else
+ content.sub!('{iflanguage}', '')
+ end
+ content.sub!('{endlanguage}', '')
+ content.gsub!('{language}', language&.name)
+ else
+ if change_count > 1
+ content.gsub!(/({iflanguage})(.*)({endlanguage})/, ' 删除了项目语言')
+ else
+ content.gsub!(/({iflanguage})(.*)({endlanguage})/, '删除了项目语言')
+ end
+ end
+ else
+ content.gsub!(/({iflanguage})(.*)({endlanguage})/, '')
+ end
+ # 项目公私有更改
+ if change_params[:is_public].present?
+ permission = change_params[:is_public][1] ? '公有' : '私有'
+ if change_count > 1
+ content.sub!('{ifpermission}', ' ')
+ else
+ content.sub!('{ifpermission}', '')
+ end
+ content.sub!('{endpermission}', '')
+ content.gsub!('{permission}', permission)
+ else
+ content.gsub!(/({ifpermission})(.*)({endpermission})/, '')
+ end
+ # 项目导航更改
+ if change_params[:navbar].present?
+ unit_types = project.project_units.order(unit_type: :asc).pluck(:unit_type)
+ unit_types.delete('code')
+ unit_types.unshift('代码库')
+ unit_types.unshift('主页')
+ unit_types.append('动态')
+ navbar = unit_types.join(',')
+ navbar.gsub!('issues', '疑修')
+ navbar.gsub!('pulls', '合并请求')
+ navbar.gsub!('wiki', 'Wiki')
+ navbar.gsub!('devops', '工作流')
+ navbar.gsub!('versions', '里程碑')
+ navbar.gsub!('resources', '资源库')
+ if change_count > 1
+ content.sub!('{ifnavbar}', ' ')
+ else
+ content.sub!('{ifnavbar}', '')
+ end
+ content.sub!('{endnavbar}', '')
+ content.gsub!('{navbar}', navbar)
+ else
+ content.gsub!(/({ifnavbar})(.*)({endnavbar})/, '')
+ end
+
+ return receiver&.mail, title, content
+ else
+ return '', '', ''
+ end
+ rescue => e
+ Rails.logger.info("MessageTemplate::ProjectSettingChanged.get_email_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+end
diff --git a/app/models/message_template/project_transfer.rb b/app/models/message_template/project_transfer.rb
new file mode 100644
index 000000000..7a99276ba
--- /dev/null
+++ b/app/models/message_template/project_transfer.rb
@@ -0,0 +1,25 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# TODO 我关注的仓库被转移
+class MessageTemplate::ProjectTransfer < MessageTemplate
+
+ # MessageTemplate::ProjectTransfer.get_message_content(User.where(login: 'yystopf'))
+ def self.get_message_content(receivers)
+ return receivers_string(receivers), content, url
+ rescue => e
+ Rails.logger.info("MessageTemplate::ProjectTransfer.get_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+end
diff --git a/app/models/message_template/project_version.rb b/app/models/message_template/project_version.rb
new file mode 100644
index 000000000..14ae46306
--- /dev/null
+++ b/app/models/message_template/project_version.rb
@@ -0,0 +1,25 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# TODO 我关注的仓库有新的发行版
+class MessageTemplate::ProjectVersion < MessageTemplate
+
+ # MessageTemplate::ProjectVersion.get_message_content(User.where(login: 'yystopf'))
+ def self.get_message_content(receivers)
+ return receivers_string(receivers), content, url
+ rescue => e
+ Rails.logger.info("MessageTemplate::ProjectVersion.get_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+end
diff --git a/app/models/message_template/pull_request_assigned.rb b/app/models/message_template/pull_request_assigned.rb
new file mode 100644
index 000000000..bca37151a
--- /dev/null
+++ b/app/models/message_template/pull_request_assigned.rb
@@ -0,0 +1,65 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# 有新指派给我的合并请求
+class MessageTemplate::PullRequestAssigned < MessageTemplate
+
+ # MessageTemplate::PullRequestAssigned.get_message_content(User.where(login: 'yystopf'), User.last, PullRequest.last)
+ def self.get_message_content(receivers, operator, pull_request)
+ receivers.each do |receiver|
+ if receiver.user_template_message_setting.present?
+ receivers = receivers.where.not(id: receiver.id) unless receiver.user_template_message_setting.notification_body["Normal::PullRequestAssigned"]
+ end
+ end
+ return '', '', '' if receivers.blank?
+ project = pull_request&.project
+ owner = project&.owner
+ content = sys_notice.gsub('{nickname1}', operator&.real_name).gsub('{nickname2}', owner&.real_name).gsub('{repository}', project&.name).gsub('{title}', pull_request&.title)
+ url = notification_url.gsub('{owner}', owner&.login).gsub('{identifier}', project&.identifier).gsub('{id}', pull_request&.id.to_s)
+ return receivers_string(receivers), content, url
+ rescue => e
+ Rails.logger.info("MessageTemplate::PullRequestAssigned.get_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+
+ def self.get_email_message_content(receiver, operator, pull_request)
+ if receiver.user_template_message_setting.present?
+ return '', '', '' unless receiver.user_template_message_setting.email_body["Normal::PullRequestAssigned"]
+ project = pull_request&.project
+ owner = project&.owner
+ title = email_title
+ title.gsub!('{nickname1}', operator&.real_name)
+ title.gsub!('{nickname2}', owner&.real_name)
+ title.gsub!('{repository}', project&.name)
+ content = email
+ content.gsub!('{receiver}', receiver&.real_name)
+ content.gsub!('{nickname1}', operator&.real_name)
+ content.gsub!('{login1}', operator&.login)
+ content.gsub!('{nickname2}', owner&.real_name)
+ content.gsub!('{login2}', owner&.login)
+ content.gsub!('{identifier}', project&.identifier)
+ content.gsub!('{repository}', project&.name)
+ content.gsub!('{baseurl}', base_url)
+ content.gsub!('{title}', pull_request&.title)
+ content.gsub!('{id}', pull_request&.id.to_s)
+
+ return receiver&.mail, title, content
+ else
+ return '', '', ''
+ end
+ rescue => e
+ Rails.logger.info("MessageTemplate::PullRequestAssigned.get_email_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+end
diff --git a/app/models/message_template/pull_request_atme.rb b/app/models/message_template/pull_request_atme.rb
new file mode 100644
index 000000000..54f8c9585
--- /dev/null
+++ b/app/models/message_template/pull_request_atme.rb
@@ -0,0 +1,29 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# 在合并请求中@我
+class MessageTemplate::PullRequestAtme < MessageTemplate
+
+ # MessageTemplate::PullRequestAtme.get_message_content(User.where(login: 'yystopf'), User.last, PullRequest.last)
+ def self.get_message_content(receivers, operator, pull_request)
+ project = pull_request&.project
+ owner = project&.owner
+ content = sys_notice.gsub('{nickname}', operator&.real_name).gsub('{title}', pull_request&.title)
+ url = notification_url.gsub('{owner}', owner&.login).gsub('{identifier}', project&.identifier).gsub('{id}', pull_request&.id.to_s)
+ return receivers_string(receivers), content, url
+ rescue => e
+ Rails.logger.info("MessageTemplate::PullRequestAtme.get_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+end
diff --git a/app/models/message_template/pull_request_changed.rb b/app/models/message_template/pull_request_changed.rb
new file mode 100644
index 000000000..ab2c73b6b
--- /dev/null
+++ b/app/models/message_template/pull_request_changed.rb
@@ -0,0 +1,196 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# 我创建或负责的合并请求状态变更
+class MessageTemplate::PullRequestChanged < MessageTemplate
+
+ # MessageTemplate::PullRequestChanged.get_message_content(User.where(login: 'yystopf'), User.last, PullRequest.last, {assigned_to_id: [nil, 203], priority_id: [2, 4], fixed_version_id: [nil, 5], issue_tags_value: ["", "7"]})
+ def self.get_message_content(receivers, operator, pull_request, change_params)
+ receivers.each do |receiver|
+ if receiver.user_template_message_setting.present?
+ receivers = receivers.where.not(id: receiver.id) unless receiver.user_template_message_setting.notification_body["CreateOrAssign::PullRequestChanged"]
+ end
+ end
+ return '', '', '' if receivers.blank?
+ return '', '', '' if change_params.blank?
+ project = pull_request&.project
+ owner = project&.owner
+ issue = pull_request&.issue
+ content = sys_notice.gsub('{nickname1}', operator&.real_name).gsub('{nickname2}', owner&.real_name).gsub('{repository}', project&.name).gsub("{title}", pull_request&.title)
+ url = notification_url.gsub('{owner}', owner&.login).gsub('{identifier}', project&.identifier).gsub('{id}', pull_request&.id.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])
+ if change_count > 1
+ content.sub!('{ifassigner}', ' ')
+ else
+ content.sub!('{ifassigner}', '')
+ end
+ content.sub!('{endassigner}', '')
+ content.gsub!('{assigner1}', assigner1.present? ? assigner1&.real_name : '未指派成员')
+ content.gsub!('{assigner2}', assigner2.present? ? assigner2&.real_name : '未指派成员')
+ else
+ content.gsub!(/({ifassigner})(.*)({endassigner})/, '')
+ end
+ # 合并请求里程碑修改
+ if change_params[:fixed_version_id].present?
+ fix_version1 = Version.find_by_id(change_params[:fixed_version_id][0])
+ fix_version2 = Version.find_by_id(change_params[:fixed_version_id][1])
+ if change_count > 1
+ content.sub!('{ifmilestone}', ' ')
+ else
+ content.sub!('{ifmilestone}', '')
+ end
+ content.sub!('{endmilestone}', '')
+ content.gsub!('{milestone1}', fix_version1.present? ? fix_version1&.name : '未选择里程碑')
+ content.gsub!('{milestone2}', fix_version2.present? ? fix_version2&.name : '未选择里程碑')
+ else
+ content.gsub!(/({ifmilestone})(.*)({endmilestone})/, '')
+ end
+ # 合并请求标记修改
+ if change_params[:issue_tags_value].present?
+ issue_tags1 = IssueTag.where(id: change_params[:issue_tags_value][0]).distinct
+ issue_tags2 = IssueTag.where(id: change_params[:issue_tags_value][1]).distinct
+ tag1 = issue_tags1.pluck(:name).join(",").blank? ? '未选择标记' : issue_tags1.pluck(:name).join(",")
+ tag2 = issue_tags2.pluck(:name).join(",").blank? ? '未选择标记' : issue_tags2.pluck(:name).join(",")
+ if change_count > 1
+ content.sub!('{iftag}', ' ')
+ else
+ content.sub!('{iftag}', '')
+ end
+ content.sub!('{endtag}', '')
+ content.gsub!('{tag1}', tag1)
+ content.gsub!('{tag2}', tag2)
+ else
+ content.gsub!(/({iftag})(.*)({endtag})()/, '')
+ end
+ # 合并请求优先级修改
+ if change_params[:priority_id].present?
+ priority1 = IssuePriority.find_by_id(change_params[:priority_id][0])
+ priority2 = IssuePriority.find_by_id(change_params[:priority_id][1])
+ if change_count > 1
+ content.sub!('{ifpriority}', ' ')
+ else
+ content.sub!('{ifpriority}', '')
+ end
+ content.sub!('{ifpriority}', '')
+ content.sub!('{endpriority}', '')
+ content.gsub!('{priority1}', priority1&.name)
+ content.gsub!('{priority2}', priority2&.name)
+ else
+ content.gsub!(/({ifpriority})(.*)({endpriority})/, '')
+ end
+ return receivers_string(receivers), content, url
+ rescue => e
+ Rails.logger.info("MessageTemplate::PullRequestChanged.get_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+
+ def self.get_email_message_content(receiver, operator, pull_request, change_params)
+ return '', '', '' if change_params.blank?
+ if receiver.user_template_message_setting.present?
+ return '', '', '' unless receiver.user_template_message_setting.email_body["CreateOrAssign::PullRequestChanged"]
+ project = pull_request&.project
+ owner = project&.owner
+ title = email_title
+ title.gsub!('{title}', pull_request&.title)
+ content = email
+ content.gsub!('{receiver}', receiver&.real_name)
+ content.gsub!('{nickname1}', operator&.real_name)
+ content.gsub!('{login1}', operator&.login)
+ content.gsub!('{nickname2}', owner&.real_name)
+ content.gsub!('{login2}', owner&.login)
+ content.gsub!('{identifier}', project&.identifier)
+ content.gsub!('{repository}', project&.name)
+ content.gsub!('{baseurl}', base_url)
+ content.gsub!('{title}', pull_request&.title)
+ content.gsub!('{id}', pull_request&.id.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])
+ if change_count > 1
+ content.sub!('{ifassigner}', ' ')
+ else
+ content.sub!('{ifassigner}', '')
+ end
+ content.sub!('{endassigner}', '')
+ content.gsub!('{assigner1}', assigner1.present? ? assigner1&.real_name : '未指派成员')
+ content.gsub!('{assigner2}', assigner2.present? ? assigner2&.real_name : '未指派成员')
+ else
+ content.gsub!(/({ifassigner})(.*)({endassigner})/, '')
+ end
+ # 合并请求里程碑修改
+ if change_params[:fixed_version_id].present?
+ fix_version1 = Version.find_by_id(change_params[:fixed_version_id][0])
+ fix_version2 = Version.find_by_id(change_params[:fixed_version_id][1])
+ if change_count > 1
+ content.sub!('{ifmilestone}', ' ')
+ else
+ content.sub!('{ifmilestone}', '')
+ end
+ content.sub!('{endmilestone}', '')
+ content.gsub!('{milestone1}', fix_version1.present? ? fix_version1&.name : '未选择里程碑')
+ content.gsub!('{milestone2}', fix_version2.present? ? fix_version2&.name : '未选择里程碑')
+ else
+ content.gsub!(/({ifmilestone})(.*)({endmilestone})/, '')
+ end
+ # 合并请求标记修改
+ if change_params[:issue_tags_value].present?
+ issue_tags1 = IssueTag.where(id: change_params[:issue_tags_value][0]).distinct
+ issue_tags2 = IssueTag.where(id: change_params[:issue_tags_value][1]).distinct
+ tag1 = issue_tags1.pluck(:name).join(",").blank? ? '未选择标记' : issue_tags1.pluck(:name).join(",")
+ tag2 = issue_tags2.pluck(:name).join(",").blank? ? '未选择标记' : issue_tags2.pluck(:name).join(",")
+ if change_count > 1
+ content.sub!('{iftag}', ' ')
+ else
+ content.sub!('{iftag}', '')
+ end
+ content.sub!('{endtag}', '')
+ content.gsub!('{tag1}', tag1)
+ content.gsub!('{tag2}', tag2)
+ else
+ content.gsub!(/({iftag})(.*)({endtag})()/, '')
+ end
+ # 合并请求优先级修改
+ if change_params[:priority_id].present?
+ priority1 = IssuePriority.find_by_id(change_params[:priority_id][0])
+ priority2 = IssuePriority.find_by_id(change_params[:priority_id][1])
+ if change_count > 1
+ content.sub!('{ifpriority}', ' ')
+ else
+ content.sub!('{ifpriority}', '')
+ end
+ content.sub!('{ifpriority}', '')
+ content.sub!('{endpriority}', '')
+ content.gsub!('{priority1}', priority1&.name)
+ content.gsub!('{priority2}', priority2&.name)
+ else
+ content.gsub!(/({ifpriority})(.*)({endpriority})/, '')
+ end
+
+ return receiver&.mail, title, content
+ else
+ return '', '', ''
+ end
+
+ rescue => e
+ Rails.logger.info("MessageTemplate::PullRequestChanged.get_email_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+end
diff --git a/app/models/message_template/pull_request_closed.rb b/app/models/message_template/pull_request_closed.rb
new file mode 100644
index 000000000..ea1ffabcb
--- /dev/null
+++ b/app/models/message_template/pull_request_closed.rb
@@ -0,0 +1,64 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# 我创建或负责的合并请求被关闭
+class MessageTemplate::PullRequestClosed < MessageTemplate
+
+ # MessageTemplate::PullRequestClosed.get_message_content(User.where(login: 'yystopf'), User.last, PullRequest.last)
+ def self.get_message_content(receivers, operator, pull_request)
+ receivers.each do |receiver|
+ if receiver.user_template_message_setting.present?
+ receivers = receivers.where.not(id: receiver.id) unless receiver.user_template_message_setting.notification_body["CreateOrAssign::PullRequestChanged"]
+ end
+ end
+ return '', '', '' if receivers.blank?
+ project = pull_request&.project
+ owner = project&.owner
+ content = sys_notice.gsub('{title}', pull_request&.title)
+ url = notification_url
+ return receivers_string(receivers), content, url
+ rescue => e
+ Rails.logger.info("MessageTemplate::PullRequestClosed.get_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+
+ def self.get_email_message_content(receiver, operator, pull_request)
+ if receiver.user_template_message_setting.present?
+ return '', '', '' unless receiver.user_template_message_setting.email_body["CreateOrAssign::PullRequestChanged"]
+ project = pull_request&.project
+ owner = project&.owner
+ title = email_title
+ title.gsub!('{title}', pull_request&.title)
+ content = email
+ content.gsub!('{receiver}', receiver&.real_name)
+ content.gsub!('{nickname1}', operator&.real_name)
+ content.gsub!('{login1}', operator&.login)
+ content.gsub!('{nickname2}', owner&.real_name)
+ content.gsub!('{login2}', owner&.login)
+ content.gsub!('{identifier}', project&.identifier)
+ content.gsub!('{repository}', project&.name)
+ content.gsub!('{baseurl}', base_url)
+ content.gsub!('{title}', pull_request&.title)
+ content.gsub!('{id}', pull_request&.id.to_s)
+
+ return receiver&.mail, title, content
+ else
+ return '', '', ''
+ end
+
+ rescue => e
+ Rails.logger.info("MessageTemplate::PullRequestClosed.get_email_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+end
diff --git a/app/models/message_template/pull_request_journal.rb b/app/models/message_template/pull_request_journal.rb
new file mode 100644
index 000000000..9b2fae949
--- /dev/null
+++ b/app/models/message_template/pull_request_journal.rb
@@ -0,0 +1,25 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# TODO 我创建或负责的合并请求有新的评论
+class MessageTemplate::PullRequestJournal < MessageTemplate
+
+ # MessageTemplate::PullRequestJournal.get_message_content(User.where(login: 'yystopf'))
+ def self.get_message_content(receivers)
+ return receivers_string(receivers), content, url
+ rescue => e
+ Rails.logger.info("MessageTemplate::PullRequestJournal.get_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+end
diff --git a/app/models/message_template/pull_request_merged.rb b/app/models/message_template/pull_request_merged.rb
new file mode 100644
index 000000000..eb4bb5e17
--- /dev/null
+++ b/app/models/message_template/pull_request_merged.rb
@@ -0,0 +1,63 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# 我创建或负责的合并请求被合并
+class MessageTemplate::PullRequestMerged < MessageTemplate
+
+ # MessageTemplate::PullRequestMerged.get_message_content(User.where(login: 'yystopf'), User.last, PullRequest.last)
+ def self.get_message_content(receivers, operator, pull_request)
+ receivers.each do |receiver|
+ if receiver.user_template_message_setting.present?
+ receivers = receivers.where.not(id: receiver.id) unless receiver.user_template_message_setting.notification_body["CreateOrAssign::PullRequestChanged"]
+ end
+ end
+ return '', '', '' if receivers.blank?
+ project = pull_request&.project
+ owner = project&.owner
+ content = sys_notice.gsub('{title}', pull_request&.title)
+ url = notification_url.gsub('{owner}', owner&.login).gsub('{identifier}', project&.identifier).gsub('{id}', pull_request&.id.to_s)
+ return receivers_string(receivers), content, url
+ rescue => e
+ Rails.logger.info("MessageTemplate::PullRequestMerged.get_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+
+ def self.get_email_message_content(receiver, operator, pull_request)
+ if receiver.user_template_message_setting.present?
+ return '', '', '' unless receiver.user_template_message_setting.email_body["CreateOrAssign::PullRequestChanged"]
+ project = pull_request&.project
+ owner = project&.owner
+ title = email_title
+ title.gsub!('{title}', pull_request&.title)
+ content = email
+ content.gsub!('{receiver}', receiver&.real_name)
+ content.gsub!('{nickname1}', operator&.real_name)
+ content.gsub!('{login1}', operator&.login)
+ content.gsub!('{nickname2}', owner&.real_name)
+ content.gsub!('{login2}', owner&.login)
+ content.gsub!('{identifier}', project&.identifier)
+ content.gsub!('{repository}', project&.name)
+ content.gsub!('{baseurl}', base_url)
+ content.gsub!('{title}', pull_request&.title)
+ content.gsub!('{id}', pull_request&.id.to_s)
+
+ return receiver&.mail, title, content
+ else
+ return '', '', ''
+ end
+ rescue => e
+ Rails.logger.info("MessageTemplate::PullRequestMerged.get_email_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+end
diff --git a/app/models/message_template/team_joined.rb b/app/models/message_template/team_joined.rb
new file mode 100644
index 000000000..f49940952
--- /dev/null
+++ b/app/models/message_template/team_joined.rb
@@ -0,0 +1,58 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# 账号被拉入组织团队
+class MessageTemplate::TeamJoined < MessageTemplate
+
+ # MessageTemplate::TeamJoined.get_message_content(User.where(login: 'yystopf'), Organization.last, Organization.last.teams.take)
+ def self.get_message_content(receivers, organization, team)
+ receivers.each do |receiver|
+ if receiver.user_template_message_setting.present?
+ receivers = receivers.where.not(id: receiver.id) unless receiver.user_template_message_setting.notification_body["Normal::Permission"]
+ end
+ end
+ return '', '', '' if receivers.blank?
+ content = sys_notice.gsub('{organization}', organization&.real_name).gsub('{team}', team&.nickname).gsub('{role}', team&.authorize_name)
+ url = notification_url.gsub('{login}', organization&.login)
+ return receivers_string(receivers), content, url
+ rescue => e
+ Rails.logger.info("MessageTemplate::TeamJoined.get_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+
+ def self.get_email_message_content(receiver, organization, role)
+ if receiver.user_template_message_setting.present?
+ return '', '', '' unless receiver.user_template_message_setting.email_body["Normal::Permission"]
+ title = email_title
+ title.gsub!('{organization}', organization&.real_name)
+ title.gsub!('{team}', team&.nickname)
+ title.gsub!('{role}', team&.authorize_name)
+ content = email
+ content.gsub!('{receiver}', receiver&.real_name)
+ content.gsub!('{baseurl}', base_url)
+ content.gsub!('{login}', organization&.login)
+ content.gsub!('{organization}', organization&.real_name)
+ content.gsub!('{team}', team&.nickname)
+ content.gsub!('{role}', team&.authorize_name)
+
+ return receiver&.mail, title, content
+ else
+ return '', '', ''
+ end
+
+ rescue => e
+ Rails.logger.info("MessageTemplate::TeamJoined.get_email_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+end
diff --git a/app/models/message_template/team_left.rb b/app/models/message_template/team_left.rb
new file mode 100644
index 000000000..572422633
--- /dev/null
+++ b/app/models/message_template/team_left.rb
@@ -0,0 +1,58 @@
+# == Schema Information
+#
+# Table name: message_templates
+#
+# id :integer not null, primary key
+# type :string(255)
+# sys_notice :text(65535)
+# email :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+# notification_url :string(255)
+# email_title :string(255)
+#
+
+# 账号被移出组织团队
+class MessageTemplate::TeamLeft < MessageTemplate
+
+ # MessageTemplate::TeamLeft.get_message_content(User.where(login: 'yystopf'), Organization.last, Organization.last.teams.take)
+ def self.get_message_content(receivers, organization, team)
+ receivers.each do |receiver|
+ if receiver.user_template_message_setting.present?
+ receivers = receivers.where.not(id: receiver.id) unless receiver.user_template_message_setting.notification_body["Normal::Permission"]
+ end
+ end
+ return '', '', '' if receivers.blank?
+ content = sys_notice.gsub('{organization}', organization&.real_name).gsub('{team}', team&.nickname).gsub('{role}', team&.authorize_name)
+ url = notification_url.gsub('{login}', organization&.login)
+ return receivers_string(receivers), content, url
+ rescue => e
+ Rails.logger.info("MessageTemplate::TeamLeft.get_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+
+ def self.get_email_message_content(receiver, organization, team)
+ if receiver.user_template_message_setting.present?
+ return '', '', '' unless receiver.user_template_message_setting.email_body["Normal::Permission"]
+ title = email_title
+ title.gsub!('{organization}', organization&.real_name)
+ title.gsub!('{team}', team&.nickname)
+ title.gsub!('{role}', team&.authorize_name)
+ content = email
+ content.gsub!('{receiver}', receiver&.real_name)
+ content.gsub!('{baseurl}', base_url)
+ content.gsub!('{login}', organization&.login)
+ content.gsub!('{organization}', organization&.real_name)
+ content.gsub!('{team}', team&.nickname)
+ content.gsub!('{role}', team&.authorize_name)
+
+ return receiver&.mail, title, content
+ else
+ return '', '', ''
+ end
+
+ rescue => e
+ Rails.logger.info("MessageTemplate::TeamLeft.get_email_message_content [ERROR] #{e}")
+ return '', '', ''
+ end
+end
diff --git a/app/models/mirror.rb b/app/models/mirror.rb
index 67ef73775..b71dce3fb 100644
--- a/app/models/mirror.rb
+++ b/app/models/mirror.rb
@@ -18,7 +18,7 @@ class Mirror < ApplicationRecord
# 0: 同步镜像成功;1: 正在同步镜像;2: 同步失败; 默认值为0
enum status: { succeeded: 0, waiting: 1, failed: 2 }
- after_update :websocket_boardcast, if: :saved_change_to_status?
+ # after_update :websocket_boardcast, if: :saved_change_to_status?
belongs_to :repository, foreign_key: :repo_id
diff --git a/app/models/organization.rb b/app/models/organization.rb
index a541b203f..40c676e05 100644
--- a/app/models/organization.rb
+++ b/app/models/organization.rb
@@ -39,13 +39,15 @@
# business :boolean default("0")
# profile_completed :boolean default("0")
# laboratory_id :integer
-# platform :string(255) default("0")
-# gitea_token :string(255)
-# gitea_uid :integer
# is_shixun_marker :boolean default("0")
+# admin_visitable :boolean default("0")
+# collaborator :boolean default("0")
+# gitea_uid :integer
# is_sync_pwd :boolean default("1")
# watchers_count :integer default("0")
# devops_step :integer default("0")
+# gitea_token :string(255)
+# platform :string(255)
#
# Indexes
#
@@ -53,8 +55,9 @@
# index_users_on_homepage_engineer (homepage_engineer)
# index_users_on_homepage_teacher (homepage_teacher)
# index_users_on_laboratory_id (laboratory_id)
-# index_users_on_login (login)
-# index_users_on_mail (mail)
+# index_users_on_login (login) UNIQUE
+# index_users_on_mail (mail) UNIQUE
+# index_users_on_phone (phone) UNIQUE
# index_users_on_type (type)
#
@@ -73,13 +76,19 @@ class Organization < Owner
validates_uniqueness_of :login, :if => Proc.new { |user| user.login_changed? && user.login.present? }, case_sensitive: false
validates :login, format: { with: NAME_REGEX, multiline: true, message: "只能含有数字、字母、下划线且不能以下划线开头和结尾" }
- delegate :description, :website, :location, :repo_admin_change_team_access,
+ delegate :description, :website, :location, :repo_admin_change_team_access, :recommend,
:visibility, :max_repo_creation, :num_projects, :num_users, :num_teams, to: :organization_extension, allow_nil: true
scope :with_visibility, ->(visibility) { joins(:organization_extension).where(organization_extensions: {visibility: visibility}) if visibility.present? }
- def self.build(name, gitea_token=nil)
- self.create!(login: name, gitea_token: gitea_token)
+ after_save :reset_cache_data
+
+ def reset_cache_data
+ Cache::V2::OwnerCommonService.new(self.id).reset
+ end
+
+ def self.build(name, nickname, gitea_token=nil)
+ self.create!(login: name, nickname: nickname, gitea_token: gitea_token)
end
def can_create_project?(user_id)
@@ -106,14 +115,35 @@ class Organization < Owner
team_users.joins(:team).where(user_id: user_id, teams: {authorize: %w(read write admin owner)}).present?
end
+ def is_only_admin?(user_id)
+ team_users.joins(:team).where(user_id: user_id, teams: {authorize: %w(admin)}).present?
+ end
+
+ def is_only_write?(user_id)
+ team_users.joins(:team).where(user_id: user_id, teams: {authorize: %w(write)}).present?
+ end
+
+ def is_only_read?(user_id)
+ team_users.joins(:team).where(user_id: user_id, teams: {authorize: %w(read)}).present?
+ end
+
# 是不是所有者团队的最后一个成员
def is_owner_team_last_one?(user_id)
owner_team_users = team_users.joins(:team).where(teams: {authorize: %w(owner)})
owner_team_users.pluck(:user_id).include?(user_id) && owner_team_users.size == 1
end
+ # 为包含组织所有项目的团队创建项目访问权限
+ def build_permit_team_projects!(project_id)
+ teams.where(includes_all_project: true).each do |team|
+ TeamProject.build(id, team.id, project_id)
+ end
+ end
+
def real_name
- login
+ name = lastname + firstname
+ name = name.blank? ? (nickname.blank? ? login : nickname) : name
+ name.gsub(/\s+/, '').strip #6.11 -hs
end
def show_real_name
diff --git a/app/models/organization_extension.rb b/app/models/organization_extension.rb
index 8b9946cd7..4b0935208 100644
--- a/app/models/organization_extension.rb
+++ b/app/models/organization_extension.rb
@@ -15,6 +15,7 @@
# num_projects :integer default("0")
# num_users :integer default("0")
# num_teams :integer default("0")
+# recommend :boolean default("0")
#
# Indexes
#
@@ -30,6 +31,8 @@ class OrganizationExtension < ApplicationRecord
enum visibility: {common: 0, limited: 1, privacy: 2}
+ before_save :set_recommend
+
def self.build(organization_id, description, website, location, repo_admin_change_team_access, visibility, max_repo_creation)
self.create!(organization_id: organization_id,
description: description,
@@ -39,4 +42,9 @@ class OrganizationExtension < ApplicationRecord
visibility: visibility,
max_repo_creation: max_repo_creation)
end
+
+ private
+ def set_recommend
+ self.recommend = false unless self.common?
+ end
end
diff --git a/app/models/organization_user.rb b/app/models/organization_user.rb
index cf9e22371..4ff6946b7 100644
--- a/app/models/organization_user.rb
+++ b/app/models/organization_user.rb
@@ -22,6 +22,9 @@ class OrganizationUser < ApplicationRecord
validates :user_id, uniqueness: {scope: :organization_id}
+ after_create :send_create_message_to_notice_system
+ after_destroy :send_destroy_message_to_notice_system
+
def self.build(organization_id, user_id)
org_user = self.find_by(organization_id: organization_id, user_id: user_id)
return org_user unless org_user.nil?
@@ -31,4 +34,12 @@ class OrganizationUser < ApplicationRecord
def teams
organization.teams.joins(:team_users).where(team_users: {user_id: user_id})
end
+
+ def send_create_message_to_notice_system
+ SendTemplateMessageJob.perform_later('OrganizationJoined', self.user_id, self.organization_id) if Site.has_notice_menu?
+ end
+
+ def send_destroy_message_to_notice_system
+ SendTemplateMessageJob.perform_later('OrganizationLeft', self.user_id, self.organization_id) if Site.has_notice_menu?
+ end
end
diff --git a/app/models/owner.rb b/app/models/owner.rb
index 1d537a1e0..b65ee0544 100644
--- a/app/models/owner.rb
+++ b/app/models/owner.rb
@@ -66,4 +66,6 @@ class Owner < ApplicationRecord
has_many :projects, foreign_key: :user_id, dependent: :destroy
has_many :repositories, foreign_key: :user_id, dependent: :destroy
+ has_many :applied_transfer_projects, dependent: :destroy
+
end
diff --git a/app/models/pinned_project.rb b/app/models/pinned_project.rb
new file mode 100644
index 000000000..8e47c9ea4
--- /dev/null
+++ b/app/models/pinned_project.rb
@@ -0,0 +1,22 @@
+# == Schema Information
+#
+# Table name: pinned_projects
+#
+# id :integer not null, primary key
+# user_id :integer
+# project_id :integer
+# position :integer default("0")
+# created_at :datetime not null
+# updated_at :datetime not null
+#
+# Indexes
+#
+# index_pinned_projects_on_project_id (project_id)
+# index_pinned_projects_on_user_id (user_id)
+#
+
+class PinnedProject < ApplicationRecord
+
+ belongs_to :user
+ belongs_to :project
+end
diff --git a/app/models/praise_tread.rb b/app/models/praise_tread.rb
index d13c17ddd..5d4ae0d80 100644
--- a/app/models/praise_tread.rb
+++ b/app/models/praise_tread.rb
@@ -1,26 +1,52 @@
-# == Schema Information
-#
-# Table name: praise_treads
-#
-# id :integer not null, primary key
-# user_id :integer not null
-# praise_tread_object_id :integer
-# praise_tread_object_type :string(255)
-# praise_or_tread :integer default("1")
-# created_at :datetime not null
-# updated_at :datetime not null
-#
-# Indexes
-#
-# praise_tread (praise_tread_object_id,praise_tread_object_type)
-#
-
+# == Schema Information
+#
+# Table name: praise_treads
+#
+# id :integer not null, primary key
+# user_id :integer not null
+# praise_tread_object_id :integer
+# praise_tread_object_type :string(255)
+# praise_or_tread :integer default("1")
+# created_at :datetime not null
+# updated_at :datetime not null
+#
+# Indexes
+#
+# praise_tread (praise_tread_object_id,praise_tread_object_type)
+#
+
+
class PraiseTread < ApplicationRecord
belongs_to :user
belongs_to :praise_tread_object, polymorphic: true, counter_cache: :praises_count
has_many :tidings, :as => :container, :dependent => :destroy
- after_create :send_tiding
+ after_create :send_tiding, :incre_project_common, :incre_user_statistic, :incre_platform_statistic
+ after_destroy :decre_project_common, :decre_user_statistic, :decre_platform_statistic
+
+ def incre_project_common
+ CacheAsyncSetJob.perform_later("project_common_service", {praises: 1}, self.praise_tread_object_id) if self.praise_tread_object_type == "Project"
+ end
+
+ def decre_project_common
+ CacheAsyncSetJob.perform_later("project_common_service", {praises: -1}, self.praise_tread_object_id) if self.praise_tread_object_type == "Project"
+ end
+
+ def incre_user_statistic
+ CacheAsyncSetJob.perform_later("user_statistic_service", {project_praise_count: 1}, self.praise_tread_object&.user_id) if self.praise_tread_object_type == "Project"
+ end
+
+ def decre_user_statistic
+ CacheAsyncSetJob.perform_later("user_statistic_service", {project_praise_count: -1}, self.praise_tread_object&.user_id) if self.praise_tread_object_type == "Project"
+ end
+
+ def incre_platform_statistic
+ CacheAsyncSetJob.perform_later("platform_statistic_service", {project_praise_count: 1}) if self.praise_tread_object_type == "Project"
+ end
+
+ def decre_platform_statistic
+ CacheAsyncSetJob.perform_later("platform_statistic_service", {project_praise_count: -1}) if self.praise_tread_object_type == "Project"
+ end
def send_tiding
case self.praise_tread_object_type
diff --git a/app/models/project.rb b/app/models/project.rb
index f644b80ae..d6bd52db4 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -1,73 +1,84 @@
-# == Schema Information
-#
-# Table name: projects
-#
-# id :integer not null, primary key
-# name :string(255) default(""), not null
-# description :text(4294967295)
-# homepage :string(255) default("")
-# is_public :boolean default("1"), not null
-# parent_id :integer
-# created_on :datetime
-# updated_on :datetime
-# identifier :string(255)
-# status :integer default("1"), not null
-# lft :integer
-# rgt :integer
-# inherit_members :boolean default("0"), not null
-# project_type :integer default("0")
-# hidden_repo :boolean default("0"), not null
-# attachmenttype :integer default("1")
-# user_id :integer
-# dts_test :integer default("0")
-# enterprise_name :string(255)
-# organization_id :integer
-# project_new_type :integer
-# gpid :integer
-# forked_from_project_id :integer
-# forked_count :integer default("0")
-# publish_resource :integer default("0")
-# visits :integer default("0")
-# hot :integer default("0")
-# invite_code :string(255)
-# qrcode :string(255)
-# qrcode_expiretime :integer default("0")
-# script :text(65535)
-# training_status :integer default("0")
-# rep_identifier :string(255)
-# project_category_id :integer
-# project_language_id :integer
-# license_id :integer
-# ignore_id :integer
-# praises_count :integer default("0")
-# watchers_count :integer default("0")
-# issues_count :integer default("0")
-# pull_requests_count :integer default("0")
-# language :string(255)
-# versions_count :integer default("0")
-# issue_tags_count :integer default("0")
-# closed_issues_count :integer default("0")
-# open_devops :boolean default("0")
-# gitea_webhook_id :integer
-# open_devops_count :integer default("0")
-# recommend :boolean default("0")
-# platform :integer default("0")
-#
-# Indexes
-#
-# index_projects_on_forked_from_project_id (forked_from_project_id)
-# index_projects_on_identifier (identifier)
-# index_projects_on_is_public (is_public)
-# index_projects_on_lft (lft)
-# index_projects_on_name (name)
-# index_projects_on_platform (platform)
-# index_projects_on_project_type (project_type)
-# index_projects_on_recommend (recommend)
-# index_projects_on_rgt (rgt)
-# index_projects_on_status (status)
-# index_projects_on_updated_on (updated_on)
-#
-
+
+# == Schema Information
+#
+# Table name: projects
+#
+# id :integer not null, primary key
+# name :string(255) default(""), not null
+# description :text(4294967295)
+# homepage :string(255) default("")
+# is_public :boolean default("1"), not null
+# parent_id :integer
+# created_on :datetime
+# updated_on :datetime
+# identifier :string(255)
+# status :integer default("1"), not null
+# lft :integer
+# rgt :integer
+# inherit_members :boolean default("0"), not null
+# project_type :integer default("0")
+# hidden_repo :boolean default("0"), not null
+# attachmenttype :integer default("1")
+# user_id :integer
+# dts_test :integer default("0")
+# enterprise_name :string(255)
+# organization_id :integer
+# project_new_type :integer
+# gpid :integer
+# forked_from_project_id :integer
+# forked_count :integer default("0")
+# publish_resource :integer default("0")
+# visits :integer default("0")
+# hot :integer default("0")
+# invite_code :string(255)
+# qrcode :string(255)
+# qrcode_expiretime :integer default("0")
+# script :text(65535)
+# training_status :integer default("0")
+# rep_identifier :string(255)
+# project_category_id :integer
+# project_language_id :integer
+# license_id :integer
+# ignore_id :integer
+# praises_count :integer default("0")
+# watchers_count :integer default("0")
+# issues_count :integer default("0")
+# pull_requests_count :integer default("0")
+# language :string(255)
+# versions_count :integer default("0")
+# issue_tags_count :integer default("0")
+# closed_issues_count :integer default("0")
+# open_devops :boolean default("0")
+# gitea_webhook_id :integer
+# open_devops_count :integer default("0")
+# recommend :boolean default("0")
+# platform :integer default("0")
+# default_branch :string(255) default("master")
+# website :string(255)
+# lesson_url :string(255)
+# is_pinned :boolean default("0")
+# recommend_index :integer default("0")
+#
+# Indexes
+#
+# index_projects_on_forked_from_project_id (forked_from_project_id)
+# index_projects_on_identifier (identifier)
+# index_projects_on_invite_code (invite_code)
+# index_projects_on_is_public (is_public)
+# index_projects_on_lft (lft)
+# index_projects_on_license_id (license_id)
+# index_projects_on_name (name)
+# index_projects_on_platform (platform)
+# index_projects_on_project_category_id (project_category_id)
+# index_projects_on_project_language_id (project_language_id)
+# index_projects_on_project_type (project_type)
+# index_projects_on_recommend (recommend)
+# index_projects_on_rgt (rgt)
+# index_projects_on_status (status)
+# index_projects_on_updated_on (updated_on)
+#
+
+
class Project < ApplicationRecord
@@ -75,6 +86,7 @@ class Project < ApplicationRecord
include Publicable
include Watchable
include ProjectOperable
+ include Dcodes
# common:开源托管项目
# mirror:普通镜像项目,没有定时同步功能
@@ -91,10 +103,12 @@ class Project < ApplicationRecord
belongs_to :organization_extension, foreign_key: :user_id, primary_key: :organization_id, optional: true, counter_cache: :num_projects
belongs_to :project_category, optional: true , :counter_cache => true
belongs_to :project_language, optional: true , :counter_cache => true
+ belongs_to :forked_from_project, class_name: 'Project', optional: true, foreign_key: :forked_from_project_id
has_many :project_trends, dependent: :destroy
has_many :watchers, as: :watchable, dependent: :destroy
has_many :fork_users, dependent: :destroy
has_many :forked_users, class_name: 'ForkUser', foreign_key: :fork_project_id, dependent: :destroy
+ has_many :forked_projects, class_name: 'Project', foreign_key: :forked_from_project_id
has_one :project_educoder, dependent: :destroy
has_one :project_score, dependent: :destroy
@@ -110,17 +124,113 @@ class Project < ApplicationRecord
has_and_belongs_to_many :trackers, :order => "#{Tracker.table_name}.position"
has_one :project_detail, dependent: :destroy
has_many :apply_signatures, dependent: :destroy
- has_many :team_projects, dependent: :destroy
-
- after_save :check_project_members
- scope :project_statics_select, -> {select(:id,:name, :is_public, :identifier, :status, :project_type, :user_id, :forked_count, :visits, :project_category_id, :project_language_id, :license_id, :ignore_id, :watchers_count, :created_on)}
+ has_many :project_units, dependent: :destroy
+ has_one :applied_transfer_project,-> { order created_at: :desc }, dependent: :destroy
+ has_many :pinned_projects, dependent: :destroy
+ has_many :has_pinned_users, through: :pinned_projects, source: :user
+ has_many :webhooks, class_name: "Gitea::Webhook", primary_key: :gpid, foreign_key: :repo_id
+ after_create :incre_user_statistic, :incre_platform_statistic
+ after_save :check_project_members, :reset_cache_data
+ before_save :set_invite_code, :reset_unmember_followed, :set_recommend_and_is_pinned
+ before_destroy :decre_project_common, :decre_forked_from_project_count
+ after_destroy :decre_user_statistic, :decre_platform_statistic
+ scope :project_statics_select, -> {select(:id,:name, :is_public, :identifier, :status, :project_type, :user_id, :forked_count, :description, :visits, :project_category_id, :project_language_id, :license_id, :ignore_id, :watchers_count, :created_on)}
scope :no_anomory_projects, -> {where("projects.user_id is not null and projects.user_id != ?", 2)}
scope :recommend, -> { visible.project_statics_select.where(recommend: true) }
-
+ scope :pinned, -> {where(is_pinned: true)}
scope :secret_and_visible, -> {left_outer_joins(:license).where("licenses.is_secret = TRUE OR projects.is_public = TRUE")}
delegate :is_secret, to: :license, allow_nil: true
+ delegate :content, to: :project_detail, allow_nil: true
+ delegate :name, to: :license, prefix: true, allow_nil: true
+
+ def self.all_visible(user_id=nil)
+ user_projects_sql = Project.joins(:owner).where(users: {type: 'User'}).to_sql
+ org_public_projects_sql = Project.joins(:owner).merge(Organization.joins(:organization_extension).where(organization_extensions: {visibility: 'common'})).to_sql
+ if user_id.present?
+ org_limit_projects_sql = Project.joins(:owner).merge(Organization.joins(:organization_extension).where(organization_extensions: {visibility: 'limited'})).to_sql
+ org_privacy_projects_sql = Project.joins(:owner).merge(Organization.joins(:organization_extension, :organization_users).where(organization_extensions: {visibility: 'privacy'}, organization_users: {user_id: user_id})).to_sql
+ return Project.from("( #{ user_projects_sql } UNION #{ org_public_projects_sql } UNION #{ org_limit_projects_sql } UNION #{org_privacy_projects_sql} ) AS projects").visible
+ else
+ return Project.from("( #{ user_projects_sql } UNION #{ org_public_projects_sql } ) AS projects").visible
+ end
+ end
+
+ def reset_cache_data
+ CacheAsyncResetJob.perform_later("project_common_service", self.id)
+ if changes[:user_id].present?
+ CacheAsyncSetJob.perform_later("user_statistic_service", {project_count: -1}, changes[:user_id].first)
+ CacheAsyncSetJob.perform_later("user_statistic_service", {project_count: 1}, changes[:user_id].last)
+ end
+ if changes[:project_language_id].present?
+ first_language = ProjectLanguage.find_by_id(changes[:project_language_id].first)
+ last_language = ProjectLanguage.find_by_id(changes[:project_language_id].last)
+ CacheAsyncSetJob.perform_later("user_statistic_service", {project_language_count_key: first_language&.name, project_language_count: -1}, self.user_id)
+ CacheAsyncSetJob.perform_later("user_statistic_service", {project_language_count_key: last_language&.name, project_language_count: 1}, self.user_id)
+ CacheAsyncSetJob.perform_later("platform_statistic_service", {project_language_count_key: first_language&.name, project_language_count: -1})
+ CacheAsyncSetJob.perform_later("platform_statistic_service", {project_language_count_key: last_language&.name, project_language_count: 1})
+ end
+ end
+
+ def decre_project_common
+ CacheAsyncClearJob.perform_later('project_common_service', self.id)
+ end
+
+ def decre_forked_from_project_count
+ forked_project = self.forked_from_project
+ if forked_project.present?
+ forked_project.decrement(:forked_count, 1)
+ forked_project.save
+ end
+ end
+
+ def incre_user_statistic
+ CacheAsyncSetJob.perform_later("user_statistic_service", {project_count: 1, project_language_count_key: self.project_language&.name, project_language_count: 1}, self.user_id)
+ end
+
+ def decre_user_statistic
+ CacheAsyncSetJob.perform_later("user_statistic_service", {project_count: -1, project_language_count_key: self.project_language&.name, project_language_count: -1}, self.user_id)
+ end
+
+ def incre_platform_statistic
+ CacheAsyncSetJob.perform_later("platform_statistic_service", {project_count: 1, project_language_count_key: self.project_language&.name, project_language_count: 1})
+ end
+
+ def decre_platform_statistic
+ CacheAsyncSetJob.perform_later("platform_statistic_service", {project_count: -1, project_language_count_key: self.project_language&.name, project_language_count: -1})
+ end
+
+ def is_full_public
+ owner = self.owner
+ if owner.is_a?(Organization)
+ return self.is_public && owner&.visibility == "common"
+ else
+ return self.is_public
+ end
+ end
+
+ def reset_unmember_followed
+ if changes[:is_public].present? && changes[:is_public] == [true, false]
+ self.watchers.where.not(user_id: self.all_collaborators).destroy_all
+ end
+ end
+
+ def set_invite_code
+ if self.invite_code.nil?
+ self.invite_code= self.generate_dcode('invite_code', 6)
+ end
+ end
+
+ def set_recommend_and_is_pinned
+ self.recommend = self.recommend_index.zero? ? false : true
+ # 私有项目不允许设置精选和推荐
+ unless self.is_public
+ self.recommend = false
+ self.recommend_index = 0
+ self.is_pinned = false
+ end
+ end
def self.search_project(search)
ransack(name_or_identifier_cont: search)
@@ -158,6 +268,7 @@ class Project < ApplicationRecord
#创建项目管理员
def check_project_members
+ return if owner.is_a?(Organization)
unless members.present? && members.exists?(user_id: self.user_id)
member_params = {
user_id: self.user_id,
@@ -239,10 +350,12 @@ class Project < ApplicationRecord
end
def get_premission user
- permission = "Reporter"
- member = members.find_by(user: user)
+ return "Owner" if owner?(user)
+ return "Manager" if manager?(user)
+ return "Developer" if develper?(user)
+ return "Reporter" if reporter?(user)
- member&.roles&.last&.name || permission
+ return ""
end
def fork_project
@@ -257,6 +370,7 @@ class Project < ApplicationRecord
logger.info "########namespace_path: #{namespace_path} ########identifier: #{identifier} "
user = Owner.find_by_login namespace_path
+ user = Owner.new(login: namespace_path) if user.nil?
project = user&.projects&.find_by(identifier: identifier) || Project.find_by(identifier: "#{namespace_path}/#{identifier}")
return nil if project.blank?
@@ -292,4 +406,7 @@ class Project < ApplicationRecord
update_column(:updated_on, time)
end
+ def is_transfering
+ applied_transfer_project&.common? ? true : false
+ end
end
diff --git a/app/models/project_category.rb b/app/models/project_category.rb
index f58fe12fb..265ba34dd 100644
--- a/app/models/project_category.rb
+++ b/app/models/project_category.rb
@@ -9,6 +9,7 @@
# created_at :datetime not null
# updated_at :datetime not null
# ancestry :string(255)
+# pinned_index :integer default("0")
#
# Indexes
#
@@ -26,5 +27,16 @@ class ProjectCategory < ApplicationRecord
def self.get_children(target_id)
where(ancestry: target_id).pluck(:id,:name)
end
+
+ def logo_url
+ image_url('logo')
+ end
+
+ private
+
+ def image_url(type)
+ return nil unless Util::FileManage.exists?(self, type)
+ Util::FileManage.source_disk_file_url(self, type)
+ end
end
diff --git a/app/models/project_trend.rb b/app/models/project_trend.rb
index 8c55c4f94..7642e1dfb 100644
--- a/app/models/project_trend.rb
+++ b/app/models/project_trend.rb
@@ -20,7 +20,8 @@
class ProjectTrend < ApplicationRecord
CLOSE = 'close'
CREATE = 'create'
-
+ MERGE = 'merge'
+
belongs_to :project
belongs_to :trend, polymorphic: true, optional: true
belongs_to :user
diff --git a/app/models/project_unit.rb b/app/models/project_unit.rb
new file mode 100644
index 000000000..cc35a6b28
--- /dev/null
+++ b/app/models/project_unit.rb
@@ -0,0 +1,44 @@
+# == Schema Information
+#
+# Table name: project_units
+#
+# id :integer not null, primary key
+# project_id :integer
+# unit_type :integer
+# created_at :datetime not null
+# updated_at :datetime not null
+#
+# Indexes
+#
+# index_project_units_on_project_id (project_id)
+#
+
+class ProjectUnit < ApplicationRecord
+ belongs_to :project
+
+ enum unit_type: {code: 1, issues: 2, pulls: 3, wiki:4, devops: 5, versions: 6, resources: 7}
+
+ validates :unit_type, uniqueness: { scope: :project_id}
+
+ def self.init_types(project_id, project_type='common')
+ unit_types = project_type == 'sync_mirror' ? ProjectUnit::unit_types.except("pulls") : ProjectUnit::unit_types
+ unit_types.each do |_, v|
+ self.create!(project_id: project_id, unit_type: v)
+ end
+ end
+
+ def self.update_by_unit_types!(project, types)
+ # 同步镜像项目不能有合并请求模块
+ types.delete("pulls") if project.sync_mirror?
+ # 默认code类型自动创建
+ types << "code"
+ before_units = project.project_units.pluck(:unit_type).sort
+ project.project_units.where.not(unit_type: types).each(&:destroy!)
+ types.each do |type|
+ project.project_units.find_or_create_by!(unit_type: type)
+ end
+ after_units = project.project_units.pluck(:unit_type).sort
+ return before_units, after_units
+ end
+
+end
diff --git a/app/models/pull_request.rb b/app/models/pull_request.rb
index 17a86144a..55b74e1a7 100644
--- a/app/models/pull_request.rb
+++ b/app/models/pull_request.rb
@@ -32,19 +32,52 @@ class PullRequest < ApplicationRecord
belongs_to :issue
belongs_to :user
- belongs_to :project, :counter_cache => true
+ belongs_to :project, counter_cache: true, touch: true
# belongs_to :fork_project, foreign_key: :fork_project_id
has_many :pull_request_assigns, foreign_key: :pull_request_id
has_many :pull_request_tags, foreign_key: :pull_request_id
has_many :project_trends, as: :trend, dependent: :destroy
has_many :attachments, as: :container, dependent: :destroy
+ has_one :gitea_pull, foreign_key: :id, primary_key: :gitea_number, class_name: 'Gitea::Pull'
+
+ scope :merged_and_closed, ->{where.not(status: 0)}
+ scope :opening, -> {where(status: 0)}
+
+ after_create :incre_project_common, :incre_user_statistic, :incre_platform_statistic
+ after_destroy :decre_project_common, :decre_user_statistic, :decre_platform_statistic
+
+ def incre_project_common
+ CacheAsyncSetJob.perform_later("project_common_service", {pullrequests: 1}, self.project_id)
+ end
+
+ def decre_project_common
+ CacheAsyncSetJob.perform_later("project_common_service", {pullrequests: -1}, self.project_id)
+ end
+
+ def incre_user_statistic
+ CacheAsyncSetJob.perform_later("user_statistic_service", {pullrequest_count: 1}, self.user_id)
+ end
+
+ def decre_user_statistic
+ CacheAsyncSetJob.perform_later("user_statistic_service", {pullrequest_count: -1}, self.user_id)
+ end
+
+ def incre_platform_statistic
+ CacheAsyncSetJob.perform_later("platform_statistic_service", {pullrequest_count: 1})
+ end
+
+ def decre_platform_statistic
+ CacheAsyncSetJob.perform_later("platform_statistic_service", {pullrequest_count: -1})
+ end
def fork_project
Project.find_by(id: self.fork_project_id)
end
- def bind_gitea_pull_request!(gitea_pull_number)
- update_column(:gpid, gitea_pull_number)
+ def bind_gitea_pull_request!(gitea_pull_number, gitea_pull_id)
+ update_columns(
+ gitea_number: gitea_pull_number,
+ gitea_id: gitea_pull_id)
end
def merge!
@@ -57,19 +90,26 @@ class PullRequest < ApplicationRecord
# TODO: sync educoder platform repo's for update some statistics count
def self.update_some_count
- PullRequest.includes(:user, :project).select(:id, :user_id, :gpid, :project_id, :fork_project_id).each do |pr|
+ PullRequest.includes(:user, :project).select(:id, :user_id, :gitea_number, :project_id, :fork_project_id).each do |pr|
puts pr.id
- next if pr.gpid.blank?
+ next if pr.gitea_number.blank?
project = pr.project
next if project.blank?
user = project.owner
- next if pr.gpid === 6 || pr.gpid === 7
- files_result = Gitea::PullRequest::FilesService.call(user.login, project.identifier, pr.gpid)
+ next if pr.gitea_number === 6 || pr.gitea_number === 7
+ files_result = Gitea::PullRequest::FilesService.call(user.login, project.identifier, pr.gitea_number)
pr.update_column(:files_count, files_result['NumFiles']) unless files_result.blank?
- commits_result = Gitea::PullRequest::CommitsService.call(user.login, project.identifier, pr.gpid)
+ commits_result = Gitea::PullRequest::CommitsService.call(user.login, project.identifier, pr.gitea_number)
pr.update_column(:commits_count, commits_result.size) unless commits_result.blank?
end
end
+
+ def conflict_files
+ file_names = self&.gitea_pull&.conflicted_files
+ return [] if file_names.blank?
+
+ JSON.parse file_names
+ end
end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 934eaa0e3..a012b449a 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -22,10 +22,11 @@
# version_releases_count :integer default("0")
# fork_url :string(255)
# is_mirror :boolean default("0")
+# accelerator_url :string(255) default("")
+# source_clone_url :string(255) default("")
#
# Indexes
#
-# index_repositories_on_identifier (identifier)
# index_repositories_on_project_id (project_id)
# index_repositories_on_user_id (user_id)
#
@@ -41,6 +42,8 @@ class Repository < ApplicationRecord
validates :identifier, presence: true
+ delegate :default_branch, to: :project, allow_nil: true
+
def to_param
self.identifier.parameterize
end
@@ -75,4 +78,12 @@ class Repository < ApplicationRecord
end
end
+ def remote_mirror_url
+ source_clone_url.blank? ? mirror_url : source_clone_url
+ end
+
+ def config_accelerator?
+ !source_clone_url.blank?
+ end
+
end
diff --git a/app/models/reversed_keyword.rb b/app/models/reversed_keyword.rb
new file mode 100644
index 000000000..10ad62fa0
--- /dev/null
+++ b/app/models/reversed_keyword.rb
@@ -0,0 +1,30 @@
+# == Schema Information
+#
+# Table name: reversed_keywords
+#
+# id :integer not null, primary key
+# identifier :string(255)
+# description :text(65535)
+# closed :boolean default("0")
+# created_at :datetime not null
+# updated_at :datetime not null
+#
+
+class ReversedKeyword < ApplicationRecord
+
+ scope :is_reversed, -> (identifier){where(identifier: identifier.downcase, closed: false) if identifier.present?}
+
+ validates :identifier, presence: true, uniqueness: true
+
+ before_validation :set_identifier
+
+ def self.check_exists?(identifier)
+ self.is_reversed(identifier).exists?
+ end
+
+ private
+
+ def set_identifier
+ self.identifier = self.identifier.downcase
+ end
+end
diff --git a/app/models/school.rb b/app/models/school.rb
index f3b7ac0ab..5b30be9a5 100644
--- a/app/models/school.rb
+++ b/app/models/school.rb
@@ -1,3 +1,39 @@
+# == Schema Information
+#
+# Table name: schools
+#
+# id :integer not null, primary key
+# name :string(255)
+# province :string(255)
+# created_at :datetime not null
+# updated_at :datetime not null
+# logo_link :string(255)
+# pinyin :string(255)
+# school_type :integer default("0")
+# city :string(255)
+# address :string(255)
+# auto_users_trial :boolean default("0")
+# shool_code :string(255)
+# authorization_time :datetime
+# ec_auth :integer default("0")
+# identifier :string(255)
+# is_online :boolean default("0")
+# video_name :string(255)
+# video_desc :string(255)
+# course_link :string(255)
+# course_name :string(255)
+# partner_id :integer
+# customer_id :integer
+# school_property_id :integer
+#
+# Indexes
+#
+# index_schools_on_customer_id (customer_id)
+# index_schools_on_identifier (identifier)
+# index_schools_on_partner_id (partner_id)
+# index_schools_on_school_property_id (school_property_id)
+#
+
class School < ApplicationRecord
has_many :departments, dependent: :destroy
diff --git a/app/models/site.rb b/app/models/site.rb
new file mode 100644
index 000000000..af5e78169
--- /dev/null
+++ b/app/models/site.rb
@@ -0,0 +1,79 @@
+# == Schema Information
+#
+# Table name: sites
+#
+# id :integer not null, primary key
+# name :string(255)
+# url :string(255)
+# key :string(255)
+# site_type :integer
+# created_at :datetime not null
+# updated_at :datetime not null
+#
+
+class Site < ApplicationRecord
+ # add: 添加类链接
+ # personal: 个人名下类链接,
+ # common: 普通链接
+ enum site_type: { add: 0, personal: 1, common: 2 }
+
+ scope :by_search, -> (keyword){ where("name LIKE :keyword OR url LIKE :keyword", keyword: "%#{strip_param(keyword)}%") unless strip_param(keyword).blank? }
+ scope :by_site_type, -> (site_type){ where(site_type: strip_param(site_type)) unless strip_param(site_type).blank? }
+
+ def self.set_default_menu
+ set_add_menu!
+ set_personal_menu!
+ set_common_menu!
+ end
+
+ def self.has_notice_menu?
+ self.common.where(key: 'notice').present?
+ end
+
+ private
+ def self.set_add_menu!
+ adds= [
+ {name: '新建项目', key: 'add_mirror_project', url: '/projects/mirror/new'},
+ {name: '导入项目', key: 'add_common', url: '/projects/deposit/new'},
+ {name: '新建组织', key: 'add_r', url: '/organize/new'}]
+
+ adds.each { |ele|
+ Site.find_or_create_by(key: ele[:key]) do |site|
+ site.name = ele[:name]
+ site.url = ele[:url]
+ site.site_type = Site.site_types[:add]
+ end
+ }
+ end
+
+ def self.set_personal_menu!
+ personals = [
+ {name: '个人中心', key: 'my_page', url: '/users/current_user'},
+ {name: '我的组织', key: 'my_organizes', url: '/users/current_user/organizes'}
+ ]
+
+ personals.each { |ele|
+ Site.find_or_create_by(key: ele[:key]) do |site|
+ site.name = ele[:name]
+ site.url = ele[:url]
+ site.site_type = Site.site_types[:personal]
+ end
+ }
+ end
+
+ def self.set_common_menu!
+ commons = [
+ {name: '通知', key: 'notice', url: '/users/current_user/user_messages'},
+ {name: '找回密码', key: 'lost_password', url: '/account/lost_password'},
+ {name: '注册', key: 'register', url: '/login?login=false'}
+ ]
+
+ commons.each { |ele|
+ Site.find_or_create_by(key: ele[:key]) do |site|
+ site.name = ele[:name]
+ site.url = ele[:url]
+ site.site_type = Site.site_types[:common]
+ end
+ }
+ end
+end
diff --git a/app/models/system_notification.rb b/app/models/system_notification.rb
new file mode 100644
index 000000000..d2b99ecf3
--- /dev/null
+++ b/app/models/system_notification.rb
@@ -0,0 +1,27 @@
+# == Schema Information
+#
+# Table name: system_notifications
+#
+# id :integer not null, primary key
+# subject :string(255)
+# sub_subject :string(255)
+# content :text(65535)
+# is_top :boolean
+# created_at :datetime not null
+# updated_at :datetime not null
+#
+
+class SystemNotification < ApplicationRecord
+
+ default_scope { order(created_at: :desc)}
+
+ has_many :system_notification_histories
+ has_many :users, through: :system_notification_histories
+
+ scope :is_top, lambda { where(is_top: true) }
+
+ def read_member?(user_id)
+ self.system_notification_histories.where(user_id: user_id).present? ? true : false
+ end
+
+end
diff --git a/app/models/system_notification_history.rb b/app/models/system_notification_history.rb
new file mode 100644
index 000000000..b629babdf
--- /dev/null
+++ b/app/models/system_notification_history.rb
@@ -0,0 +1,23 @@
+# == Schema Information
+#
+# Table name: system_notification_histories
+#
+# id :integer not null, primary key
+# system_message_id :integer
+# user_id :integer
+# created_at :datetime not null
+# updated_at :datetime not null
+#
+# Indexes
+#
+# index_system_notification_histories_on_system_message_id (system_message_id)
+# index_system_notification_histories_on_user_id (user_id)
+#
+
+class SystemNotificationHistory < ApplicationRecord
+
+ belongs_to :system_notification
+ belongs_to :user
+
+ validates :system_notification_id, uniqueness: { scope: :user_id, message: '只能阅读一次'}
+end
diff --git a/app/models/team.rb b/app/models/team.rb
index 82a3e88c1..c2e633380 100644
--- a/app/models/team.rb
+++ b/app/models/team.rb
@@ -14,6 +14,7 @@
# gtid :integer
# created_at :datetime not null
# updated_at :datetime not null
+# nickname :string(255)
#
# Indexes
#
@@ -30,11 +31,12 @@ class Team < ApplicationRecord
validates :name, uniqueness: {scope: :organization_id}
- enum authorize: {common: 0, read: 1, write: 2, admin: 3, owner: 4}
+ enum authorize: {read: 1, write: 2, admin: 3, owner: 4}
- def self.build(organization_id, name, description, authorize, includes_all_project, can_create_org_project)
+ def self.build(organization_id, name, nickname, description, authorize, includes_all_project, can_create_org_project)
self.create!(organization_id: organization_id,
name: name,
+ nickname: nickname,
description: description,
authorize: authorize,
includes_all_project: includes_all_project,
@@ -52,4 +54,15 @@ class Team < ApplicationRecord
team_users.where(user_id: user_id).present?
end
+ def authorize_name
+ case self.authorize
+ when 'read' then '读取'
+ when 'write' then '写入'
+ when 'admin' then '管理员'
+ when 'owner' then '管理员'
+ else
+ ''
+ end
+ end
+
end
diff --git a/app/models/team_unit.rb b/app/models/team_unit.rb
index c757cc684..fdd1b975b 100644
--- a/app/models/team_unit.rb
+++ b/app/models/team_unit.rb
@@ -20,7 +20,7 @@ class TeamUnit < ApplicationRecord
belongs_to :organization
belongs_to :team
- enum unit_type: {code: 1, issues: 2, pulls: 3, releases: 4}
+ enum unit_type: {code: 1, issues: 2, pulls: 3, wiki: 4, releases: 5}
validates :unit_type, uniqueness: { scope: [:organization_id, :team_id]}
diff --git a/app/models/team_user.rb b/app/models/team_user.rb
index 9670013a7..9f90bc15b 100644
--- a/app/models/team_user.rb
+++ b/app/models/team_user.rb
@@ -22,9 +22,17 @@ class TeamUser < ApplicationRecord
belongs_to :team, counter_cache: :num_users
belongs_to :user
+ has_one :member
+
validates :user_id, uniqueness: {scope: [:organization_id, :team_id]}
+ before_destroy :remove_project_member
+
def self.build(organization_id, user_id, team_id)
self.create!(organization_id: organization_id, user_id: user_id, team_id: team_id)
end
+
+ def remove_project_member
+ member.destroy if member.present?
+ end
end
diff --git a/app/models/template_message_setting.rb b/app/models/template_message_setting.rb
new file mode 100644
index 000000000..a9c81500b
--- /dev/null
+++ b/app/models/template_message_setting.rb
@@ -0,0 +1,30 @@
+# == Schema Information
+#
+# Table name: template_message_settings
+#
+# id :integer not null, primary key
+# type :string(255)
+# name :string(255)
+# key :string(255)
+# openning :boolean
+# notification_disabled :boolean
+# email_disabled :boolean
+# created_at :datetime not null
+# updated_at :datetime not null
+#
+
+class TemplateMessageSetting < ApplicationRecord
+
+ scope :openning, ->() {where(openning: true)}
+
+ def self.type_name
+ ""
+ end
+
+ def self.build_init_data
+ TemplateMessageSetting::CreateOrAssign.build_init_data
+ TemplateMessageSetting::ManageProject.build_init_data
+ TemplateMessageSetting::Normal.build_init_data
+ TemplateMessageSetting::WatchProject.build_init_data
+ end
+end
diff --git a/app/models/template_message_setting/create_or_assign.rb b/app/models/template_message_setting/create_or_assign.rb
new file mode 100644
index 000000000..50ef86433
--- /dev/null
+++ b/app/models/template_message_setting/create_or_assign.rb
@@ -0,0 +1,31 @@
+# == Schema Information
+#
+# Table name: template_message_settings
+#
+# id :integer not null, primary key
+# type :string(255)
+# name :string(255)
+# key :string(255)
+# openning :boolean
+# notification_disabled :boolean
+# email_disabled :boolean
+# created_at :datetime not null
+# updated_at :datetime not null
+#
+
+#我创建的或负责的
+class TemplateMessageSetting::CreateOrAssign < TemplateMessageSetting
+
+ def self.type_name
+ "我创建的或负责的"
+ end
+
+ def self.order_index
+ 20
+ end
+
+ def self.build_init_data
+ self.find_or_create_by(name: "疑修状态变更", key: "IssueChanged")
+ self.find_or_create_by(name: "合并请求状态变更", key: "PullRequestChanged")
+ end
+end
diff --git a/app/models/template_message_setting/manage_project.rb b/app/models/template_message_setting/manage_project.rb
new file mode 100644
index 000000000..7a80c9999
--- /dev/null
+++ b/app/models/template_message_setting/manage_project.rb
@@ -0,0 +1,33 @@
+# == Schema Information
+#
+# Table name: template_message_settings
+#
+# id :integer not null, primary key
+# type :string(255)
+# name :string(255)
+# key :string(255)
+# openning :boolean
+# notification_disabled :boolean
+# email_disabled :boolean
+# created_at :datetime not null
+# updated_at :datetime not null
+#
+
+#我管理的
+class TemplateMessageSetting::ManageProject < TemplateMessageSetting
+
+ def self.type_name
+ "我管理的仓库"
+ end
+
+ def self.order_index
+ 30
+ end
+
+ def self.build_init_data
+ self.find_or_create_by(name: "有新的疑修", key: "Issue")
+ self.find_or_create_by(name: "有新的合并请求", key: "PullRequest")
+ self.find_or_create_by(name: "有成员变动", key: "Member")
+ self.find_or_create_by(name: "仓库设置被更改", key: "SettingChanged")
+ end
+end
diff --git a/app/models/template_message_setting/normal.rb b/app/models/template_message_setting/normal.rb
new file mode 100644
index 000000000..771fba2f2
--- /dev/null
+++ b/app/models/template_message_setting/normal.rb
@@ -0,0 +1,33 @@
+# == Schema Information
+#
+# Table name: template_message_settings
+#
+# id :integer not null, primary key
+# type :string(255)
+# name :string(255)
+# key :string(255)
+# openning :boolean
+# notification_disabled :boolean
+# email_disabled :boolean
+# created_at :datetime not null
+# updated_at :datetime not null
+#
+
+class TemplateMessageSetting::Normal < TemplateMessageSetting
+
+ def self.type_name
+ "我的状态"
+ end
+
+ def self.order_index
+ 10
+ end
+
+ def self.build_init_data
+ self.find_or_create_by(name: "账号有权限变更", key: "Permission")
+ self.find_or_create_by(name: "被拉入或移出组织", key: "Organization")
+ self.find_or_create_by(name: "被拉入或移出项目", key: "Project")
+ self.find_or_create_by(name: "有新的疑修指派给我", key: "IssueAssigned")
+ self.find_or_create_by(name: "有新的合并请求指派给我", key: "PullRequestAssigned")
+ end
+end
diff --git a/app/models/template_message_setting/watch_project.rb b/app/models/template_message_setting/watch_project.rb
new file mode 100644
index 000000000..35dfef6db
--- /dev/null
+++ b/app/models/template_message_setting/watch_project.rb
@@ -0,0 +1,29 @@
+# == Schema Information
+#
+# Table name: template_message_settings
+#
+# id :integer not null, primary key
+# type :string(255)
+# name :string(255)
+# key :string(255)
+# openning :boolean
+# notification_disabled :boolean
+# email_disabled :boolean
+# created_at :datetime not null
+# updated_at :datetime not null
+#
+
+#我关注的
+class TemplateMessageSetting::WatchProject < TemplateMessageSetting
+
+ def self.type_name
+ "我关注的仓库"
+ end
+
+ def self.order_index
+ 40
+ end
+
+ def self.build_init_data
+ end
+end
diff --git a/app/models/token.rb b/app/models/token.rb
index 140b9fa5d..4fc15eaa1 100644
--- a/app/models/token.rb
+++ b/app/models/token.rb
@@ -1,19 +1,19 @@
-# == Schema Information
-#
-# Table name: tokens
-#
-# id :integer not null, primary key
-# user_id :integer default("0"), not null
-# action :string(30) default(""), not null
-# value :string(40) default(""), not null
-# created_on :datetime not null
-#
-# Indexes
-#
-# index_tokens_on_user_id (user_id)
-# tokens_value (value) UNIQUE
-#
-
+# == Schema Information
+#
+# Table name: tokens
+#
+# id :integer not null, primary key
+# user_id :integer default("0"), not null
+# action :string(30) default(""), not null
+# value :string(40) default(""), not null
+# created_on :datetime not null
+#
+# Indexes
+#
+# index_tokens_on_user_id (user_id)
+# tokens_value (value) UNIQUE
+#
+
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -105,7 +105,7 @@ class Token < ActiveRecord::Base
end
def self.generate_token_value
- Educoder::Utils.random_hex(20)
+ Gitlink::Utils.random_hex(20)
end
def self.delete_user_all_tokens(user)
diff --git a/app/models/trustie/course.rb b/app/models/trustie/course.rb
index 6cf6411e1..1c951ad68 100644
--- a/app/models/trustie/course.rb
+++ b/app/models/trustie/course.rb
@@ -1,4 +1,87 @@
+# == Schema Information
+#
+# Table name: courses
+#
+# id :integer not null, primary key
+# tea_id :integer
+# name :string(255)
+# state :integer
+# code :string(255)
+# time :integer
+# extra :string(255)
+# created_at :datetime not null
+# updated_at :datetime not null
+# location :string(255)
+# term :string(255)
+# string :string(255)
+# password :string(255)
+# setup_time :string(255)
+# endup_time :string(255)
+# class_period :integer default("0")
+# school_id :integer
+# description :text(65535)
+# status :integer default("1")
+# attachmenttype :integer default("2")
+# lft :integer
+# rgt :integer
+# is_public :integer default("1")
+# inherit_members :integer default("1")
+# open_student :integer default("0")
+# outline :integer default("0")
+# publish_resource :integer default("0")
+# is_delete :integer default("0")
+# end_time :integer
+# end_term :string(255)
+# is_excellent :integer default("0")
+# excellent_option :integer default("0")
+# is_copy :integer default("0")
+# visits :integer default("0")
+# syllabus_id :integer
+# invite_code :string(255)
+# qrcode :string(255)
+# qrcode_expiretime :integer default("0")
+# invite_code_halt :integer default("0")
+# os_allow :integer default("0")
+# credit :float(24)
+# is_end :boolean default("0")
+# end_date :date
+# choose_group_allow :boolean default("0")
+# homepage_show :boolean default("0")
+# course_list_id :integer
+# members_count :integer default("0")
+# homework_commons_count :integer default("0")
+# show_unit :boolean default("0")
+# teacher_list :string(255) default("老师")
+# student_list :string(255) default("学生")
+# is_hidden :boolean default("0")
+# course_members_count :integer default("0")
+# course_groups_count :integer default("0")
+# authentication :boolean default("0")
+# professional_certification :boolean default("0")
+# graduation_topics_count :integer default("0")
+# graduation_tasks_count :integer default("0")
+# polls_count :integer default("0")
+# exercises_count :integer default("0")
+# start_date :date
+# subject_id :integer default("0")
+# excellent :boolean default("0")
+# email_notify :boolean default("0")
+# sticky :boolean default("0")
+# sticky_time :datetime
+# laboratory_id :integer
+# mooc_course_id :integer
+#
+# Indexes
+#
+# index_courses_on_invite_code (invite_code) UNIQUE
+# index_courses_on_laboratory_id (laboratory_id)
+# index_courses_on_mooc_course_id (mooc_course_id)
+# index_courses_on_school_id_and_is_delete (school_id,is_delete)
+# index_courses_on_subject_id (subject_id)
+# index_courses_on_tea_id (tea_id)
+#
+
class Trustie::Course < Trustie::Database
has_many :course_groups, class_name: "Trustie::CourseGroup"
has_many :homework_commons, class_name: "Trustie::HomeworkCommon"
-end
\ No newline at end of file
+end
diff --git a/app/models/trustie/course_group.rb b/app/models/trustie/course_group.rb
index d9fee73b3..539ddaa11 100644
--- a/app/models/trustie/course_group.rb
+++ b/app/models/trustie/course_group.rb
@@ -1,3 +1,25 @@
+# == Schema Information
+#
+# Table name: course_groups
+#
+# id :integer not null, primary key
+# name :string(255)
+# course_id :integer
+# created_at :datetime not null
+# updated_at :datetime not null
+# members_count :integer
+# invite_code :string(255)
+# position :integer default("0")
+# course_members_count :integer default("0")
+# invite_code_halt :boolean default("0")
+# invite_code_set :integer default("0")
+#
+# Indexes
+#
+# index_course_groups_on_course_id (course_id)
+# index_course_groups_on_invite_code (invite_code) UNIQUE
+#
+
class Trustie::CourseGroup < Trustie::Database
belongs_to :course, class_name: "Trustie::Course"
-end
\ No newline at end of file
+end
diff --git a/app/models/trustie/homework_common.rb b/app/models/trustie/homework_common.rb
index 70dd31e6a..32af834ef 100644
--- a/app/models/trustie/homework_common.rb
+++ b/app/models/trustie/homework_common.rb
@@ -1,3 +1,54 @@
+# == Schema Information
+#
+# Table name: homework_commons
+#
+# id :integer not null, primary key
+# name :string(255)
+# user_id :integer
+# description :text(65535)
+# publish_time :datetime
+# end_time :datetime
+# homework_type :integer default("1")
+# late_penalty :string(255) default("0")
+# course_id :integer
+# created_at :datetime not null
+# updated_at :datetime not null
+# teacher_priority :integer default("1")
+# anonymous_comment :boolean default("0")
+# quotes :integer default("0")
+# is_open :integer default("0")
+# simi_time :datetime
+# score_open :boolean default("0")
+# anonymous_appeal :boolean default("0")
+# homework_bank_id :integer
+# is_update :boolean default("0")
+# is_public :boolean default("0")
+# reference_answer :text(65535)
+# answer_public :boolean default("0")
+# archive_time :datetime
+# allow_late :boolean default("0")
+# late_time :datetime
+# work_public :boolean default("0")
+# explanation :text(65535)
+# unified_setting :boolean default("1")
+# comment_public :boolean default("1")
+# course_homework_category_id :integer
+# work_efficiency :boolean default("0")
+# eff_score :float(24) default("0")
+# max_efficiency :float(24) default("0")
+# course_second_category_id :integer default("0")
+# calculation_time :datetime
+# position :integer default("0")
+# total_score :float(24) default("100")
+# category_position :integer default("0")
+#
+# Indexes
+#
+# index_homework_commons_on_course_id_and_id (course_id,id)
+# index_homework_commons_on_course_second_category_id (course_second_category_id)
+# index_homework_commons_on_homework_bank_id (homework_bank_id)
+#
+
class Trustie::HomeworkCommon < Trustie::Database
belongs_to :course, class_name: "Trustie::Course"
-end
\ No newline at end of file
+end
diff --git a/app/models/user.rb b/app/models/user.rb
index 0c315e7a3..047adc137 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -39,13 +39,15 @@
# business :boolean default("0")
# profile_completed :boolean default("0")
# laboratory_id :integer
-# platform :string(255) default("0")
-# gitea_token :string(255)
-# gitea_uid :integer
# is_shixun_marker :boolean default("0")
+# admin_visitable :boolean default("0")
+# collaborator :boolean default("0")
+# gitea_uid :integer
# is_sync_pwd :boolean default("1")
# watchers_count :integer default("0")
# devops_step :integer default("0")
+# gitea_token :string(255)
+# platform :string(255)
#
# Indexes
#
@@ -53,8 +55,9 @@
# index_users_on_homepage_engineer (homepage_engineer)
# index_users_on_homepage_teacher (homepage_teacher)
# index_users_on_laboratory_id (laboratory_id)
-# index_users_on_login (login)
-# index_users_on_mail (mail)
+# index_users_on_login (login) UNIQUE
+# index_users_on_mail (mail) UNIQUE
+# index_users_on_phone (phone) UNIQUE
# index_users_on_type (type)
#
@@ -66,6 +69,7 @@ class User < Owner
include Likeable
include BaseModel
include Droneable
+ include User::Avatar
# include Searchable::Dependents::User
# devops step
@@ -132,10 +136,6 @@ class User < Owner
has_many :attachments,foreign_key: :author_id, :dependent => :destroy
- # 关注
- # has_many :be_watchers, foreign_key: :user_id, dependent: :destroy # 我的关注
- # has_many :be_watcher_users, through: :be_watchers, dependent: :destroy # 我关注的用户
-
has_one :ci_cloud_account, class_name: 'Ci::CloudAccount', dependent: :destroy
# 认证
@@ -146,9 +146,14 @@ class User < Owner
has_many :trail_auth_apply_actions, -> { where(container_type: 'TrialAuthorization') }, class_name: 'ApplyAction'
# has_many :attendances
-
+ has_many :applied_messages, dependent: :destroy
+ has_many :operate_applied_messages, class_name: 'AppliedMessage', dependent: :destroy
# 项目
has_many :applied_projects, dependent: :destroy
+ has_many :operate_applied_transfer_projects, class_name: 'AppliedTransferProject', dependent: :destroy
+ has_many :members, dependent: :destroy
+ has_many :team_users, dependent: :destroy
+ has_many :teams, through: :team_users
# 教学案例
# has_many :libraries, dependent: :destroy
@@ -158,20 +163,35 @@ class User < Owner
has_many :organization_users, dependent: :destroy
has_many :organizations, through: :organization_users
+ has_many :pinned_projects, dependent: :destroy
+ has_many :is_pinned_projects, through: :pinned_projects, source: :project
+ accepts_nested_attributes_for :is_pinned_projects
+ has_many :issues, dependent: :destroy, foreign_key: :author_id
+ has_many :pull_requests, dependent: :destroy
+ has_many :public_keys, class_name: "Gitea::PublicKey",primary_key: :gitea_uid, foreign_key: :owner_id, dependent: :destroy
+ has_one :user_template_message_setting, dependent: :destroy
+
+ has_many :system_notification_histories
+ has_many :system_notifications, through: :system_notification_histories
+
# Groups and active users
scope :active, lambda { where(status: STATUS_ACTIVE) }
scope :like, lambda { |keywords|
- where("LOWER(concat(nickname, lastname, firstname, login, mail)) LIKE ?", "%#{keywords.split(" ").join('|')}%") unless keywords.blank?
+ 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.split(" ").join('|')}%") unless keywords.blank?
}
scope :simple_select, -> {select(:id, :login, :lastname,:firstname, :nickname, :gitea_uid, :type)}
attr_accessor :password, :password_confirmation
- delegate :gender, :department_id, :school_id, :location, :location_city, :technical_title, to: :user_extension, allow_nil: true
+ delegate :description, :gender, :department_id, :school_id, :location, :location_city,
+ :show_email, :show_location, :show_department,
+ :technical_title, :province, :city, :custom_department, to: :user_extension, allow_nil: true
- before_save :update_hashed_password
+ before_save :update_hashed_password, :set_lastname
+ after_save :reset_cache_data
after_create do
SyncTrustieJob.perform_later("user", 1) if allow_sync_to_trustie?
end
@@ -188,6 +208,28 @@ class User < Owner
validate :validate_sensitive_string
validate :validate_password_length
+ def reset_cache_data
+ Cache::V2::OwnerCommonService.new(self.id).reset
+ end
+
+ # 用户参与的所有项目
+ def full_member_projects
+ normal_projects = Project.members_projects(self.id).to_sql
+ org_projects = Project.joins(teams: :team_users).where(team_users: {user_id: self.id}).to_sql
+ return Project.from("( #{ normal_projects} UNION #{ org_projects } ) AS projects").distinct
+ end
+
+ # 用户管理的所有项目
+ def full_admin_projects
+ normal_projects = Project.joins(members: :roles).where(roles: {name: 'Manager'}, members: {user_id: self.id}).to_sql
+ org_projects = Project.joins(teams: :team_users).where(teams: {authorize: %w(admin owner)}, team_users: {user_id: self.id}).to_sql
+ return Project.from("( #{ normal_projects} UNION #{ org_projects } ) AS projects").distinct
+ end
+
+ def name
+ login
+ end
+
# 删除自动登录的token,一旦退出下次会提示需要登录
def delete_autologin_token(value)
Token.where(:user_id => id, :action => autologin_name, :value => value).delete_all
@@ -202,7 +244,7 @@ class User < Owner
end
def project_manager?(project)
- project.managers.exists?(user: self) || self.admin?
+ project.manager?(self) || self.admin?
end
# 学号
@@ -649,7 +691,7 @@ class User < Owner
end
def self.generate_salt
- Educoder::Utils.random_hex(16)
+ Gitlink::Utils.random_hex(16)
end
# 全部已认证
@@ -731,6 +773,10 @@ class User < Owner
laboratory_id.present? && laboratory_id != 1
end
+ def profile_is_completed?
+ self.nickname.present? && self.mail.present?
+ end
+
protected
def validate_password_length
# 管理员的初始密码是5位
@@ -757,6 +803,10 @@ class User < Owner
self.laboratory = Laboratory.current if laboratory_id.blank?
end
+
+ def set_lastname
+ self.lastname = self.nickname if changes[:nickname].present?
+ end
end
diff --git a/app/models/user/avatar.rb b/app/models/user/avatar.rb
new file mode 100644
index 000000000..22f770cdd
--- /dev/null
+++ b/app/models/user/avatar.rb
@@ -0,0 +1,43 @@
+require 'letter_avatar/has_avatar'
+require 'chinese_pinyin'
+
+class User
+ module Avatar
+ extend ActiveSupport::Concern
+ include LetterAvatar::HasAvatar
+
+ def username
+ self.lastname.blank? ? self.login : Pinyin.t(self.lastname)
+ end
+
+ def get_letter_avatar_url(size = :lg)
+ avatar_path(self.username, size).split('public/')&.last
+ end
+
+ def self.get_letter_avatar_url(name)
+ return "" if name.blank?
+ avatar = LetterAvatar.generate Pinyin.t(name), 120
+ avatar.split('public/')&.last
+ end
+
+ def avatar_path(username, size)
+ LetterAvatar.generate username, avatar_size(size)
+ end
+
+ # 返回头像尺寸
+ # xs: 22px
+ # sm: 32px
+ # md: 48px
+ # lg: 120px
+ def avatar_size(size)
+ case size
+ when :xs then 22
+ when :sm then 32
+ when :md then 48
+ when :lg then 120
+ else size
+ end
+ end
+
+ end
+end
diff --git a/app/models/user_action.rb b/app/models/user_action.rb
index 3ad8010ea..179359695 100644
--- a/app/models/user_action.rb
+++ b/app/models/user_action.rb
@@ -12,7 +12,9 @@
#
# Indexes
#
-# index_user_actions_on_ip (ip)
+# index_user_actions_on_ip (ip)
+# index_user_actions_on_user_id (user_id)
+# index_user_actions_on_user_id_and_action_type (user_id,action_type)
#
class UserAction < ApplicationRecord
diff --git a/app/models/user_agent.rb b/app/models/user_agent.rb
index 49d7b35a1..ba519d6fb 100644
--- a/app/models/user_agent.rb
+++ b/app/models/user_agent.rb
@@ -10,10 +10,13 @@
# updated_at :datetime not null
# register_status :integer default("0")
# action_status :integer default("0")
+# is_delete :boolean default("0")
+# user_id :integer
#
# Indexes
#
-# index_user_agents_on_ip (ip) UNIQUE
+# index_user_agents_on_ip (ip)
+# index_user_agents_on_user_id (user_id)
#
class UserAgent < ApplicationRecord
diff --git a/app/models/user_extension.rb b/app/models/user_extension.rb
index 20e2b5c4f..ee208af7e 100644
--- a/app/models/user_extension.rb
+++ b/app/models/user_extension.rb
@@ -22,6 +22,15 @@
# school_id :integer
# description :string(255) default("")
# department_id :integer
+# honor :text(65535)
+# edu_background :integer
+# edu_entry_year :integer
+# province :string(255)
+# city :string(255)
+# custom_department :string(255)
+# show_email :boolean default("0")
+# show_location :boolean default("0")
+# show_department :boolean default("0")
#
# Indexes
#
@@ -30,25 +39,25 @@
# index_user_extensions_on_user_id (user_id)
#
-class UserExtension < ApplicationRecord
- # identity 0: 教师教授 1: 学生, 2: 专业人士, 3: 开发者
- enum identity: { teacher: 0, student: 1, professional: 2, developer: 3, enterprise: 4, unselect: -1 }
-
- belongs_to :user, touch: true
- belongs_to :school, optional: true
- # belongs_to :department, optional: true
-
- # before_save :set_laboratory_school
-
- def identity_text
- I18n.t("user.identity.#{identity}")
- end
-
- private
-
- def set_laboratory_school
- # return unless new_record?
-
- # self.school_id = Laboratory.current.school_id if school_id.blank? && !Laboratory.current.main_site?
- end
-end
+class UserExtension < ApplicationRecord
+ # identity 0: 教师教授 1: 学生, 2: 专业人士, 3: 开发者
+ enum identity: { teacher: 0, student: 1, professional: 2, developer: 3, enterprise: 4, unselect: -1 }
+
+ belongs_to :user, touch: true
+ belongs_to :school, optional: true
+ # belongs_to :department, optional: true
+
+ # before_save :set_laboratory_school
+
+ def identity_text
+ I18n.t("user.identity.#{identity}")
+ end
+
+ private
+
+ def set_laboratory_school
+ # return unless new_record?
+
+ # self.school_id = Laboratory.current.school_id if school_id.blank? && !Laboratory.current.main_site?
+ end
+end
diff --git a/app/models/user_template_message_setting.rb b/app/models/user_template_message_setting.rb
new file mode 100644
index 000000000..3a3416ee3
--- /dev/null
+++ b/app/models/user_template_message_setting.rb
@@ -0,0 +1,72 @@
+# == Schema Information
+#
+# Table name: user_template_message_settings
+#
+# id :integer not null, primary key
+# user_id :integer
+# notification_body :text(65535)
+# email_body :text(65535)
+# created_at :datetime not null
+# updated_at :datetime not null
+#
+# Indexes
+#
+# index_user_template_message_settings_on_user_id (user_id)
+#
+
+class UserTemplateMessageSetting < ApplicationRecord
+ serialize :notification_body, Hash
+ serialize :email_body, Hash
+
+ belongs_to :user
+
+ before_update :set_body_value
+
+ def self.build(user_id)
+ self.create!(user_id: user_id, notification_body: init_notification_body, email_body: init_email_body)
+ end
+
+ def self.init_notification_body
+ {
+ "Normal::Permission": true,
+ "Normal::Project": true,
+ "Normal::Organization": true,
+ "Normal::IssueAssigned": true,
+ "Normal::PullRequestAssigned": true,
+ "CreateOrAssign::IssueChanged": true,
+ "CreateOrAssign::PullRequestChanged": true,
+ "ManageProject::Issue": true,
+ "ManageProject::PullRequest": true,
+ "ManageProject::Member": true,
+ "ManageProject::SettingChanged": true,
+ }.stringify_keys!
+ end
+
+ def self.init_email_body
+ {
+ "Normal::Permission": false,
+ "Normal::Project": false,
+ "Normal::Organization": false,
+ "Normal::IssueAssigned": false,
+ "Normal::PullRequestAssigned": false,
+ "CreateOrAssign::IssueChanged": false,
+ "CreateOrAssign::PullRequestChanged": false,
+ "ManageProject::Issue": false,
+ "ManageProject::PullRequest": false,
+ "ManageProject::Member": false,
+ "ManageProject::SettingChanged": false,
+ }.stringify_keys!
+ end
+
+ private
+
+ def set_body_value
+ self.notification_body.each do |k, v|
+ self.notification_body[k] = ActiveModel::Type::Boolean.new.cast(v).nil? ? false : ActiveModel::Type::Boolean.new.cast(v)
+ end
+
+ self.email_body.each do |k, v|
+ self.email_body[k] = ActiveModel::Type::Boolean.new.cast(v).nil? ? false : ActiveModel::Type::Boolean.new.cast(v)
+ end
+ end
+end
diff --git a/app/models/version.rb b/app/models/version.rb
index 1bafa2fb4..436881653 100644
--- a/app/models/version.rb
+++ b/app/models/version.rb
@@ -25,11 +25,13 @@
class Version < ApplicationRecord
include DunCheckAble
- belongs_to :project, counter_cache: true
+ belongs_to :project, counter_cache: true, touch: true
has_many :issues, class_name: "Issue", foreign_key: "fixed_version_id"
belongs_to :user, optional: true
scope :version_includes, ->{includes(:issues, :user)}
+ scope :closed, ->{where(status: 'closed')}
+ scope :opening, ->{where(status: 'open')}
# def open_issues_count
# issues.select(:id,:status_id).where(status_id: [1,2,3,4,6]).size
diff --git a/app/models/version_release.rb b/app/models/version_release.rb
index 16b823a78..3c97420ea 100644
--- a/app/models/version_release.rb
+++ b/app/models/version_release.rb
@@ -17,6 +17,7 @@
# created_at :datetime not null
# updated_at :datetime not null
# repository_id :integer
+# sha :string(255)
#
# Indexes
#
@@ -29,4 +30,9 @@ class VersionRelease < ApplicationRecord
has_many :project_trends, as: :trend, dependent: :destroy
scope :releases_size, ->{where(draft: false, prerelease: false).size}
has_many :attachments, as: :container, dependent: :destroy
+
+ def update_sha
+ git_release = Gitea::Versions::GetService.call(user.gitea_token, repository&.owner&.login, repository&.identifier, version_gid)
+ self.update(sha: git_release["sha"])
+ end
end
diff --git a/app/models/watcher.rb b/app/models/watcher.rb
index 76a82702b..5a2cd96fb 100644
--- a/app/models/watcher.rb
+++ b/app/models/watcher.rb
@@ -15,10 +15,47 @@
# watchers_user_id_type (user_id,watchable_type)
#
-class Watcher < ApplicationRecord
- belongs_to :user
-
- belongs_to :watchable, polymorphic: true, counter_cache: :watchers_count
-
- scope :watching_users, ->(watchable_id){ where("watchable_type = ? and user_id = ?",'User',watchable_id)}
-end
+class Watcher < ApplicationRecord
+ belongs_to :user
+
+ belongs_to :watchable, polymorphic: true, counter_cache: :watchers_count
+
+ scope :watching_users, ->(watchable_id){ where("watchable_type = ? and user_id = ?",'User',watchable_id)}
+
+ after_create :send_create_message_to_notice_system, :incre_project_common, :incre_user_statistic, :incre_platform_statistic
+ after_destroy :decre_project_common, :decre_user_statistic, :decre_platform_statistic
+
+
+ def incre_project_common
+ CacheAsyncSetJob.perform_later("project_common_service", {watchers: 1}, self.watchable_id) if self.watchable_type == "Project"
+ end
+
+ def decre_project_common
+ CacheAsyncSetJob.perform_later("project_common_service", {watchers: -1}, self.watchable_id) if self.watchable_type == "Project"
+ end
+
+ def incre_user_statistic
+ CacheAsyncSetJob.perform_later("user_statistic_service", {follow_count: 1}, self.watchable_id) if self.watchable_type == "User"
+ CacheAsyncSetJob.perform_later("user_statistic_service", {project_watcher_count: 1}, self.watchable&.user_id) if self.watchable_type == "Project"
+ end
+
+ def decre_user_statistic
+ CacheAsyncSetJob.perform_later("user_statistic_service", {follow_count: -1}, self.watchable_id) if self.watchable_type == "User"
+ CacheAsyncSetJob.perform_later("user_statistic_service", {project_watcher_count: -1}, self.watchable&.user_id) if self.watchable_type == "Project"
+ end
+
+ def incre_platform_statistic
+ CacheAsyncSetJob.perform_later("platform_statistic_service", {follow_count: 1}) if self.watchable_type == "User"
+ CacheAsyncSetJob.perform_later("platform_statistic_service", {project_watcher_count: 1}) if self.watchable_type == "Project"
+ end
+
+ def decre_platform_statistic
+ CacheAsyncSetJob.perform_later("platform_statistic_service", {follow_count: -1}) if self.watchable_type == "User"
+ CacheAsyncSetJob.perform_later("platform_statistic_service", {project_watcher_count: -1}) if self.watchable_type == "Project"
+ end
+
+ def send_create_message_to_notice_system
+ SendTemplateMessageJob.perform_later('FollowTip', self.id) if self.watchable.is_a?(User) if Site.has_notice_menu?
+ end
+
+end
diff --git a/app/queries/admins/apply_item_bank_query.rb b/app/queries/admins/apply_item_bank_query.rb
index 37b6d25a6..8608e238d 100644
--- a/app/queries/admins/apply_item_bank_query.rb
+++ b/app/queries/admins/apply_item_bank_query.rb
@@ -26,7 +26,7 @@ class Admins::ApplyItemBankQuery < ApplicationQuery
keyword = params[:keyword].to_s.strip
if keyword.present?
applies = applies.joins(user: { user_extension: :school })
- .where('CONCAT(lastname,firstname) LIKE :keyword OR schools.name LIKE :keyword', keyword: "%#{keyword}%")
+ .where('CONCAT(lastname,firstname) LIKE :keyword OR users.nickname LIKE :keyword OR schools.name LIKE :keyword', keyword: "%#{keyword}%")
end
custom_sort(applies, params[:sort_by], params[:sort_direction])
diff --git a/app/queries/admins/apply_user_authentication_query.rb b/app/queries/admins/apply_user_authentication_query.rb
index a4d1c2a4a..741f28e5a 100644
--- a/app/queries/admins/apply_user_authentication_query.rb
+++ b/app/queries/admins/apply_user_authentication_query.rb
@@ -26,7 +26,7 @@ class Admins::ApplyUserAuthenticationQuery < ApplicationQuery
keyword = params[:keyword].to_s.strip
if keyword.present?
applies = applies.joins(user: { user_extension: :school })
- .where('CONCAT(lastname,firstname) LIKE :keyword OR schools.name LIKE :keyword', keyword: "%#{keyword}%")
+ .where('CONCAT(lastname,firstname) LIKE :keyword OR users.nickname LIKE :keyword OR schools.name LIKE :keyword', keyword: "%#{keyword}%")
end
custom_sort(applies, params[:sort_by], params[:sort_direction])
diff --git a/app/queries/admins/course_list_query.rb b/app/queries/admins/course_list_query.rb
deleted file mode 100644
index 24eedaf1b..000000000
--- a/app/queries/admins/course_list_query.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-class Admins::CourseListQuery < ApplicationQuery
- include CustomSortable
-
- attr_reader :params
-
- sort_columns :created_at, default_by: :created_at, default_direction: :desc
-
- def initialize(params)
- @params = params
- end
-
- def call
- course_lists = CourseList.all
-
- # 关键字模糊查询
- keyword = params[:keyword].to_s.strip
- if keyword.present?
- search_type = params[:search_type] || "0"
- case search_type
- when "0"
- course_lists = course_lists.joins(:user)
- .where('CONCAT(lastname, firstname) like :keyword', keyword: "%#{keyword}%")
- when "1"
- course_lists = course_lists.where('name like :keyword', keyword: "%#{keyword}%")
- end
- end
-
- custom_sort(course_lists, params[:sort_by], params[:sort_direction])
- end
-end
\ No newline at end of file
diff --git a/app/queries/admins/course_query.rb b/app/queries/admins/course_query.rb
deleted file mode 100644
index e883650d0..000000000
--- a/app/queries/admins/course_query.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-class Admins::CourseQuery < ApplicationQuery
- include CustomSortable
-
- attr_reader :params
-
- sort_columns :created_at, default_by: :created_at, default_direction: :desc, default_table: 'courses'
-
- def initialize(params)
- @params = params
- end
-
- def call
- courses = Course.all
-
- courses = courses.where(id: params[:id]) if params[:id].present?
-
- # 状态过滤
- status =
- case params[:status].to_s.strip
- when 'processing' then 0
- when 'ended' then 1
- end
- courses = courses.where(is_end: status) if status
-
- # 单位
- if params[:school_id].present?
- courses = courses.where(school_id: params[:school_id])
- end
-
- # 首页展示
- if params[:homepage_show].present? && params[:homepage_show].to_s == 'true'
- courses = courses.where(homepage_show: true)
- end
-
- # 关键字
- keyword = params[:keyword].to_s.strip
- if keyword
- sql = 'CONCAT(lastname, firstname) LIKE :keyword OR courses.name LIKE :keyword OR course_lists.name LIKE :keyword'
- courses = courses.joins(:teacher, :course_list).where(sql, keyword: "%#{keyword}%")
- end
-
- custom_sort(courses, params[:sort_by], params[:sort_direction])
- end
-end
\ No newline at end of file
diff --git a/app/queries/admins/department_apply_query.rb b/app/queries/admins/department_apply_query.rb
deleted file mode 100644
index 500536ddb..000000000
--- a/app/queries/admins/department_apply_query.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-class Admins::DepartmentApplyQuery < ApplicationQuery
- include CustomSortable
-
- attr_reader :params
-
- sort_columns :created_at, default_by: :created_at, default_direction: :desc
-
- def initialize(params)
- @params = params
- end
-
- def call
- status = params[:status]
-
- applies = ApplyAddDepartment.where(status: status) if status.present?
-
- # 关键字模糊查询
- keyword = params[:keyword].to_s.strip
- if keyword.present?
- applies = applies.where('name LIKE :keyword', keyword: "%#{keyword}%")
- end
-
- custom_sort(applies, params[:sort_by], params[:sort_direction])
- end
-end
\ No newline at end of file
diff --git a/app/queries/admins/department_query.rb b/app/queries/admins/department_query.rb
deleted file mode 100644
index f0b8c5d24..000000000
--- a/app/queries/admins/department_query.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-class Admins::DepartmentQuery < ApplicationQuery
- include CustomSortable
-
- attr_reader :params
-
- sort_columns :created_at, default_by: :created_at, default_direction: :desc
-
- def initialize(params)
- @params = params
- end
-
- def call
- departments = Department.where(is_auth: true).without_deleted
-
- keyword = params[:keyword].to_s.strip
- if keyword.present?
- departments = departments.joins(:school)
- .where('schools.name LIKE :keyword OR departments.name LIKE :keyword', keyword: "%#{keyword}%")
- end
-
- if params[:with_member].to_s == 'true'
- subquery = DepartmentMember.where('department_id = departments.id').select('1 AS one').to_sql
- departments = departments.where("EXISTS(#{subquery})")
- end
-
- if params[:with_identifier].to_s == 'true'
- departments = departments.where.not(identifier: nil).where.not(identifier: '')
- end
-
- custom_sort(departments, params[:sort_by], params[:sort_direction])
- end
-end
\ No newline at end of file
diff --git a/app/queries/admins/edu_setting_query.rb b/app/queries/admins/edu_setting_query.rb
new file mode 100644
index 000000000..3e61ffd33
--- /dev/null
+++ b/app/queries/admins/edu_setting_query.rb
@@ -0,0 +1,28 @@
+class Admins::EduSettingQuery < ApplicationQuery
+ include CustomSortable
+
+ attr_reader :params
+
+ sort_columns :id, default_by: :id, default_direction: :desc
+
+ def initialize(params)
+ @params = params
+ end
+
+ def call
+ collection = EduSetting.all
+ collection = filter_settings(collection)
+
+ custom_sort collection, params[:sort_by], params[:sort_direction]
+ end
+
+ def filter_settings(collection)
+ by_search(collection)
+ end
+
+ def by_search(collection)
+ keyword = strip_param(:search)
+ collection.by_search(keyword)
+ end
+
+end
\ No newline at end of file
diff --git a/app/queries/admins/laboratory_shixun_query.rb b/app/queries/admins/laboratory_shixun_query.rb
deleted file mode 100644
index d489aed21..000000000
--- a/app/queries/admins/laboratory_shixun_query.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-class Admins::LaboratoryShixunQuery < ApplicationQuery
- attr_reader :laboratory, :params
-
- def initialize(laboratory, params)
- @laboratory = laboratory
- @params = params
- end
-
- def call
- laboratory_shixuns = laboratory.laboratory_shixuns.joins(:shixun)
-
- keyword = params[:keyword].to_s.strip
- if keyword.present?
- like_sql = 'shixuns.name LIKE :keyword OR CONCAT(users.lastname, users.firstname) LIKE :keyword'
- laboratory_shixuns = laboratory_shixuns.joins(shixun: :user).where(like_sql, keyword: "%#{keyword}%")
- end
-
- # 实训状态
- laboratory_shixuns = laboratory_shixuns.where(shixuns: { status: params[:status] }) if params[:status].present?
-
- # 技术平台
- if params[:tag_id].present?
- laboratory_shixuns = laboratory_shixuns.joins(shixun: :shixun_mirror_repositories)
- .where(shixun_mirror_repositories: { mirror_repository_id: params[:tag_id] })
- end
-
- # 首页展示、单位自建
- %i[homepage ownership].each do |column|
- if params[column].present? && params[column].to_s == 'true'
- laboratory_shixuns = laboratory_shixuns.where(column => true)
- end
- end
-
- laboratory_shixuns
- end
-end
\ No newline at end of file
diff --git a/app/queries/admins/laboratory_subject_query.rb b/app/queries/admins/laboratory_subject_query.rb
index 316ea9743..d47463332 100644
--- a/app/queries/admins/laboratory_subject_query.rb
+++ b/app/queries/admins/laboratory_subject_query.rb
@@ -11,7 +11,7 @@ class Admins::LaboratorySubjectQuery < ApplicationQuery
keyword = params[:keyword].to_s.strip
if keyword.present?
- like_sql = 'subjects.name LIKE :keyword OR CONCAT(users.lastname, users.firstname) LIKE :keyword'
+ like_sql = 'subjects.name LIKE :keyword OR CONCAT(users.lastname, users.firstname) LIKE :keyword OR users.nickname LIKE :keyword'
laboratory_subjects = laboratory_subjects.joins(subject: :user).where(like_sql, keyword: "%#{keyword}%")
end
diff --git a/app/queries/admins/school_query.rb b/app/queries/admins/school_query.rb
deleted file mode 100644
index 3206f0858..000000000
--- a/app/queries/admins/school_query.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-class Admins::SchoolQuery < ApplicationQuery
- include CustomSortable
-
- attr_reader :params
-
- sort_columns :users_count, :created_at, default_by: :created_at, default_direction: :desc
-
- def initialize(params)
- @params = params
- end
-
- def call
- schools = School.all
-
- keyword = strip_param(:keyword)
- Rails.logger.info("###########{keyword}")
- if keyword
- schools = schools.where('schools.name LIKE ?', "%#{keyword}%")
- end
- schools = schools.left_joins(:user_extensions).select('schools.*, IFNULL(count(user_extensions.user_id),0) users_count').group('schools.id')
- custom_sort schools, params[:sort_by], params[:sort_direction]
- end
-end
\ No newline at end of file
diff --git a/app/queries/admins/site_query.rb b/app/queries/admins/site_query.rb
new file mode 100644
index 000000000..1d15f0312
--- /dev/null
+++ b/app/queries/admins/site_query.rb
@@ -0,0 +1,35 @@
+class Admins::SiteQuery < ApplicationQuery
+ include CustomSortable
+
+ attr_reader :params
+
+ sort_columns :id, default_by: :id, default_direction: :desc
+
+ def initialize(params)
+ @params = params
+ end
+
+ def call
+ collection = Site.all
+ collection = filter_sites(collection)
+
+ custom_sort collection, params[:sort_by], params[:sort_direction]
+ end
+
+ def filter_sites(collection)
+ collection = by_search(collection)
+ collection = by_stie_type(collection)
+ collection
+ end
+
+ def by_search(collection)
+ keyword = strip_param(:search)
+ collection.by_search(keyword)
+ end
+
+ def by_stie_type(collection)
+ site_type = strip_param(:site_type)
+ collection.by_site_type(site_type)
+ end
+
+end
\ No newline at end of file
diff --git a/app/queries/admins/subject_query.rb b/app/queries/admins/subject_query.rb
deleted file mode 100644
index 0415deb9f..000000000
--- a/app/queries/admins/subject_query.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-class Admins::SubjectQuery < ApplicationQuery
- include CustomSortable
-
- attr_reader :params
-
- sort_columns :created_at, default_by: :created_at, default_direction: :desc, default_table: 'subjects'
-
- def initialize(params)
- @params = params
- end
-
- def call
- subjects = Subject.all
-
- subjects = subjects.where(id: params[:id]) if params[:id].present?
-
- # 状态过滤
- status =
- case params[:status].to_s.strip
- when "editing" then {status: 0}
- when "applying" then {status: 2, public: [0, 1]}
- when "pending" then {public: 1}
- when "published" then {public: 2}
- end
-
- subjects = subjects.where(status) if status
-
- # 创建者单位
- if params[:school_id].present?
- subjects = subjects.joins(user: :user_extension).where(user_extensions: { school_id: params[:school_id] })
- end
-
- # 首页展示、金课
- %i[homepage_show excellent].each do |column|
- if params[column].present? && params[column].to_s == 'true'
- subjects = subjects.where(column => true)
- end
- end
-
- # 关键字
- keyword = params[:keyword].to_s.strip
- if keyword
- sql = 'CONCAT(lastname, firstname) LIKE :keyword OR subjects.name LIKE :keyword'
- subjects = subjects.joins(:user).where(sql, keyword: "%#{keyword}%")
- end
-
- custom_sort(subjects, params[:sort_by], params[:sort_direction])
- end
-end
\ No newline at end of file
diff --git a/app/queries/admins/user_query.rb b/app/queries/admins/user_query.rb
index 972201fbe..f9506ddf5 100644
--- a/app/queries/admins/user_query.rb
+++ b/app/queries/admins/user_query.rb
@@ -30,14 +30,14 @@ class Admins::UserQuery < ApplicationQuery
# 关键字检索
keyword = params[:keyword].to_s.strip.presence
if keyword
- sql = 'CONCAT(lastname, firstname) LIKE :keyword OR login LIKE :keyword OR mail LIKE :keyword OR phone LIKE :keyword OR nickname LIKE :keyword '
+ sql = 'CONCAT(lastname, firstname) LIKE :keyword OR nickname LIKE :keyword OR login LIKE :keyword OR mail LIKE :keyword OR phone LIKE :keyword'
users = users.where(sql, keyword: "%#{keyword}%")
end
# 姓名
name = params[:name].to_s.strip.presence
if name.present?
- users = users.where('CONCAT(lastname, firstname) LIKE :name', name: "%#{name}%")
+ users = users.where('CONCAT(lastname, firstname) LIKE :name OR nickname LIKE :name', name: "%#{name}%")
end
# 单位ID
diff --git a/app/queries/ci/builds/list_query.rb b/app/queries/ci/builds/list_query.rb
index 7091723a4..e548c3a45 100644
--- a/app/queries/ci/builds/list_query.rb
+++ b/app/queries/ci/builds/list_query.rb
@@ -25,7 +25,7 @@ class Ci::Builds::ListQuery < ApplicationQuery
scope
end
- builds = scope.by_branch(params[:branch]) if params[:branch]
+ builds = builds.by_branch(params[:branch]) if params[:branch]
custom_sort(builds, params[:sort_by], params[:sort_direction])
end
diff --git a/app/queries/projects/list_my_query.rb b/app/queries/projects/list_my_query.rb
index 058e81224..d0c82d099 100644
--- a/app/queries/projects/list_my_query.rb
+++ b/app/queries/projects/list_my_query.rb
@@ -18,7 +18,9 @@ class Projects::ListMyQuery < ApplicationQuery
end
if params[:category].blank?
- projects = projects.members_projects(user.id)
+ normal_projects = projects.members_projects(user.id).to_sql
+ org_projects = projects.joins(team_projects: [team: :team_users]).where(team_users: {user_id: user.id}).to_sql
+ projects = Project.from("( #{ normal_projects} UNION #{ org_projects } ) AS projects").distinct
elsif params[:category].to_s == "join"
normal_projects = projects.where.not(user_id: user.id).members_projects(user.id).to_sql
org_projects = projects.joins(team_projects: [team: :team_users]).where(team_users: {user_id: user.id}).to_sql
@@ -51,10 +53,15 @@ class Projects::ListMyQuery < ApplicationQuery
q = projects.ransack(name_or_identifier_cont: params[:search])
- scope = q.result.includes(:project_category, :project_language,:owner, :repository, :members)
+ scope = q.result.includes(:project_category, :project_language,:owner, :repository, :has_pinned_users, :members)
- sort = params[:sort_by] || "updated_on"
- sort_direction = params[:sort_direction] || "desc"
- scope.order("projects.#{sort} #{sort_direction}")
+ sort = Project.column_names.include?(params[:sort_by]) ? params[:sort_by] : "updated_on"
+ sort_direction = %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : "desc"
+
+ if params[:choosed].present? && params[:choosed].is_a?(Array)
+ scope.order("FIELD(id, #{params[:choosed].reverse.join(",")}) desc")
+ else
+ scope.order("projects.#{sort} #{sort_direction}")
+ end
end
end
diff --git a/app/queries/projects/list_query.rb b/app/queries/projects/list_query.rb
index eee99a71f..b06791bd3 100644
--- a/app/queries/projects/list_query.rb
+++ b/app/queries/projects/list_query.rb
@@ -1,28 +1,55 @@
class Projects::ListQuery < ApplicationQuery
include CustomSortable
- attr_reader :params
+ attr_reader :params, :current_user_id
sort_columns :updated_on, :created_on, :forked_count, :praises_count, default_by: :updated_on, default_direction: :desc
- def initialize(params)
+ def initialize(params, current_user_id=nil)
@params = params
+ @current_user_id = current_user_id
end
def call
- q = Project.secret_and_visible.by_name_or_identifier(params[:search])
-
- scope = q
- .with_project_type(params[:project_type])
- .with_project_category(params[:category_id])
- .with_project_language(params[:language_id]).order(order_index: :desc)
+ collection = Project.all
+ collection = filter_projects(collection)
sort = params[:sort_by] || "updated_on"
sort_direction = params[:sort_direction] || "desc"
- custom_sort(scope, sort, sort_direction)
+ custom_sort(collection, sort, sort_direction)
# scope = scope.reorder("projects.#{sort} #{sort_direction}")
# scope
end
+
+ def filter_projects(collection)
+ collection = by_pinned(collection)
+ collection = by_search(collection)
+ collection = by_project_type(collection)
+ collection = by_project_category(collection)
+ collection = by_project_language(collection)
+ collection
+ end
+
+ def by_search(items)
+ items.visible.by_name_or_identifier(params[:search])
+ end
+
+ def by_project_type(items)
+ items.with_project_type(params[:project_type])
+ end
+
+ def by_project_category(items)
+ items.with_project_category(params[:category_id])
+ end
+
+ def by_project_language(items)
+ items.with_project_language(params[:language_id])
+ end
+
+ def by_pinned(items)
+ (params[:pinned].present? && params[:category_id].present?) ? items.pinned : items
+ end
+
end
diff --git a/app/queries/user_query.rb b/app/queries/user_query.rb
index 326665fe9..6668da5c7 100644
--- a/app/queries/user_query.rb
+++ b/app/queries/user_query.rb
@@ -10,7 +10,7 @@ class UserQuery < ApplicationQuery
# 真实姓名
if name = strip_param(:name)
- users = users.where('LOWER(CONCAT(users.lastname, users.firstname)) LIKE ?', "%#{name.downcase}%")
+ users = users.where('LOWER(CONCAT(users.lastname, users.firstname)) LIKE ? OR users.nickname LIKE ?', "%#{name.downcase}%", "%#{name.downcase}%")
end
# 单位名称
diff --git a/app/queries/users/video_query.rb b/app/queries/users/video_query.rb
deleted file mode 100644
index cfaa314cc..000000000
--- a/app/queries/users/video_query.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-class Users::VideoQuery < ApplicationQuery
- include CustomSortable
-
- sort_columns :published_at, :title, default_by: :published_at, default_direction: :desc
-
- attr_reader :user, :params
-
- def initialize(user, params)
- @user = user
- @params = params
- end
-
- def call
- videos = user.videos
-
- videos =
- case params[:status]
- when 'published' then videos.published
- when 'processing' then videos.processing
- else videos.published
- end
-
- keyword = params[:keyword].to_s.strip
- videos = videos.where('title LIKE ?', "%#{keyword}%") if keyword.present?
-
- custom_sort(videos, params[:sort_by], params[:sort_direction])
- end
-end
\ No newline at end of file
diff --git a/app/queries/weapps/search_query.rb b/app/queries/weapps/search_query.rb
deleted file mode 100644
index 665480073..000000000
--- a/app/queries/weapps/search_query.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-class Weapps::SearchQuery < ApplicationQuery
- include ElasticsearchAble
-
- attr_reader :params
-
- def initialize(params)
- @params = params
- end
-
- def call
- modal_name.search(keyword, search_options)
- end
-
- private
-
- def search_options
- hash = {
- fields: [:name],
- page: page,
- per_page: per_page
- }
- hash.merge(where: { status: 2 }) if modal_name == Shixun
-
- hash
- end
-
- def modal_name
- @_modal_name ||= begin
- case params[:type].to_s
- when 'subject' then Subject
- when 'shixun' then Shixun
- when 'course' then Course
- else Subject
- end
- end
- end
-end
\ No newline at end of file
diff --git a/app/queries/weapps/subject_query.rb b/app/queries/weapps/subject_query.rb
deleted file mode 100644
index e2f5625e1..000000000
--- a/app/queries/weapps/subject_query.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-class Weapps::SubjectQuery < ApplicationQuery
- include CustomSortable
- attr_reader :params
-
- def initialize(current_laboratory, params)
- @current_laboratory = current_laboratory
- @params = params
- end
-
- def call
- subjects = @current_laboratory.subjects.unhidden.publiced.show_moblied
-
- # 课程体系的过滤
- if params[:sub_discipline_id].present?
- subjects = subjects.joins(:sub_disciplines).where(sub_disciplines: {id: params[:sub_discipline_id]})
- elsif params[:discipline_id].present?
- subjects = subjects.joins(:sub_disciplines).where(sub_disciplines: {discipline_id: params[:discipline_id]})
- else
- subjects = subjects.joins(:sub_discipline_containers).where(sub_discipline_containers: {container_type: "Subject"})
- end
-
- subjects = subjects.left_joins(:shixuns).select('subjects.id, subjects.name, subjects.excellent, subjects.stages_count, subjects.status, subjects.homepage_show,
- subjects.shixuns_count, subjects.updated_at, IFNULL(sum(shixuns.myshixuns_count), 0) myshixuns_count')
- .group('subjects.id').order("subjects.homepage_show #{sort_type}, #{order_type} #{sort_type}")
- subjects
- end
-
- private
-
- def order_type
- params[:order] || "updated_at"
- end
-
- def sort_type
- params[:sort] || "desc"
- end
-end
\ No newline at end of file
diff --git a/app/services/admins/add_department_member_service.rb b/app/services/admins/add_department_member_service.rb
deleted file mode 100644
index f8331cf4a..000000000
--- a/app/services/admins/add_department_member_service.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-class Admins::AddDepartmentMemberService < ApplicationService
-
- attr_reader :department, :params
-
- def initialize(department, params)
- @department = department
- @params = params
- end
-
- def call
- columns = %i[]
- DepartmentMember.bulk_insert(*columns) do |worker|
- Array.wrap(params[:user_ids]).compact.each do |user_id|
- next if department.department_members.exists?(user_id: user_id)
-
- worker.add(department_id: department.id, user_id: user_id)
- end
- end
- end
-end
\ No newline at end of file
diff --git a/app/services/admins/check_shixun_mirrors_service.rb b/app/services/admins/check_shixun_mirrors_service.rb
deleted file mode 100644
index 4aa0af4cf..000000000
--- a/app/services/admins/check_shixun_mirrors_service.rb
+++ /dev/null
@@ -1,89 +0,0 @@
-class Admins::CheckShixunMirrorsService < ApplicationService
- Error = Class.new(StandardError)
-
- def call
- bridge_images
-
- ActiveRecord::Base.transaction do
- check_sync_mirrors!
-
- check_mirrors!
- end
- end
-
- private
-
- def mirrors
- bridge_images['images']
- end
-
- def sync_mirrors
- bridge_images['imagesNotSync']
- end
-
- def check_mirrors!
- return if mirrors.blank?
- image_names = []
-
- mirrors.each do |data|
- mirror = JSON.parse(data)
-
- name_repository = MirrorRepository.find_by(name: mirror['imageName'])
- id_repository = MirrorRepository.find_by(mirrorID: mirror['imageID'])
-
- image_names << mirror['imageName']
-
- if name_repository.blank? && id_repository.present? # 镜像名称被修改
- id_repository.update_column(:status, 2)
- MirrorOperationRecord.create!(mirror_repository_id: id_repository.id, mirror_id: mirror['imageID'],
- mirror_name: mirror['imageName'], status: 2, user_id: -1)
- elsif name_repository.blank? # 镜像不存在、创建镜像
- new_repository = MirrorRepository.create!(mirrorID: mirror['imageID'], name: mirror['imageName'])
- MirrorOperationRecord.create!(mirror_repository_id: new_repository.id, mirror_id: mirror['imageID'],
- mirror_name: mirror['imageName'], status: 0, user_id: -1)
- elsif name_repository.mirrorID != mirror['imageID'] # 镜像ID被修改
- name_repository.update_column(:status, 2)
- MirrorOperationRecord.create!(mirror_repository_id: name_repository.id, mirror_id: mirror['imageID'],
- mirror_name: mirror['imageName'], status: 1, user_id: -1)
- end
- end
-
- # 判断中间层镜像是否被删除
- MirrorRepository.find_each do |mirror|
- next if mirror&.name.blank? || image_names.index(mirror.name)
-
- mirror.update_column(:status, 4)
- MirrorOperationRecord.create!(mirror_repository_id: mirror.id, mirror_id: mirror&.mirrorID,
- mirror_name: mirror.name, status: 3, user_id: -1)
- end
- end
-
- def check_sync_mirrors!
- return if sync_mirrors.blank?
-
- sync_mirrors.each do |data|
- mirror = JSON.parse(data)
-
- repository = MirrorRepository.find_by(name: mirror['imageName'])
- next if repository.blank? || repository.status != 1
-
- repository.update_column(:status, 5)
- MirrorOperationRecord.create!(mirror_repository_id: repository.id, mirror_id: mirror['imageID'],
- mirror_name: mirror['imageName'], status: 4, user_id: -1)
- end
- end
-
- def bridge_images
- @_bridge_images ||= begin
- url = "#{EduSetting.get('cloud_bridge')}/bridge/docker/images"
- res = Faraday.get(url)
- res = JSON.parse(res.body)
- raise Error, '拉取镜像信息异常' if res && res['code'] != 0
-
- res
- rescue => e
- Rails.logger.error("get response failed ! #{e.message}")
- raise Error, '实训云平台繁忙(繁忙等级:84)'
- end
- end
-end
\ No newline at end of file
diff --git a/app/services/admins/delete_unit_apply_service.rb b/app/services/admins/delete_unit_apply_service.rb
index a64c8d64d..af2e9668d 100644
--- a/app/services/admins/delete_unit_apply_service.rb
+++ b/app/services/admins/delete_unit_apply_service.rb
@@ -18,7 +18,7 @@ class Admins::DeleteUnitApplyService < ApplicationService
use_extensions = UserExtension&.where(school_id: @unit_apply.school_id)
user_ids = UserExtension&.where(school_id: @unit_apply.school_id)&.pluck(:user_id)
- User.where(id: user_ids).update_all(profile_completed: false)
+ User.where(id: user_ids)
use_extensions.update_all(school_id: nil,department_id: nil)
@unit_apply&.user&.user_extension&.update_attribute("department_id", nil)
diff --git a/app/services/admins/drag_cooperative_service.rb b/app/services/admins/drag_cooperative_service.rb
deleted file mode 100644
index 241b7eb11..000000000
--- a/app/services/admins/drag_cooperative_service.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-class Admins::DragCooperativeService < ApplicationService
- Error = Class.new(StandardError)
-
- attr_reader :move, :after
-
- def initialize(move, after)
- @move = move
- @after = after # 移动后下一个位置的元素
- end
-
- def call
- return if move.position + 1 == after&.position # 未移动
- raise Error, '未知错误' if after && move.img_type != after.img_type
-
- coo_imgs = CooImg.where(img_type: move.img_type)
-
- ActiveRecord::Base.transaction do
- if after.blank? # 移动至末尾
- total = coo_imgs.count
-
- coo_imgs.where('position > ?', move.position).update_all('position = position - 1')
- move.update!(position: total)
- return
- end
-
- if move.position > after.position # 前移
- coo_imgs.where('position >= ? AND position < ?', after.position, move.position).update_all('position = position + 1')
- move.update!(position: after.position)
- else # 后移
- coo_imgs.where('position > ? AND position <= ?', move.position, after.position).update_all('position = position - 1')
- move.update!(position: after.position)
- end
- end
- end
-end
\ No newline at end of file
diff --git a/app/services/admins/drag_portal_image_service.rb b/app/services/admins/drag_portal_image_service.rb
deleted file mode 100644
index 5555c08b2..000000000
--- a/app/services/admins/drag_portal_image_service.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-class Admins::DragPortalImageService < ApplicationService
- Error = Class.new(StandardError)
-
- attr_reader :laboratory, :move, :after
-
- def initialize(laboratory, move, after)
- @laboratory = laboratory
- @move = move
- @after = after # 移动后下一个位置的元素
- end
-
- def call
- return if move.position + 1 == after&.position # 未移动
-
- images = laboratory.portal_images
-
- ActiveRecord::Base.transaction do
- if after.blank? || move.id == after.id # 移动至末尾
- total = images.count
-
- images.where('position > ?', move.position).update_all('position = position - 1')
- move.update!(position: total)
- return
- end
-
- if move.position > after.position # 前移
- images.where('position >= ? AND position < ?', after.position, move.position).update_all('position = position + 1')
- move.update!(position: after.position)
- else # 后移
- images.where('position > ? AND position < ?', move.position, after.position).update_all('position = position - 1')
- move.update!(position: after.position - 1)
- end
- end
- end
-end
\ No newline at end of file
diff --git a/app/services/admins/drag_weapp_advert_service.rb b/app/services/admins/drag_weapp_advert_service.rb
deleted file mode 100644
index 8bfb7c317..000000000
--- a/app/services/admins/drag_weapp_advert_service.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-class Admins::DragWeappAdvertService < ApplicationService
- attr_reader :move, :after
-
- def initialize(move, after)
- @move = move
- @after = after # 移动后下一个位置的元素
- end
-
- def call
- return if move.position + 1 == after&.position # 未移动
-
- adverts = WeappSettings::Advert.all
-
- ActiveRecord::Base.transaction do
- if after.blank? || move.id == after.id # 移动至末尾
- total = adverts.count
-
- adverts.where('position > ?', move.position).update_all('position = position - 1')
- move.update!(position: total)
- return
- end
-
- if move.position > after.position # 前移
- adverts.where('position >= ? AND position < ?', after.position, move.position).update_all('position = position + 1')
- move.update!(position: after.position)
- else # 后移
- adverts.where('position > ? AND position < ?', move.position, after.position).update_all('position = position - 1')
- move.update!(position: after.position - 1)
- end
- end
- end
-end
\ No newline at end of file
diff --git a/app/services/admins/drag_weapp_carousel_service.rb b/app/services/admins/drag_weapp_carousel_service.rb
deleted file mode 100644
index f0b3832b2..000000000
--- a/app/services/admins/drag_weapp_carousel_service.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-class Admins::DragWeappCarouselService < ApplicationService
- attr_reader :move, :after
-
- def initialize(move, after)
- @move = move
- @after = after # 移动后下一个位置的元素
- end
-
- def call
- return if move.position + 1 == after&.position # 未移动
-
- carousels = WeappSettings::Carousel.all
-
- ActiveRecord::Base.transaction do
- if after.blank? || move.id == after.id # 移动至末尾
- total = carousels.count
-
- carousels.where('position > ?', move.position).update_all('position = position - 1')
- move.update!(position: total)
- return
- end
-
- if move.position > after.position # 前移
- carousels.where('position >= ? AND position < ?', after.position, move.position).update_all('position = position + 1')
- move.update!(position: after.position)
- else # 后移
- carousels.where('position > ? AND position < ?', move.position, after.position).update_all('position = position - 1')
- move.update!(position: after.position - 1)
- end
- end
- end
-end
\ No newline at end of file
diff --git a/app/services/admins/import_course_member_service.rb b/app/services/admins/import_course_member_service.rb
deleted file mode 100644
index 3ea559766..000000000
--- a/app/services/admins/import_course_member_service.rb
+++ /dev/null
@@ -1,63 +0,0 @@
-class Admins::ImportCourseMemberService < ApplicationService
- Error = Class.new(StandardError)
-
- attr_reader :file, :result
-
- def initialize(file)
- @file = file
- @result = { success: 0, fail: [] }
- end
-
- def call
- raise Error, '文件不存在' if file.blank?
-
- excel = Admins::ImportCourseMemberExcel.new(file)
- excel.read_each(&method(:create_course_member))
-
- result
- rescue ApplicationImport::Error => ex
- raise Error, ex.message
- end
-
- private
-
- def create_course_member(data)
- raise '课堂角色必须为 2、3、4' unless [2, 3, 4].include?(data.role.to_i)
-
- user = User.joins(:user_extension).where(user_extensions: { student_id: data.student_id, school_id: data.school_id }).first
- raise '该学号的用户不存在' if user.blank?
- course = Course.find_by(id: data.course_id)
- raise '该课堂不存在' if course.blank?
-
- course_group = nil
- if data.course_group_name.present?
- course_group = course.course_groups.find_or_create_by!(name: data.course_group_name)
- end
-
- member = course.course_members.find_by(user_id: user.id, role: data.role.to_i)
- # 如果已是课堂成员且是学生身份and不在指定的分班则移动到该分班
- if member.present? && member.role == 'STUDENT' && course_group && member.course_group_id != course_group&.id.to_i
- member.update!(course_group_id: course_group&.id.to_i)
- elsif member.blank?
- course.course_members.create!(user_id: user.id, role: data.role.to_i, course_group_id: course_group&.id.to_i)
- extra =
- case data.role.to_i
- when 2 then 9
- when 3 then 7
- else 10
- end
-
- Tiding.create!(user_id: user.id, trigger_user_id: course.tea_id, container_id: course.id,
- container_type: 'TeacherJoinCourse', belong_container_id: course.id,
- belong_container_type: 'Course', tiding_type: 'System', extra: extra)
- end
-
- result[:success] += 1
- rescue Exception => ex
- fail_data = data.as_json
- fail_data[:data] = fail_data.values.join(',')
- fail_data[:message] = ex.message
-
- result[:fail] << fail_data
- end
-end
\ No newline at end of file
diff --git a/app/services/admins/import_user_service.rb b/app/services/admins/import_user_service.rb
index f3408051b..2fac412e1 100644
--- a/app/services/admins/import_user_service.rb
+++ b/app/services/admins/import_user_service.rb
@@ -57,7 +57,7 @@ class Admins::ImportUserService < ApplicationService
password: '12345678',
phone: data.phone,
mail: "#{prefix}#{data.student_id}@qq.com",
- profile_completed: true
+ # profile_completed: true
}
ActiveRecord::Base.transaction do
user = User.create!(attr)
@@ -84,7 +84,7 @@ class Admins::ImportUserService < ApplicationService
if data.identity.to_i == 1
users = users.where(user_extensions: { student_id: data.student_id })
else
- users = users.where(user_extensions: { technical_title: data.technical_title }).where('CONCAT(users.lastname,users.firstname) = ?', data.name)
+ users = users.where(user_extensions: { technical_title: data.technical_title }).where('CONCAT(users.lastname,users.firstname) = ? OR users.nickname = ?', data.name, data.name)
end
users.first
diff --git a/app/services/admins/school_daily_statistic_service.rb b/app/services/admins/school_daily_statistic_service.rb
deleted file mode 100644
index 64bb97864..000000000
--- a/app/services/admins/school_daily_statistic_service.rb
+++ /dev/null
@@ -1,123 +0,0 @@
-class Admins::SchoolDailyStatisticService < ApplicationService
- include CustomSortable
-
- attr_reader :params
-
- sort_columns :student_count, :teacher_count, :homework_count, :other_homework_count,
- :course_count, :active_course_count, :nearly_course_time, :shixun_count, :shixun_evaluate_count,
- default_by: :teacher_count, default_direction: :desc
-
- def initialize(params)
- @params = params
- end
-
- def call
- schools = School.group('schools.id')
-
- keyword = params[:keyword].try(:to_s).try(:strip)
- if keyword.present?
- schools = schools.where("schools.name LIKE :keyword OR schools.id LIKE :keyword", keyword: "%#{keyword}%")
- end
-
- count = schools.count.count
-
- # 根据排序字段进行查询
- schools = query_by_sort_column(schools, params[:sort_by])
- schools = custom_sort(schools, params[:sort_by], params[:sort_direction])
-
- schools = schools.limit(page_size).offset(offset)
- # 查询并组装其它数据
- schools = package_other_data(schools)
-
- [count, schools]
- end
-
- def package_other_data(schools)
- ids = schools.map(&:id)
-
- student_map = UserExtension.where(school_id: ids, identity: :student).group(:school_id).count
- teacher_map = UserExtension.where(school_id: ids, identity: :teacher).group(:school_id).count
-
- homeworks = HomeworkCommon.joins(:course)
- shixun_homework_map = homeworks.where(homework_type: 4, courses: { school_id: ids }).group('school_id').count
- other_homework_map = homeworks.where(homework_type: [1, 3], courses: { school_id: ids }).group('school_id').count
-
- courses = Course.where(is_delete: 0, school_id: ids).group('school_id')
- course_map = courses.count
- nearly_course_time_map = courses.joins(:course_acts).maximum('course_activities.updated_at')
- active_course_map = courses.where(is_end: false).count
-
- shixun_map = Shixun.joins(user: :user_extension).where(user_extensions: { identity: :teacher, school_id: ids })
- .where(fork_from: nil).group('school_id').count
-
- reports = SchoolReport.where(school_id: ids)
- evaluate_count_map = reports.each_with_object({}) { |report, obj| obj[report.school_id] = report.shixun_evaluate_count }
-
- schools.map do |school|
- {
- id: school.id,
- name: school.name,
- teacher_count: teacher_map[school.id],
- student_count: student_map[school.id],
- homework_count: shixun_homework_map[school.id],
- other_homework_count: other_homework_map[school.id],
- course_count: course_map[school.id],
- nearly_course_time: nearly_course_time_map[school.id],
- active_course_count: active_course_map[school.id],
- shixun_count: shixun_map.fetch(school.id, 0),
- shixun_evaluate_count: evaluate_count_map.fetch(school.id, 0)
- }
- end
- end
-
- private
- def query_by_sort_column(schools, sort_by_column)
- base_query_column = 'schools.id, schools.name'
-
- case sort_by_column.to_s
- when 'teacher_count' then
- schools.joins('LEFT JOIN user_extensions ue ON ue.school_id = schools.id AND ue.identity = 0')
- .select("#{base_query_column}, COUNT(*) teacher_count")
- when 'student_count' then
- schools.joins('LEFT JOIN user_extensions ue ON ue.school_id = schools.id AND ue.identity = 1')
- .select("#{base_query_column}, COUNT(*) student_count")
- when 'homework_count' then
- schools.joins('LEFT JOIN courses ON courses.school_id = schools.id')
- .joins('LEFT JOIN homework_commons hc ON hc.course_id = courses.id AND hc.homework_type = 4')
- .select("#{base_query_column}, COUNT(*) homework_count")
- when 'other_homework_count' then
- schools.joins('LEFT JOIN courses ON courses.school_id = schools.id')
- .joins('LEFT JOIN homework_commons hc ON hc.course_id = courses.id AND hc.homework_type IN (1, 3)')
- .select("#{base_query_column}, COUNT(*) other_homework_count")
- when 'course_count' then
- schools.joins('LEFT JOIN courses cs ON cs.school_id = schools.id AND cs.is_delete = 0')
- .select("#{base_query_column}, COUNT(*) course_count")
- when 'shixun_count' then
- schools.joins('LEFT JOIN user_extensions ue ON ue.school_id = schools.id AND ue.identity = 0')
- .joins('LEFT JOIN users ON users.id = ue.user_id')
- .joins('LEFT JOIN shixuns sx ON sx.user_id = users.id AND sx.fork_from IS NULL')
- .select("#{base_query_column}, COUNT(*) shixun_count")
- when 'shixun_evaluate_count' then
- schools.joins('LEFT JOIN school_reports ON school_reports.school_id = schools.id')
- .select("#{base_query_column}, shixun_evaluate_count")
- when 'nearly_course_time' then
- schools.joins('LEFT JOIN courses cs ON cs.school_id = schools.id AND cs.is_delete = 0')
- .joins('LEFT JOIN course_activities acs ON acs.course_id = cs.id')
- .select("#{base_query_column}, MAX(acs.updated_at) nearly_course_time")
- when 'active_course_count' then
- schools.joins('LEFT JOIN courses cs ON cs.school_id = schools.id AND cs.is_delete = 0 AND cs.is_end = false')
- .select("#{base_query_column}, COUNT(*) active_course_count")
- else
- schools.joins('LEFT JOIN user_extensions ue ON ue.school_id = schools.id AND ue.identity = 0')
- .select("#{base_query_column}, COUNT(*) teacher_count")
- end
- end
-
- def page_size
- params[:per_page] || 20
- end
-
- def offset
- (params[:page].to_i.zero? ? 0 : params[:page].to_i - 1) * page_size
- end
-end
\ No newline at end of file
diff --git a/app/services/admins/shixun_auths/agree_apply_service.rb b/app/services/admins/shixun_auths/agree_apply_service.rb
deleted file mode 100644
index b8875cf09..000000000
--- a/app/services/admins/shixun_auths/agree_apply_service.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-class Admins::ShixunAuths::AgreeApplyService < ApplicationService
- attr_reader :apply, :user, :shixun
-
- def initialize(apply, user)
- @apply = apply
- @user = user
- @shixun = Shixun.find(apply.container_id)
- end
-
- def call
- ActiveRecord::Base.transaction do
- apply.update!(status: 1, dealer_id: user.id)
- shixun.update!(public: 2, publish_time: Time.now)
-
- # 奖励金币、经验
- reward_grade_and_experience!
-
- deal_tiding!
- end
- end
-
- private
-
- def reward_grade_and_experience!
- score = shixun.all_score
- shixun_creator = shixun.user
-
- RewardGradeService.call(shixun_creator, container_id: shixun.id, container_type: 'shixunPublish', score: score)
-
- Experience.create!(user_id: shixun_creator.id, container_id: shixun.id, container_type: 'shixunPublish', score: score)
- shixun_creator.update_column(:experience, shixun_creator.experience.to_i + score)
- end
-
- def deal_tiding!
- apply.tidings.where(tiding_type: 'Apply', status: 0).update_all(status: 1)
-
- Tiding.create!(user_id: apply.user_id, trigger_user_id: 0,
- container_id: apply.id, container_type: 'ApplyAction',
- parent_container_id: apply.container_id, parent_container_type: apply.container_type,
- belong_container_id: apply.container_id, belong_container_type: 'Shixun',
- status: 1, tiding_type: 'System')
- end
-end
\ No newline at end of file
diff --git a/app/services/admins/shixun_auths/refuse_apply_service.rb b/app/services/admins/shixun_auths/refuse_apply_service.rb
deleted file mode 100644
index 76d420e53..000000000
--- a/app/services/admins/shixun_auths/refuse_apply_service.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-class Admins::ShixunAuths::RefuseApplyService < ApplicationService
- attr_reader :apply, :user, :shixun, :params
-
- def initialize(apply, user, params)
- @apply = apply
- @user = user
- @shixun = Shixun.find(apply.container_id)
- @params = params
- end
-
- def call
- ActiveRecord::Base.transaction do
- shixun.update!(public: 0)
- apply.update!(status: 2, reason: reason, dealer_id: user.id)
-
- deal_tiding!
- end
- end
-
- private
-
- def reason
- params[:reason].to_s.strip
- end
-
- def deal_tiding!
- apply.tidings.where(tiding_type: 'Apply', status: 0).update_all(status: 1)
-
- Tiding.create!(user_id: apply.user_id, trigger_user_id: 0,
- container_id: apply.id, container_type: 'ApplyAction',
- parent_container_id: apply.container_id, parent_container_type: apply.container_type,
- belong_container_id: apply.container_id, belong_container_type: 'Shixun',
- status: 2, tiding_type: 'System')
- end
-end
\ No newline at end of file
diff --git a/app/services/admins/statistic_school_contrast_data_service.rb b/app/services/admins/statistic_school_contrast_data_service.rb
deleted file mode 100644
index 0496b1371..000000000
--- a/app/services/admins/statistic_school_contrast_data_service.rb
+++ /dev/null
@@ -1,80 +0,0 @@
-class Admins::StatisticSchoolContrastDataService < ApplicationService
- ParameterError = Class.new(StandardError)
-
- PAGE_SIZE = 20
- CONTRAST_COLUMN_LIST = %w(
- teacher_increase_count student_increase_count course_increase_count
- shixun_increase_count active_user_count shixun_homework_count shixun_evaluate_count
- ).freeze
-
- attr_reader :params, :sort_direction, :contrast_column
-
- def initialize(params)
- @params = params
- @sort_direction = params[:sort_direction].to_s
- @contrast_column = params[:contrast_column].to_s
- end
-
- def call
- validate_parameter!
- reports = School.joins(:school_daily_reports).select(select_columns)
-
- keyword = params[:keyword].try(:to_s).try(:strip)
- if keyword.present?
- reports = reports.where("schools.name LIKE :keyword OR schools.id LIKE :keyword", keyword: "%#{keyword}%")
- end
-
- count = reports.count('distinct(schools.id)')
-
- sql = query_report_sql(reports.group('schools.id').to_sql)
- reports = SchoolDailyReport.find_by_sql(sql)
-
- [count, reports]
- end
-
- private
- def validate_parameter!
- if %i[begin_date end_date other_begin_date other_end_date].any? { |key| params[key].blank? }
- raise ParameterError
- end
-
- unless %w(desc asc).include?(sort_direction)
- raise ParameterError
- end
-
- unless CONTRAST_COLUMN_LIST.include?(contrast_column)
- raise ParameterError
- end
- end
-
- def format_date(date)
- Time.zone.parse(date).strftime("%Y-%m-%d")
- end
-
- def offset
- (params[:page].to_i.zero? ? 0 : params[:page].to_i - 1) * PAGE_SIZE
- end
-
- def select_columns
- if contrast_column != 'active_user_count'
- "schools.id school_id, schools.name school_name,"\
- "(SUM(IF(date BETWEEN '#{format_date(params[:begin_date])}' AND '#{format_date(params[:end_date])}', #{contrast_column}, 0))) total,"\
- "(SUM(IF(date BETWEEN '#{format_date(params[:other_begin_date])}' AND '#{format_date(params[:other_end_date])}', #{contrast_column}, 0))) other_total"
- else
- # 活跃用户对比时处理方法不同
- relations = SchoolDailyActiveUser.select('COUNT(distinct user_id)').joins(:school_daily_report)
- .where('school_id = schools.id')
- total_subquery = relations.where("date BETWEEN '#{format_date(params[:begin_date])}' AND '#{format_date(params[:end_date])}'").to_sql
- other_total_subquery = relations.where("date BETWEEN '#{format_date(params[:other_begin_date])}' AND '#{format_date(params[:other_end_date])}'").to_sql
-
- "schools.id school_id, schools.name school_name, (#{total_subquery}) AS total, (#{other_total_subquery}) AS other_total"
- end
- end
-
- def query_report_sql(from_sql)
- order_by = "(total = 0 AND other_total != 0) #{sort_direction}, (percentage != 0) #{sort_direction}, percentage #{sort_direction}"
-
- "SELECT reports.*, (other_total - total) increase, (IF(other_total - total = 0, 0.0, round((other_total - total) / IF(total = 0, 1, total), 5))) percentage "\
- "FROM (#{from_sql}) reports ORDER BY #{order_by} LIMIT #{PAGE_SIZE} OFFSET #{offset}"
- end
-end
\ No newline at end of file
diff --git a/app/services/admins/statistic_school_data_grow_service.rb b/app/services/admins/statistic_school_data_grow_service.rb
deleted file mode 100644
index 8df106666..000000000
--- a/app/services/admins/statistic_school_data_grow_service.rb
+++ /dev/null
@@ -1,107 +0,0 @@
-class Admins::StatisticSchoolDataGrowService < ApplicationService
- include CustomSortable
-
- PAGE_SIZE = 20
-
- attr_reader :params
-
- sort_columns :teacher_increase_count, :student_increase_count,
- :course_increase_count, :shixun_increase_count, :uniq_active_user_count,
- :shixun_homework_count, :shixun_evaluate_count,
- default_by: :teacher_increase_count, default_direction: :desc
-
- def initialize(params)
- @params = params
- end
-
- def call
- reports = School.where(nil)
-
- reports = search_filter(reports)
-
- count = reports.count
-
- subquery = SchoolDailyActiveUser.select('COUNT(distinct(user_id))').joins(:school_daily_report)
- .where(date_condition_sql).where("school_id is not null and school_id = schools.id").to_sql
- reports = reports.joins("LEFT JOIN school_daily_reports sdr ON sdr.school_id = schools.id AND #{date_condition_sql}")
- reports = reports.select(
- 'schools.id school_id, schools.name school_name,'\
- 'SUM(teacher_increase_count) teacher_increase_count,'\
- 'SUM(student_increase_count) student_increase_count,'\
- 'SUM(course_increase_count) course_increase_count,'\
- 'SUM(shixun_increase_count) shixun_increase_count,'\
- 'SUM(shixun_homework_count) shixun_homework_count,'\
- 'SUM(shixun_evaluate_count) shixun_evaluate_count,'\
- "(#{subquery}) uniq_active_user_count,"\
- 'SUM(active_user_count) active_user_count').group('schools.id')
-
- reports = custom_sort(reports, params[:sort_by], params[:sort_direction])
- reports = reports.order('school_id asc').limit(PAGE_SIZE).offset(offset)
-
- [count, reports]
- end
-
- def grow_summary
- @_grow_summary ||= begin
- reports = School.joins("LEFT JOIN school_daily_reports sdr ON sdr.school_id = schools.id")
- .where(date_condition_sql)
-
- subquery = SchoolDailyActiveUser.select('COUNT(distinct user_id)')
- .joins('LEFT JOIN school_daily_reports sdr ON sdr.id = school_daily_active_users.school_daily_report_id')
- .where(date_condition_sql).to_sql
- reports = search_filter(reports)
- reports.select(
- 'SUM(teacher_increase_count) teacher_increase_count,'\
- 'SUM(student_increase_count) student_increase_count,'\
- 'SUM(course_increase_count) course_increase_count,'\
- 'SUM(shixun_increase_count) shixun_increase_count,'\
- 'SUM(shixun_homework_count) shixun_homework_count,'\
- 'SUM(shixun_evaluate_count) shixun_evaluate_count,'\
- "(#{subquery}) uniq_active_user_count,"\
- 'SUM(active_user_count) active_user_count'
- ).first
- end
- end
-
- private
-
- def search_filter(relations)
- keyword = params[:keyword].try(:to_s).try(:strip)
- if keyword.present?
- relations = relations.where("schools.name LIKE :keyword OR schools.id LIKE :keyword", keyword: "%#{keyword}%")
- end
-
- relations
- end
-
- def date_condition_sql
- date = query_date
- if date.is_a?(Range)
- "date BETWEEN '#{date.min.strftime('%Y-%m-%d')}' AND '#{date.max.strftime('%Y-%m-%d')}'"
- else
- "date = '#{date.strftime('%Y-%m-%d')}'"
- end
- end
-
- def query_date
- if params[:grow_begin_date].present?
- begin_time = Time.zone.parse(params[:grow_begin_date])
- end_date = if params[:grow_end_date].present?
- Time.zone.parse(params[:grow_end_date])
- end
-
- end_date.blank? || end_date == begin_time ? begin_time : begin_time..end_date
- else
- yesterday
- end
- end
-
- def yesterday
- # 每日凌晨5点为节点, 25日凌晨4点、3点、2点等等,未到更新数据时间点,看到的数据是:23日-24日的统计数据
- (Time.zone.now - 5.hours).beginning_of_day - 1.days
- end
-
- def offset
- (params[:page].to_i.zero? ? 0 : params[:page].to_i - 1) * PAGE_SIZE
- end
-end
\ No newline at end of file
diff --git a/app/services/admins/subject_auths/agree_apply_service.rb b/app/services/admins/subject_auths/agree_apply_service.rb
deleted file mode 100644
index ec5fec4bb..000000000
--- a/app/services/admins/subject_auths/agree_apply_service.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-class Admins::SubjectAuths::AgreeApplyService < ApplicationService
- attr_reader :apply, :user, :subject
-
- def initialize(apply, user)
- @apply = apply
- @user = user
- @subject = Subject.find(apply.container_id)
- end
-
- def call
- ActiveRecord::Base.transaction do
- apply.update!(status: 1, dealer_id: user.id)
- subject.update!(public: 2, publish_time: Time.now)
-
- deal_tiding!
- end
- end
-
- private
-
- def deal_tiding!
- apply.tidings.where(tiding_type: 'Apply', status: 0).update_all(status: 1)
-
- Tiding.create!(user_id: apply.user_id, trigger_user_id: 0,
- container_id: apply.id, container_type: 'ApplyAction',
- parent_container_id: apply.container_id, parent_container_type: apply.container_type,
- belong_container_id: apply.container_id, belong_container_type: 'Subject',
- status: 1, tiding_type: 'System')
- end
-end
\ No newline at end of file
diff --git a/app/services/admins/subject_auths/refuse_apply_service.rb b/app/services/admins/subject_auths/refuse_apply_service.rb
deleted file mode 100644
index 45f2d44d3..000000000
--- a/app/services/admins/subject_auths/refuse_apply_service.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-class Admins::SubjectAuths::RefuseApplyService < ApplicationService
- attr_reader :apply, :user, :subject, :params
-
- def initialize(apply, user, params)
- @apply = apply
- @user = user
- @subject = Subject.find(apply.container_id)
- @params = params
- end
-
- def call
- ActiveRecord::Base.transaction do
- subject.update!(public: 0)
- apply.update!(status: 2, reason: reason, dealer_id: user.id)
-
- deal_tiding!
- end
- end
-
- private
-
- def reason
- params[:reason].to_s.strip
- end
-
- def deal_tiding!
- apply.tidings.where(tiding_type: 'Apply', status: 0).update_all(status: 1)
-
- Tiding.create!(user_id: apply.user_id, trigger_user_id: 0,
- container_id: apply.id, container_type: 'ApplyAction',
- parent_container_id: apply.container_id, parent_container_type: apply.container_type,
- belong_container_id: apply.container_id, belong_container_type: 'Subject',
- status: 2, tiding_type: 'System')
- end
-end
\ No newline at end of file
diff --git a/app/services/atme_service.rb b/app/services/atme_service.rb
new file mode 100644
index 000000000..f93314e47
--- /dev/null
+++ b/app/services/atme_service.rb
@@ -0,0 +1,37 @@
+class AtmeService < ApplicationService
+ Error = Class.new(StandardError)
+
+ attr_reader :user, :receivers, :atmeable
+
+ def initialize(user, receivers, atmeable)
+ @user = user
+ @receivers = receivers
+ @atmeable = atmeable
+ end
+
+ def call
+ Rails.logger.info "[ATME] service args: [user]=>#{user}, [receivers]=>#{receivers}, [atmeable]=>#{atmeable}"
+ return if atmeable.nil?
+ Rails.logger.info "[ATME] atmeable class name is: #{ atmeable.class.name}"
+ case atmeable.class.name
+ when 'Issue'
+ message_source = 'IssueAtme'
+ when 'PullRequest'
+ message_source = 'PullRequestAtme'
+ when 'Journal'
+ journal = Journal.find_by_id(atmeable.id)
+ if journal.present?
+ if journal&.issue&.pull_request.present?
+ @atmeable = journal&.issue&.pull_request
+ message_source = 'PullRequestAtme'
+ else
+ @atmeable = journal&.issue
+ message_source = 'IssueAtme'
+ end
+ end
+ else
+ return
+ end
+ SendTemplateMessageJob.perform_later(message_source, receivers.pluck(:id), user.id, @atmeable.id) if Site.has_notice_menu?
+ end
+end
diff --git a/app/services/cache/platform_follow_count_service.rb b/app/services/cache/platform_follow_count_service.rb
new file mode 100644
index 000000000..5398da5a8
--- /dev/null
+++ b/app/services/cache/platform_follow_count_service.rb
@@ -0,0 +1,41 @@
+class Cache::PlatformFollowCountService < ApplicationService
+ attr_reader :increment_count
+
+ def initialize(increment_count=0)
+ @increment_count = increment_count
+ end
+
+ def call
+ set_platform_follow_count
+
+ platform_follow_count
+ end
+
+ def reset
+ reset_platform_follow_count
+
+ platform_follow_count
+ end
+
+ private
+
+ def platform_follow_count_key
+ "platform-follow-count"
+ end
+
+ def platform_follow_count
+ $redis_cache.get(platform_follow_count_key).to_i
+ end
+
+ def set_platform_follow_count
+ if $redis_cache.exists(platform_follow_count_key)
+ $redis_cache.incrby(platform_follow_count_key, increment_count)
+ else
+ reset_platform_follow_count
+ end
+ end
+
+ def reset_platform_follow_count
+ $redis_cache.set(platform_follow_count_key, Watcher.where(watchable_type: 'User').count)
+ end
+end
\ No newline at end of file
diff --git a/app/services/cache/platform_issue_count_service.rb b/app/services/cache/platform_issue_count_service.rb
new file mode 100644
index 000000000..95511f480
--- /dev/null
+++ b/app/services/cache/platform_issue_count_service.rb
@@ -0,0 +1,41 @@
+class Cache::PlatformIssueCountService < ApplicationService
+ attr_reader :increment_count
+
+ def initialize(increment_count=0)
+ @increment_count = increment_count
+ end
+
+ def call
+ set_platform_issue_count
+
+ platform_issue_count
+ end
+
+ def reset
+ reset_platform_issue_count
+
+ platform_issue_count
+ end
+
+ private
+
+ def platform_issue_count_key
+ "platform-issue-count"
+ end
+
+ def platform_issue_count
+ $redis_cache.get(platform_issue_count_key).to_i
+ end
+
+ def set_platform_issue_count
+ if $redis_cache.exists(platform_issue_count_key)
+ $redis_cache.incrby(platform_issue_count_key, increment_count)
+ else
+ reset_platform_issue_count
+ end
+ end
+
+ def reset_platform_issue_count
+ $redis_cache.set(platform_issue_count_key, Issue.count)
+ end
+end
\ No newline at end of file
diff --git a/app/services/cache/platform_project_count_service.rb b/app/services/cache/platform_project_count_service.rb
new file mode 100644
index 000000000..d807682f1
--- /dev/null
+++ b/app/services/cache/platform_project_count_service.rb
@@ -0,0 +1,41 @@
+class Cache::PlatformProjectCountService < ApplicationService
+ attr_reader :increment_count
+
+ def initialize(increment_count=0)
+ @increment_count = increment_count
+ end
+
+ def call
+ set_platform_project_count
+
+ platform_project_count
+ end
+
+ def reset
+ reset_platform_project_count
+
+ platform_project_count
+ end
+
+ private
+
+ def platform_project_count_key
+ "platform-project-count"
+ end
+
+ def platform_project_count
+ $redis_cache.get(platform_project_count_key).to_i
+ end
+
+ def set_platform_project_count
+ if $redis_cache.exists(platform_project_count_key)
+ $redis_cache.incrby(platform_project_count_key, increment_count)
+ else
+ reset_platform_project_count
+ end
+ end
+
+ def reset_platform_project_count
+ $redis_cache.set(platform_project_count_key, Project.count)
+ end
+end
\ No newline at end of file
diff --git a/app/services/cache/platform_project_fork_count_service.rb b/app/services/cache/platform_project_fork_count_service.rb
new file mode 100644
index 000000000..21d6c0636
--- /dev/null
+++ b/app/services/cache/platform_project_fork_count_service.rb
@@ -0,0 +1,41 @@
+class Cache::PlatformProjectForkCountService < ApplicationService
+ attr_reader :increment_count
+
+ def initialize(increment_count=0)
+ @increment_count = increment_count
+ end
+
+ def call
+ set_platform_project_fork_count
+
+ platform_project_fork_count
+ end
+
+ def reset
+ reset_platform_project_fork_count
+
+ platform_project_fork_count
+ end
+
+ private
+
+ def platform_project_fork_count_key
+ "platform-project-fork-count"
+ end
+
+ def platform_project_fork_count
+ $redis_cache.get(platform_project_fork_count_key).to_i
+ end
+
+ def set_platform_project_fork_count
+ if $redis_cache.exists(platform_project_fork_count_key)
+ $redis_cache.incrby(platform_project_fork_count_key, increment_count)
+ else
+ reset_platform_project_fork_count
+ end
+ end
+
+ def reset_platform_project_fork_count
+ $redis_cache.set(platform_project_fork_count_key, ForkUser.count)
+ end
+end
\ No newline at end of file
diff --git a/app/services/cache/platform_project_languages_count_service.rb b/app/services/cache/platform_project_languages_count_service.rb
new file mode 100644
index 000000000..0c6ffab19
--- /dev/null
+++ b/app/services/cache/platform_project_languages_count_service.rb
@@ -0,0 +1,57 @@
+class Cache::PlatformProjectLanguagesCountService < ApplicationService
+ attr_reader :key, :increment_count
+
+ def initialize(key=nil, increment_count=0)
+ @key = key
+ @increment_count = increment_count
+ end
+
+ def call
+ set_platform_project_language_count
+
+ platform_project_language_count
+ end
+
+ def reset_by_key
+ reset_platform_project_language_count_by_key
+
+ platform_project_language_count
+ end
+
+ def reset
+ reset_platform_project_language_count
+
+ platform_project_language_count
+ end
+
+ private
+
+ def platform_project_language_count_key
+ "platform-project-language-count"
+ end
+
+ def platform_project_language_count
+ $redis_cache.hgetall(platform_project_language_count_key).transform_values(&:to_i)
+ end
+
+ def set_platform_project_language_count
+ if $redis_cache.hlen(platform_project_language_count_key) == 0
+ reset_platform_project_language_count
+ elsif $redis_cache.hget(platform_project_language_count_key, key).nil?
+ reset_platform_project_language_count_by_key
+ else
+ $redis_cache.hincrby(platform_project_language_count_key, key, increment_count)
+ end
+ end
+
+ def reset_platform_project_language_count_by_key
+ return if key.nil?
+ $redis_cache.hset(platform_project_language_count_key, key, ProjectLanguage.where(name: key).projects_count)
+ end
+
+ def reset_platform_project_language_count
+ ProjectLanguage.where.not(projects_count: 0).group("project_languages.name").sum(:projects_count).each do |k, v|
+ $redis_cache.hset(platform_project_language_count_key, k, v)
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/services/cache/platform_project_praises_count_service.rb b/app/services/cache/platform_project_praises_count_service.rb
new file mode 100644
index 000000000..6c78b8022
--- /dev/null
+++ b/app/services/cache/platform_project_praises_count_service.rb
@@ -0,0 +1,41 @@
+class Cache::PlatformProjectPraisesCountService < ApplicationService
+ attr_reader :increment_count
+
+ def initialize(increment_count=0)
+ @increment_count = increment_count
+ end
+
+ def call
+ set_platform_project_praises_count
+
+ platform_project_praises_count
+ end
+
+ def reset
+ reset_platform_project_praises_count
+
+ platform_project_praises_count
+ end
+
+ private
+
+ def platform_project_praises_count_key
+ "platform-project-praises-count"
+ end
+
+ def platform_project_praises_count
+ $redis_cache.get(platform_project_praises_count_key).to_i
+ end
+
+ def set_platform_project_praises_count
+ if $redis_cache.exists(platform_project_praises_count_key)
+ $redis_cache.incrby(platform_project_praises_count_key, increment_count)
+ else
+ reset_platform_project_praises_count
+ end
+ end
+
+ def reset_platform_project_praises_count
+ $redis_cache.set(platform_project_praises_count_key, PraiseTread.where(praise_tread_object_type: "Project").count)
+ end
+end
\ No newline at end of file
diff --git a/app/services/cache/platform_project_watchers_count_service.rb b/app/services/cache/platform_project_watchers_count_service.rb
new file mode 100644
index 000000000..70c834d2e
--- /dev/null
+++ b/app/services/cache/platform_project_watchers_count_service.rb
@@ -0,0 +1,41 @@
+class Cache::PlatformProjectWatchersCountService < ApplicationService
+ attr_reader :increment_count
+
+ def initialize(increment_count=0)
+ @increment_count = increment_count
+ end
+
+ def call
+ set_platform_project_watchers_count
+
+ platform_project_watchers_count
+ end
+
+ def reset
+ reset_platform_project_watchers_count
+
+ platform_project_watchers_count
+ end
+
+ private
+
+ def platform_project_watchers_count_key
+ "platform-project-watchers-count"
+ end
+
+ def platform_project_watchers_count
+ $redis_cache.get(platform_project_watchers_count_key).to_i
+ end
+
+ def set_platform_project_watchers_count
+ if $redis_cache.exists(platform_project_watchers_count_key)
+ $redis_cache.incrby(platform_project_watchers_count_key, increment_count)
+ else
+ reset_platform_project_watchers_count
+ end
+ end
+
+ def reset_platform_project_watchers_count
+ $redis_cache.set(platform_project_watchers_count_key, Watcher.where(watchable_type: 'Project').count)
+ end
+end
\ No newline at end of file
diff --git a/app/services/cache/platform_pullrequest_count_service.rb b/app/services/cache/platform_pullrequest_count_service.rb
new file mode 100644
index 000000000..c497c488a
--- /dev/null
+++ b/app/services/cache/platform_pullrequest_count_service.rb
@@ -0,0 +1,41 @@
+class Cache::PlatformPullrequestCountService < ApplicationService
+ attr_reader :increment_count
+
+ def initialize(increment_count=0)
+ @increment_count = increment_count
+ end
+
+ def call
+ set_platform_pullrequest_count
+
+ platform_pullrequest_count
+ end
+
+ def reset
+ reset_platform_pullrequest_count
+
+ platform_pullrequest_count
+ end
+
+ private
+
+ def platform_pullrequest_count_key
+ "platform-pullrequest-count"
+ end
+
+ def platform_pullrequest_count
+ $redis_cache.get(platform_pullrequest_count_key).to_i
+ end
+
+ def set_platform_pullrequest_count
+ if $redis_cache.exists(platform_pullrequest_count_key)
+ $redis_cache.incrby(platform_pullrequest_count_key, increment_count)
+ else
+ reset_platform_pullrequest_count
+ end
+ end
+
+ def reset_platform_pullrequest_count
+ $redis_cache.set(platform_pullrequest_count_key, PullRequest.count)
+ end
+end
\ No newline at end of file
diff --git a/app/services/cache/user_follow_count_service.rb b/app/services/cache/user_follow_count_service.rb
new file mode 100644
index 000000000..c583c32b6
--- /dev/null
+++ b/app/services/cache/user_follow_count_service.rb
@@ -0,0 +1,43 @@
+class Cache::UserFollowCountService < ApplicationService
+ attr_reader :user, :increment_count
+
+ def initialize(user, increment_count=0)
+ @user = user
+ @increment_count = increment_count
+ end
+
+ def call
+ set_user_follow_count
+
+ user_follow_count
+ end
+
+ def reset
+ reset_user_follow_count
+
+ user_follow_count
+ end
+
+ private
+
+ def user_follow_count_key
+ "user-follow-count-#{user.id}"
+ end
+
+ def user_follow_count
+ $redis_cache.get(user_follow_count_key).to_i
+ end
+
+ def set_user_follow_count
+ if $redis_cache.exists(user_follow_count_key)
+ $redis_cache.incrby(user_follow_count_key, increment_count)
+ else
+ reset_user_follow_count
+ end
+ end
+
+ def reset_user_follow_count
+ return if user.nil?
+ $redis_cache.set(user_follow_count_key, Watcher.where(watchable: user).count)
+ end
+end
\ No newline at end of file
diff --git a/app/services/cache/user_issue_count_service.rb b/app/services/cache/user_issue_count_service.rb
new file mode 100644
index 000000000..9e5f2d491
--- /dev/null
+++ b/app/services/cache/user_issue_count_service.rb
@@ -0,0 +1,43 @@
+class Cache::UserIssueCountService < ApplicationService
+ attr_reader :user, :increment_count
+
+ def initialize(user, increment_count=0)
+ @user = user
+ @increment_count = increment_count
+ end
+
+ def call
+ set_user_issue_count
+
+ user_issue_count
+ end
+
+ def reset
+ reset_user_issue_count
+
+ user_issue_count
+ end
+
+ private
+
+ def user_issue_count_key
+ "user-issue-count-#{user.id}"
+ end
+
+ def user_issue_count
+ $redis_cache.get(user_issue_count_key).to_i
+ end
+
+ def set_user_issue_count
+ if $redis_cache.exists(user_issue_count_key)
+ $redis_cache.incrby(user_issue_count_key, increment_count)
+ else
+ reset_user_issue_count
+ end
+ end
+
+ def reset_user_issue_count
+ return if user.nil?
+ $redis_cache.set(user_issue_count_key, Issue.where(author_id: user.id).count)
+ end
+end
\ No newline at end of file
diff --git a/app/services/cache/user_project_count_service.rb b/app/services/cache/user_project_count_service.rb
new file mode 100644
index 000000000..677184161
--- /dev/null
+++ b/app/services/cache/user_project_count_service.rb
@@ -0,0 +1,43 @@
+class Cache::UserProjectCountService < ApplicationService
+ attr_reader :user, :increment_count
+
+ def initialize(user, increment_count=0)
+ @user = user
+ @increment_count = increment_count
+ end
+
+ def call
+ set_user_project_count
+
+ user_project_count
+ end
+
+ def reset
+ reset_user_project_count
+
+ user_project_count
+ end
+
+ private
+
+ def user_project_count_key
+ "user-project-count-#{user.id}"
+ end
+
+ def user_project_count
+ $redis_cache.get(user_project_count_key).to_i
+ end
+
+ def set_user_project_count
+ if $redis_cache.exists(user_project_count_key)
+ $redis_cache.incrby(user_project_count_key, increment_count)
+ else
+ reset_user_project_count
+ end
+ end
+
+ def reset_user_project_count
+ return if user.nil?
+ $redis_cache.set(user_project_count_key, user.projects.count)
+ end
+end
\ No newline at end of file
diff --git a/app/services/cache/user_project_fork_count_service.rb b/app/services/cache/user_project_fork_count_service.rb
new file mode 100644
index 000000000..134e4ed65
--- /dev/null
+++ b/app/services/cache/user_project_fork_count_service.rb
@@ -0,0 +1,43 @@
+class Cache::UserProjectForkCountService < ApplicationService
+ attr_reader :user, :increment_count
+
+ def initialize(user, increment_count=0)
+ @user = user
+ @increment_count = increment_count
+ end
+
+ def call
+ set_user_project_fork_count
+
+ user_project_fork_count
+ end
+
+ def reset
+ reset_user_project_fork_count
+
+ user_project_fork_count
+ end
+
+ private
+
+ def user_project_fork_count_key
+ "user-project-fork-count-#{user.id}"
+ end
+
+ def user_project_fork_count
+ $redis_cache.get(user_project_fork_count_key).to_i
+ end
+
+ def set_user_project_fork_count
+ if $redis_cache.exists(user_project_fork_count_key)
+ $redis_cache.incrby(user_project_fork_count_key, increment_count)
+ else
+ reset_user_project_fork_count
+ end
+ end
+
+ def reset_user_project_fork_count
+ return if user.nil?
+ $redis_cache.set(user_project_fork_count_key, ForkUser.joins(:project).where(projects: {user_id: user.id}).count)
+ end
+end
\ No newline at end of file
diff --git a/app/services/cache/user_project_languages_count_service.rb b/app/services/cache/user_project_languages_count_service.rb
new file mode 100644
index 000000000..35b8a418e
--- /dev/null
+++ b/app/services/cache/user_project_languages_count_service.rb
@@ -0,0 +1,60 @@
+class Cache::UserProjectLanguagesCountService < ApplicationService
+ attr_reader :user, :key, :increment_count
+
+ def initialize(user, key=nil, increment_count=0)
+ @user = user
+ @key = key
+ @increment_count = increment_count
+ end
+
+ def call
+ set_user_project_language_count
+
+ user_project_language_count
+ end
+
+ def reset_by_key
+ reset_user_project_language_count_by_key
+
+ user_project_language_count
+ end
+
+ def reset
+ reset_user_project_language_count
+
+ user_project_language_count
+ end
+
+ private
+
+ def user_project_language_count_key
+ "user-project-language-count-#{user.id}"
+ end
+
+ def user_project_language_count
+ $redis_cache.hgetall(user_project_language_count_key).transform_values(&:to_i)
+ end
+
+ def set_user_project_language_count
+ if $redis_cache.hlen(user_project_language_count_key) == 0
+ reset_user_project_language_count
+ elsif $redis_cache.hget(user_project_language_count_key, key).nil?
+ reset_user_project_language_count_by_key
+ else
+ $redis_cache.hincrby(user_project_language_count_key, key, increment_count)
+ end
+ end
+
+ def reset_user_project_language_count_by_key
+ return if user.nil?
+ return if key.nil?
+ $redis_cache.hset(user_project_language_count_key, key, user.projects.joins(:project_language).where(project_languages: {name: key}).count)
+ end
+
+ def reset_user_project_language_count
+ return if user.nil?
+ user.projects.joins(:project_language).group("project_languages.name").count.each do |k, v|
+ $redis_cache.hset(user_project_language_count_key, k, v)
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/services/cache/user_project_praises_count_service.rb b/app/services/cache/user_project_praises_count_service.rb
new file mode 100644
index 000000000..0c7d49e3c
--- /dev/null
+++ b/app/services/cache/user_project_praises_count_service.rb
@@ -0,0 +1,43 @@
+class Cache::UserProjectPraisesCountService < ApplicationService
+ attr_reader :user, :increment_count
+
+ def initialize(user, increment_count=0)
+ @user = user
+ @increment_count = increment_count
+ end
+
+ def call
+ set_user_project_praises_count
+
+ user_project_praises_count
+ end
+
+ def reset
+ reset_user_project_praises_count
+
+ user_project_praises_count
+ end
+
+ private
+
+ def user_project_praises_count_key
+ "user-project-praises-count-#{user.id}"
+ end
+
+ def user_project_praises_count
+ $redis_cache.get(user_project_praises_count_key).to_i
+ end
+
+ def set_user_project_praises_count
+ if $redis_cache.exists(user_project_praises_count_key)
+ $redis_cache.incrby(user_project_praises_count_key, increment_count)
+ else
+ reset_user_project_praises_count
+ end
+ end
+
+ def reset_user_project_praises_count
+ return if user.nil?
+ $redis_cache.set(user_project_praises_count_key, PraiseTread.where(praise_tread_object_type: 'Project', praise_tread_object_id: user.projects).count)
+ end
+end
\ No newline at end of file
diff --git a/app/services/cache/user_project_watchers_count_service.rb b/app/services/cache/user_project_watchers_count_service.rb
new file mode 100644
index 000000000..50733cad7
--- /dev/null
+++ b/app/services/cache/user_project_watchers_count_service.rb
@@ -0,0 +1,43 @@
+class Cache::UserProjectWatchersCountService < ApplicationService
+ attr_reader :user, :increment_count
+
+ def initialize(user, increment_count=0)
+ @user = user
+ @increment_count = increment_count
+ end
+
+ def call
+ set_user_project_watchers_count
+
+ user_project_watchers_count
+ end
+
+ def reset
+ reset_user_project_watchers_count
+
+ user_project_watchers_count
+ end
+
+ private
+
+ def user_project_watchers_count_key
+ "user-project-watchers-count-#{user.id}"
+ end
+
+ def user_project_watchers_count
+ $redis_cache.get(user_project_watchers_count_key).to_i
+ end
+
+ def set_user_project_watchers_count
+ if $redis_cache.exists(user_project_watchers_count_key)
+ $redis_cache.incrby(user_project_watchers_count_key, increment_count)
+ else
+ reset_user_project_watchers_count
+ end
+ end
+
+ def reset_user_project_watchers_count
+ return if user.nil?
+ $redis_cache.set(user_project_watchers_count_key, Watcher.where(watchable_type: 'Project', watchable_id: user.projects).count)
+ end
+end
\ No newline at end of file
diff --git a/app/services/cache/user_pullrequest_count_service.rb b/app/services/cache/user_pullrequest_count_service.rb
new file mode 100644
index 000000000..da478b2af
--- /dev/null
+++ b/app/services/cache/user_pullrequest_count_service.rb
@@ -0,0 +1,43 @@
+class Cache::UserPullrequestCountService < ApplicationService
+ attr_reader :user, :increment_count
+
+ def initialize(user, increment_count=0)
+ @user = user
+ @increment_count = increment_count
+ end
+
+ def call
+ set_user_pullrequest_count
+
+ user_pullrequest_count
+ end
+
+ def reset
+ reset_user_pullrequest_count
+
+ user_pullrequest_count
+ end
+
+ private
+
+ def user_pullrequest_count_key
+ "user-pullrequest-count-#{user.id}"
+ end
+
+ def user_pullrequest_count
+ $redis_cache.get(user_pullrequest_count_key).to_i
+ end
+
+ def set_user_pullrequest_count
+ if $redis_cache.exists(user_pullrequest_count_key)
+ $redis_cache.incrby(user_pullrequest_count_key, increment_count)
+ else
+ reset_user_pullrequest_count
+ end
+ end
+
+ def reset_user_pullrequest_count
+ return if user.nil?
+ $redis_cache.set(user_pullrequest_count_key, PullRequest.where(user_id: user.id).count)
+ end
+end
\ No newline at end of file
diff --git a/app/services/cache/v2/owner_common_service.rb b/app/services/cache/v2/owner_common_service.rb
new file mode 100644
index 000000000..c97e34d48
--- /dev/null
+++ b/app/services/cache/v2/owner_common_service.rb
@@ -0,0 +1,135 @@
+class Cache::V2::OwnerCommonService < ApplicationService
+ include AvatarHelper
+ attr_reader :owner_id, :name
+ attr_accessor :owner, :login, :email
+
+ def initialize(owner_id, params={})
+ @owner_id = owner_id
+ @email = params[:email]
+ @name = params[:name]
+ @avatar_url = params[:avatar_url]
+ end
+
+ def read
+ owner_common
+ end
+
+ def call
+ set_owner_common
+ end
+
+ def reset
+ reset_owner_common
+ end
+
+ def clear
+ clear_owner_common
+ end
+
+ private
+ def load_owner
+ @owner = Owner.find_by_id @owner_id
+ @login = @owner&.login
+ @email ||= @owner&.mail
+ end
+
+ def owner_common_key
+ "v2-owner-common:#{@login}-#{@email.to_s}"
+ end
+
+ def owner_common_key_by_id
+ "v2-owner-common:#{@owner&.id}"
+ end
+
+ def owner_common
+ result = $redis_cache.hgetall(owner_common_key_by_id)
+ result.blank? ? reset_owner_common : result
+ end
+
+ def set_owner_common
+ if $redis_cache.hgetall(owner_common_key_by_id).blank?
+ reset_owner_common
+ return
+ else
+ load_owner
+ return if @owner.nil?
+ if @name.present?
+ if $redis_cache.hget(owner_common_key, "name").nil?
+ reset_owner_name
+ else
+ $redis_cache.hset(owner_common_key, "name", @name)
+ $redis_cache.hset(owner_common_key_by_id, "name", @name)
+ end
+ end
+ if @email.present?
+ if $redis_cache.hget(owner_common_key, "email").nil?
+ reset_owner_email
+ else
+ # 更改邮箱这里把旧数据删除
+ $redis_cache.del("v2-owner-common:#{@login}-*")
+ $redis_cache.hset(owner_common_key, "email", @email)
+ $redis_cache.hset(owner_common_key_by_id, "email", @email)
+ end
+ end
+ if @avatar_url.present?
+ if $redis_cache.hget(owner_common_key, "avatar_url").nil?
+ reset_owner_avatar_url
+ else
+ $redis_cache.hset(owner_common_key, "avatar_url", @avatar_url)
+ $redis_cache.hset(owner_common_key_by_id, "avatar_url", @avatar_url)
+ end
+
+ end
+ end
+
+ $redis_cache.hgetall(owner_common_key)
+ end
+ def reset_owner_id
+ $redis_cache.hset(owner_common_key, "id", owner&.id)
+ $redis_cache.hset(owner_common_key_by_id, "id", owner&.id)
+ end
+
+ def reset_owner_type
+ $redis_cache.hset(owner_common_key, "type", owner&.type)
+ $redis_cache.hset(owner_common_key_by_id, "type", owner&.type)
+ end
+
+ def reset_owner_login
+ $redis_cache.hset(owner_common_key, "login", owner&.login)
+ $redis_cache.hset(owner_common_key_by_id, "login", owner&.login)
+ end
+
+ def reset_owner_email
+ $redis_cache.hset(owner_common_key, "email", owner&.mail)
+ $redis_cache.hset(owner_common_key_by_id, "email", owner&.mail)
+ end
+
+ def reset_owner_name
+ $redis_cache.hset(owner_common_key, "name", owner&.real_name)
+ $redis_cache.hset(owner_common_key_by_id, "name", owner&.real_name)
+ end
+
+ def reset_owner_avatar_url
+ $redis_cache.hset(owner_common_key, "avatar_url", url_to_avatar(owner))
+ $redis_cache.hset(owner_common_key_by_id, "avatar_url", url_to_avatar(owner))
+ end
+
+ def reset_owner_common
+ clear_owner_common
+ reset_owner_id
+ reset_owner_type
+ reset_owner_login
+ reset_owner_email
+ reset_owner_name
+ reset_owner_avatar_url
+
+ $redis_cache.hgetall(owner_common_key)
+ end
+
+ def clear_owner_common
+ load_owner
+ return if @owner.nil?
+ $redis_cache.del(owner_common_key)
+ $redis_cache.del(owner_common_key_by_id)
+ end
+end
\ No newline at end of file
diff --git a/app/services/cache/v2/platform_statistic_service.rb b/app/services/cache/v2/platform_statistic_service.rb
new file mode 100644
index 000000000..5bf4f4a74
--- /dev/null
+++ b/app/services/cache/v2/platform_statistic_service.rb
@@ -0,0 +1,184 @@
+class Cache::V2::PlatformStatisticService < ApplicationService
+ attr_reader :follow_count, :fork_count, :issue_count, :project_count, :project_language_count_key, :project_language_count, :project_praise_count, :project_watcher_count, :pullrequest_count
+
+ def initialize(params={})
+ @follow_count = params[:follow_count]
+ @fork_count = params[:fork_count]
+ @issue_count = params[:issue_count]
+ @project_count = params[:project_count]
+ @project_language_count_key = params[:project_language_count_key]
+ @project_language_count = params[:project_language_count]
+ @project_praise_count = params[:project_praise_count]
+ @project_watcher_count = params[:project_watcher_count]
+ @pullrequest_count = params[:pullrequest_count]
+ end
+
+ def read
+ platform_statistic
+ end
+
+ def call
+ set_platform_statistic
+ end
+
+ def reset
+ reset_platform_statistic
+ end
+
+ private
+
+ def platform_statistic_key
+ "v2-platform-statistic"
+ end
+
+ def follow_count_key
+ "follow-count"
+ end
+
+ def fork_count_key
+ "fork-count"
+ end
+
+ def issue_count_key
+ "issue-count"
+ end
+
+ def project_count_key
+ "project-count"
+ end
+
+ def project_language_key
+ "project-language"
+ end
+
+ def project_praise_count_key
+ "project-praise-count"
+ end
+
+ def project_watcher_count_key
+ "project-watcher-count"
+ end
+
+ def pullrequest_count_key
+ "pullrequest-count"
+ end
+
+ def platform_statistic
+ result = $redis_cache.hgetall(platform_statistic_key)
+
+ result.blank? ? reset_platform_statistic : result
+ end
+
+ def set_platform_statistic
+ if $redis_cache.hgetall(platform_statistic_key).blank?
+ reset_platform_statistic
+ return
+ end
+ if @follow_count.present?
+ if $redis_cache.hget(platform_statistic_key, follow_count_key).nil?
+ reset_platform_follow_count
+ else
+ $redis_cache.hincrby(platform_statistic_key, follow_count_key, @follow_count)
+ end
+ end
+ if @fork_count.present?
+ if $redis_cache.hget(platform_statistic_key, fork_count_key).nil?
+ reset_platform_fork_count
+ else
+ $redis_cache.hincrby(platform_statistic_key, fork_count_key, @fork_count)
+ end
+ end
+ if @issue_count.present?
+ if $redis_cache.hget(platform_statistic_key, issue_count_key).nil?
+ reset_platform_issue_count
+ else
+ $redis_cache.hincrby(platform_statistic_key, issue_count_key, @issue_count)
+ end
+ end
+ if @project_count.present?
+ if $redis_cache.hget(platform_statistic_key, project_count_key).nil?
+ reset_platform_project_count
+ else
+ $redis_cache.hincrby(platform_statistic_key, project_count_key, @project_count)
+ end
+ end
+ if @project_language_count_key.present? && project_language_count.present?
+ if $redis_cache.hget(platform_statistic_key, project_language_key).nil?
+ reset_platform_project_language
+ else
+ result = JSON.parse($redis_cache.hget(platform_statistic_key, project_language_key))
+ result[@project_language_count_key] ||= 0
+ result[@project_language_count_key] += project_language_count.to_i
+ $redis_cache.hset(platform_statistic_key, project_language_key, result.to_json)
+ end
+ end
+ if @project_praise_count.present?
+ if $redis_cache.hget(platform_statistic_key, project_praise_count_key).nil?
+ reset_platform_project_praise_count
+ else
+ $redis_cache.hincrby(platform_statistic_key, project_praise_count_key, @project_praise_count)
+ end
+ end
+ if @project_watcher_count.present?
+ if $redis_cache.hget(platform_statistic_key, project_watcher_count_key).nil?
+ reset_platform_project_watcher_count
+ else
+ $redis_cache.hincrby(platform_statistic_key, project_watcher_count_key, @project_watcher_count)
+ end
+ end
+ if @pullrequest_count.present?
+ if $redis_cache.hget(platform_statistic_key, pullrequest_count_key).nil?
+ reset_platform_pullrequest_count
+ else
+ $redis_cache.hincrby(platform_statistic_key, pullrequest_count_key, @pullrequest_count)
+ end
+ end
+ $redis_cache.hgetall(platform_statistic_key)
+ end
+
+ def reset_platform_follow_count
+ $redis_cache.hset(platform_statistic_key, follow_count_key, Watcher.where(watchable_type: 'User').count)
+ end
+
+ def reset_platform_fork_count
+ $redis_cache.hset(platform_statistic_key, fork_count_key, ForkUser.count)
+ end
+
+ def reset_platform_issue_count
+ $redis_cache.hset(platform_statistic_key, issue_count_key, Issue.count)
+ end
+
+ def reset_platform_project_count
+ $redis_cache.hset(platform_statistic_key, project_count_key, Project.count)
+ end
+
+ def reset_platform_project_language
+ $redis_cache.hset(platform_statistic_key, project_language_key, ProjectLanguage.where.not(projects_count: 0).group("project_languages.name").sum(:projects_count).to_json)
+ end
+
+ def reset_platform_project_praise_count
+ $redis_cache.hset(platform_statistic_key, project_praise_count_key, PraiseTread.where(praise_tread_object_type: "Project").count)
+ end
+
+ def reset_platform_project_watcher_count
+ $redis_cache.hset(platform_statistic_key, project_watcher_count_key, Watcher.where(watchable_type: 'Project').count)
+ end
+
+ def reset_platform_pullrequest_count
+ $redis_cache.hset(platform_statistic_key, pullrequest_count_key, PullRequest.count)
+ end
+
+ def reset_platform_statistic
+ $redis_cache.del(platform_statistic_key)
+ reset_platform_follow_count
+ reset_platform_fork_count
+ reset_platform_issue_count
+ reset_platform_project_count
+ reset_platform_project_language
+ reset_platform_project_praise_count
+ reset_platform_project_watcher_count
+ reset_platform_pullrequest_count
+
+ $redis_cache.hgetall(platform_statistic_key)
+ end
+end
\ No newline at end of file
diff --git a/app/services/cache/v2/project_common_service.rb b/app/services/cache/v2/project_common_service.rb
new file mode 100644
index 000000000..8ed0a0636
--- /dev/null
+++ b/app/services/cache/v2/project_common_service.rb
@@ -0,0 +1,220 @@
+class Cache::V2::ProjectCommonService < ApplicationService
+ attr_reader :project_id, :owner_id, :name, :identifier, :description, :visits, :watchers, :praises, :forks, :issues, :pullrequests
+ attr_accessor :project
+
+ def initialize(project_id, params={})
+ @project_id = project_id
+ @owner_id = params[:owner_id]
+ @name = params[:name]
+ @identifier = params[:identifier]
+ @description = params[:description]
+ @visits = params[:visits]
+ @watchers = params[:watchers]
+ @praises = params[:praises]
+ @forks = params[:forks]
+ @issues = params[:issues]
+ @pullrequests = params[:pullrequests]
+ end
+
+ def read
+ project_common
+ end
+
+ def call
+ set_project_common
+ end
+
+ def reset
+ reset_project_common
+ end
+
+ def clear
+ clear_project_common
+ end
+
+ private
+ def load_project
+ @project = Project.find_by_id(project_id)
+ end
+
+ def project_common_key
+ "v2-project-common:#{@project_id}"
+ end
+
+ def owner_id_key
+ "owner_id"
+ end
+
+ def name_key
+ "name"
+ end
+
+ def identifier_key
+ "identifier"
+ end
+
+ def description_key
+ "description"
+ end
+
+ def visits_key
+ "visits"
+ end
+
+ def watchers_key
+ "watchers"
+ end
+
+ def praises_key
+ "praises"
+ end
+
+ def forks_key
+ "forks"
+ end
+
+ def issues_key
+ "issues"
+ end
+
+ def pullrequests_key
+ "pullrequests"
+ end
+
+ def project_common
+ result = $redis_cache.hgetall(project_common_key)
+ result.blank? ? reset_project_common : result
+ end
+
+ def set_project_common
+ if $redis_cache.hgetall(project_common_key).blank?
+ reset_project_common
+ return
+ else
+ load_project
+ return unless @project.is_full_public
+ if @owner_id.present?
+ if $redis_cache.hget(project_common_key, owner_id_key).nil?
+ reset_project_owner_id
+ else
+ $redis_cache.hset(project_common_key, owner_id_key, @owner_id)
+ end
+ end
+ if @name.present?
+ if $redis_cache.hget(project_common_key, name_key).nil?
+ reset_project_name
+ else
+ $redis_cache.hset(project_common_key, name_key, @name)
+ end
+ end
+ if @identifier.present?
+ if $redis_cache.hget(project_common_key, identifier_key).nil?
+ reset_project_identifier
+ else
+ $redis_cache.hset(project_common_key, identifier_key, @identifier)
+ end
+ end
+ if @description.present?
+ if $redis_cache.hget(project_common_key, description_key).nil?
+ reset_project_description
+ else
+ $redis_cache.hset(project_common_key, description_key, @description)
+ end
+ end
+ if @visits.present?
+ $redis_cache.hincrby(project_common_key, visits_key, @visits.to_s)
+ Cache::V2::ProjectRankService.call(@project_id, {visits: @visits})
+ Cache::V2::ProjectDateRankService.call(@project_id, Date.today, {visits: @visits})
+ end
+ if @watchers.present?
+ $redis_cache.hincrby(project_common_key, watchers_key, @watchers)
+ end
+ if @praises.present?
+ $redis_cache.hincrby(project_common_key, praises_key, @praises)
+ Cache::V2::ProjectRankService.call(@project_id, {praises: @praises})
+ Cache::V2::ProjectDateRankService.call(@project_id, Date.today, {praises: @praises})
+ end
+ if @forks.present?
+ $redis_cache.hincrby(project_common_key, forks_key, @forks)
+ Cache::V2::ProjectRankService.call(@project_id, {forks: @forks})
+ Cache::V2::ProjectDateRankService.call(@project_id, Date.today, {forks: @forks})
+ end
+ if @issues.present?
+ $redis_cache.hincrby(project_common_key, issues_key, @issues)
+ Cache::V2::ProjectRankService.call(@project_id, {issues: @issues})
+ Cache::V2::ProjectDateRankService.call(@project_id, Date.today, {issues: @issues})
+ end
+ if @pullrequests.present?
+ $redis_cache.hincrby(project_common_key, pullrequests_key, @pullrequests)
+ Cache::V2::ProjectRankService.call(@project_id, {pullrequests: @pullrequests})
+ Cache::V2::ProjectDateRankService.call(@project_id, Date.today, {pullrequests: @pullrequests})
+ end
+ end
+
+ $redis_cache.hgetall(project_common_key)
+ end
+
+ def reset_project_owner_id
+ $redis_cache.hset(project_common_key, owner_id_key, @project&.user_id)
+ end
+
+ def reset_project_name
+ $redis_cache.hset(project_common_key, name_key, @project&.name)
+ end
+
+ def reset_project_identifier
+ $redis_cache.hset(project_common_key, identifier_key, @project&.identifier)
+ end
+
+ def reset_project_description
+ $redis_cache.hset(project_common_key, description_key, @project&.description)
+ end
+
+ def reset_project_visits
+ $redis_cache.hset(project_common_key, visits_key, @project&.visits || 0)
+ end
+
+ def reset_project_watchers
+ $redis_cache.hset(project_common_key, watchers_key, Watcher.where(watchable_type: 'Project', watchable_id: @project_id).count)
+ end
+
+ def reset_project_praises
+ $redis_cache.hset(project_common_key, praises_key, PraiseTread.where(praise_tread_object_type: 'Project', praise_tread_object_id: @project_id).count)
+ end
+
+ def reset_project_forks
+ $redis_cache.hset(project_common_key, forks_key, ForkUser.where(project_id: @project_id).count)
+ end
+
+ def reset_project_issues
+ $redis_cache.hset(project_common_key, issues_key, Issue.issue_issue.where(project_id: @project_id).count)
+ end
+
+ def reset_project_pullrequests
+ $redis_cache.hset(project_common_key, pullrequests_key, PullRequest.where(project_id: @project_id).count)
+ end
+
+ def reset_project_common
+ load_project
+ return unless @project.present?
+ return unless @project.is_full_public
+ $redis_cache.del(project_common_key)
+ reset_project_owner_id
+ reset_project_name
+ reset_project_identifier
+ reset_project_description
+ reset_project_visits
+ reset_project_watchers
+ reset_project_praises
+ reset_project_forks
+ reset_project_issues
+ reset_project_pullrequests
+
+ $redis_cache.hgetall(project_common_key)
+ end
+
+ def clear_project_common
+ $redis_cache.del(project_common_key)
+ Cache::V2::ProjectRankService.new(@project_id).clear
+ end
+end
\ No newline at end of file
diff --git a/app/services/cache/v2/project_date_rank_service.rb b/app/services/cache/v2/project_date_rank_service.rb
new file mode 100644
index 000000000..092aff796
--- /dev/null
+++ b/app/services/cache/v2/project_date_rank_service.rb
@@ -0,0 +1,51 @@
+class Cache::V2::ProjectDateRankService < ApplicationService
+ attr_reader :project_id, :rank_date, :visits, :praises, :forks, :issues, :pullrequests
+ attr_accessor :project_common
+
+ def initialize(project_id, rank_date=Date.today, params={})
+ @project_id = project_id
+ @rank_date = rank_date
+ @visits = params[:visits]
+ @praises = params[:praises]
+ @forks = params[:forks]
+ @issues = params[:issues]
+ @pullrequests = params[:pullrequests]
+ end
+
+ def read
+ project_rank
+ end
+
+ def call
+ set_project_rank
+ end
+
+ private
+ def project_rank_key
+ "v2-project-rank-#{@rank_date.to_s}"
+ end
+
+ def project_rank
+ $redis_cache.zscore(project_rank_key, @project_id)
+ end
+
+ def set_project_rank
+ if @visits.present?
+ $redis_cache.zincrby(project_rank_key, @visits.to_i * 1, @project_id)
+ end
+ if @praises.present?
+ $redis_cache.zincrby(project_rank_key, @praises.to_i * 5, @project_id)
+ end
+ if @forks.present?
+ $redis_cache.zincrby(project_rank_key, @forks.to_i * 5, @project_id)
+ end
+ if @issues.present?
+ $redis_cache.zincrby(project_rank_key, @issues.to_i * 10, @project_id)
+ end
+ if @pullrequests.present?
+ $redis_cache.zincrby(project_rank_key, @pullrequests.to_i * 10, @project_id)
+ end
+
+ $redis_cache.zscore(project_rank_key, @project_id)
+ end
+end
\ No newline at end of file
diff --git a/app/services/cache/v2/project_rank_service.rb b/app/services/cache/v2/project_rank_service.rb
new file mode 100644
index 000000000..7e5d323bf
--- /dev/null
+++ b/app/services/cache/v2/project_rank_service.rb
@@ -0,0 +1,87 @@
+class Cache::V2::ProjectRankService < ApplicationService
+ attr_reader :project_id, :visits, :praises, :forks, :issues, :pullrequests
+ attr_accessor :project_common
+
+ def initialize(project_id, params={})
+ @project_id = project_id
+ @visits = params[:visits]
+ @praises = params[:praises]
+ @forks = params[:forks]
+ @issues = params[:issues]
+ @pullrequests = params[:pullrequests]
+ end
+
+ def read
+ project_rank
+ end
+
+ def call
+ set_project_rank
+ end
+
+ def reset
+ reset_project_rank
+ end
+
+ def clear
+ clear_project_rank
+ end
+
+ private
+ def load_project_common
+ @project_common = Cache::V2::ProjectCommonService.new(@project_id).read
+ end
+
+ def project_rank_key
+ "v2-project-rank"
+ end
+
+ def project_rank
+ result = $redis_cache.zscore(project_rank_key, @project_id)
+ result.blank? ? reset_project_rank : result
+ end
+
+ def set_project_rank
+ load_project_common
+ if $redis_cache.zscore(project_rank_key, @project_id).blank?
+ reset_project_rank
+ return
+ else
+ if @visits.present?
+ $redis_cache.zincrby(project_rank_key, @visits.to_i * 1, @project_id)
+ end
+ if @praises.present?
+ $redis_cache.zincrby(project_rank_key, @praises.to_i * 5, @project_id)
+ end
+ if @forks.present?
+ $redis_cache.zincrby(project_rank_key, @forks.to_i * 5, @project_id)
+ end
+ if @issues.present?
+ $redis_cache.zincrby(project_rank_key, @issues.to_i * 10, @project_id)
+ end
+ if @pullrequests.present?
+ $redis_cache.zincrby(project_rank_key, @pullrequests.to_i * 10, @project_id)
+ end
+ reset_user_project_rank
+ end
+
+ $redis_cache.zscore(project_rank_key, @project_id)
+ end
+
+ def reset_project_rank
+ load_project_common
+ score = @project_common["visits"].to_i * 1 + @project_common["praises"].to_i * 5 + @project_common["forks"].to_i * 5 + @project_common["issues"].to_i * 10 + @project_common["pullrequests"].to_i * 10
+ $redis_cache.zadd(project_rank_key, score, @project_id)
+ reset_user_project_rank
+
+ $redis_cache.zscore(project_rank_key, @project_id)
+ end
+
+ def reset_user_project_rank
+ $redis_cache.zadd("v2-user-project-rank:#{@project_common["owner_id"]}", $redis_cache.zscore(project_rank_key, @project_id), @project_id)
+ end
+
+ def clear_project_rank
+ $redis_cache.sadd('v2-project-rank-deleted', @project_id)
+ end
+end
\ No newline at end of file
diff --git a/app/services/cache/v2/user_date_rank_service.rb b/app/services/cache/v2/user_date_rank_service.rb
new file mode 100644
index 000000000..00073a8e8
--- /dev/null
+++ b/app/services/cache/v2/user_date_rank_service.rb
@@ -0,0 +1,119 @@
+class Cache::V2::UserDateRankService < ApplicationService
+ attr_reader :user_id, :rank_date, :follow_count, :fork_count, :issue_count, :project_count, :project_language_count_key, :project_language_count, :project_praise_count, :project_watcher_count, :pullrequest_count
+
+ def initialize(user_id, rank_date=Date.today, params={})
+ @user_id = user_id
+ @rank_date = rank_date
+ @follow_count = params[:follow_count]
+ @fork_count = params[:fork_count]
+ @issue_count = params[:issue_count]
+ @project_count = params[:project_count]
+ @project_language_count_key = params[:project_language_count_key]
+ @project_language_count = params[:project_language_count]
+ @project_praise_count = params[:project_praise_count]
+ @project_watcher_count = params[:project_watcher_count]
+ @pullrequest_count = params[:pullrequest_count]
+ end
+
+ def read_rank
+ user_rank
+ end
+
+ def read_statistic
+ user_statistic
+ end
+
+ def call
+ set_user_rank
+ end
+
+ private
+ def user_rank_key
+ "v2-user-rank-#{@rank_date.to_s}"
+ end
+
+ def user_date_statistic_key
+ "v2-user-statistic:#{@user_id}-#{@rank_date.to_s}"
+ end
+
+ def user_rank
+ $redis_cache.zscore(user_rank_key, @user_id)
+ end
+
+ def user_statistic
+ $redis_cache.hgetall(user_date_statistic_key)
+ end
+
+ def set_user_statistic
+ if @follow_count.present?
+ $redis_cache.hincrby(user_date_statistic_key, "follow-count", @follow_count.to_i)
+ end
+ if @fork_count.present?
+ $redis_cache.hincrby(user_date_statistic_key, "fork-count", @fork_count.to_i)
+ end
+ if @issue_count.present?
+ $redis_cache.hincrby(user_date_statistic_key, "issue-count", @issue_count.to_i)
+ end
+ if @project_count.present?
+ $redis_cache.hincrby(user_date_statistic_key, "project-count", @project_count.to_i)
+ end
+ if project_language_count_key.present? && project_language_count.present?
+ if $redis_cache.hget(user_date_statistic_key, "project-language").nil?
+ result = {}
+ result[@project_language_count_key] = project_language_count.to_i
+ result.delete(@project_language_count_key) if result[@project_language_count_key] == 0
+ $redis_cache.hset(user_date_statistic_key, "project-language", result.to_json)
+ else
+ result = JSON.parse($redis_cache.hget(user_date_statistic_key, "project-language"))
+ result[@project_language_count_key] ||= 0
+ result[@project_language_count_key] += project_language_count.to_i
+ result.delete(@project_language_count_key) if result[@project_language_count_key] == 0
+ $redis_cache.hset(user_date_statistic_key, "project-language", result.to_json)
+ end
+ end
+ if @project_praise_count.present?
+ $redis_cache.hincrby(user_date_statistic_key, "project-praise-count", @project_praise_count.to_i)
+ end
+ if @project_watcher_count.present?
+ $redis_cache.hincrby(user_date_statistic_key, "project-watcher-count", @project_watcher_count.to_i)
+ end
+ if @pullrequest_count.present?
+ $redis_cache.hincrby(user_date_statistic_key, "pullrequest-count", @pullrequest_count.to_i)
+ end
+
+ $redis_cache.hgetall(user_date_statistic_key)
+ end
+
+ def set_user_rank
+ set_user_statistic
+ follow_count = $redis_cache.hget(user_date_statistic_key, "follow-count") || 0
+ pullrequest_count = $redis_cache.hget(user_date_statistic_key, "pullrequest-count") || 0
+ issues_count = $redis_cache.hget(user_date_statistic_key, "issue-count") || 0
+ project_count = $redis_cache.hget(user_date_statistic_key, "project-count") || 0
+ fork_count = $redis_cache.hget(user_date_statistic_key, "fork-count") || 0
+ project_watchers_count = $redis_cache.hget(user_date_statistic_key, "project-watcher-count") || 0
+ project_praises_count = $redis_cache.hget(user_date_statistic_key, "project-praise-count") || 0
+ project_language = $redis_cache.hget(user_date_statistic_key, "project-language")
+ project_languages_count = project_language.nil? || project_language == "{}" ? 0 : JSON.parse(project_language).length
+ # 影响力
+ influence = (60.0 + follow_count.to_i / (follow_count.to_i + 20.0) * 40.0).to_i
+
+ # 贡献度
+ contribution = (60.0 + pullrequest_count.to_i / (pullrequest_count.to_i + 20.0) * 40.0).to_i
+
+ # 活跃度
+ activity = (60.0 + issues_count.to_i / (issues_count.to_i + 80.0) * 40.0).to_i
+
+ # 项目经验
+ experience = 10 * project_count.to_i + 5 * fork_count.to_i + project_watchers_count.to_i + project_praises_count.to_i
+ experience = (60.0 + experience / (experience + 100.0) * 40.0).to_i
+ # 语言能力
+ language = (60.0 + project_languages_count.to_i / (project_languages_count.to_i + 5.0) * 40.0).to_i
+
+ score = influence+ contribution + activity + experience + language
+ $redis_cache.zrem(user_rank_key, @user_id)
+ $redis_cache.zadd(user_rank_key, score-300, @user_id) if score > 300
+
+ $redis_cache.zscore(user_rank_key, @user_id)
+ end
+end
\ No newline at end of file
diff --git a/app/services/cache/v2/user_statistic_service.rb b/app/services/cache/v2/user_statistic_service.rb
new file mode 100644
index 000000000..b82797d84
--- /dev/null
+++ b/app/services/cache/v2/user_statistic_service.rb
@@ -0,0 +1,202 @@
+class Cache::V2::UserStatisticService < ApplicationService
+ attr_reader :user_id, :follow_count, :fork_count, :issue_count, :project_count, :project_language_count_key, :project_language_count, :project_praise_count, :project_watcher_count, :pullrequest_count
+
+ def initialize(user_id, params={})
+ @user_id = user_id
+ @follow_count = params[:follow_count]
+ @fork_count = params[:fork_count]
+ @issue_count = params[:issue_count]
+ @project_count = params[:project_count]
+ @project_language_count_key = params[:project_language_count_key]
+ @project_language_count = params[:project_language_count]
+ @project_praise_count = params[:project_praise_count]
+ @project_watcher_count = params[:project_watcher_count]
+ @pullrequest_count = params[:pullrequest_count]
+ Cache::V2::OwnerCommonService.new(user_id).read
+ end
+
+ def read
+ user_statistic
+ end
+
+ def call
+ set_user_statistic
+ end
+
+ def reset
+ reset_user_statistic
+ end
+
+ private
+
+ def user_statistic_key
+ "v2-user-statistic:#{@user_id}"
+ end
+
+ def follow_count_key
+ "follow-count"
+ end
+
+ def fork_count_key
+ "fork-count"
+ end
+
+ def issue_count_key
+ "issue-count"
+ end
+
+ def project_count_key
+ "project-count"
+ end
+
+ def project_language_key
+ "project-language"
+ end
+
+ def project_praise_count_key
+ "project-praise-count"
+ end
+
+ def project_watcher_count_key
+ "project-watcher-count"
+ end
+
+ def pullrequest_count_key
+ "pullrequest-count"
+ end
+
+ def user_statistic
+ result = $redis_cache.hgetall(user_statistic_key)
+ result.blank? ? reset_user_statistic : result
+ end
+
+ def set_user_statistic
+ if $redis_cache.hgetall(user_statistic_key).blank?
+ reset_user_statistic
+ return
+ end
+ if @follow_count.present?
+ if $redis_cache.hget(user_statistic_key, follow_count_key).nil?
+ reset_user_follow_count
+ Cache::V2::UserDateRankService.call(@user_id, Date.today, {follow_count: @follow_count})
+ else
+ $redis_cache.hincrby(user_statistic_key, follow_count_key, @follow_count)
+ Cache::V2::UserDateRankService.call(@user_id, Date.today, {follow_count: @follow_count})
+ end
+ end
+ if @fork_count.present?
+ if $redis_cache.hget(user_statistic_key, fork_count_key).nil?
+ reset_user_fork_count
+ Cache::V2::UserDateRankService.call(@user_id, Date.today, {fork_count: @fork_count})
+ else
+ $redis_cache.hincrby(user_statistic_key, fork_count_key, @fork_count)
+ Cache::V2::UserDateRankService.call(@user_id, Date.today, {fork_count: @fork_count})
+ end
+ end
+ if @issue_count.present?
+ if $redis_cache.hget(user_statistic_key, issue_count_key).nil?
+ reset_user_issue_count
+ Cache::V2::UserDateRankService.call(@user_id, Date.today, {issue_count: @issue_count})
+ else
+ $redis_cache.hincrby(user_statistic_key, issue_count_key, @issue_count)
+ Cache::V2::UserDateRankService.call(@user_id, Date.today, {issue_count: @issue_count})
+ end
+ end
+ if @project_count.present?
+ if $redis_cache.hget(user_statistic_key, project_count_key).nil?
+ reset_user_project_count
+ Cache::V2::UserDateRankService.call(@user_id, Date.today, {project_count: @project_count})
+ else
+ $redis_cache.hincrby(user_statistic_key, project_count_key, @project_count)
+ Cache::V2::UserDateRankService.call(@user_id, Date.today, {project_count: @project_count})
+ end
+ end
+ if @project_language_count_key.present? && project_language_count.present?
+ if $redis_cache.hget(user_statistic_key, project_language_key).nil?
+ reset_user_project_language
+ Cache::V2::UserDateRankService.call(@user_id, Date.today, {project_language_count_key: @project_language_count_key, project_language_count: @project_language_count})
+ else
+ result = JSON.parse($redis_cache.hget(user_statistic_key, project_language_key))
+ result[@project_language_count_key] ||= 0
+ result[@project_language_count_key] += project_language_count.to_i
+ result.delete(@project_language_count_key) if result[@project_language_count_key] == 0
+ $redis_cache.hset(user_statistic_key, project_language_key, result.to_json)
+ Cache::V2::UserDateRankService.call(@user_id, Date.today, {project_language_count_key: @project_language_count_key, project_language_count: @project_language_count})
+ end
+ end
+ if @project_praise_count.present?
+ if $redis_cache.hget(user_statistic_key, project_praise_count_key).nil?
+ reset_user_project_praise_count
+ Cache::V2::UserDateRankService.call(@user_id, Date.today, {project_praise_count: @project_praise_count})
+ else
+ $redis_cache.hincrby(user_statistic_key, project_praise_count_key, @project_praise_count)
+ Cache::V2::UserDateRankService.call(@user_id, Date.today, {project_praise_count: @project_praise_count})
+ end
+ end
+ if @project_watcher_count.present?
+ if $redis_cache.hget(user_statistic_key, project_watcher_count_key).nil?
+ reset_user_project_watcher_count
+ Cache::V2::UserDateRankService.call(@user_id, Date.today, {project_watcher_count: @project_watcher_count})
+ else
+ $redis_cache.hincrby(user_statistic_key, project_watcher_count_key, @project_watcher_count)
+ Cache::V2::UserDateRankService.call(@user_id, Date.today, {project_watcher_count: @project_watcher_count})
+ end
+ end
+ if @pullrequest_count.present?
+ if $redis_cache.hget(user_statistic_key, pullrequest_count_key).nil?
+ reset_user_pullrequest_count
+ Cache::V2::UserDateRankService.call(@user_id, Date.today, {pullrequest_count: @pullrequest_count})
+ else
+ $redis_cache.hincrby(user_statistic_key, pullrequest_count_key, @pullrequest_count)
+ Cache::V2::UserDateRankService.call(@user_id, Date.today, {pullrequest_count: @pullrequest_count})
+ end
+ end
+ $redis_cache.hgetall(user_statistic_key)
+ end
+
+ def reset_user_follow_count
+ $redis_cache.hset(user_statistic_key, follow_count_key, Watcher.where(watchable_type: 'User', watchable_id: @user_id).count)
+ end
+
+ def reset_user_fork_count
+ $redis_cache.hset(user_statistic_key, fork_count_key, ForkUser.joins(:project).where(projects: {user_id: @user_id}).count)
+ end
+
+ def reset_user_issue_count
+ $redis_cache.hset(user_statistic_key, issue_count_key, Issue.where(author_id: @user_id).count)
+ end
+
+ def reset_user_project_count
+ $redis_cache.hset(user_statistic_key, project_count_key, Project.where(user_id: @user_id).count)
+ end
+
+ def reset_user_project_language
+ $redis_cache.hset(user_statistic_key, project_language_key, Project.where(user_id: @user_id).joins(:project_language).group("project_languages.name").count.to_json)
+ end
+
+ def reset_user_project_praise_count
+ $redis_cache.hset(user_statistic_key, project_praise_count_key, PraiseTread.where(praise_tread_object_type: 'Project', praise_tread_object_id: Project.where(user_id: @user_id)).count)
+ end
+
+ def reset_user_project_watcher_count
+ $redis_cache.hset(user_statistic_key, project_watcher_count_key, Watcher.where(watchable_type: 'Project', watchable_id: Project.where(user_id: @user_id)).count)
+ end
+
+ def reset_user_pullrequest_count
+ $redis_cache.hset(user_statistic_key, pullrequest_count_key, PullRequest.where(user_id: @user_id).count)
+ end
+
+ def reset_user_statistic
+ $redis_cache.del(user_statistic_key)
+ reset_user_follow_count
+ reset_user_fork_count
+ reset_user_issue_count
+ reset_user_project_count
+ reset_user_project_language
+ reset_user_project_praise_count
+ reset_user_project_watcher_count
+ reset_user_pullrequest_count
+
+ $redis_cache.hgetall(user_statistic_key)
+ end
+end
\ No newline at end of file
diff --git a/app/services/courses_service.rb b/app/services/courses_service.rb
deleted file mode 100644
index 8229c7c32..000000000
--- a/app/services/courses_service.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-class CoursesService
-end
\ No newline at end of file
diff --git a/app/services/create_add_department_apply_service.rb b/app/services/create_add_department_apply_service.rb
deleted file mode 100644
index e172b001a..000000000
--- a/app/services/create_add_department_apply_service.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-class CreateAddDepartmentApplyService < ApplicationService
- Error = Class.new(StandardError)
-
- attr_reader :user, :params
-
- def initialize(user, params)
- @user = user
- @params = params
- end
-
- def call
- name = params[:name].to_s.strip
- raise Error, '名称不能为空' if name.blank?
-
- school = School.find_by(id: params[:school_id])
- raise Error, '学校/单位不存在' if school.blank?
- raise Error, '部门已存在' if school.departments.exists?(name: name)
-
- department = Department.new
- department.name = name
- department.school = school
-
- ActiveRecord::Base.transaction do
- department.save!
-
- attrs = {
- user_id: user.id, department: department, school: school,
- name: department.name, remarks: params[:remarks], status: 0,
- }
- apply = ApplyAddDepartment.create!(attrs)
-
- unless user.professional_certification?
- user.user_extension.update!(department_id: department.id)
- end
-
- # 向管理员发送通知
- message = AppliedMessage.new(user_id: 1, status: 0, applied_user_id: user.id, viewed: 0,
- applied_id: apply.id, applied_type: 'ApplyAddDepartment', name: department.name)
- message.save(validate: false)
- end
-
- department
- end
-end
diff --git a/app/services/create_add_school_apply_service.rb b/app/services/create_add_school_apply_service.rb
deleted file mode 100644
index 96619c681..000000000
--- a/app/services/create_add_school_apply_service.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-class CreateAddSchoolApplyService < ApplicationService
- Error = Class.new(StandardError)
-
- attr_reader :user, :params
-
- def initialize(user, params)
- @user = user
- @params = params
- end
-
- def call
- AddSchoolApplyForm.new(params).validate!
-
- name = params[:name].to_s.strip
- raise Error, '学校/单位已经存在' if name.present? && School.exists?(name: name)
-
- school = School.new
- school.name = name
- school.province = params[:province].to_s.strip
- school.city = params[:city].to_s.strip
- school.address = params[:address].to_s.strip
-
- ActiveRecord::Base.transaction do
- school.save!
-
- school_attrs = school.as_json(only: %i[name province city address])
- ApplyAddSchool.create!(school_attrs.merge(school: school, user_id: user.id, remarks: params[:remarks]))
-
- # 向管理员发送通知
- message = AppliedMessage.new(user_id: 1, status: 0, applied_user_id: user.id, viewed: 0,
- applied_id: school.id, applied_type: 'ApplyAddSchools', name: school.name)
- message.save(validate: false)
- end
-
- school
- end
-end
diff --git a/app/services/duplicate_course_service.rb b/app/services/duplicate_course_service.rb
deleted file mode 100644
index fa57a8901..000000000
--- a/app/services/duplicate_course_service.rb
+++ /dev/null
@@ -1,164 +0,0 @@
-class DuplicateCourseService < ApplicationService
- attr_reader :origin_course, :user, :course
-
- def initialize(origin_course, user)
- @user = user
- @origin_course = origin_course
- end
-
- def call
- ActiveRecord::Base.transaction do
- @course = copy_course!
-
- copy_course_modules!
-
- join_course!
-
- copy_homework_commons!
-
- copy_exercises!
-
- copy_polls!
-
- copy_attachments!
-
- course
- end
- end
-
- private
-
- def copy_course!
- create_attrs = origin_course.as_json(only: %i[name class_period credit course_list_id])
- create_attrs.merge!(tea_id: user.id, school_id: user.school_id, is_public: 0, is_copy: 1)
-
- Course.create!(create_attrs)
- end
-
- def copy_course_modules!
- @second_category_list = {}
- origin_course.course_modules.each do |course_module|
- attrs = course_module.as_json(only: %i[module_type position hidden module_name])
- new_course_module = CourseModule.create!(attrs.merge(course_id: course.id))
- # 复制子目录
- course_module.course_second_categories.each do |second_category|
- category_attr = second_category.as_json(only: %i[category_type name position])
- new_second_category =
- CourseSecondCategory.create!(category_attr.merge(course_id: course.id, course_module_id: new_course_module.id))
- @second_category_list[second_category.id] = new_second_category.id
- end
- end
- end
-
- def join_course!
- CourseMember.create!(course_id: course.id, user_id: user.id, role: 1)
- end
-
- def copy_homework_commons!
- origin_course.homework_commons.where(homework_type: %i[normal group practice]).find_each do |origin_homework|
- homework_attrs = origin_homework.as_json(only: %i[name description homework_type homework_bank_id reference_answer])
-
- course_second_category_id = @second_category_list[origin_homework.course_second_category_id]
-
- homework = HomeworkCommon.create!(homework_attrs.merge(user_id: user.id, course_id: course.id,
- course_second_category_id:course_second_category_id))
-
- origin_homework.attachments.find_each do |origin_attachment|
- attachment = origin_attachment.copy
- attrs = { container: homework, author_id: origin_homework.user_id, copy_from: origin_attachment.id }
- attachment.assign_attributes(attrs)
- attachment.save!
-
- origin_attachment.increment!(:quotes)
- end
-
- homework.create_homework_detail_manual!
-
- if homework.group_homework_type?
- attrs = origin_homework.homework_detail_group.as_json(only: %i[min_num max_num base_on_project])
- homework.create_homework_detail_group!(attrs)
- elsif homework.practice_homework_type?
- HomeworkCommonsShixun.create!(homework_common_id: homework.id, shixun_id: origin_homework.homework_commons_shixun.shixun_id)
- HomeworksService.new.create_shixun_homework_cha_setting(homework, origin_homework.shixuns.first)
- end
-
-
- origin_homework.increment!(:quotes)
- origin_homework.homework_bank.increment!(:quotes) if origin_homework.homework_bank
- end
- end
-
- def copy_exercises!
- origin_course.exercises.find_each do |origin_exercise|
- attrs = origin_exercise.as_json(only: %i[exercise_name exercise_description exercise_bank_id])
- exercise = course.exercises.create!(attrs.merge(user_id: user.id))
-
- origin_exercise.exercise_questions.find_each do |origin_question|
- question_attrs = origin_question.as_json(only: %i[question_title question_type question_number question_score shixun_name shixun_id is_ordered level])
- # question_attrs[:question_type] ||= 1
- question = exercise.exercise_questions.create!(question_attrs)
-
- exercise_choice_map = {}
- origin_question.exercise_choices.each_with_index do |origin_choice, index|
- choice_attrs = { choice_position: index + 1, choice_text: origin_choice.choice_text }
- choice = question.exercise_choices.create!(choice_attrs)
-
- # exercise_choice_map[origin_choice.id] = choice.id 标准答案中存的是choice_position, 直接取原题的exercise_choice_id就行
- end
-
- origin_question.exercise_standard_answers.find_each do |origin_answer|
- question.exercise_standard_answers.create!(
- exercise_choice_id: origin_answer.exercise_choice_id,
- answer_text: origin_answer.answer_text
- )
- end
-
- origin_question.exercise_shixun_challenges.each_with_index do |sc, index|
- question.exercise_shixun_challenges.create!({position: index+1, challenge_id: sc.challenge_id,
- shixun_id: sc.shixun_id, question_score: sc.question_score})
- end
- end
-
- origin_exercise.exercise_bank.increment!(:quotes) if exercise.exercise_bank
- end
- end
-
- def copy_polls!
- origin_course.polls.includes(poll_questions: :poll_answers).find_each do |origin_poll|
- poll_attrs = origin_poll.as_json(only: %i[polls_name polls_description exercise_bank_id])
- poll = course.polls.create!(poll_attrs.merge(user_id: user.id))
-
- origin_poll.poll_questions.each do |origin_question|
- attr_names = %i[question_title question_type is_necessary question_number max_choices min_choices]
- question_attrs = origin_question.as_json(only: attr_names)
- question_attrs[:question_type] ||= 1
-
- question = poll.poll_questions.create!(question_attrs)
-
- origin_question.poll_answers.each_with_index do |origin_answer, index|
- question.poll_answers.create!(answer_position: index + 1, answer_text: origin_answer.answer_text)
- end
- end
-
- origin_poll.exercise_bank.increment!(:quotes) if origin_poll.exercise_bank
- end
- end
-
- def copy_attachments!
- origin_course.attachments.each do |origin_attachment|
- attachment = origin_attachment.copy
- # attachment.tag_list.add(origin_attachment.tag_list) # tag关联
- attachment.container = course
- attachment.created_on = Time.now
- attachment.publish_time = nil
- attachment.author_id = User.current.id
- attachment.copy_from = origin_attachment.copy_from || origin_attachment.id
- attachment.is_publish = 0
- attachment.attachtype ||= 4
- attachment.course_second_category_id = @second_category_list[origin_attachment.course_second_category_id]
-
- attachment.save!
- origin_course.update_quotes(attachment)
- end
- end
-end
\ No newline at end of file
diff --git a/app/services/gitea/accelerator/base_service.rb b/app/services/gitea/accelerator/base_service.rb
new file mode 100644
index 000000000..e63d77aab
--- /dev/null
+++ b/app/services/gitea/accelerator/base_service.rb
@@ -0,0 +1,96 @@
+class Gitea::Accelerator::BaseService < ApplicationService
+
+ def post(url, params)
+ puts "[gitea] request params: #{params}"
+ puts "[gitea] access_username: #{access_username}"
+ puts "[gitea] access_password: #{access_password}"
+ conn.post do |req|
+ req.url full_url(url)
+ req.body = params.to_json
+ end
+ end
+
+ private
+ def conn
+ @client ||= begin
+ Faraday.new(url: domain) do |req|
+ req.request :url_encoded
+ req.headers['Content-Type'] = 'application/json'
+ req.response :logger # 显示日志
+ req.adapter Faraday.default_adapter
+ req.basic_auth(access_username, access_password)
+ end
+ end
+ @client
+ end
+
+ def base_url
+ accelerator["base_url"]
+ end
+
+ def domain
+ accelerator["domain"]
+ end
+
+ def api_url
+ [domain, base_url].join('')
+ end
+
+ def full_url(api_rest, action='post')
+ url = [api_url, api_rest].join('').freeze
+ url = action === 'get' ? url : URI.escape(url)
+ puts "[gitea] request url: #{url}"
+ url
+ end
+
+ def access_username
+ accelerator["access_key_id"]
+ end
+
+ def access_password
+ accelerator["access_key_secret"]
+ end
+
+ def access_uid
+ accelerator["access_admin_uid"]
+ end
+
+ def accelerator
+ Gitea.gitea_config[:accelerator]
+ end
+
+ def render_status(response)
+ puts "[gitea] response status: #{response.status}"
+ puts "[gitea] response body: #{response.body}"
+ case response.status
+ when 201
+ success
+ when 403
+ error('APIForbiddenError')
+ when 422
+ error('APIValidationError')
+ else
+ error("MigrateError")
+ end
+ end
+
+ def error(message)
+ {
+ status: :error,
+ message: message,
+ data: nil
+ }
+ end
+
+ def success(data=nil)
+ {
+ status: :success,
+ message: nil,
+ data: data
+ }
+ end
+
+ def check_accelerator!
+ accelerator.blank? || access_username.blank? || access_password.blank? || domain.blank?
+ end
+end
\ No newline at end of file
diff --git a/app/services/gitea/accelerator/migrate_service.rb b/app/services/gitea/accelerator/migrate_service.rb
new file mode 100644
index 000000000..86b8b3db1
--- /dev/null
+++ b/app/services/gitea/accelerator/migrate_service.rb
@@ -0,0 +1,55 @@
+class Gitea::Accelerator::MigrateService < Gitea::Accelerator::BaseService
+ attr_reader :params
+
+ # params description:
+ # {
+ # auth_username string
+ # clone_addr* string #clone地址
+ # description string
+ # issues boolean
+ # labels boolean
+ # milestones boolean
+ # mirror boolean
+ # private boolean
+ # pull_requests boolean
+ # releases boolean
+ # repo_name* string #仓库名称
+ # uid* integer($int64) #gitea用户id或组织id
+ # wiki boolean
+ # }
+ # EX:
+ # params = {
+ # clone_addr: 'xxx.com',
+ # repo_name: 'repo_name',
+ # uid: 2,
+ # private: false
+ # }
+
+ def initialize(params)
+ @params = params
+ end
+
+ def call
+ return error('[gitea:] accelerator config missing') if check_accelerator!
+ response = post(url, request_params)
+
+ render_status(response)
+ end
+
+
+ private
+ def request_params
+ {
+ uid: access_uid,
+ clone_addr: params[:clone_addr],
+ repo_name: params[:repository_name],
+ auth_username: params[:auth_username],
+ auth_password: Base64.decode64(params[:auth_password]),
+ mirror: ActiveModel::Type::Boolean.new.cast(params[:is_mirror])
+ }
+ end
+
+ def url
+ "/repos/migrate".freeze
+ end
+end
\ No newline at end of file
diff --git a/app/services/gitea/accelerator/sync_mirrored_service.rb b/app/services/gitea/accelerator/sync_mirrored_service.rb
new file mode 100644
index 000000000..0e0a7ca94
--- /dev/null
+++ b/app/services/gitea/accelerator/sync_mirrored_service.rb
@@ -0,0 +1,31 @@
+# Sync a mirrored repository
+class Gitea::Accelerator::SyncMirroredService < Gitea::Accelerator::BaseService
+ attr_reader :repo, :token
+
+ # repo *
+ # name of the repo to sync
+ # example:
+ # Gitea::Accelerator::SyncMirroredService.call(repo.identifier)
+ def initialize(repo, token=nil)
+ @repo = repo
+ @token = token
+ end
+
+ def call
+ return error('[gitea:] accelerator config missing') if check_accelerator!
+
+ response = post(url, request_params)
+
+ {status: response.status}
+ end
+
+ private
+
+ def request_params
+ Hash.new.merge(token: token).compact
+ end
+
+ def url
+ "/repos/#{access_username}/#{repo}/mirror-sync".freeze
+ end
+end
diff --git a/app/services/gitea/client_service.rb b/app/services/gitea/client_service.rb
index b631ba304..7cdfdd495 100644
--- a/app/services/gitea/client_service.rb
+++ b/app/services/gitea/client_service.rb
@@ -82,6 +82,8 @@ class Gitea::ClientService < ApplicationService
req.headers['Content-Type'] = 'application/json'
req.response :logger # 显示日志
req.adapter Faraday.default_adapter
+ req.options.timeout = 100 # open/read timeout in seconds
+ req.options.open_timeout = 10 # connection open timeout in seconds
if token.blank?
req.basic_auth(username, secret)
else
@@ -108,6 +110,7 @@ class Gitea::ClientService < ApplicationService
def full_url(api_rest, action='post')
url = [api_url, api_rest].join('').freeze
url = action === 'get' ? url : URI.escape(url)
+ url = URI.escape(url) unless url.ascii_only?
puts "[gitea] request url: #{url}"
return url
end
@@ -141,6 +144,8 @@ class Gitea::ClientService < ApplicationService
{status: 403, message: '你没有权限操作!'}
when 404
{status: 404, message: '你访问的链接不存在!'}
+ when 500
+ {status: 500, message: ''}
else
if response&.body.blank?
message = "请求失败"
@@ -176,6 +181,25 @@ class Gitea::ClientService < ApplicationService
[status, message, body]
end
+ def render_gitea_response(response)
+ status = response.status
+ body = response&.body
+
+ log_error(status, body)
+ message = nil
+ begin
+ translate = YAML.load(File.read('config/gitea_response.yml'))
+
+ self.class.to_s.underscore.split("/").map{|i| translate=translate[i]}
+ message = body.nil? ? translate[status]['default'] : JSON.parse(body)['message']
+ message = translate[status][message].nil? ? message : translate[status][message]
+
+ return [status, message]
+ rescue
+ return [status, message]
+ end
+ end
+
def get_body_by_status(status, body)
body, message =
case status
diff --git a/app/services/gitea/organization/repository/create_service.rb b/app/services/gitea/organization/repository/create_service.rb
index 060a8ab05..2990f711e 100644
--- a/app/services/gitea/organization/repository/create_service.rb
+++ b/app/services/gitea/organization/repository/create_service.rb
@@ -15,8 +15,8 @@ class Gitea::Organization::Repository::CreateService < Gitea::ClientService
private
def request_params
- create_params = params.merge(readme: "readme")
- Hash.new.merge(token: token, data: create_params)
+ # create_params = params.merge(readme: "readme")
+ Hash.new.merge(token: token, data: params)
end
def url
diff --git a/app/services/gitea/organization/update_service.rb b/app/services/gitea/organization/update_service.rb
index c04dcbc86..963099ad9 100644
--- a/app/services/gitea/organization/update_service.rb
+++ b/app/services/gitea/organization/update_service.rb
@@ -16,6 +16,7 @@ class Gitea::Organization::UpdateService < Gitea::ClientService
def request_params
update_params = {
name: org.login,
+ full_name: org.nickname,
description: org.description,
location: org.location,
repo_admin_change_team_access: org.repo_admin_change_team_access,
diff --git a/app/services/gitea/pull_request/close_service.rb b/app/services/gitea/pull_request/close_service.rb
index aed5251b7..3ca062e64 100644
--- a/app/services/gitea/pull_request/close_service.rb
+++ b/app/services/gitea/pull_request/close_service.rb
@@ -8,7 +8,7 @@ class Gitea::PullRequest::CloseService < Gitea::PullRequest::UpdateService
# number: number of pull request
# token: token of gitea user
# eq:
- # Gitea::PullRequest::CloseService.call(owner.login, repo.identifier, pull.gpid, pull.base, current_user.gitea_token)
+ # Gitea::PullRequest::CloseService.call(owner.login, repo.identifier, pull.gitea_number, pull.base, current_user.gitea_token)
def initialize(owner, repo, number, base,token=nil)
colse_pull_params = Hash.new.merge(base: base, state: 'closed').compact
diff --git a/app/services/gitea/pull_request/files_service.rb b/app/services/gitea/pull_request/files_service.rb
index 9785588e2..a8cb26627 100644
--- a/app/services/gitea/pull_request/files_service.rb
+++ b/app/services/gitea/pull_request/files_service.rb
@@ -24,7 +24,7 @@ class Gitea::PullRequest::FilesService < Gitea::ClientService
def params
Hash.new.merge(token: token)
end
-
+
def url
"/repos/#{owner}/#{repo}/pulls/#{pull_number}/files".freeze
end
diff --git a/app/services/gitea/pull_request/get_service.rb b/app/services/gitea/pull_request/get_service.rb
index 6a7ec30f4..f9a35fdca 100644
--- a/app/services/gitea/pull_request/get_service.rb
+++ b/app/services/gitea/pull_request/get_service.rb
@@ -1,15 +1,14 @@
# Get a pull request
class Gitea::PullRequest::GetService < Gitea::ClientService
- attr_reader :user, :repo, :pull_request_id
+ attr_reader :owner, :repo, :number, :token
- # user: 用户
- # repo: 仓库名称/标识
- # pull_request_id: pull request主键id
- def initialize(user, repo, pull_request_id)
- super({token: user.gitea_token})
- @user = user
+ #eq:
+ # Gitea::PullRequest::GetService.call(user.login, repository.identifier, pull.gitea_number, user.gitea_token)
+ def initialize(owner, repo, number, token=nil)
+ @owner = owner
@repo = repo
- @pull_request_id = pull_request_id
+ @number = number
+ @token = token
end
def call
@@ -19,11 +18,11 @@ class Gitea::PullRequest::GetService < Gitea::ClientService
private
def params
- Hash.new.merge(token: user.gitea_token)
+ Hash.new.merge(token: token)
end
def url
- "/repos/#{user.login}/#{repo}/pulls/#{pull_request_id}".freeze
+ "/repos/#{owner}/#{repo}/pulls/#{number}".freeze
end
def render_result(response)
@@ -31,7 +30,7 @@ class Gitea::PullRequest::GetService < Gitea::ClientService
when 200
JSON.parse(response.body)
else
- nil
+ {}
end
end
end
diff --git a/app/services/gitea/pull_request/merge_service.rb b/app/services/gitea/pull_request/merge_service.rb
index c33c8781d..bdf2b23be 100644
--- a/app/services/gitea/pull_request/merge_service.rb
+++ b/app/services/gitea/pull_request/merge_service.rb
@@ -20,7 +20,7 @@ class Gitea::PullRequest::MergeService < Gitea::ClientService
def call
response = post(url, request_params)
- render_200_no_body(response)
+ render_gitea_response(response)
end
private
diff --git a/app/services/gitea/pull_request/open_service.rb b/app/services/gitea/pull_request/open_service.rb
index affdfe112..4d2137f11 100644
--- a/app/services/gitea/pull_request/open_service.rb
+++ b/app/services/gitea/pull_request/open_service.rb
@@ -8,7 +8,7 @@ class Gitea::PullRequest::OpenService < Gitea::PullRequest::UpdateService
# number: number of pull request
# token: token of gitea user
# eq:
- # Gitea::PullRequest::OpenService.new(owner.login, repo.identifier, pr.gpid, pr.base, current_user.gitea_token)
+ # Gitea::PullRequest::OpenService.new(owner.login, repo.identifier, pr.gitea_number, pr.base, current_user.gitea_token)
def initialize(owner, repo, number, base, token=nil)
open_pull_params = Hash.new.merge(base: base, state: 'open').compact
diff --git a/app/services/gitea/repository/branches/list_name_service.rb b/app/services/gitea/repository/branches/list_name_service.rb
new file mode 100644
index 000000000..716ee464c
--- /dev/null
+++ b/app/services/gitea/repository/branches/list_name_service.rb
@@ -0,0 +1,22 @@
+class Gitea::Repository::Branches::ListNameService < Gitea::ClientService
+ attr_reader :user, :repo
+
+ def initialize(user, repo)
+ @user = user
+ @repo = repo
+ end
+
+ def call
+ response = get(url, params)
+ render_200_response(response)
+ end
+
+ private
+ def params
+ Hash.new.merge(token: user.gitea_token)
+ end
+
+ def url
+ "/repos/#{user.login}/#{repo}/branch_name_set".freeze
+ end
+end
diff --git a/app/services/gitea/repository/branches/list_slice_service.rb b/app/services/gitea/repository/branches/list_slice_service.rb
new file mode 100644
index 000000000..6b643831a
--- /dev/null
+++ b/app/services/gitea/repository/branches/list_slice_service.rb
@@ -0,0 +1,22 @@
+class Gitea::Repository::Branches::ListSliceService < Gitea::ClientService
+ attr_reader :user, :repo
+
+ def initialize(user, repo)
+ @user = user
+ @repo = repo
+ end
+
+ def call
+ response = get(url, params)
+ render_200_response(response)
+ end
+
+ private
+ def params
+ Hash.new.merge(token: user.gitea_token)
+ end
+
+ def url
+ "/repos/#{user.login}/#{repo}/branches/branches_slice".freeze
+ end
+end
diff --git a/app/services/gitea/repository/commits/file_list_service.rb b/app/services/gitea/repository/commits/file_list_service.rb
new file mode 100644
index 000000000..b1606a0f3
--- /dev/null
+++ b/app/services/gitea/repository/commits/file_list_service.rb
@@ -0,0 +1,43 @@
+# Get a list of all commits from a repository
+class Gitea::Repository::Commits::FileListService < Gitea::ClientService
+ attr_reader :owner, :repo_name, :filepath, :args
+
+ # sha: SHA or branch to start listing commits from (usually 'master')
+ # ex:
+ # Gitea::Repository::Commits::ListService.new(@project.owner.login, @project.identifier,
+ # sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token).call
+ def initialize(owner, repo_name, filepath, **args)
+ @owner = owner
+ @repo_name = repo_name
+ @filepath = filepath
+ @args = args
+ end
+
+ def call
+ response = get(url, params)
+ render_result(response)
+ end
+
+ private
+ def params
+ {sha: args[:sha] || 'master', page: args[:page] || PAGINATE_DEFAULT_PAGE, limit: args[:limit] || PAGINATE_DEFAULT_LIMIT, token: args[:token] || "" }
+ end
+
+ def url
+ "/repos/#{owner}/#{repo_name}/file_commits/#{filepath}".freeze
+ end
+
+ def render_result(response)
+ case response.status
+ when 200
+ result = {}
+ headers = response.headers.to_hash
+ body = JSON.parse(response.body)
+ total_count = headers["x-total"]
+ result.merge(total_count: total_count.to_i, body: body)
+ else
+ nil
+ # {status: -1, message: "#{body['message']}"}
+ end
+ end
+end
diff --git a/app/services/gitea/repository/commits/list_slice_service.rb b/app/services/gitea/repository/commits/list_slice_service.rb
new file mode 100644
index 000000000..04f45f55b
--- /dev/null
+++ b/app/services/gitea/repository/commits/list_slice_service.rb
@@ -0,0 +1,42 @@
+# Get a list of all commits from a repository
+class Gitea::Repository::Commits::ListSliceService < Gitea::ClientService
+ attr_reader :owner, :repo_name, :args
+
+ # sha: SHA or branch to start listing commits from (usually 'master')
+ # ex:
+ # Gitea::Repository::Commits::ListService.new(@project.owner.login, @project.identifier,
+ # sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token).call
+ def initialize(owner, repo_name, **args)
+ @owner = owner
+ @repo_name = repo_name
+ @args = args
+ end
+
+ def call
+ response = get(url, params)
+ render_result(response)
+ end
+
+ private
+ def params
+ { sha: args[:sha] || 'master', page: args[:page] || PAGINATE_DEFAULT_PAGE, limit: args[:limit] || PAGINATE_DEFAULT_LIMIT, token: args[:token] || "" }
+ end
+
+ def url
+ "/repos/#{owner}/#{repo_name}/commits_slice".freeze
+ end
+
+ def render_result(response)
+ case response.status
+ when 200
+ result = {}
+ headers = response.headers.to_hash
+ body = JSON.parse(response.body)
+ total_count = headers["x-total"]
+ result.merge(total_count: total_count.to_i, body: body)
+ else
+ nil
+ # {status: -1, message: "#{body['message']}"}
+ end
+ end
+end
diff --git a/app/services/gitea/repository/contributors/get_service.rb b/app/services/gitea/repository/contributors/get_service.rb
new file mode 100644
index 000000000..1ee1c3955
--- /dev/null
+++ b/app/services/gitea/repository/contributors/get_service.rb
@@ -0,0 +1,22 @@
+class Gitea::Repository::Contributors::GetService < Gitea::ClientService
+ attr_reader :owner, :repo_name
+
+ def initialize(owner, repo_name)
+ @owner = owner
+ @repo_name = repo_name
+ end
+
+ def call
+ response = get(url, params)
+ render_status(response)
+ end
+
+ private
+ def params
+ Hash.new.merge(token: owner.gitea_token)
+ end
+
+ def url
+ "/repos/#{owner.login}/#{repo_name}/contributors"
+ end
+end
\ No newline at end of file
diff --git a/app/services/gitea/repository/create_service.rb b/app/services/gitea/repository/create_service.rb
index 168aaab24..98a283034 100644
--- a/app/services/gitea/repository/create_service.rb
+++ b/app/services/gitea/repository/create_service.rb
@@ -25,8 +25,8 @@ class Gitea::Repository::CreateService < Gitea::ClientService
private
def request_params
- create_params = params.merge(readme: "readme")
- Hash.new.merge(token: token, data: create_params)
+ # create_params = params.merge(readme: "readme")
+ Hash.new.merge(token: token, data: params)
end
def url
diff --git a/app/services/gitea/repository/entries/create_service.rb b/app/services/gitea/repository/entries/create_service.rb
index 5e1a4c4b3..1bdc7246d 100644
--- a/app/services/gitea/repository/entries/create_service.rb
+++ b/app/services/gitea/repository/entries/create_service.rb
@@ -55,7 +55,12 @@ class Gitea::Repository::Entries::CreateService < Gitea::ClientService
when 201 then success(json_parse!(body))
when 403 then error("你没有权限操作!")
when 404 then error("你操作的链接不存在!")
- when 422 then error("#{filepath}文件已存在,不能重复创建!")
+ when 422
+ if @body[:new_branch].include?('/') || @body[:new_branch].include?('\'') || @body[:new_branch].include?('^') || @body[:new_branch].include?('*')
+ error("不合法的分支名称!")
+ else
+ error("#{filepath}文件已存在,不能重复创建!")
+ end
else error("系统错误!")
end
end
diff --git a/app/services/gitea/repository/files/get_service.rb b/app/services/gitea/repository/files/get_service.rb
new file mode 100644
index 000000000..e5b96e7ba
--- /dev/null
+++ b/app/services/gitea/repository/files/get_service.rb
@@ -0,0 +1,25 @@
+class Gitea::Repository::Files::GetService < Gitea::ClientService
+ attr_reader :owner, :repo_name, :ref, :q, :token
+
+ def initialize(owner, repo_name, ref, q=nil, token=nil)
+ @owner = owner
+ @repo_name = repo_name
+ @ref = ref
+ @q = q
+ @token = token
+ end
+
+ def call
+ response = get(url, params)
+ render_status(response)
+ end
+
+ private
+ def params
+ Hash.new.merge(token: token, ref: ref, q: q)
+ end
+
+ def url
+ "/repos/#{owner.login}/#{repo_name}/find"
+ end
+end
\ No newline at end of file
diff --git a/app/services/gitea/repository/get_branch_and_tag_total_num_service.rb b/app/services/gitea/repository/get_branch_and_tag_total_num_service.rb
new file mode 100644
index 000000000..0b8a52467
--- /dev/null
+++ b/app/services/gitea/repository/get_branch_and_tag_total_num_service.rb
@@ -0,0 +1,37 @@
+
+module Gitea
+ module Repository
+ class GetBranchAndTagTotalNumService < Gitea::ClientService
+ attr_reader :owner, :repo, :token
+
+ def initialize(owner, repo, token=nil)
+ @owner = owner
+ @repo = repo
+ @token = token
+ end
+
+ def call
+ response = get(url, params)
+ render_result(response)
+ end
+
+ private
+ def params
+ Hash.new.merge(token: token)
+ end
+
+ def url
+ "/repos/#{owner}/#{repo}/branch_tag_count".freeze
+ end
+
+ def render_result(response)
+ case response.status
+ when 200
+ JSON.parse(response.body)
+ else
+ {}
+ end
+ end
+ end
+ end
+end
diff --git a/app/services/gitea/repository/migrate_service.rb b/app/services/gitea/repository/migrate_service.rb
index 508812087..ab60b42f3 100644
--- a/app/services/gitea/repository/migrate_service.rb
+++ b/app/services/gitea/repository/migrate_service.rb
@@ -33,7 +33,7 @@ class Gitea::Repository::MigrateService < Gitea::ClientService
def call
response = post(url, request_params)
- render_201_response(response)
+ render_response(response)
end
private
diff --git a/app/services/gitea/repository/readme/dir_service.rb b/app/services/gitea/repository/readme/dir_service.rb
new file mode 100644
index 000000000..587fb5d55
--- /dev/null
+++ b/app/services/gitea/repository/readme/dir_service.rb
@@ -0,0 +1,34 @@
+class Gitea::Repository::Readme::DirService < Gitea::ClientService
+ attr_reader :owner, :repo, :ref, :dir, :token
+
+ def initialize(owner, repo, dir, ref='', token=nil)
+ @owner = owner
+ @repo = repo
+ @dir = dir
+ @ref = ref
+ @token = token
+ end
+
+ def call
+ response = get(url, params)
+ status, message, body = render_response(response)
+ json_format(status, message, body)
+ end
+
+ private
+ def params
+ Hash.new.merge(token: token, ref: ref)
+ end
+
+ def url
+ "/repos/#{owner}/#{repo}/readme/#{dir}".freeze
+ end
+
+ def json_format(status, message, body)
+ case status
+ when 200 then success(body)
+ when 404 then error(message, 404)
+ else error(message, status)
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/services/gitea/repository/update_service.rb b/app/services/gitea/repository/update_service.rb
index 0d27922b8..6c4eff1b3 100644
--- a/app/services/gitea/repository/update_service.rb
+++ b/app/services/gitea/repository/update_service.rb
@@ -19,7 +19,8 @@ class Gitea::Repository::UpdateService < Gitea::ClientService
end
def call
- patch(url, data_params)
+ response = patch(url, data_params)
+ render_200_response(response)
end
private
diff --git a/app/services/gitea/repository/webhooks/create_service.rb b/app/services/gitea/repository/webhooks/create_service.rb
new file mode 100644
index 000000000..33c9a9b0c
--- /dev/null
+++ b/app/services/gitea/repository/webhooks/create_service.rb
@@ -0,0 +1,23 @@
+class Gitea::Repository::Webhooks::CreateService < Gitea::ClientService
+ attr_reader :token, :owner, :repo, :params
+ def initialize(token, owner, repo, params)
+ @token = token
+ @owner = owner
+ @repo = repo
+ @params = params
+ end
+
+ def call
+ response = post(url, request_params)
+ render_response(response)
+ end
+
+ private
+ def request_params
+ Hash.new.merge({token: token, data: params})
+ end
+
+ def url
+ "/repos/#{owner}/#{repo}/hooks".freeze
+ end
+end
\ No newline at end of file
diff --git a/app/services/gitea/repository/webhooks/delete_service.rb b/app/services/gitea/repository/webhooks/delete_service.rb
new file mode 100644
index 000000000..997e9006e
--- /dev/null
+++ b/app/services/gitea/repository/webhooks/delete_service.rb
@@ -0,0 +1,24 @@
+class Gitea::Repository::Webhooks::DeleteService < Gitea::ClientService
+ attr_reader :token, :owner, :repo, :id
+
+ def initialize(token, owner, repo, id)
+ @token = token
+ @owner = owner
+ @repo = repo
+ @id = id
+ end
+
+ def call
+ response = delete(url, params)
+ render_response(response)
+ end
+
+ private
+ def params
+ Hash.new.merge(token: token)
+ end
+
+ def url
+ "/repos/#{owner}/#{repo}/hooks/#{id}".freeze
+ end
+end
\ No newline at end of file
diff --git a/app/services/gitea/repository/webhooks/tasks_service.rb b/app/services/gitea/repository/webhooks/tasks_service.rb
new file mode 100644
index 000000000..e4c62edb4
--- /dev/null
+++ b/app/services/gitea/repository/webhooks/tasks_service.rb
@@ -0,0 +1,27 @@
+class Gitea::Repository::Webhooks::TasksService < Gitea::ClientService
+ attr_reader :token, :owner, :repo, :webhook_id
+
+ # ref: The name of the commit/branch/tag. Default the repository’s default branch (usually master)
+ # repo_name: the name of repository
+ def initialize(token, owner, repo, webhook_id)
+ @token = token
+ @owner = owner
+ @repo = repo
+ @webhook_id = webhook_id
+ end
+
+ def call
+ response = get(url, params)
+ render_response(response)
+ end
+
+ private
+ def params
+ Hash.new.merge(token: user.gitea_token)
+ end
+
+ def url
+ "/repos/#{owner}/#{repo}/hooks/#{webhook_id}/hook_tasks".freeze
+ end
+
+end
diff --git a/app/services/gitea/repository/webhooks/test_service.rb b/app/services/gitea/repository/webhooks/test_service.rb
new file mode 100644
index 000000000..7f1837611
--- /dev/null
+++ b/app/services/gitea/repository/webhooks/test_service.rb
@@ -0,0 +1,24 @@
+class Gitea::Repository::Webhooks::TestService < Gitea::ClientService
+ attr_reader :token, :owner, :repo, :webhook_id
+
+ def initialize(token, owner, repo, webhook_id)
+ @token = token
+ @owner = owner
+ @repo = repo
+ @webhook_id = webhook_id
+ end
+
+ def call
+ response = post(url, request_params)
+ render_response(response)
+ end
+
+ private
+ def request_params
+ Hash.new.merge({token: token})
+ end
+
+ def url
+ "/repos/#{owner}/#{repo}/hooks/#{webhook_id}/tests".freeze
+ end
+end
\ No newline at end of file
diff --git a/app/services/gitea/repository/webhooks/update_service.rb b/app/services/gitea/repository/webhooks/update_service.rb
new file mode 100644
index 000000000..6094c6c51
--- /dev/null
+++ b/app/services/gitea/repository/webhooks/update_service.rb
@@ -0,0 +1,24 @@
+class Gitea::Repository::Webhooks::UpdateService < Gitea::ClientService
+ attr_reader :token, :owner, :repo, :id, :params
+ def initialize(token, owner, repo, id, params)
+ @token = token
+ @owner = owner
+ @repo = repo
+ @id = id
+ @params = params
+ end
+
+ def call
+ response = patch(url, data_params)
+ render_response(response)
+ end
+
+ private
+ def url
+ "/repos/#{owner}/#{repo}/hooks/#{id}"
+ end
+
+ def data_params
+ Hash.new.merge(token: token, data: params).compact
+ end
+end
\ No newline at end of file
diff --git a/app/services/gitea/user/delete_service.rb b/app/services/gitea/user/delete_service.rb
new file mode 100644
index 000000000..5df3cb6b2
--- /dev/null
+++ b/app/services/gitea/user/delete_service.rb
@@ -0,0 +1,31 @@
+class Gitea::User::DeleteService < Gitea::ClientService
+ attr_reader :username
+
+ def initialize(username)
+ @username = username
+ end
+
+ def call
+ response = delete(request_url, params)
+
+ render_status(response)
+ end
+
+ private
+ def token
+ {
+ username: Gitea.gitea_config[:access_key_id],
+ password: Gitea.gitea_config[:access_key_secret]
+ }
+ end
+
+ def request_url
+ "/admin/users/#{username}"
+ end
+
+ def params
+ Hash.new.merge(token: token)
+ end
+
+
+end
diff --git a/app/services/gitea/user/headmap_service.rb b/app/services/gitea/user/headmap_service.rb
new file mode 100644
index 000000000..611a8b9d0
--- /dev/null
+++ b/app/services/gitea/user/headmap_service.rb
@@ -0,0 +1,23 @@
+class Gitea::User::HeadmapService < Gitea::ClientService
+ attr_reader :start_time, :end_time, :username
+
+ def initialize(username, start_time, end_time)
+ @username = username
+ @start_time = start_time
+ @end_time = end_time
+ end
+
+ def call
+ response = get(url, params)
+ render_response(response)
+ end
+
+ private
+ def params
+ Hash.new.merge(start: start_time, end: end_time)
+ end
+
+ def url
+ "/users/#{username}/heatmap".freeze
+ end
+end
\ No newline at end of file
diff --git a/app/services/gitea/user/keys/create_service.rb b/app/services/gitea/user/keys/create_service.rb
new file mode 100644
index 000000000..2b720c483
--- /dev/null
+++ b/app/services/gitea/user/keys/create_service.rb
@@ -0,0 +1,21 @@
+class Gitea::User::Keys::CreateService < Gitea::ClientService
+ attr_reader :token, :params
+ def initialize(token, params)
+ @token = token
+ @params = params
+ end
+
+ def call
+ response = post(url, request_params)
+ render_response(response)
+ end
+
+ private
+ def request_params
+ Hash.new.merge({token: token, data: params})
+ end
+
+ def url
+ '/user/keys'.freeze
+ end
+end
\ No newline at end of file
diff --git a/app/services/gitea/user/keys/delete_service.rb b/app/services/gitea/user/keys/delete_service.rb
new file mode 100644
index 000000000..3ae263e3b
--- /dev/null
+++ b/app/services/gitea/user/keys/delete_service.rb
@@ -0,0 +1,23 @@
+class Gitea::User::Keys::DeleteService < Gitea::ClientService
+ attr_reader :token, :key_id
+
+ def initialize(token, key_id)
+ @token = token
+ @key_id = key_id
+ end
+
+ def call
+ response = delete(url, params)
+ render_response(response)
+ end
+
+ private
+
+ def params
+ Hash.new.merge(token: token)
+ end
+
+ def url
+ "/user/keys/#{key_id}".freeze
+ end
+end
diff --git a/app/services/gitea/user/keys/get_service.rb b/app/services/gitea/user/keys/get_service.rb
new file mode 100644
index 000000000..fbcf1c86e
--- /dev/null
+++ b/app/services/gitea/user/keys/get_service.rb
@@ -0,0 +1,22 @@
+class Gitea::User::Keys::GetService < Gitea::ClientService
+ attr_reader :token, :key_id
+
+ def initialize(token, key_id)
+ @token = token
+ @key_id = key_id
+ end
+
+ def call
+ response = get(url, params)
+ render_response(response)
+ end
+
+ private
+ def params
+ Hash.new.merge({token: token})
+ end
+
+ def url
+ "/user/keys/#{key_id}".freeze
+ end
+end
\ No newline at end of file
diff --git a/app/services/gitea/user/keys/list_service.rb b/app/services/gitea/user/keys/list_service.rb
new file mode 100644
index 000000000..1ddfde413
--- /dev/null
+++ b/app/services/gitea/user/keys/list_service.rb
@@ -0,0 +1,26 @@
+class Gitea::User::Keys::ListService < Gitea::ClientService
+ attr_reader :token, :page, :limit, :fingerprint
+
+ def initialize(token, page, limit, fingerprint="")
+ @token = token
+ @page = page
+ @limit = limit
+ @fingerprint = fingerprint
+ end
+
+ def call
+ response = get(url, params)
+ render_response(response)
+ end
+
+ private
+
+ def params
+ Hash.new.merge({token: token, fingerprint: fingerprint, page: page, limit: limit})
+ end
+
+ def url
+ '/user/keys'.freeze
+ end
+
+end
\ No newline at end of file
diff --git a/app/services/gitea/versions/create_service.rb b/app/services/gitea/versions/create_service.rb
index de21ad97a..04fed00f2 100644
--- a/app/services/gitea/versions/create_service.rb
+++ b/app/services/gitea/versions/create_service.rb
@@ -18,7 +18,8 @@ class Gitea::Versions::CreateService < Gitea::ClientService
end
def call
- post(url, request_params)
+ response = post(url, request_params)
+ render_status(response)
end
private
diff --git a/app/services/gitea/versions/get_service.rb b/app/services/gitea/versions/get_service.rb
new file mode 100644
index 000000000..b3c6cf9cc
--- /dev/null
+++ b/app/services/gitea/versions/get_service.rb
@@ -0,0 +1,37 @@
+# Get a list of all commits from a repository
+class Gitea::Versions::GetService < Gitea::ClientService
+ attr_reader :token, :user_name, :repo, :gid, :args
+
+ # sha: SHA or branch to start listing commits from (usually 'master')
+ def initialize(token, user_name, repo, gid, args={})
+ @token = token
+ @user_name = user_name
+ @repo = repo
+ @gid = gid
+ @args = args
+ end
+
+ def call
+ response = get(url, params)
+ render_result(response)
+ end
+
+ private
+ def params
+ args.merge(token: token)
+ end
+
+ def url
+ "/repos/#{@user_name}/#{@repo}/releases/#{@gid}".freeze
+ end
+
+ def render_result(response)
+ body = JSON.parse(response.body)
+ case response.status
+ when 200
+ body
+ else
+ {status: -1, message: "#{body['message']}"}
+ end
+ end
+end
diff --git a/app/services/gitea/versions/list_service.rb b/app/services/gitea/versions/list_service.rb
index 40db0d149..5d160fc3e 100644
--- a/app/services/gitea/versions/list_service.rb
+++ b/app/services/gitea/versions/list_service.rb
@@ -1,12 +1,13 @@
# Get a list of all commits from a repository
class Gitea::Versions::ListService < Gitea::ClientService
- attr_reader :token, :user_name, :repo
+ attr_reader :token, :user_name, :repo, :args
# sha: SHA or branch to start listing commits from (usually 'master')
- def initialize(token, user_name, repo)
+ def initialize(token, user_name, repo, args={})
@token = token
@user_name = user_name
@repo = repo
+ @args = args
end
def call
@@ -16,7 +17,7 @@ class Gitea::Versions::ListService < Gitea::ClientService
private
def params
- Hash.new.merge(token: token)
+ args.merge(token: token)
end
def url
diff --git a/app/services/issues/list_query_service.rb b/app/services/issues/list_query_service.rb
index dbb502ab6..f85e2cb9d 100644
--- a/app/services/issues/list_query_service.rb
+++ b/app/services/issues/list_query_service.rb
@@ -28,7 +28,7 @@ class Issues::ListQueryService < ApplicationService
end
if search_name.present?
- issues = issues.where("subject like ?", "%#{search_name}%")
+ issues = issues.where("subject LIKE ? OR description LIKE ? ", "%#{search_name}%", "%#{search_name}%")
end
if start_time&.present? || end_time&.present?
@@ -45,9 +45,17 @@ class Issues::ListQueryService < ApplicationService
issues = issues.where(issue_type: params[:issue_type].to_s) if params[:issue_type].present? && params[:issue_type].to_s != "all"
issues = issues.joins(:issue_tags).where(issue_tags: {id: params[:issue_tag_id].to_i}) if params[:issue_tag_id].present? && params[:issue_tag_id].to_s != "all"
- order_type = params[:order_type] || "desc" #或者"asc"
- order_name = params[:order_name] || "updated_on" #或者"updated_on"
issues.reorder("issues.#{order_name} #{order_type}")
end
+ private
+
+ def order_name
+ Issue.column_names.include?(params[:order_name]) ? params[:order_name] : 'updated_on'
+ end
+
+ def order_type
+ %w(desc asc).include?(params[:order_type]) ? params[:order_type] : 'desc'
+ end
+
end
\ No newline at end of file
diff --git a/app/services/notice/read/client_service.rb b/app/services/notice/read/client_service.rb
new file mode 100644
index 000000000..95ab8159e
--- /dev/null
+++ b/app/services/notice/read/client_service.rb
@@ -0,0 +1,108 @@
+class Notice::Read::ClientService < ApplicationService
+ attr_reader :url, :params
+
+ def initialize(options={})
+ @url = options[:url]
+ @params = options[:params]
+ end
+
+ def post(url, params={})
+ puts "[notice][read][POST] request params: #{params}"
+ conn.post do |req|
+ req.url = full_url(url)
+ req.body = params[:data].to_json
+ end
+ end
+
+ def get(url, params={})
+ puts "[notice][read][GET] request params: #{params}"
+ conn.get do |req|
+ req.url full_url(url, 'get')
+ params.each_pair do |key, value|
+ req.params["#{key}"] = value
+ end
+ end
+ end
+
+ def delete(url, params={})
+ puts "[notice][read][DELETE] request params: #{params}"
+ conn.delete do |req|
+ req.url full_url(url)
+ req.body = params[:data].to_json
+ end
+ end
+
+ def patch(url, params={})
+ puts "[notice][read][PATCH] request params: #{params}"
+ conn.patch do |req|
+ req.url full_url(url)
+ req.body = params[:data].to_json
+ end
+ end
+
+ def put(url, params={})
+ puts "[notice][read][PUT] request params: #{params}"
+ conn.put do |req|
+ req.url full_url(url)
+ req.body = params[:data].to_json
+ end
+ end
+
+ def platform
+ Notice.notice_config[:platform]
+ end
+
+ private
+ def conn
+ @client ||= begin
+ Faraday.new(url: domain) do |req|
+ req.request :url_encoded
+ req.headers['Content-Type'] = 'application/json'
+ req.adapter Faraday.default_adapter
+ end
+ end
+
+ @client
+ end
+
+ def base_url
+ Notice.notice_config[:base_url]
+ end
+
+ def domain
+ Notice.notice_config[:read_domain]
+ end
+
+ def api_url
+ [domain, base_url].join('')
+ end
+
+ def full_url(api_rest, action='post')
+ url = [api_url, api_rest].join('').freeze
+ url = action === 'get' ? url : URI.escape(url)
+ url = URI.escape(url) unless url.ascii_only?
+ puts "[notice][read] request url: #{url}"
+ return url
+ end
+
+ def log_error(status, body)
+ puts "[notice][read] status: #{status}"
+ puts "[notice][read] body: #{body}"
+ end
+
+ def render_response(response)
+ status = response.status
+ body = JSON.parse(response&.body)
+
+ log_error(status, body)
+
+ if status == 200
+ if body["code"] == 1
+ return [body["code"], body["message"], body["data"]]
+ else
+ puts "[notice][read][ERROR] code: #{body["code"]}"
+ puts "[notice][read][ERROR] message: #{body["message"]}"
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/services/notice/read/count_service.rb b/app/services/notice/read/count_service.rb
new file mode 100644
index 000000000..fade12278
--- /dev/null
+++ b/app/services/notice/read/count_service.rb
@@ -0,0 +1,25 @@
+class Notice::Read::CountService < Notice::Read::ClientService
+ attr_accessor :receiver, :type
+
+ def initialize(receiver, type=-1)
+ @receiver = receiver
+ @type = type
+ end
+
+ def call
+ result = get(url, request_params)
+ response = render_response(result)
+ end
+
+ private
+ def request_params
+ {
+ receiver: receiver,
+ type: type
+ }.stringify_keys
+ end
+
+ def url
+ "/notification/#{platform}/count".freeze
+ end
+end
\ No newline at end of file
diff --git a/app/services/notice/read/list_service.rb b/app/services/notice/read/list_service.rb
new file mode 100644
index 000000000..3f6645d77
--- /dev/null
+++ b/app/services/notice/read/list_service.rb
@@ -0,0 +1,32 @@
+class Notice::Read::ListService < Notice::Read::ClientService
+ attr_accessor :receiver, :type, :status, :page, :size
+
+ def initialize(receiver, type=-1, status=2, page=1, size=15)
+ @receiver = receiver
+ @type = type
+ @status = status
+ @page = page
+ @size = size
+ end
+
+ def call
+ result = get(url, request_params)
+ response = render_response(result)
+ end
+
+ private
+
+ def request_params
+ {
+ receiver: receiver,
+ page: page,
+ status: status,
+ size: size,
+ type: type
+ }.stringify_keys
+ end
+
+ def url
+ "/notification/#{platform}/list".freeze
+ end
+end
\ No newline at end of file
diff --git a/app/services/notice/write/change_status_service.rb b/app/services/notice/write/change_status_service.rb
new file mode 100644
index 000000000..de2c89815
--- /dev/null
+++ b/app/services/notice/write/change_status_service.rb
@@ -0,0 +1,35 @@
+class Notice::Write::ChangeStatusService < Notice::Write::ClientService
+ attr_accessor :notification_ids, :receiver, :type, :status
+
+ def initialize(notification_ids, receiver, type=-1, status=2)
+ @notification_ids = notification_ids
+ @receiver = receiver
+ @type = type
+ @status = status
+ end
+
+ def call
+ result = put(url, request_params)
+ response = render_response(result)
+ end
+
+ private
+
+ def request_notification_ids
+ notification_ids.join(",")
+ end
+
+ def request_params
+ Hash.new.merge(data: {
+ notificationIds: request_notification_ids,
+ receiver: receiver,
+ type: type,
+ status: status
+ }.stringify_keys)
+ end
+
+ def url
+ "/notification/#{platform}".freeze
+ end
+
+end
\ No newline at end of file
diff --git a/app/services/notice/write/client_service.rb b/app/services/notice/write/client_service.rb
new file mode 100644
index 000000000..d9dc841f6
--- /dev/null
+++ b/app/services/notice/write/client_service.rb
@@ -0,0 +1,110 @@
+class Notice::Write::ClientService < ApplicationService
+ attr_reader :url, :params
+
+ def initialize(options={})
+ @url = options[:url]
+ @params = options[:params]
+ end
+
+ def post(url, params={})
+ puts "[notice][write][POST] request params: #{params}"
+ conn.post do |req|
+ req.url full_url(url)
+ req.body = params[:data].to_json
+ end
+ end
+
+ def get(url, params={})
+ puts "[notice][write][GET] request params: #{params}"
+ conn.get do |req|
+ req.url full_url(url, 'get')
+ params.each_pair do |key, value|
+ req.params["#{key}"] = value
+ end
+ end
+ end
+
+ def delete(url, params={})
+ puts "[notice][write][DELETE] request params: #{params}"
+ conn.delete do |req|
+ req.url full_url(url)
+ req.body = params[:data].to_json
+ end
+ end
+
+ def patch(url, params={})
+ puts "[notice][write][PATCH] request params: #{params}"
+ conn.patch do |req|
+ req.url full_url(url)
+ req.body = params[:data].to_json
+ end
+ end
+
+ def put(url, params={})
+ puts "[notice][write][PUT] request params: #{params}"
+ conn.put do |req|
+ req.url full_url(url)
+ req.body = params[:data].to_json
+ end
+ end
+
+ def platform
+ Notice.notice_config[:platform]
+ end
+
+ private
+ def conn
+ @client ||= begin
+ Faraday.new(url: domain) do |req|
+ req.request :url_encoded
+ req.headers['Content-Type'] = 'application/json'
+ req.adapter Faraday.default_adapter
+ req.options.timeout = 100 # open/read timeout in seconds
+ req.options.open_timeout = 10 # connection open timeout in seconds
+ end
+ end
+
+ @client
+ end
+
+ def base_url
+ Notice.notice_config[:base_url]
+ end
+
+ def domain
+ Notice.notice_config[:write_domain]
+ end
+
+ def api_url
+ [domain, base_url].join('')
+ end
+
+ def full_url(api_rest, action='post')
+ url = [api_url, api_rest].join('').freeze
+ url = action === 'get' ? url : URI.escape(url)
+ url = URI.escape(url) unless url.ascii_only?
+ puts "[notice][write] request url: #{url}"
+ return url
+ end
+
+ def log_error(status, body)
+ puts "[notice][write] status: #{status}"
+ puts "[notice][write] body: #{body}"
+ end
+
+ def render_response(response)
+ status = response.status
+ body = JSON.parse(response&.body)
+
+ log_error(status, body)
+
+ if status == 200
+ if body["code"] == 1
+ return [body["code"], body["message"], body["data"]]
+ else
+ puts "[notice][write][ERROR] code: #{body["code"]}"
+ puts "[notice][write][ERROR] message: #{body["message"]}"
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/services/notice/write/create_service.rb b/app/services/notice/write/create_service.rb
new file mode 100644
index 000000000..8dfc88483
--- /dev/null
+++ b/app/services/notice/write/create_service.rb
@@ -0,0 +1,42 @@
+class Notice::Write::CreateService < Notice::Write::ClientService
+ attr_accessor :receivers, :sender, :content, :notification_url, :source, :extra, :type
+
+ def initialize(receivers, content, notification_url, source, extra={}, type=1, sender=-1)
+ @receivers = receivers
+ @sender = sender
+ @content = content
+ @notification_url = notification_url
+ @source = source
+ @extra = extra
+ @type = type
+ end
+
+ def call
+ return nil if request_receivers.blank?
+ result = post(url, request_params)
+ response = render_response(result)
+ end
+
+ private
+
+ def request_receivers
+ receivers.is_a?(Array) ? receivers.join(",") : receivers
+ end
+
+ def request_params
+ Hash.new.merge(data: {
+ receivers: request_receivers,
+ sender: sender,
+ content: content,
+ notification_url: notification_url,
+ source: source,
+ extra: extra.to_json.to_s,
+ type: type
+ }.stringify_keys)
+ end
+
+ def url
+ "/notification/#{platform}".freeze
+ end
+
+end
\ No newline at end of file
diff --git a/app/services/notice/write/delete_service.rb b/app/services/notice/write/delete_service.rb
new file mode 100644
index 000000000..f63584819
--- /dev/null
+++ b/app/services/notice/write/delete_service.rb
@@ -0,0 +1,33 @@
+class Notice::Write::DeleteService < Notice::Write::ClientService
+ attr_accessor :notification_ids, :receiver, :type
+
+ def initialize(notification_ids, receiver, type=-1)
+ @notification_ids = notification_ids
+ @receiver = receiver
+ @type = type
+ end
+
+ def call
+ result = delete(url, request_params)
+ response = render_response(result)
+ end
+
+ private
+
+ def request_notification_ids
+ notification_ids.join(",")
+ end
+
+ def request_params
+ Hash.new.merge(data: {
+ notificationIds: request_notification_ids,
+ receiver: receiver,
+ type: type
+ }.stringify_keys)
+ end
+
+ def url
+ "/notification/#{platform}".freeze
+ end
+
+end
\ No newline at end of file
diff --git a/app/services/notice/write/email_create_service.rb b/app/services/notice/write/email_create_service.rb
new file mode 100644
index 000000000..86ec2761f
--- /dev/null
+++ b/app/services/notice/write/email_create_service.rb
@@ -0,0 +1,40 @@
+class Notice::Write::EmailCreateService < Notice::Write::ClientService
+ attr_accessor :receivers, :sender, :content, :subject
+
+ def initialize(receivers, subject, content, sender=-1)
+ @receivers = receivers
+ @sender = sender
+ @content = content
+ @subject = subject
+ end
+
+ def call
+ return nil if request_receivers.blank?
+ result = post(url, request_params)
+ response = render_response(result)
+ end
+
+ private
+
+ def request_receivers
+ receivers.is_a?(Array) ? receivers.join(",") : receivers
+ end
+
+ def request_subject
+ "#{subject}"
+ end
+
+ def request_params
+ Hash.new.merge(data: {
+ emails: request_receivers,
+ sender: sender,
+ content: content,
+ subject: request_subject
+ }.stringify_keys)
+ end
+
+ def url
+ "/email/#{platform}".freeze
+ end
+
+end
\ No newline at end of file
diff --git a/app/services/organizations/create_service.rb b/app/services/organizations/create_service.rb
index 2d2b29e0f..a430ed6f4 100644
--- a/app/services/organizations/create_service.rb
+++ b/app/services/organizations/create_service.rb
@@ -47,14 +47,14 @@ class Organizations::CreateService < ApplicationService
end
def create_org_and_extension
- @organization = Organization.build(params[:name], user.gitea_token)
+ @organization = Organization.build(params[:name], params[:nickname], user.gitea_token)
org_extension = OrganizationExtension.build(organization.id, description, website,
location, repo_admin_change_team_access,
visibility, max_repo_creation)
end
def create_owner_info
- @owner_team = Team.build(organization.id, "Owners", "", 4, true, true)
+ @owner_team = Team.build(organization.id, "Owners", "Owner团队", "", 4, true, true)
TeamUnit.unit_types.keys.each do |u_type|
TeamUnit.build(organization.id, owner_team.id, u_type)
end
diff --git a/app/services/organizations/teams/create_service.rb b/app/services/organizations/teams/create_service.rb
index 81ad78772..171732293 100644
--- a/app/services/organizations/teams/create_service.rb
+++ b/app/services/organizations/teams/create_service.rb
@@ -28,12 +28,16 @@ class Organizations::Teams::CreateService < ApplicationService
params[:name]
end
+ def nickname
+ params[:nickname]
+ end
+
def description
params[:description]
end
def authorize
- params[:authorize].present? ? params[:authorize] : "common"
+ params[:authorize].present? ? params[:authorize] : "read"
end
def includes_all_project
@@ -45,12 +49,12 @@ class Organizations::Teams::CreateService < ApplicationService
end
def create_team
- @team = Team.build(org.id, name, description, authorize,
+ @team = Team.build(org.id, name, nickname, description, authorize,
includes_all_project, can_create_org_project)
end
def units_params
- %w(admin owner).include?(authorize) ? %w(code issues pulls releases) : params[:unit_types]
+ %w(code issues pulls wiki releases)
end
def create_units
diff --git a/app/services/organizations/teams/update_service.rb b/app/services/organizations/teams/update_service.rb
index 71c82c40b..d77bba9e4 100644
--- a/app/services/organizations/teams/update_service.rb
+++ b/app/services/organizations/teams/update_service.rb
@@ -25,15 +25,15 @@ class Organizations::Teams::UpdateService < ApplicationService
private
def update_params
if team.authorize == "owner"
- update_params = params.slice(:description)
+ update_params = params.slice(:description, :nickname)
else
- update_params = params.slice(:name, :description, :authorize, :includes_all_project, :can_create_org_project)
+ update_params = params.slice(:name, :nickname, :description, :authorize, :includes_all_project, :can_create_org_project)
end
update_params
end
def units_params
- %w(admin owner).include?(team.authorize) ? %w(code issues pulls releases) : params[:unit_types]
+ %w(code issues pulls wiki releases)
end
def update_team(update_params)
diff --git a/app/services/projects/accept_join_service.rb b/app/services/projects/accept_join_service.rb
new file mode 100644
index 000000000..2bbacad69
--- /dev/null
+++ b/app/services/projects/accept_join_service.rb
@@ -0,0 +1,63 @@
+class Projects::AcceptJoinService < ApplicationService
+ attr_accessor :applied_project, :owner
+ attr_reader :user, :project
+
+ def initialize(user, applied_project)
+ @user = user
+ @project = applied_project.project
+ @applied_project = applied_project
+ end
+
+ def call
+ Rails.logger.info("###### Project accept_join_service begin ######")
+ ActiveRecord::Base.transaction do
+ validate!
+ update_apply
+ operate_project_member
+ send_apply_message
+ end
+
+ Rails.logger.info("##### Project accept_join_service end ######")
+
+
+ return @applied_project
+ end
+
+ private
+ def permission
+ case @applied_project.role
+ when 'manager'
+ 'admin'
+ when 'developer'
+ 'write'
+ when 'reporter'
+ 'read'
+ else
+ 'read'
+ end
+ end
+
+ def validate!
+ raise Error, '该申请已经被接受' if @applied_project.accepted?
+ raise Error, '该申请不存在' unless @applied_project.present?
+ raise Error, '未拥有接受申请权限' unless is_permit_operator
+ end
+
+ def is_permit_operator
+ return @user.admin? || @project.manager?(@user)
+ end
+
+ def update_apply
+ @applied_project.update!(status: 'accepted')
+ end
+
+ def operate_project_member
+ Projects::AddMemberInteractor.call(@project.owner, @project, @applied_project.user, permission)
+ SendTemplateMessageJob.perform_later('ProjectJoined', @user.id, @applied_project.user_id, @project.id) if Site.has_notice_menu?
+ SendTemplateMessageJob.perform_later('ProjectMemberJoined', @user.id, @applied_project.user_id, @project.id) if Site.has_notice_menu?
+ end
+
+ def send_apply_message
+ SendJoinProjectAppliedMessageJob.perform_now(@applied_project, @user, 'successed')
+ end
+end
\ No newline at end of file
diff --git a/app/services/projects/accept_transfer_service.rb b/app/services/projects/accept_transfer_service.rb
new file mode 100644
index 000000000..c99885ca0
--- /dev/null
+++ b/app/services/projects/accept_transfer_service.rb
@@ -0,0 +1,49 @@
+class Projects::AcceptTransferService < ApplicationService
+ attr_accessor :applied_transfer_project, :owner
+ attr_reader :user, :project
+
+ def initialize(user, project)
+ @user = user
+ @project = project
+ @applied_transfer_project = project.applied_transfer_project
+ @owner = @applied_transfer_project.owner
+ end
+
+ def call
+ Rails.logger.info("###### Project accept_transfer_service begin ######")
+ ActiveRecord::Base.transaction do
+ validate!
+ update_apply
+ operate_project
+ send_apply_message
+ end
+
+ Rails.logger.info("##### Project accept_transfer_service end ######")
+
+
+ return @applied_transfer_project
+ end
+
+ private
+ def validate!
+ raise Error, '该仓库未在迁移' unless @applied_transfer_project.present? && @project.is_transfering
+ raise Error, '未拥有接受转移权限' unless is_permit_operator
+ end
+
+ def is_permit_operator
+ return true if @user == @owner
+ return @owner.is_a?(Organization) && @owner.is_admin?(@user)
+ end
+
+ def update_apply
+ @applied_transfer_project.update!(status: 'accepted')
+ end
+
+ def operate_project
+ @project = Projects::TransferService.call(@project, @owner)
+ end
+
+ def send_apply_message
+ SendTransferProjectAppliedMessageJob.perform_now(@applied_transfer_project, @user, 'successed')
+ end
+end
\ No newline at end of file
diff --git a/app/services/projects/apply_join_service.rb b/app/services/projects/apply_join_service.rb
index 0b57712d0..958fd810c 100644
--- a/app/services/projects/apply_join_service.rb
+++ b/app/services/projects/apply_join_service.rb
@@ -9,26 +9,25 @@ class Projects::ApplyJoinService < ApplicationService
end
def call
- validate!
# 项目报告人员直接加入项目
- if params[:role] == 'reporter'
- # Projects::JoinService.call(project, user, role: 'reporter')
- return project
- end
+ # if params[:role] == 'reporter'
+ # # Projects::JoinService.call(project, user, role: 'reporter')
+ # return project
+ # end
ActiveRecord::Base.transaction do
+ validate!
apply = user.applied_projects.create!(project: project, role: role_value)
+ apply
+ # apply.forge_activities.find_or_create_by!(user: user, project: project)
- apply.forge_activities.find_or_create_by!(user: user, project: project)
-
- notify_project_manager!(apply)
+ # notify_project_manager!(apply)
end
# notify_project_owner
- ApplyJoinProjectNotifyJob.perform_later(user.id, project.id, role_value)
+ # ApplyJoinProjectNotifyJob.perform_later(user.id, project.id, role_value)
- project
end
private
@@ -43,7 +42,8 @@ class Projects::ApplyJoinService < ApplicationService
when 'manager' then 3
when 'developer' then 4
when 'reporter' then 5
- else raise Error, '角色无效'
+ else
+ 5
end
end
@@ -65,7 +65,7 @@ class Projects::ApplyJoinService < ApplicationService
owner = project.user
return if owner.phone.blank?
- Educoder::Sms.send(mobile: owner.phone, send_type:'applied_project_info',
+ Gitlink::Sms.send(mobile: owner.phone, send_type:'applied_project_info',
user_name: owner.show_name, name: project.name)
rescue Exception => ex
Rails.logger.error("发送短信失败 => #{ex.message}")
@@ -74,12 +74,18 @@ class Projects::ApplyJoinService < ApplicationService
def validate!
# params check
raise Error, '邀请码不能为空' if params[:code].blank?
- raise Error, '角色不能为空' if params[:role].blank?
- raise Error, '角色无效' unless %w(manager developer reporter).include?(params[:role])
+ raise Error, '请输入6位项目邀请码' unless valid_invite_code( params[:code])
# logical check
raise Error, '邀请码无效' if project.blank?
- raise Error, '您已在该项目中' if project.member?(user)
- raise Error, '您已经提交过申请' if user.applied_projects.pending.exists?(project: project)
+ raise Error, '您已是项目成员' if project.member?(user)
+ raise Error, '您已经提交过申请' if user.applied_projects.common.exists?(project: project)
+ end
+
+ def valid_invite_code(str)
+ if (str =~ /^[A-Za-z0-9]{6}+$/)
+ return true
+ end
+ return false
end
end
\ No newline at end of file
diff --git a/app/services/projects/apply_transfer_service.rb b/app/services/projects/apply_transfer_service.rb
new file mode 100644
index 000000000..98847a91c
--- /dev/null
+++ b/app/services/projects/apply_transfer_service.rb
@@ -0,0 +1,44 @@
+class Projects::ApplyTransferService < ApplicationService
+ attr_accessor :owner, :applied_transfer_project
+ attr_reader :user, :project, :params
+
+ def initialize(user, project, params)
+ @user = user
+ @project = project
+ @params = params
+ @owner = Owner.find_by(login: params[:owner_name])
+ end
+
+ def call
+ Rails.logger.info("###### Project apply_transfer_service begin ######")
+ validate!
+ create_apply
+ send_apply_message
+ Rails.logger.info("###### Project apply_transfer_service end ######")
+
+ return @applied_transfer_project
+ end
+
+ private
+ def validate!
+ raise Error, '仓库标识不正确' if @project.identifier != params[:identifier]
+ raise Error, '该仓库正在迁移' if @project.is_transfering
+ raise Error, '新拥有者不存在' unless @owner.present?
+ raise Error, '新拥有者资料不完善' if @owner.is_a?(User) && !@owner.profile_is_completed?
+ raise Error, '新拥有者已经存在同名仓库!' if Project.where(user_id: @owner.id, identifier: params[:identifier]).present?
+ raise Error, '未拥有转移权限' unless is_permit_owner
+ end
+
+ def is_permit_owner
+ return true unless @owner.is_a?(Organization)
+ return @owner.is_admin?(@user)
+ end
+
+ def create_apply
+ @applied_transfer_project = AppliedTransferProject.create!(user_id: user.id, project_id: project.id, owner_id: @owner.id)
+ end
+
+ def send_apply_message
+ SendTransferProjectAppliedMessageJob.perform_now(@applied_transfer_project, @user, 'common')
+ end
+end
\ No newline at end of file
diff --git a/app/services/projects/cancel_transfer_service.rb b/app/services/projects/cancel_transfer_service.rb
new file mode 100644
index 000000000..153b24ffb
--- /dev/null
+++ b/app/services/projects/cancel_transfer_service.rb
@@ -0,0 +1,33 @@
+class Projects::CancelTransferService < ApplicationService
+ attr_accessor :applied_transfer_project
+ attr_reader :user, :project
+
+ def initialize(user, project)
+ @user = user
+ @project = project
+ @applied_transfer_project = project.applied_transfer_project
+ end
+
+ def call
+ Rails.logger.info("###### Project cancel_transfer_service begin ######")
+ validate!
+ update_apply
+ send_apply_message
+ Rails.logger.info("###### Project cancel_transfer_service end ######")
+
+ return @applied_transfer_project
+ end
+
+ private
+ def validate!
+ raise Error, '该仓库未在迁移' unless @applied_transfer_project.present? && @project.is_transfering
+ end
+
+ def update_apply
+ @applied_transfer_project.update!(status: 'canceled')
+ end
+
+ def send_apply_message
+ SendTransferProjectAppliedMessageJob.perform_now(@applied_transfer_project, @user, 'canceled')
+ end
+end
\ No newline at end of file
diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb
index 8d6aecb12..56a007b39 100644
--- a/app/services/projects/create_service.rb
+++ b/app/services/projects/create_service.rb
@@ -8,11 +8,13 @@ class Projects::CreateService < ApplicationService
def call
Rails.logger.info("#############__________project_params______###########{project_params}")
+ raise Error, "user_id不正确." unless authroize_user_id_success
@project = Project.new(project_params)
ActiveRecord::Base.transaction do
if @project.save!
Project.update_common_projects_count!
+ ProjectUnit.init_types(@project.id)
Repositories::CreateService.new(user, @project, repository_params).call
else
Rails.logger.info("#############___________create_project_erros______###########{@project.errors.messages}")
@@ -26,6 +28,10 @@ class Projects::CreateService < ApplicationService
private
+ def authroize_user_id_success
+ (user.id == params[:user_id].to_i) || (user.organizations.find_by_id(params[:user_id]).present?)
+ end
+
def project_params
{
name: params[:name],
@@ -36,7 +42,8 @@ class Projects::CreateService < ApplicationService
is_public: repo_is_public,
ignore_id: params[:ignore_id],
license_id: params[:license_id],
- identifier: params[:repository_name] #新增,hs
+ website: params[:website],
+ identifier: params[:repository_name]
}
end
@@ -57,7 +64,7 @@ class Projects::CreateService < ApplicationService
if license.is_secret
false
else
- params[:private].blank? ? true : !params[:private]
+ params[:private].blank? ? true : !(ActiveModel::Type::Boolean.new.cast(params[:private]).nil? ? false : ActiveModel::Type::Boolean.new.cast(params[:private]))
end
end
end
diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb
index 3204e5fc7..84b80f50f 100644
--- a/app/services/projects/fork_service.rb
+++ b/app/services/projects/fork_service.rb
@@ -15,8 +15,11 @@ class Projects::ForkService < ApplicationService
:rep_identifier, :project_category_id, :project_language_id,
:license_id, :ignore_id, {repository: [:identifier, :hidden]}]
+ result = Gitea::Repository::ForkService.new(@project.owner, @target_owner, @project.identifier, @organization).call
+
clone_project.owner = @target_owner
clone_project.forked_from_project_id = @project.id
+ clone_project.gpid = result['id']
clone_project.save!
new_repository = clone_project.repository
@@ -24,7 +27,7 @@ class Projects::ForkService < ApplicationService
new_repository.identifier = @project.identifier
new_repository.save!
- result = Gitea::Repository::ForkService.new(@project.owner, @target_owner, @project.identifier, @organization).call
+ ProjectUnit.init_types(clone_project.id)
@project.update_column('forked_count', @project&.forked_count.to_i + 1)
new_repository.update_column('url', result['clone_url']) if result
diff --git a/app/services/projects/migrate_service.rb b/app/services/projects/migrate_service.rb
index 2f7725db6..68ed9f642 100644
--- a/app/services/projects/migrate_service.rb
+++ b/app/services/projects/migrate_service.rb
@@ -1,5 +1,6 @@
class Projects::MigrateService < ApplicationService
attr_reader :user, :params
+ attr_accessor :project
def initialize(user, params)
@user = user
@@ -7,9 +8,13 @@ class Projects::MigrateService < ApplicationService
end
def call
+ raise Error, "user_id不正确." unless authroize_user_id_success
+
@project = Project.new(project_params)
if @project.save!
+ ProjectUnit.init_types(@project.id, project.project_type)
Project.update_mirror_projects_count!
+ @project.set_owner_permission(user)
Repositories::MigrateService.new(user, @project, repository_params).call
else
#
@@ -21,6 +26,9 @@ class Projects::MigrateService < ApplicationService
end
private
+ def authroize_user_id_success
+ (user.id == params[:user_id].to_i) || (user.organizations.find_by_id(params[:user_id]).present?)
+ end
def project_params
{
@@ -47,7 +55,8 @@ class Projects::MigrateService < ApplicationService
user_id: params[:user_id],
login: params[:auth_username],
password: params[:auth_password],
- is_mirror: params[:is_mirror]
+ is_mirror: params[:is_mirror],
+ source_clone_url: params[:source_clone_url]
}
end
diff --git a/app/services/projects/refuse_join_service.rb b/app/services/projects/refuse_join_service.rb
new file mode 100644
index 000000000..f9452586c
--- /dev/null
+++ b/app/services/projects/refuse_join_service.rb
@@ -0,0 +1,39 @@
+class Projects::RefuseJoinService < ApplicationService
+ attr_accessor :applied_project, :owner
+ attr_reader :user, :project
+
+ def initialize(user, applied_project)
+ @user = user
+ @project = applied_project.project
+ @applied_project = applied_project
+ end
+
+ def call
+ Rails.logger.info("###### Project refuse_join_service begin ######")
+ validate!
+ update_apply
+ send_apply_message
+ Rails.logger.info("###### Project refuse_join_service end ######")
+
+ return @applied_project
+ end
+
+ private
+ def validate!
+ raise Error, '该申请已被拒绝' if @applied_project.refused?
+ raise Error, '该申请不存在' unless @applied_project.present?
+ raise Error, '未拥有接受申请权限' unless is_permit_operator
+ end
+
+ def is_permit_operator
+ return @user.admin? || @project.manager?(@user)
+ end
+
+ def update_apply
+ @applied_project.update!(status: 'refused')
+ end
+
+ def send_apply_message
+ SendJoinProjectAppliedMessageJob.perform_now(@applied_project, @user, 'failure')
+ end
+end
\ No newline at end of file
diff --git a/app/services/projects/refuse_transfer_service.rb b/app/services/projects/refuse_transfer_service.rb
new file mode 100644
index 000000000..1b81b5741
--- /dev/null
+++ b/app/services/projects/refuse_transfer_service.rb
@@ -0,0 +1,40 @@
+class Projects::RefuseTransferService < ApplicationService
+ attr_accessor :applied_transfer_project, :owner
+ attr_reader :user, :project
+
+ def initialize(user, project)
+ @user = user
+ @project = project
+ @applied_transfer_project = project.applied_transfer_project
+ @owner = @applied_transfer_project.owner
+ end
+
+ def call
+ Rails.logger.info("###### Project refuse_transfer_service begin ######")
+ validate!
+ update_apply
+ send_apply_message
+ Rails.logger.info("###### Project refuse_transfer_service end ######")
+
+ return @applied_transfer_project
+ end
+
+ private
+ def validate!
+ raise Error, '该仓库未在迁移' unless @applied_transfer_project.present? && @project.is_transfering
+ raise Error, '未拥有拒绝转移权限' unless is_permit_operator
+ end
+
+ def is_permit_operator
+ return true if @user == @owner
+ return @owner.is_a?(Organization) && @owner.is_admin?(@user)
+ end
+
+ def update_apply
+ @applied_transfer_project.update!(status: 'refused')
+ end
+
+ def send_apply_message
+ SendTransferProjectAppliedMessageJob.perform_now(@applied_transfer_project, @user, 'failure')
+ end
+end
\ No newline at end of file
diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb
index b32b24817..71c6ff68e 100644
--- a/app/services/projects/transfer_service.rb
+++ b/app/services/projects/transfer_service.rb
@@ -23,18 +23,18 @@ class Projects::TransferService < ApplicationService
private
def update_owner
+ project.members.map{|m| m.destroy! if m.user_id == owner.id || (new_owner.is_a?(Organization) && new_owner.is_member?(m.user_id)) }
project.update!(user_id: new_owner.id)
end
def update_repo_url
- project.repository.update!(url: @gitea_repo["clone_url"])
+ project.repository.update!(user_id: new_owner.id, url: @gitea_repo["clone_url"])
end
def update_visit_teams
if new_owner.is_a?(Organization)
- new_owner.teams.where(includes_all_project: true).each do |team|
- TeamProject.build(new_owner.id, team.id, project.id)
- end
+ # 为包含组织所有项目的团队创建项目访问权限
+ new_owner.build_permit_team_projects!(project.id)
else
project.team_projects.each(&:destroy!)
end
diff --git a/app/services/pull_requests/close_service.rb b/app/services/pull_requests/close_service.rb
index e0ac19b4e..60a0ab13f 100644
--- a/app/services/pull_requests/close_service.rb
+++ b/app/services/pull_requests/close_service.rb
@@ -23,7 +23,7 @@ class PullRequests::CloseService < ApplicationService
def close_gitea_pull
Gitea::PullRequest::CloseService.call(@owner.login, @repo.identifier,
- @pull.gpid, @pull.base, current_user.gitea_token)
+ @pull.gitea_number, @pull.base, current_user.gitea_token)
end
def update_pull_status!
diff --git a/app/services/pull_requests/create_service.rb b/app/services/pull_requests/create_service.rb
index 42dfc676d..d15b3f9fc 100644
--- a/app/services/pull_requests/create_service.rb
+++ b/app/services/pull_requests/create_service.rb
@@ -13,6 +13,7 @@ class PullRequests::CreateService < ApplicationService
def call
ActiveRecord::Base.transaction do
validate!
+ compare_head_base!
save_pull_issue!
save_pull_request!
save_issue_tags_relates!
@@ -33,7 +34,7 @@ class PullRequests::CreateService < ApplicationService
assigned_to_id: @params[:assigned_to_id],
fixed_version_id: @params[:fixed_version_id],
issue_tags_value: @params[:issue_tag_ids].present? ? @params[:issue_tag_ids].join(",") : "",
- priority_id: @params[:priority_id] || "2",
+ priority_id: @params[:priority_id],
issue_classify: "pull_request",
issue_type: @params[:issue_type] || "1",
tracker_id: 2,
@@ -58,8 +59,12 @@ class PullRequests::CreateService < ApplicationService
end
def save_issue_tags_relates!
- issue_tag_ids.each do |tag|
- IssueTagsRelate.create!(issue_id: pull_issue.id, issue_tag_id: tag)
+ if issue_tag_ids.size > 1
+ raise "最多只能创建一个标记。"
+ else
+ issue_tag_ids.each do |tag|
+ IssueTagsRelate.create!(issue_id: pull_issue.id, issue_tag_id: tag)
+ end
end
end
@@ -94,7 +99,7 @@ class PullRequests::CreateService < ApplicationService
user: @current_user,
issue: pull_issue,
fork_project_id: @params[:fork_project_id],
- is_original: @params[:is_original],
+ is_original: is_original,
files_count: @params[:files_count] || 0,
commits_count: @params[:commits_count] || 0
})
@@ -120,12 +125,17 @@ class PullRequests::CreateService < ApplicationService
end
def gitea_pull_request_params
- merge_original_pull_params.except(:milestone)
+ assignee_login = User.find_by_id(params[:assigned_to_id])&.login
+ merge_original_pull_params.except(:milestone).merge(
+ # assignees: ["#{params[:assigned_login].to_s}"],
+ assignees: ["#{assignee_login.to_s}"])
end
def merge_original_pull_params
+ base_pull_params[:head] = CGI.escape(base_pull_params[:head])
+ base_pull_params[:base] = CGI.escape(base_pull_params[:base])
if pull_request.is_original && @params[:merge_user_login]
- base_pull_params.merge(head: "#{@params[:merge_user_login]}:#{@params[:head]}")
+ base_pull_params.merge(head: "#{@params[:merge_user_login]}:#{base_pull_params[:head]}")
else
base_pull_params
end
@@ -143,9 +153,23 @@ class PullRequests::CreateService < ApplicationService
def validate!
raise "title参数不能为空" if @params[:title].blank?
+ raise "title不能超过255个字符" if @params[:title].length > 255
raise "head参数不能为空" if @params[:head].blank?
raise "base参数不能为空" if @params[:base].blank?
+ raise "fork_project_id参数错误" if is_original && !@project.forked_projects.pluck(:id).include?(@params[:fork_project_id].to_i)
+ raise "分支内容相同,无需创建合并请求" if @params[:head] === @params[:base] && !is_original
+ raise "合并请求已存在" if @project&.pull_requests.where(head: @params[:head], base: @params[:base], status: 0, is_original: is_original, fork_project_id: @params[:fork_project_id]).present?
raise @pull_issue.errors.full_messages.join(", ") unless pull_issue.valid?
raise @pull_request.errors.full_messages.join(", ") unless pull_request.valid?
end
+
+ def compare_head_base!
+ head = pull_request.is_original && @params[:merge_user_login] ? "#{@params[:merge_user_login]}/#{@project.identifier}:#{@params[:head]}" : @params[:head]
+ compare_result = Gitea::Repository::Commits::CompareService.call(@owner.login, @project.identifier, CGI.escape(@params[:base]), CGI.escape(head), @current_user.gitea_token)
+ raise '分支内容相同,无需创建合并请求' if compare_result["Commits"].blank? && compare_result["Diff"].blank?
+ end
+
+ def is_original
+ @params[:is_original] || false
+ end
end
diff --git a/app/services/pull_requests/merge_service.rb b/app/services/pull_requests/merge_service.rb
index a20ee3aa9..463412120 100644
--- a/app/services/pull_requests/merge_service.rb
+++ b/app/services/pull_requests/merge_service.rb
@@ -1,6 +1,6 @@
class PullRequests::MergeService < ApplicationService
attr_reader :owner, :repo, :pull, :current_user, :params
-
+ attr_accessor :status, :message
# eq:
# PullRequests::MergeService.call(owner, repo, pull, current_user, params)
def initialize(owner, repo, pull, current_user, params)
@@ -15,15 +15,15 @@ class PullRequests::MergeService < ApplicationService
ActiveRecord::Base.transaction do
gitea_pull_merge!
end
+ self
end
private
def gitea_pull_merge!
result = Gitea::PullRequest::MergeService.call(@current_user.gitea_token, @owner.login,
- @repo.identifier, @pull.gpid, gitea_merge_pull_params)
-
- result[:status] === 200 ? true : false
+ @repo.identifier, @pull.gitea_number, gitea_merge_pull_params)
+ @status, @message = result
end
def gitea_merge_pull_params
diff --git a/app/services/pull_requests/open_service.rb b/app/services/pull_requests/open_service.rb
index 3081e52b5..490071c05 100644
--- a/app/services/pull_requests/open_service.rb
+++ b/app/services/pull_requests/open_service.rb
@@ -23,7 +23,7 @@ class PullRequests::OpenService < ApplicationService
def open_gitea_pull
Gitea::PullRequest::OpenService.call(@owner.login, @repo.identifier,
- @pull.gpid, @pull.base, @current_user.gitea_token)
+ @pull.gitea_number, @pull.base, @current_user.gitea_token)
end
def update_pull_status!
diff --git a/app/services/repositories/create_service.rb b/app/services/repositories/create_service.rb
index 4c5519f2e..f0c584e18 100644
--- a/app/services/repositories/create_service.rb
+++ b/app/services/repositories/create_service.rb
@@ -15,6 +15,7 @@ class Repositories::CreateService < ApplicationService
create_gitea_repository
sync_project
sync_repository
+ @project.set_owner_permission(user)
# if project.project_type == "common"
# chain_params = {
# type: "create",
@@ -44,24 +45,15 @@ class Repositories::CreateService < ApplicationService
@gitea_repository = Gitea::Repository::CreateService.new(user.gitea_token, gitea_repository_params).call
elsif project.owner.is_a?(Organization)
@gitea_repository = Gitea::Organization::Repository::CreateService.call(user.gitea_token, project.owner.login, gitea_repository_params)
- project.owner.teams.each do |team|
- next unless team.includes_all_project
- TeamProject.build(project.user_id, team.id, project.id)
- end
- create_manager_member
end
end
- def create_manager_member
- return if project.owner.is_owner?(user.id)
- project.add_member!(user.id, "Manager")
- end
-
def sync_project
if gitea_repository
project.update_columns(
gpid: gitea_repository["id"],
identifier: repository.identifier,
+ default_branch: gitea_repository["default_branch"],
forked_count: gitea_repository["forks_count"])
end
end
diff --git a/app/services/repositories/detail_service.rb b/app/services/repositories/detail_service.rb
new file mode 100644
index 000000000..b39b408ce
--- /dev/null
+++ b/app/services/repositories/detail_service.rb
@@ -0,0 +1,30 @@
+class Repositories::DetailService < ApplicationService
+ include Repository::LanguagesPercentagable
+ attr_reader :owner, :repo, :user
+ def initialize(owner, repo, user)
+ @owner = owner
+ @repo = repo
+ @user = user
+ end
+
+ def call
+ return {
+ repo: repo_suitable,
+ branch_tag_total_count: branch_tag_total_count
+ }
+ rescue
+ return {
+ repo: {},
+ branch_tag_total_count: {}
+ }
+ end
+
+ private
+ def branch_tag_total_count
+ Gitea::Repository::GetBranchAndTagTotalNumService.call(@owner.login, @repo.identifier, @owner.gitea_token)
+ end
+
+ def repo_suitable
+ Gitea::Repository::GetService.call(@owner, @repo.identifier)
+ end
+end
diff --git a/app/services/repositories/migrate_service.rb b/app/services/repositories/migrate_service.rb
index 156777cc3..cccfaed04 100644
--- a/app/services/repositories/migrate_service.rb
+++ b/app/services/repositories/migrate_service.rb
@@ -21,7 +21,7 @@ class Repositories::MigrateService < ApplicationService
private
def repository_params
- params.merge(project_id: project.id, identifier: params[:identifier])
+ params.merge(project_id: project.id)
end
def gitea_repository_params
@@ -32,7 +32,7 @@ class Repositories::MigrateService < ApplicationService
private: params[:hidden],
mirror: wrapper_mirror || false,
auth_username: params[:login],
- auth_password: params[:password]
+ auth_password: Base64.decode64(params[:password] || "")
}
end
diff --git a/app/services/reward_experience_service.rb b/app/services/reward_experience_service.rb
deleted file mode 100644
index 70b47d1a3..000000000
--- a/app/services/reward_experience_service.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-class RewardExperienceService
- attr_reader :user, :attrs
-
- def initialize(user, **attrs)
- @user = user
- @attrs = attrs.slice(*%i[container_id container_type score])
- end
-
- def call
- return if user.experiences.exists?(attrs.except(:score))
-
- ActiveRecord::Base.transaction do
- experience = user.experiences.create!(attrs)
-
- user.increment!(:experience, experience.score)
-
- experience
- end
- end
-
- def self.call(user, **attrs)
- new(user, attrs).call
- end
-end
\ No newline at end of file
diff --git a/app/services/users/apply_authentication_service.rb b/app/services/users/apply_authentication_service.rb
index a6b02f431..cd7b931a5 100644
--- a/app/services/users/apply_authentication_service.rb
+++ b/app/services/users/apply_authentication_service.rb
@@ -7,7 +7,7 @@ class Users::ApplyAuthenticationService < ApplicationService
end
def call
- raise Error, '请先完善基本信息' unless user.profile_completed?
+ raise Error, '请先完善基本信息' unless user.profile_is_completed?
Users::ApplyAuthenticationForm.new(params).validate!
# raise Error, '您已经申请过实名认证了' if ApplyUserAuthentication.real_name_auth.processing.exists?(user_id: user.id)
@@ -52,7 +52,7 @@ class Users::ApplyAuthenticationService < ApplicationService
end
def sms_notify_admin
- Educoder::Sms.notify_admin(send_type: 'apply_auth')
+ Gitlink::Sms.notify_admin(send_type: 'apply_auth')
rescue => ex
Util.logger_error(ex)
end
diff --git a/app/services/users/apply_professional_auth_service.rb b/app/services/users/apply_professional_auth_service.rb
index c94481890..2d487f3c3 100644
--- a/app/services/users/apply_professional_auth_service.rb
+++ b/app/services/users/apply_professional_auth_service.rb
@@ -9,7 +9,7 @@ class Users::ApplyProfessionalAuthService < ApplicationService
end
def call
- raise Error, '请先完善基本信息' unless user.profile_completed?
+ raise Error, '请先完善基本信息' unless user.profile_is_completed?
Users::ApplyProfessionalAuthForm.new(params).validate!
# raise Error, '您已经申请过职业认证了' if ApplyUserAuthentication.professional_auth.processing.exists?(user_id: user.id)
@@ -62,7 +62,7 @@ class Users::ApplyProfessionalAuthService < ApplicationService
def sms_notify_admin
sms_cache = Rails.cache.read('apply_pro_certification')
if sms_cache.nil?
- Educoder::Sms.notify_admin(send_type: 'apply_pro_certification')
+ Gitlink::Sms.notify_admin(send_type: 'apply_pro_certification')
Rails.cache.write('apply_pro_certification', 1, expires_in: 5.minutes)
end
rescue => ex
diff --git a/app/services/users/apply_trail_service.rb b/app/services/users/apply_trail_service.rb
index 082a89c9a..d058bb5d0 100644
--- a/app/services/users/apply_trail_service.rb
+++ b/app/services/users/apply_trail_service.rb
@@ -51,7 +51,7 @@ class Users::ApplyTrailService < ApplicationService
end
def send_trial_apply_notify!
- Educoder::Sms.notify_admin(send_type:'user_apply_auth')
+ Gitlink::Sms.notify_admin(send_type:'user_apply_auth')
rescue => ex
Rails.logger.error('发送通知管理员短信失败')
Rails.logger.error(ex.message)
diff --git a/app/services/users/course_service.rb b/app/services/users/course_service.rb
deleted file mode 100644
index 6cc9669cb..000000000
--- a/app/services/users/course_service.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-class Users::CourseService
- include CustomSortable
-
- sort_columns :created_at, :updated_at, default_by: :updated_at, default_direction: :desc
-
- attr_reader :user, :params
-
- def initialize(user, params)
- @user = user
- @params = params
- end
-
- def call
- courses = category_scope_courses.not_deleted.not_excellent
-
- courses = status_filter(courses)
-
- custom_sort(courses, params[:sort_by], params[:sort_direction])
- end
-
- private
-
- def category_scope_courses
- case params[:category]
- when 'study' then
- user.as_student_courses.started
- when 'manage' then
- user.manage_courses
- else
- ids = user.as_student_courses.started.pluck(:id) + user.manage_courses.pluck(:id)
- Course.where(id: ids)
- end
- end
-
- def status_filter(relations)
- # 只有自己查看才有过滤
- return relations unless observed_logged_user?
-
- case params[:status]
- when 'processing' then
- relations.processing
- when 'end' then
- relations.ended
- else
- relations
- end
- end
-
- def observed_logged_user?
- User.current.id == user.id
- end
-end
diff --git a/app/services/users/question_bank_service.rb b/app/services/users/question_bank_service.rb
deleted file mode 100644
index dbaa92e6e..000000000
--- a/app/services/users/question_bank_service.rb
+++ /dev/null
@@ -1,99 +0,0 @@
-class Users::QuestionBankService
- attr_reader :user, :params
-
- def initialize(user, params)
- @user = user
- @params = params
- end
-
- def call
- relations = class_name.classify.constantize.all
-
- relations = category_filter(relations)
- relations = type_filter(relations) if params[:type].present?
-
- relations = relations.where(course_list_id: params[:course_list_id]) if params[:course_list_id].present?
-
- custom_sort(relations, params[:sort_by], params[:sort_direction])
- end
-
- def course_lists
- relation_name = class_name.underscore.pluralize.to_sym
- course_lists = CourseList.joins(relation_name).where.not(relation_name => { id: nil })
-
- category_condition =
- case params[:object_type]
- when 'normal' then { homework_type: 1 }
- when 'group' then { homework_type: 3 }
- when 'exercise' then { container_type: 'Exercise' }
- when 'poll' then { container_type: 'Poll' }
- when 'gtask', 'gtopic' then {}
- else raise ArgumentError
- end
- course_lists = course_lists.where(relation_name => category_condition) if category_condition.present?
-
- type_condition =
- case params[:type]
- when 'personal' then { user_id: user.id }
- when 'publicly' then { is_public: true }
- else {}
- end
- course_lists = course_lists.where(relation_name => type_condition) if type_condition.present?
-
- course_lists.distinct.select(:id, :name)
- end
-
- private
-
- def class_name
- @_class_name ||= begin
- case params[:object_type]
- when 'normal', 'group' then 'HomeworkBank'
- when 'exercise', 'poll' then 'ExerciseBank'
- when 'gtask' then 'GtaskBank'
- when 'gtopic' then 'GtopicBank'
- else raise ArgumentError
- end
- end
- end
-
- def category_filter(relations)
- case params[:object_type]
- when 'normal' then
- relations.where(homework_type: 1)
- when 'group' then
- relations.where(homework_type: 3)
- when 'exercise' then
- relations.where(container_type: 'Exercise')
- when 'poll' then
- relations.where(container_type: 'Poll')
- when 'gtask', 'gtopic' then
- relations.all
- else
- raise ArgumentError
- end
- end
-
- def type_filter(relations)
- case params[:type]
- when 'personal' then relations.where(user_id: user.id)
- when 'publicly' then relations.where(is_public: true)
- else relations
- end
- end
-
- def custom_sort(relations, sort_by, sort_direction)
- case sort_by
- when 'updated_at' then
- relations.order("updated_at #{sort_direction}, id #{sort_direction}")
- when 'name' then
- relations.order("CONVERT(name USING gbk) COLLATE gbk_chinese_ci #{sort_direction}")
- when 'contributor' then
- order_sql = "CONVERT (users.lastname USING gbk) COLLATE gbk_chinese_ci #{sort_direction},"\
- " CONVERT (users.firstname USING gbk) COLLATE gbk_chinese_ci #{sort_direction}"
- relations.joins(:user).where(users: { status: 1 }).order(order_sql)
- else
- relations
- end
- end
-end
diff --git a/app/services/users/register_service.rb b/app/services/users/register_service.rb
new file mode 100644
index 000000000..bb3b3ada1
--- /dev/null
+++ b/app/services/users/register_service.rb
@@ -0,0 +1,58 @@
+class Users::RegisterService < ApplicationService
+ def initialize(params)
+ @login = params[:login]
+ @namespace = params[:namespace]
+ @password = params[:password]
+ @code = params[:code]
+ end
+
+ def call
+ code = strip(@code)
+ login = strip(@login)
+ namespace = strip(@namespace)
+ password = strip(@password)
+
+ Rails.logger.info "Users::RegisterService params: ##### #{params} "
+
+ email, phone =
+ if register_type == 1
+ phone_register(login, code)
+ elsif register_type == 0
+ mail_register(login, code)
+ end
+
+ user = User.new(admin: false, login: namespace, mail: email, phone: phone, type: "User")
+ user.password = password
+ user.activate # 现在因为是验证码,所以在注册的时候就可以激活
+
+ user
+ end
+
+ private
+ # 手机注册
+ def phone_register(login, code)
+ Rails.logger.info("start register by phone: phone is #{login}")
+ email = nil
+ phone = login
+
+ [email, phone]
+ end
+
+ # 邮箱注册
+ def mail_register(login, code)
+ Rails.logger.info("start register by email: email is #{login}")
+ email = login
+ phone = nil
+
+ [email, phone]
+ end
+
+ def register_type
+ phone_mail_type(@login)
+ end
+
+ def phone_mail_type value
+ value =~ /^1\d{10}$/ ? 1 : 0
+ end
+
+end
diff --git a/app/services/users/update_account_service.rb b/app/services/users/update_account_service.rb
index 16824a90e..f8024ff18 100644
--- a/app/services/users/update_account_service.rb
+++ b/app/services/users/update_account_service.rb
@@ -43,7 +43,7 @@ class Users::UpdateAccountService < ApplicationService
end
# 表示资料完整
- user.profile_completed = true
+ # user.profile_completed = true
extension.save!
user.save!
@@ -71,7 +71,7 @@ class Users::UpdateAccountService < ApplicationService
end
def sms_notify_admin name
- Educoder::Sms.send(mobile:'17680641960', send_type:'teacher_register', name: name, user_name:'管理员')
+ Gitlink::Sms.send(mobile:'17680641960', send_type:'teacher_register', name: name, user_name:'管理员')
rescue => ex
Util.logger_error(ex)
end
diff --git a/app/services/weapps/create_course_service.rb b/app/services/weapps/create_course_service.rb
deleted file mode 100644
index 2e5cc5d2f..000000000
--- a/app/services/weapps/create_course_service.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-class Weapps::CreateCourseService < ApplicationService
- attr_reader :course, :params
-
- def initialize(course, params)
- @course = course
- @params = params
- end
-
- def call
- Weapps::CreateCourseForm.new(form_params).validate!
-
- ActiveRecord::Base.transaction do
- course.name = params[:name].to_s.strip
- course.school_id = course.teacher&.school_id
- course.is_public = 0
- course.credit = params[:credit].blank? ? nil : params[:credit]
- course.end_date = params[:end_date].blank? ? nil : params[:end_date]
- course_list = CourseList.find_by(name: params[:course_list_name].to_s.strip)
- if course_list
- course.course_list_id = course_list.id
- else
- new_course_list = CourseList.create!(name: params[:course_list_name].to_s.strip, user_id: course.tea_id, is_admin: 0)
- course.course_list_id = new_course_list.id
- end
- course.is_end = course.end_date.present? && course.end_date < Date.today
-
- course.save!
-
- course.generate_invite_code
- CourseMember.create!(course_id: course.id, user_id: course.tea_id, role: 1)
- course.create_course_modules(params[:course_module_types])
- end
- end
-
- private
-
- def form_params
- params.merge(course: course)
- end
-end
\ No newline at end of file
diff --git a/app/services/weapps/update_course_service.rb b/app/services/weapps/update_course_service.rb
deleted file mode 100644
index b6663bc73..000000000
--- a/app/services/weapps/update_course_service.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-class Weapps::UpdateCourseService < ApplicationService
- attr_reader :course, :params
-
- def initialize(course, params)
- @course = course
- @params = params
- end
-
- def call
- Weapps::UpdateCourseForm.new(form_params).validate!
-
- ActiveRecord::Base.transaction do
- course.name = params[:name].to_s.strip
- course.credit = params[:credit].blank? ? nil : params[:credit]
- course.end_date = params[:end_date].blank? ? nil : params[:end_date]
- course_list = CourseList.find_by(name: params[:course_list_name].to_s.strip)
- if course_list
- course.course_list_id = course_list.id
- else
- new_course_list = CourseList.create!(name: params[:course_list_name].to_s.strip, user_id: course.tea_id, is_admin: 0)
- course.course_list_id = new_course_list.id
- end
- course.is_end = course.end_date.present? && course.end_date < Date.today
- course.save!
- end
- course
- end
-
- private
-
- def form_params
- params.merge(course: course)
- end
-end
\ No newline at end of file
diff --git a/app/views/admins/course_lists/index.html.erb b/app/views/admins/course_lists/index.html.erb
deleted file mode 100644
index cd814ed8a..000000000
--- a/app/views/admins/course_lists/index.html.erb
+++ /dev/null
@@ -1,22 +0,0 @@
-<% define_admin_breadcrumbs do %>
- <% add_admin_breadcrumb('课程列表') %>
-<% end %>
-
-
-
-
- <%= render partial: 'admins/course_lists/shared/list', locals: { courses: @course_lists } %>
-
-
-<%= render 'admins/course_lists/shared/merge_course_list_modal' %>
\ No newline at end of file
diff --git a/app/views/admins/course_lists/index.js.erb b/app/views/admins/course_lists/index.js.erb
deleted file mode 100644
index e4bfead7d..000000000
--- a/app/views/admins/course_lists/index.js.erb
+++ /dev/null
@@ -1 +0,0 @@
-$(".course-list-list-container").html("<%= j render partial: 'admins/course_lists/shared/list', locals: { courses: @course_lists }%>");
\ No newline at end of file
diff --git a/app/views/admins/course_lists/shared/_list.html.erb b/app/views/admins/course_lists/shared/_list.html.erb
deleted file mode 100644
index 228385b3e..000000000
--- a/app/views/admins/course_lists/shared/_list.html.erb
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
- 序号
- ID
- 课程名称
- 课堂数
- 创建者
- <%= sort_tag('创建时间', name: 'created_at', path: admins_course_lists_path) %>
- 操作
-
-
- <% if courses.present? %>
- <% courses.each_with_index do |course_list,index| %>
-
- <%= list_index_no(@params_page.to_i, index) %>
- <%= course_list.id %>
- <%= course_list.name %>
- <% course_count = course_list.courses.size %>
- <%= course_count %>
- <%= link_to course_list.user.try(:real_name),"/users/#{course_list.user.try(:login)}",target:'_blank' %>
- <%= format_time course_list.created_at %>
-
- <% if course_count == 0 %>
- <%= delete_link '删除', admins_course_list_path(course_list, element: ".course-list-item-#{course_list.id}"), class: 'delete-department-action' %>
- <% end %>
- <%= javascript_void_link '修改', class: 'action', data: { course_list_id: course_list.id,
- toggle: 'modal', target: '.admin-merge-course-list-modal', url: merge_admins_course_lists_path } %>
-
-
- <% end %>
- <% else %>
- <%= render 'admins/shared/no_data_for_table' %>
- <% end %>
-
-
-
-<%= render partial: 'admins/shared/paginate', locals: { objects: courses } %>
\ No newline at end of file
diff --git a/app/views/admins/course_lists/shared/_merge_course_list_modal.html.erb b/app/views/admins/course_lists/shared/_merge_course_list_modal.html.erb
deleted file mode 100644
index 4858f5372..000000000
--- a/app/views/admins/course_lists/shared/_merge_course_list_modal.html.erb
+++ /dev/null
@@ -1,29 +0,0 @@
-
\ No newline at end of file
diff --git a/app/views/admins/courses/destroy.js.erb b/app/views/admins/courses/destroy.js.erb
deleted file mode 100644
index 811038193..000000000
--- a/app/views/admins/courses/destroy.js.erb
+++ /dev/null
@@ -1,2 +0,0 @@
-alert("删除成功");
-$(".course-item-<%= @course.id %>").find(".delete-course-action").remove();
\ No newline at end of file
diff --git a/app/views/admins/courses/index.html.erb b/app/views/admins/courses/index.html.erb
deleted file mode 100644
index 84ea98223..000000000
--- a/app/views/admins/courses/index.html.erb
+++ /dev/null
@@ -1,34 +0,0 @@
-<% define_admin_breadcrumbs do %>
- <% add_admin_breadcrumb('课堂列表') %>
-<% end %>
-
-
-
-
- <%= render partial: 'admins/courses/shared/list', locals: { courses: @courses } %>
-
\ No newline at end of file
diff --git a/app/views/admins/courses/index.js.erb b/app/views/admins/courses/index.js.erb
deleted file mode 100644
index 7073c2a81..000000000
--- a/app/views/admins/courses/index.js.erb
+++ /dev/null
@@ -1 +0,0 @@
-$('.course-list-container').html("<%= j( render partial: 'admins/courses/shared/list', locals: { courses: @courses } ) %>");
\ No newline at end of file
diff --git a/app/views/admins/courses/index.xlsx.axlsx b/app/views/admins/courses/index.xlsx.axlsx
deleted file mode 100644
index 7cab54482..000000000
--- a/app/views/admins/courses/index.xlsx.axlsx
+++ /dev/null
@@ -1,29 +0,0 @@
-wb = xlsx_package.workbook
-
-wb.styles do |s|
- blue_cell = s.add_style :bg_color => "FAEBDC", :sz => 10,:height => 25,:b => true, :border => { :style => :thin, :color =>"000000" },:alignment => {wrap_text: true,:horizontal => :center,:vertical => :center}
- wb.add_worksheet(name: "课堂列表") do |sheet|
- sheet.add_row %w(ID 课堂名称 成员 资源 普通作业 分组作业 实训作业 试卷 评测次数 私有 状态 单位 创建者 创建时间 动态时间), :height => 25,:style => blue_cell
-
- @courses.each do |course|
- data = [
- course.id,
- course.name,
- course.course_members_count,
- get_attachment_count(course, 0),
- course.course_homework_count(1),
- course.course_homework_count(3),
- course.course_homework_count(4),
- course.exercises_count,
- course.evaluate_count,
- course.is_public == 1 ? "--" : "√",
- course.is_end ? "已结束" : "正在进行",
- course.school&.name,
- course.teacher&.real_name,
- course.created_at&.strftime('%Y-%m-%d %H:%M'),
- course.max_activity_time ? course.max_activity_time&.strftime('%Y-%m-%d %H:%M') : "--"
- ]
- sheet.add_row(data)
- end
- end
-end
diff --git a/app/views/admins/courses/shared/_import_course_member_modal.html.erb b/app/views/admins/courses/shared/_import_course_member_modal.html.erb
deleted file mode 100644
index d52a60b09..000000000
--- a/app/views/admins/courses/shared/_import_course_member_modal.html.erb
+++ /dev/null
@@ -1,30 +0,0 @@
-
\ No newline at end of file
diff --git a/app/views/admins/courses/shared/_list.html.erb b/app/views/admins/courses/shared/_list.html.erb
deleted file mode 100644
index 4105c8153..000000000
--- a/app/views/admins/courses/shared/_list.html.erb
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-
- 序号
- ID
- 课堂名称
- 成员
- 资源
- 普通作业
- 分组作业
- 实训作业
- 试卷
- 评测次数
- 私有
- 状态
- 单位
- 创建者
- <%= sort_tag('创建时间', name: 'created_at', path: admins_courses_path) %>
- 首页
- 邮件通知
- 操作
-
-
-
- <% if courses.present? %>
- <% courses.each_with_index do |course, index| %>
-
- <%= render partial: 'admins/courses/shared/td', locals: {course: course, no: index} %>
-
- <% end %>
- <% else %>
- <%= render 'admins/shared/no_data_for_table' %>
- <% end %>
-
-
-
-<%= render partial: 'admins/shared/paginate', locals: { objects: courses } %>
\ No newline at end of file
diff --git a/app/views/admins/courses/shared/_td.html.erb b/app/views/admins/courses/shared/_td.html.erb
deleted file mode 100644
index 51cc4b199..000000000
--- a/app/views/admins/courses/shared/_td.html.erb
+++ /dev/null
@@ -1,28 +0,0 @@
-<%= list_index_no((params[:page] || 1).to_i, no) %>
-
<%= course.id %>
-
- <%= link_to(course.name, "/courses/#{course.id}", target: '_blank') %>
-
-
<%= course.course_members_count %>
-
<%= get_attachment_count(course, 0) %>
-
<%= course.course_homework_count(1) %>
-
<%= course.course_homework_count(3) %>
-
<%= course.course_homework_count(4) %>
-
<%= course.exercises_count %>
-
<%= course.evaluate_count %>
-
<%= course.is_public == 1 ? "--" : "√" %>
-
<%= course.is_end ? "已结束" : "正在进行" %>
-
<%= course.school&.name %>
-
<%= course.teacher&.real_name %>
-
<%= course.created_at&.strftime('%Y-%m-%d %H:%M') %>
-
- <%= check_box_tag :homepage_show,!course.homepage_show,course.homepage_show,remote:true,data:{id:course.id},class:"course-setting-form" %>
-
-
- <%= check_box_tag :email_notify,!course.email_notify,course.email_notify,remote:true,data:{id:course.id},class:"course-setting-form" %>
-
-
- <% if course.is_delete == 0 %>
- <%= delete_link '删除', admins_course_path(course, element: ".course-item-#{course.id}"), class: 'delete-course-action' %>
- <% end %>
-
\ No newline at end of file
diff --git a/app/views/admins/courses/update.js.erb b/app/views/admins/courses/update.js.erb
deleted file mode 100644
index 983ac22f0..000000000
--- a/app/views/admins/courses/update.js.erb
+++ /dev/null
@@ -1,3 +0,0 @@
-var index = $("#course-item-<%= @course.id %>").children(":first").html();
-$("#course-item-<%= @course.id %>").html("<%= j render partial: "admins/courses/shared/td",locals: {course: @course, no: 1} %>");
-$("#course-item-<%= @course.id %>").children(":first").html(index);
\ No newline at end of file
diff --git a/app/views/admins/customers/index.html.erb b/app/views/admins/customers/index.html.erb
deleted file mode 100644
index b93a5c1e8..000000000
--- a/app/views/admins/customers/index.html.erb
+++ /dev/null
@@ -1,19 +0,0 @@
-<% define_admin_breadcrumbs do %>
- <% add_admin_breadcrumb('合作伙伴', admins_partners_path) %>
- <% add_admin_breadcrumb(current_partner.school&.name || current_partner.name) %>
-<% end %>
-
-
- <%= form_tag(admins_partner_customers_path(current_partner), method: :get, class: 'form-inline search-form', remote: true) do %>
- <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-md-4 ml-3', placeholder: '客户名称检索') %>
- <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %>
- <% end %>
-
- <%= javascript_void_link('添加', class: 'btn btn-primary', data: { toggle: 'modal', target: '.admin-select-school-modal' }) %>
-
-
-
- <%= render 'admins/customers/shared/list', customers: @customers %>
-
-
-<%= render partial: 'admins/shared/modal/select_school_modal', locals: { title: '添加客户', multiple: true, url: admins_partner_customers_path(current_partner) } %>
\ No newline at end of file
diff --git a/app/views/admins/customers/index.js.erb b/app/views/admins/customers/index.js.erb
deleted file mode 100644
index 8fa2e205d..000000000
--- a/app/views/admins/customers/index.js.erb
+++ /dev/null
@@ -1 +0,0 @@
-$('.customer-list-container').html("<%= j(render partial: 'admins/customers/shared/list', locals: { customers: @customers }) %>");
\ No newline at end of file
diff --git a/app/views/admins/customers/shared/_list.html.erb b/app/views/admins/customers/shared/_list.html.erb
deleted file mode 100644
index 6f84db4e5..000000000
--- a/app/views/admins/customers/shared/_list.html.erb
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
- 序号
- 客户名称
- <%= sort_tag('添加时间', name: 'created_at', path: admins_partner_customers_path(current_partner)) %>
- 操作
-
-
-
- <% if customers.present? %>
- <% customers.each_with_index do |customer, index| %>
-
- <%= list_index_no((params[:page] || 1).to_i, index) %>
- <%= customer.school&.name %>
- <%= customer.created_at&.strftime('%Y-%m-%d %H:%M') %>
-
- <%= delete_link '删除', admins_partner_customer_path(current_partner, customer, element: ".customer-item-#{customer.id}"), class: 'delete-customer-action' %>
-
-
- <% end %>
- <% else %>
- <%= render 'admins/shared/no_data_for_table' %>
- <% end %>
-
-
-
-<%= render partial: 'admins/shared/paginate', locals: { objects: customers } %>
\ No newline at end of file
diff --git a/app/views/admins/daily_school_statistics/export.xlsx.axlsx b/app/views/admins/daily_school_statistics/export.xlsx.axlsx
deleted file mode 100644
index 1757a8fe2..000000000
--- a/app/views/admins/daily_school_statistics/export.xlsx.axlsx
+++ /dev/null
@@ -1,13 +0,0 @@
-wb = xlsx_package.workbook
-wb.add_worksheet(name: '统计总表') do |sheet|
- sheet.add_row %w(ID 单位名称 教师总人数 学生总人数 课堂总数 正在进行课堂数 总实训数 实训评测总数 实训作业总数 其它作业总数 动态时间)
-
- @schools.each do |school|
- sheet.add_row([
- school[:id].to_s, school[:name].to_s, (school[:teacher_count] || 0).to_s, (school[:student_count] || 0).to_s,
- (school[:course_count] || 0).to_s, (school[:active_course_count] || 0).to_s,
- (school[:shixun_count] || 0).to_s,(school[:shixun_evaluate_count] || 0).to_s, (school[:homework_count] || 0).to_s,
- (school[:other_homework_count] || 0).to_s, format_time(school[:nearly_course_time])
- ])
- end
-end
\ No newline at end of file
diff --git a/app/views/admins/daily_school_statistics/index.html.erb b/app/views/admins/daily_school_statistics/index.html.erb
deleted file mode 100644
index c3eb43691..000000000
--- a/app/views/admins/daily_school_statistics/index.html.erb
+++ /dev/null
@@ -1,29 +0,0 @@
-<% define_admin_breadcrumbs do %>
- <% add_admin_breadcrumb('统计总表', admins_daily_school_statistics_path) %>
-<% end %>
-
-
- <%= form_tag(admins_daily_school_statistics_path, method: :get, class: 'form-inline search-form', remote: true) do %>
- <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: 'ID/单位名称搜索') %>
- <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %>
- <% end %>
-
- <%#= link_to '导出Excel', export_admins_daily_school_statistics_path(format: :xlsx), class: 'btn btn-outline-primary export-action' %>
- <%= javascript_void_link '导出Excel', class: 'btn btn-outline-primary export-action', 'data-url': export_admins_daily_school_statistics_path(format: :xlsx) %>
-
-
-
- 统计总计:
- 教师总人数<%= @teacher_total %> 人,
- 学生总人数<%= @student_total %> 人,
- 课堂总数<%= @course_total %> 个,
- 正在进行课堂总数<%= @active_course_total %> 个,
- 实训总数<%= @shixun_total %> 个,
- 实训评测总数<%= @shixun_evaluate_total %> 个,
- 实训作业总数<%= @shixun_homework_total %> 个,
- 其它作业总数<%= @other_homework_total %> 个
-
-
-
- <%= render partial: 'admins/daily_school_statistics/shared/list', locals: { statistics: @statistics } %>
-
\ No newline at end of file
diff --git a/app/views/admins/daily_school_statistics/index.js.erb b/app/views/admins/daily_school_statistics/index.js.erb
deleted file mode 100644
index d3e261f64..000000000
--- a/app/views/admins/daily_school_statistics/index.js.erb
+++ /dev/null
@@ -1 +0,0 @@
-$(".daily-school-statistic-list-container").html("<%= j(render partial: 'admins/daily_school_statistics/shared/list', locals: { statistics: @statistics }) %>")
\ No newline at end of file
diff --git a/app/views/admins/daily_school_statistics/shared/_list.html.erb b/app/views/admins/daily_school_statistics/shared/_list.html.erb
deleted file mode 100644
index 6982891ee..000000000
--- a/app/views/admins/daily_school_statistics/shared/_list.html.erb
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
- 序号
- 单位名称
- <%= sort_tag('教师总数', name: 'teacher_count', path: admins_daily_school_statistics_path) %>
- <%= sort_tag('学生总数', name: 'student_count', path: admins_daily_school_statistics_path) %>
- <%= sort_tag('课堂总数', name: 'course_count', path: admins_daily_school_statistics_path) %>
- <%= sort_tag('正在进行课堂数', name: 'active_course_count', path: admins_daily_school_statistics_path) %>
- <%= sort_tag('实训总数', name: 'shixun_count', path: admins_daily_school_statistics_path) %>
-
- <%= sort_tag(name: 'shixun_evaluate_count', path: admins_daily_school_statistics_path) do %>
- 实训评测总数
-
- <% end %>
-
- <%= sort_tag('实训作业总数', name: 'homework_count', path: admins_daily_school_statistics_path) %>
- <%= sort_tag('其它作业总数', name: 'other_homework_count', path: admins_daily_school_statistics_path) %>
- <%= sort_tag('动态时间', name: 'nearly_course_time', path: admins_daily_school_statistics_path) %>
-
-
-
- <% if statistics.present? %>
- <% statistics.each_with_index do |statistic, index| %>
-
- <%= list_index_no(@params_page.to_i, index) %>
-
- <%= link_to statistic[:name], "/colleges/#{statistic[:id]}/statistics",
- target: '_blank', data: { toggle: 'tooltip', title: '点击查看学校统计概况' } %>
-
- <%= statistic[:teacher_count].to_i %>
- <%= statistic[:student_count].to_i %>
- <%= statistic[:course_count].to_i %>
- <%= statistic[:active_course_count].to_i %>
- <%= statistic[:shixun_count].to_i %>
- <%= statistic[:shixun_evaluate_count].to_i %>
- <%= statistic[:homework_count].to_i %>
- <%= statistic[:other_homework_count].to_i %>
- <%= statistic[:nearly_course_time]&.strftime('%Y-%m-%d %H:%M') %>
-
- <% end %>
- <% else %>
- <%= render 'admins/shared/no_data_for_table' %>
- <% end %>
-
-
-
-<%= render partial: 'admins/shared/paginate', locals: { objects: statistics } %>
\ No newline at end of file
diff --git a/app/views/admins/department_applies/index.html.erb b/app/views/admins/department_applies/index.html.erb
deleted file mode 100644
index 8a8a41e41..000000000
--- a/app/views/admins/department_applies/index.html.erb
+++ /dev/null
@@ -1,18 +0,0 @@
-<% define_admin_breadcrumbs do %>
- <% add_admin_breadcrumb('部门审批') %>
-<% end %>
-
-
- <%= form_tag(admins_department_applies_path(unsafe_params), method: :get, class: 'form-inline search-form mt-3', remote: true) do %>
- <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: '部门名称检索') %>
- <%= submit_tag('搜索', class: 'btn btn-primary ml-3','data-disable-with':"搜索中...") %>
- <%= link_to "清除",admins_department_applies_path(keyword:nil),class:"btn btn-default",remote:true %>
- <% end %>
-
-
-
- <%= render(partial: 'admins/department_applies/shared/list', locals: { applies: @depart_applies }) %>
-
-
-<%= render(partial: 'admins/shared/admin_common_refuse_modal') %>
-<%= render 'admins/departments/shared/merge_department_modal' %>
\ No newline at end of file
diff --git a/app/views/admins/department_applies/index.js.erb b/app/views/admins/department_applies/index.js.erb
deleted file mode 100644
index 8e11b834c..000000000
--- a/app/views/admins/department_applies/index.js.erb
+++ /dev/null
@@ -1 +0,0 @@
-$(".department-applies-list-container").html("<%= j render partial: "admins/department_applies/shared/list",locals: {applies:@depart_applies} %>")
\ No newline at end of file
diff --git a/app/views/admins/department_applies/shared/_list.html.erb b/app/views/admins/department_applies/shared/_list.html.erb
deleted file mode 100644
index 87d5ab66f..000000000
--- a/app/views/admins/department_applies/shared/_list.html.erb
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
- 序号
- ID
- 部门名称
- 单位名称
- 创建者
- <%= sort_tag('创建于', name: 'created_at', path: admins_department_applies_path) %>
- 操作
-
-
-
- <% if applies.present? %>
- <% applies.each_with_index do |apply, index| %>
-
- <%= list_index_no((params[:page] || 1).to_i, index) %>
- <%= apply.id %>
- <%= apply.name %>
- <%= apply.school.try(:name) %>
- <%= apply&.user&.real_name %>
- <%= format_time apply.created_at %>
-
- <%= agree_link '批准', agree_admins_department_apply_path(apply, element: ".department-apply-#{apply.id}"), 'data-confirm': '确认批准通过?' %>
- <%= javascript_void_link('删除', class: 'action refuse-action',
- data: {
- toggle: 'modal', target: '.admin-common-refuse-modal', id: apply.id, title: "删除原因", type: "delete",
- url: admins_department_apply_path(apply,tip:"unapplied", element: ".department-apply-#{apply.id}")
- }) %>
- <%= javascript_void_link '更改', class: 'action', data: { school_id: apply.school_id, department_id: apply.id,
- toggle: 'modal', target: '.admin-merge-department-modal', url: merge_admins_department_applies_path } %>
-
-
- <% end %>
- <% else %>
- <%= render 'admins/shared/no_data_for_table' %>
- <% end %>
-
-
-
-<%= render partial: 'admins/shared/paginate', locals: { objects: applies } %>
\ No newline at end of file
diff --git a/app/views/admins/department_members/create.js.erb b/app/views/admins/department_members/create.js.erb
deleted file mode 100644
index 6bf0a6ac3..000000000
--- a/app/views/admins/department_members/create.js.erb
+++ /dev/null
@@ -1,6 +0,0 @@
-$('.modal.admin-add-department-member-modal').modal('hide');
-$.notify({ message: '操作成功' });
-
-var index = $(".department-item-<%= current_department.id %>").children(":first").html();
-$('.department-list-table .department-item-<%= current_department.id %>').html("<%= j(render partial: 'admins/departments/shared/department_item', locals: { department: current_department, index: 1 }) %>");
-$(".department-item-<%= current_department.id %>").children(":first").html(index);
\ No newline at end of file
diff --git a/app/views/admins/department_members/destroy.js.erb b/app/views/admins/department_members/destroy.js.erb
deleted file mode 100644
index d3eb3755b..000000000
--- a/app/views/admins/department_members/destroy.js.erb
+++ /dev/null
@@ -1,2 +0,0 @@
-$.notify({ message: '操作成功' });
-$('.department-list-container .department-item-<%= current_department.id %> .member-user-item-<%= @member.user_id %>').remove();
\ No newline at end of file
diff --git a/app/views/admins/departments/edit.js.erb b/app/views/admins/departments/edit.js.erb
deleted file mode 100644
index dc86d3ae0..000000000
--- a/app/views/admins/departments/edit.js.erb
+++ /dev/null
@@ -1,2 +0,0 @@
-$('.admin-modal-container').html("<%= j( render partial: 'admins/departments/shared/edit_department_modal', locals: { department: current_department } ) %>");
-$('.modal.admin-edit-department-modal').modal('show');
\ No newline at end of file
diff --git a/app/views/admins/departments/index.html.erb b/app/views/admins/departments/index.html.erb
deleted file mode 100644
index b1a0fee3e..000000000
--- a/app/views/admins/departments/index.html.erb
+++ /dev/null
@@ -1,33 +0,0 @@
-<% define_admin_breadcrumbs do %>
- <% add_admin_breadcrumb('部门列表') %>
-<% end %>
-
-
-
-
- <%= render partial: 'admins/departments/shared/list',
- locals: { departments: @departments, users_count: @users_count, professional_auth_count: @professional_auth_count } %>
-
-
-<%= render 'admins/departments/shared/create_department_modal' %>
-<%= render 'admins/departments/shared/add_department_member_modal' %>
-<%= render 'admins/departments/shared/merge_department_modal' %>
\ No newline at end of file
diff --git a/app/views/admins/departments/index.js.erb b/app/views/admins/departments/index.js.erb
deleted file mode 100644
index bd2e4b25d..000000000
--- a/app/views/admins/departments/index.js.erb
+++ /dev/null
@@ -1 +0,0 @@
-$('.department-list-container').html("<%= j(render partial: 'admins/departments/shared/list', locals: { departments: @departments, users_count: @users_count, professional_auth_count: @professional_auth_count }) %>");
\ No newline at end of file
diff --git a/app/views/admins/departments/shared/_add_department_member_modal.html.erb b/app/views/admins/departments/shared/_add_department_member_modal.html.erb
deleted file mode 100644
index 5d2707222..000000000
--- a/app/views/admins/departments/shared/_add_department_member_modal.html.erb
+++ /dev/null
@@ -1,30 +0,0 @@
-
\ No newline at end of file
diff --git a/app/views/admins/departments/shared/_create_department_modal.html.erb b/app/views/admins/departments/shared/_create_department_modal.html.erb
deleted file mode 100644
index ae6605eb8..000000000
--- a/app/views/admins/departments/shared/_create_department_modal.html.erb
+++ /dev/null
@@ -1,35 +0,0 @@
-
\ No newline at end of file
diff --git a/app/views/admins/departments/shared/_department_item.html.erb b/app/views/admins/departments/shared/_department_item.html.erb
deleted file mode 100644
index 6018686ff..000000000
--- a/app/views/admins/departments/shared/_department_item.html.erb
+++ /dev/null
@@ -1,36 +0,0 @@
-
<%= list_index_no((params[:page] || 1).to_i, index) %>
-<% not_list = defined?(:users_count) %>
-
-
<%= overflow_hidden_span department.name, width: 150 %>
-
<%= overflow_hidden_span department.school.name, width: 150 %>
-
-<% if not_list %>
-
<%= department.user_extensions.count %>
-
<%= department.user_extensions.joins(:user).where(users: { professional_certification: true }).count %>
-<% else %>
-
<%= users_count.fetch(department.id, 0) %>
-
<%= professional_auth_count.fetch(department.id, 0) %>
-<% end %>
-
-
- <%= render partial: 'admins/departments/shared/member_users', locals: { department: department } %>
-
-
- <% if department.identifier.present? %>
- <%= link_to department.identifier.to_s, "/colleges/#{department.identifier}/statistics", target: '_blank' %>
- <% else %>
- --
- <% end %>
-
-
<%= department.host_count %>
-
<%= department.created_at&.strftime('%Y-%m-%d %H:%M') %>
-
- <%= link_to '编辑', edit_admins_department_path(department), remote: true, class: 'action' %>
-
- <%= javascript_void_link '添加管理员', class: 'action', data: { department_id: department.id, toggle: 'modal', target: '.admin-add-department-member-modal' } %>
-
- <%= javascript_void_link '更改', class: 'action', data: { school_id: department.school_id, department_id: department.id,
- toggle: 'modal', target: '.admin-merge-department-modal', url: merge_admins_departments_path } %>
-
- <%= delete_link '删除', admins_department_path(department, element: ".department-item-#{department.id}"), class: 'delete-department-action' %>
-
\ No newline at end of file
diff --git a/app/views/admins/departments/shared/_edit_department_modal.html.erb b/app/views/admins/departments/shared/_edit_department_modal.html.erb
deleted file mode 100644
index 38b43bbce..000000000
--- a/app/views/admins/departments/shared/_edit_department_modal.html.erb
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
- <%= simple_form_for([:admins, department], html: { class: 'admin-edit-department-form' }, defaults: { wrapper_html: { class: 'offset-md-1 col-md-10' } }) do |f| %>
- <%= f.input :name, as: :string, label: '名称' %>
- <%= f.input :identifier, as: :string, label: '统计链接' %>
- <%= f.input :host_count, as: :integer, label: '云主机数' %>
-
-
- <% end %>
-
-
-
-
-
\ No newline at end of file
diff --git a/app/views/admins/departments/shared/_list.html.erb b/app/views/admins/departments/shared/_list.html.erb
deleted file mode 100644
index 09ba2a65f..000000000
--- a/app/views/admins/departments/shared/_list.html.erb
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
- 序号
- 部门名称
- 单位名称
- 用户数
- 已职业认证
- 部门管理员
- 统计链接
- 云主机数
- <%= sort_tag('创建时间', name: 'created_at', path: admins_departments_path) %>
- 操作
-
-
-
- <% if departments.present? %>
- <% departments.each_with_index do |department, index| %>
-
- <%= render partial: 'admins/departments/shared/department_item', locals: {department: department, index: index} %>
-
- <% end %>
- <% else %>
- <%= render 'admins/shared/no_data_for_table' %>
- <% end %>
-
-
-
-<%= render partial: 'admins/shared/paginate', locals: { objects: departments } %>
\ No newline at end of file
diff --git a/app/views/admins/departments/shared/_member_users.html.erb b/app/views/admins/departments/shared/_member_users.html.erb
deleted file mode 100644
index 8d4d466db..000000000
--- a/app/views/admins/departments/shared/_member_users.html.erb
+++ /dev/null
@@ -1,12 +0,0 @@
-
- <% department.member_users.each do |user| %>
-
- <%= link_to user.real_name, "/users/#{user.login}", target: '_blank', data: { toggle: 'tooltip', title: '个人主页' } %>
- <%= link_to(admins_department_department_member_path(department, user_id: user.id),
- method: :delete, remote: true, class: 'ml-1 delete-member-action',
- data: { confirm: '确认删除吗?' }) do %>
-
- <% end %>
-
- <% end %>
-
\ No newline at end of file
diff --git a/app/views/admins/departments/shared/_merge_department_modal.html.erb b/app/views/admins/departments/shared/_merge_department_modal.html.erb
deleted file mode 100644
index 5c1ca6892..000000000
--- a/app/views/admins/departments/shared/_merge_department_modal.html.erb
+++ /dev/null
@@ -1,30 +0,0 @@
-
\ No newline at end of file
diff --git a/app/views/admins/departments/update.js.erb b/app/views/admins/departments/update.js.erb
deleted file mode 100644
index d20ca9524..000000000
--- a/app/views/admins/departments/update.js.erb
+++ /dev/null
@@ -1,6 +0,0 @@
-$('.modal.admin-edit-department-modal').modal('hide');
-$.notify({ message: '操作成功' });
-
-var index = $(".department-item-<%= current_department.id %>").children(":first").html();
-$('.department-list-table .department-item-<%= current_department.id %>').html("<%= j(render partial: 'admins/departments/shared/department_item', locals: {department: current_department, index: 1}) %>");
-$(".department-item-<%= current_department.id %>").children(":first").html(index);
\ No newline at end of file
diff --git a/app/views/admins/disciplines/adjust_position.js.erb b/app/views/admins/disciplines/adjust_position.js.erb
deleted file mode 100644
index a0928056e..000000000
--- a/app/views/admins/disciplines/adjust_position.js.erb
+++ /dev/null
@@ -1,5 +0,0 @@
-<% if @message.present? %>
-$.notify({ message: "<%= @message %>" });
-<% else %>
-$(".discipline-list-container").html("<%= j(render :partial => 'admins/disciplines/shared/list') %>");
-<% end %>
\ No newline at end of file
diff --git a/app/views/admins/disciplines/destroy.js.erb b/app/views/admins/disciplines/destroy.js.erb
deleted file mode 100644
index ea9aedcd8..000000000
--- a/app/views/admins/disciplines/destroy.js.erb
+++ /dev/null
@@ -1,2 +0,0 @@
-$.notify({ message: '删除成功' });
-$(".discipline-item-<%= @discipline_id %>").remove();
\ No newline at end of file
diff --git a/app/views/admins/disciplines/edit.js.erb b/app/views/admins/disciplines/edit.js.erb
deleted file mode 100644
index 48c5b789f..000000000
--- a/app/views/admins/disciplines/edit.js.erb
+++ /dev/null
@@ -1,2 +0,0 @@
-$('.admin-modal-container').html("<%= j( render partial: 'admins/disciplines/shared/edit_discipline_modal', locals: { discipline: @discipline } ) %>");
-$('.modal.admin-edit-discipline-modal').modal('show');
\ No newline at end of file
diff --git a/app/views/admins/disciplines/index.html.erb b/app/views/admins/disciplines/index.html.erb
deleted file mode 100644
index f4b116b93..000000000
--- a/app/views/admins/disciplines/index.html.erb
+++ /dev/null
@@ -1,17 +0,0 @@
-<% define_admin_breadcrumbs do %>
- <% add_admin_breadcrumb('课程方向', admins_disciplines_path) %>
-<% end %>
-
-
-
-
- <%= render(partial: 'admins/disciplines/shared/list') %>
-
-
-<%= render 'admins/disciplines/shared/create_discipline_modal' %>
-<%= render partial: 'admins/disciplines/shared/import_discipline_modal' %>
diff --git a/app/views/admins/disciplines/shared/_create_discipline_modal.html.erb b/app/views/admins/disciplines/shared/_create_discipline_modal.html.erb
deleted file mode 100644
index 7eb5d8985..000000000
--- a/app/views/admins/disciplines/shared/_create_discipline_modal.html.erb
+++ /dev/null
@@ -1,28 +0,0 @@
-
\ No newline at end of file
diff --git a/app/views/admins/disciplines/shared/_edit_discipline_modal.html.erb b/app/views/admins/disciplines/shared/_edit_discipline_modal.html.erb
deleted file mode 100644
index ce511e27c..000000000
--- a/app/views/admins/disciplines/shared/_edit_discipline_modal.html.erb
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
- <%= simple_form_for([:admins, discipline], html: { class: 'admin-edit-discipline-form' }, defaults: { wrapper_html: { class: 'offset-md-1 col-md-10' } }) do |f| %>
- <%= f.input :name, as: :string, label: '名称' %>
-
-
- <% end %>
-
-
-
-
-
\ No newline at end of file
diff --git a/app/views/admins/disciplines/shared/_import_discipline_modal.html.erb b/app/views/admins/disciplines/shared/_import_discipline_modal.html.erb
deleted file mode 100644
index de54fd758..000000000
--- a/app/views/admins/disciplines/shared/_import_discipline_modal.html.erb
+++ /dev/null
@@ -1,30 +0,0 @@
-
\ No newline at end of file
diff --git a/app/views/admins/disciplines/shared/_list.html.erb b/app/views/admins/disciplines/shared/_list.html.erb
deleted file mode 100644
index 38cbe8c9c..000000000
--- a/app/views/admins/disciplines/shared/_list.html.erb
+++ /dev/null
@@ -1,37 +0,0 @@
-<% max_position = @disciplines.pluck(:position).max %>
-
-
-
- 序号
- 课程方向
- 实践课程
- 实训
- 题库
- 操作
-
-
-
- <% if @disciplines.present? %>
- <% @disciplines.each do |discipline| %>
-
- <%= discipline.position %>
-
- <%= link_to discipline.name, admins_sub_disciplines_path(discipline_id: discipline), :title => discipline.name %>
-
- <%= check_box_tag :subject,!discipline.subject,discipline.subject,remote:true,data:{id:discipline.id},class:"discipline-source-form" %>
- <%= check_box_tag :shixun,!discipline.shixun,discipline.shixun,remote:true,data:{id:discipline.id},class:"discipline-source-form" %>
- <%= check_box_tag :question,!discipline.question,discipline.question,remote:true,data:{id:discipline.id},class:"discipline-source-form" %>
-
- <%= javascript_void_link('上移', class: 'move-action', data: { id: discipline.id, opr: "up" }, style: discipline.position == 1 ? 'display:none' : '') %>
- <%= javascript_void_link('下移', class: 'move-action', data: { id: discipline.id, opr: "down" }, style: discipline.position == max_position ? 'display:none' : '') %>
-
- <%= link_to '编辑', edit_admins_discipline_path(discipline), remote: true, class: 'action' %>
- <%= delete_link '删除', admins_discipline_path(discipline, element: ".discipline-item-#{discipline.id}"), class: 'delete-discipline-action' %>
-
-
- <% end %>
- <% else %>
- <%= render 'admins/shared/no_data_for_table' %>
- <% end %>
-
-
\ No newline at end of file
diff --git a/app/views/admins/disciplines/update.js.erb b/app/views/admins/disciplines/update.js.erb
deleted file mode 100644
index 0c051e1dd..000000000
--- a/app/views/admins/disciplines/update.js.erb
+++ /dev/null
@@ -1,6 +0,0 @@
-<% if @message.present? %>
-$.notify({ message: "<%= @message %>" });
-<% else %>
-$('.modal.admin-edit-discipline-modal').modal("hide");
-$(".discipline-list-container").html("<%= j(render :partial => 'admins/disciplines/shared/list') %>");
-<% end %>
\ No newline at end of file
diff --git a/app/views/admins/edu_settings/_form.html.erb b/app/views/admins/edu_settings/_form.html.erb
new file mode 100644
index 000000000..cce930f2a
--- /dev/null
+++ b/app/views/admins/edu_settings/_form.html.erb
@@ -0,0 +1,38 @@
+
+
+
+
+ <%= form_for @edu_setting, url: {controller: "edu_settings", action: "#{type}"} do |p| %>
+
+
+
+ 变量名 *
+
+ <%= p.text_field :name, class: "form-control input-lg",required: true%>
+
+
+
+ 变量值 *
+
+ <%= p.text_field :value, class: "form-control input-lg",required: true%>
+
+
+
+ 备注说明
+
+ <%= p.text_area :description, class: "form-control", placeholder: ""%>
+
+
+
+ <% end %>
+
+
+
\ No newline at end of file
diff --git a/app/views/admins/edu_settings/_list.html.erb b/app/views/admins/edu_settings/_list.html.erb
new file mode 100644
index 000000000..a37cc9bef
--- /dev/null
+++ b/app/views/admins/edu_settings/_list.html.erb
@@ -0,0 +1,36 @@
+
+
+
+ 序号
+ 变量名
+ 变量值
+ 备注说明
+ <%= sort_tag('创建时间', name: 'created_at', path: admins_edu_settings_path) %>
+ 操作
+
+
+
+ <% if edu_settings.present? %>
+ <% edu_settings.each_with_index do |edu_setting, index| %>
+
+ <%= list_index_no((params[:page] || 1).to_i, index) %>
+
+ <%= edu_setting.name %>
+
+ <%= edu_setting.value %>
+
+ <%= overflow_hidden_span display_text(edu_setting.description), width: 200 %>
+ <%= edu_setting.created_at&.strftime('%Y-%m-%d %H:%M') %>
+
+ <%= link_to "编辑", edit_admins_edu_setting_path(edu_setting), remote: true, class: "action" %>
+ <%= link_to "删除", admins_edu_setting_path(edu_setting), method: :delete, data:{confirm: "确认删除的吗?"}, class: "action" %>
+
+
+ <% end %>
+ <% else %>
+ <%= render 'admins/shared/no_data_for_table' %>
+ <% end %>
+
+
+
+<%= render partial: 'admins/shared/paginate', locals: { objects: edu_settings } %>
\ No newline at end of file
diff --git a/app/views/admins/edu_settings/edit.js.erb b/app/views/admins/edu_settings/edit.js.erb
new file mode 100644
index 000000000..c2e0405f2
--- /dev/null
+++ b/app/views/admins/edu_settings/edit.js.erb
@@ -0,0 +1,2 @@
+$("#edu_setting-modals").html("<%= j render(partial: 'admins/edu_settings/form', locals: {type: 'update'}) %>")
+$(".edu_setting-change-modal").modal('show');
\ No newline at end of file
diff --git a/app/views/admins/edu_settings/index.html.erb b/app/views/admins/edu_settings/index.html.erb
new file mode 100644
index 000000000..d52480bae
--- /dev/null
+++ b/app/views/admins/edu_settings/index.html.erb
@@ -0,0 +1,22 @@
+<% define_admin_breadcrumbs do %>
+ <% add_admin_breadcrumb('全局变量配置') %>
+<% end %>
+
+
+ <%= form_tag(admins_edu_settings_path, method: :get, class: 'form-inline search-form flex-1', remote: true) do %>
+ <%= text_field_tag(:search, params[:search], class: 'form-control col-12 col-md-2 mr-3', placeholder: '关键字检索') %>
+ <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %>
+
+ <% end %>
+ <%= link_to "新增", new_admins_edu_setting_path, remote: true, class: "btn btn-primary pull-right", "data-disabled-with":"...新增" %>
+
+
+
+ 说明:该界面适用于存储全局变量.
+
+
+
+ <%= render partial: 'admins/edu_settings/list', locals: { edu_settings: @edu_settings } %>
+
+
+
diff --git a/app/views/admins/edu_settings/index.js.erb b/app/views/admins/edu_settings/index.js.erb
new file mode 100644
index 000000000..92a4bfeff
--- /dev/null
+++ b/app/views/admins/edu_settings/index.js.erb
@@ -0,0 +1 @@
+$('.edu_settings-list-container').html("<%= j( render partial: 'admins/edu_settings/list', locals: { edu_settings: @edu_settings } ) %>");
\ No newline at end of file
diff --git a/app/views/admins/edu_settings/new.js.erb b/app/views/admins/edu_settings/new.js.erb
new file mode 100644
index 000000000..21abf3895
--- /dev/null
+++ b/app/views/admins/edu_settings/new.js.erb
@@ -0,0 +1,2 @@
+$("#edu_setting-modals").html("<%= j render(partial: 'admins/edu_settings/form', locals: {type: 'create'}) %>")
+$(".edu_setting-change-modal").modal('show');
\ No newline at end of file
diff --git a/app/views/admins/examination_authentications/index.html.erb b/app/views/admins/examination_authentications/index.html.erb
deleted file mode 100644
index c360a7a18..000000000
--- a/app/views/admins/examination_authentications/index.html.erb
+++ /dev/null
@@ -1,30 +0,0 @@
-<% define_admin_breadcrumbs do %>
- <% add_admin_breadcrumb('试卷审批') %>
-<% end %>
-
-
-
-
- <%= render(partial: 'admins/examination_authentications/shared/list', locals: { applies: @applies }) %>
-
\ No newline at end of file
diff --git a/app/views/admins/examination_authentications/index.js.erb b/app/views/admins/examination_authentications/index.js.erb
deleted file mode 100644
index 361059e73..000000000
--- a/app/views/admins/examination_authentications/index.js.erb
+++ /dev/null
@@ -1 +0,0 @@
-$('.examination-authentication-list-container').html("<%= j( render partial: 'admins/examination_authentications/shared/list', locals: { applies: @applies } ) %>");
\ No newline at end of file
diff --git a/app/views/admins/examination_authentications/shared/_item_show_modal.html.erb b/app/views/admins/examination_authentications/shared/_item_show_modal.html.erb
deleted file mode 100644
index 9a1c891fe..000000000
--- a/app/views/admins/examination_authentications/shared/_item_show_modal.html.erb
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
-
-
-
题型:<%= item.type_string %>
-
难度:<%= item.difficulty_string %>
-
-
-
-
<%= item.name %>
- <% item.item_choices.each do |choice| %>
-
- <% if item.item_type == "MULTIPLE" %>
- <%= check_box_tag(:choice, true, choice.is_answer, class: 'form-check-input') %>
- <%= choice.choice_text %>
- <% elsif item.item_type == "SINGLE" || item.item_type == "JUDGMENT" %>
- <%= radio_button_tag(:choice, true, choice.is_answer, class: 'form-check-input') %>
- <%= choice.choice_text %>
- <% else %>
- 答案:<%= choice.choice_text %>
- <% end %>
-
- <% end %>
-
-
-
-
-
\ No newline at end of file
diff --git a/app/views/admins/examination_authentications/shared/_list.html.erb b/app/views/admins/examination_authentications/shared/_list.html.erb
deleted file mode 100644
index a751a7fa9..000000000
--- a/app/views/admins/examination_authentications/shared/_list.html.erb
+++ /dev/null
@@ -1,54 +0,0 @@
-<% is_processed = params[:status].to_s != 'pending' %>
-
-
-
- 序号
- 头像
- 创建者
- 学校
- 试卷
- 提交时间
- <% if !is_processed %>
- 操作
- <% else %>
- 审批结果
- <% end %>
-
-
-
- <% if applies.present? %>
- <% applies.each_with_index do |apply, index| %>
- <% user = apply.user %>
- <% exam = ExaminationBank.find apply.container_id %>
-
- <%= list_index_no((params[:page] || 1).to_i, index) %>
-
- <%= link_to "/users/#{user.login}", class: 'examination-authentication-avatar', target: '_blank', data: { toggle: 'tooltip', title: '个人主页' } do %>
-
- <% end %>
-
- <%= user.real_name %>
- <%= raw [user.school_name.presence, user.department_name.presence].compact.join(' ') %>
-
- <%= link_to exam.name, "/paperlibrary/see/#{exam.id}", target: "_blank" %>
-
-
- <%= apply.updated_at.strftime('%Y-%m-%d %H:%M') %>
-
-
- <% if !is_processed %>
- <%= agree_link '同意', agree_admins_examination_authentication_path(apply, element: ".examination-authentication-#{apply.id}"), 'data-confirm': '确认同意该审批?', 'data-disable-with': "提交中..." %>
- <%= agree_link '拒绝', refuse_admins_examination_authentication_path(apply, element: ".examination-authentication-#{apply.id}"), 'data-confirm': '确认拒绝该审批?', 'data-disable-with': "拒绝中..." %>
- <% else %>
- <%= apply.status_text %>
- <% end %>
-
-
- <% end %>
- <% else %>
- <%= render 'admins/shared/no_data_for_table' %>
- <% end %>
-
-
-
-<%= render partial: 'admins/shared/paginate', locals: { objects: applies } %>
\ No newline at end of file
diff --git a/app/views/admins/examination_authentications/show.js.erb b/app/views/admins/examination_authentications/show.js.erb
deleted file mode 100644
index 70e44404f..000000000
--- a/app/views/admins/examination_authentications/show.js.erb
+++ /dev/null
@@ -1,2 +0,0 @@
-$('.admin-modal-container').html("<%= j( render partial: 'admins/item_authentications/shared/item_show_modal', locals: { item: @item } ) %>");
-$('.modal.admin-item-show-modal').modal('show');
\ No newline at end of file
diff --git a/app/views/admins/faqs/_form_modal.html.erb b/app/views/admins/faqs/_form_modal.html.erb
new file mode 100644
index 000000000..45ceb901c
--- /dev/null
+++ b/app/views/admins/faqs/_form_modal.html.erb
@@ -0,0 +1,24 @@
+
+
+
+
+ <%= form_for @faq, url: {controller: "faqs", action: "#{type}"} do |p| %>
+
+ <%= p.text_field :question,class: "form-control input-lg",placeholder: "问题",required: true, maxlength: 100%>
+
+
+ <%= p.text_field :url,class: "form-control input-lg",placeholder: "链接",required: true, maxlength: 100%>
+
+
+ <% end %>
+
+
+
\ No newline at end of file
diff --git a/app/views/admins/faqs/_list.html.erb b/app/views/admins/faqs/_list.html.erb
new file mode 100644
index 000000000..81e658edd
--- /dev/null
+++ b/app/views/admins/faqs/_list.html.erb
@@ -0,0 +1,33 @@
+
+
+
+ 序号
+ 标题
+ url
+ <%= sort_tag('创建于', name: 'created_at', path: admins_faqs_path) %>
+ <%= sort_tag('更新于', name: 'updated_at', path: admins_faqs_path) %>
+ 操作
+
+
+
+ <% if faqs.present? %>
+ <% faqs.each_with_index do |faq, index| %>
+
+ <%= list_index_no((params[:page] || 1).to_i, index) %>
+ <%= faq.question%>
+ <%= link_to faq.url, target: '_blank' %>
+ <%= display_text(faq.created_at&.strftime('%Y-%m-%d %H:%M')) %>
+ <%= display_text(faq.updated_at&.strftime('%Y-%m-%d %H:%M')) %>
+
+ <%= link_to "编辑", edit_admins_faq_path(faq), remote: true, class: "action" %>
+ <%= link_to "删除", admins_faqs_path(faq.id), method: :delete, data:{confirm: "确认删除的吗?"}, class: "delete-project-action" %>
+
+
+ <% end %>
+ <% else %>
+ <%= render 'admins/shared/no_data_for_table' %>
+ <% end %>
+
+
+
+<%= render partial: 'admins/shared/paginate', locals: { objects: faqs } %>
diff --git a/app/views/admins/faqs/edit.js.erb b/app/views/admins/faqs/edit.js.erb
new file mode 100644
index 000000000..fc121b18e
--- /dev/null
+++ b/app/views/admins/faqs/edit.js.erb
@@ -0,0 +1,2 @@
+$("#faq-modals").html("<%= j render(partial: 'admins/faqs/form_modal', locals: {type: 'update'}) %>")
+$(".faq-change-modal").modal('show');
\ No newline at end of file
diff --git a/app/views/admins/faqs/index.html.erb b/app/views/admins/faqs/index.html.erb
new file mode 100644
index 000000000..3b327249b
--- /dev/null
+++ b/app/views/admins/faqs/index.html.erb
@@ -0,0 +1,17 @@
+<% define_admin_breadcrumbs do %>
+ <% add_admin_breadcrumb('FAQ', admins_faqs_path) %>
+<% end %>
+
+
+ <%= form_tag(admins_faqs_path, method: :get, class: 'form-inline search-form flex-1', remote: true) do %>
+ <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: '输入标题搜素') %>
+ <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %>
+ <% end %>
+ <%= link_to "新增", new_admins_faq_path, remote: true, class: "btn btn-primary pull-right", "data-disabled-with":"...新增" %>
+
+
+
+ <%= render partial: 'admins/faqs/list', locals: { faqs: @faqs } %>
+
+
+
\ No newline at end of file
diff --git a/app/views/admins/faqs/index.js.erb b/app/views/admins/faqs/index.js.erb
new file mode 100644
index 000000000..ebb1900e5
--- /dev/null
+++ b/app/views/admins/faqs/index.js.erb
@@ -0,0 +1 @@
+$('.faqs-list-container').html("<%= j( render partial: 'admins/faqs/list', locals: { faqs: @faqs } ) %>");
\ No newline at end of file
diff --git a/app/views/admins/faqs/new.js.erb b/app/views/admins/faqs/new.js.erb
new file mode 100644
index 000000000..edeb83a81
--- /dev/null
+++ b/app/views/admins/faqs/new.js.erb
@@ -0,0 +1,2 @@
+$("#faq-modals").html("<%= j render(partial: 'admins/faqs/form_modal', locals: {type: 'create'}) %>")
+$(".faq-change-modal").modal('show');
\ No newline at end of file
diff --git a/app/views/admins/laboratories/shared/_laboratory_item.html.erb b/app/views/admins/laboratories/shared/_laboratory_item.html.erb
index da080a516..7366f752a 100644
--- a/app/views/admins/laboratories/shared/_laboratory_item.html.erb
+++ b/app/views/admins/laboratories/shared/_laboratory_item.html.erb
@@ -10,7 +10,6 @@
<% if school && school.identifier.present? %>
- <%= link_to school.identifier.to_s, statistics_college_path(school.identifier), target: '_blank' %>
<% else %>
--
<% end %>
@@ -30,26 +29,10 @@
<%= laboratory.created_at.strftime('%Y-%m-%d %H:%M') %>
-
- <% if school.present? && laboratory.id != 1 %>
- <%= check_box_tag :sync_course,!laboratory.sync_course,laboratory.sync_course,remote:true,data:{id:laboratory.id},class:"laboratory-sync-form" %>
- <% end %>
-
-
- <% if school.present? && laboratory.id != 1 %>
- <%= check_box_tag :sync_subject,!laboratory.sync_subject,laboratory.sync_subject,remote:true,data:{id:laboratory.id},class:"laboratory-sync-form" %>
- <% end %>
-
-
- <% if school.present? && laboratory.id != 1 %>
- <%= check_box_tag :sync_shixun,!laboratory.sync_shixun,laboratory.sync_shixun,remote:true,data:{id:laboratory.id},class:"laboratory-sync-form" %>
- <% end %>
-
- <%= link_to '定制', admins_laboratory_laboratory_setting_path(laboratory), class: 'action' %>
+ <%= link_to '设置', admins_laboratory_laboratory_setting_path(laboratory), class: 'action' %>
<% if school.present? && laboratory.id != 1 %>
- <%= javascript_void_link '添加管理员', class: 'action', data: { laboratory_id: laboratory.id, toggle: 'modal', target: '.admin-add-laboratory-user-modal' } %>
- <%= link_to '同步用户', synchronize_user_admins_laboratory_path(laboratory), remote: true, data: { confirm: '确认同步该单位下的所有用户到云上实验室吗?' }, class: 'action' %>
- <% end %>
+ <%= delete_link '删除', admins_laboratory_path(laboratory, element: ".laboratory-item-#{laboratory.id}"), class: 'action' %>
+ <% end %>
diff --git a/app/views/admins/laboratories/shared/_list.html.erb b/app/views/admins/laboratories/shared/_list.html.erb
index 621ad7a41..0b6466039 100644
--- a/app/views/admins/laboratories/shared/_list.html.erb
+++ b/app/views/admins/laboratories/shared/_list.html.erb
@@ -7,9 +7,6 @@
统计链接
管理员
<%= sort_tag('创建时间', name: 'id', path: admins_laboratories_path) %>
-
同步课堂
-
同步实践课程
-
同步实训
操作
diff --git a/app/views/admins/laboratory_settings/show.html.erb b/app/views/admins/laboratory_settings/show.html.erb
index 8905869ed..b6df65b29 100644
--- a/app/views/admins/laboratory_settings/show.html.erb
+++ b/app/views/admins/laboratory_settings/show.html.erb
@@ -23,7 +23,7 @@
=begin%>
<% rails_env = EduSetting.get('rails_env') %>
- <%= rails_env && rails_env != 'production' ? ".#{rails_env}.educoder.net" : '.educoder.net' %>
+ <%= rails_env && rails_env != 'production' ? ".#{rails_env}.gitlink.org.cn" : '.gitlink.org.cn' %>
<%
=end%>
@@ -90,7 +90,9 @@
-
-
+
+<%
+=end%>
diff --git a/app/views/admins/laboratory_shixuns/index.html.erb b/app/views/admins/laboratory_shixuns/index.html.erb
deleted file mode 100644
index c91ddc60b..000000000
--- a/app/views/admins/laboratory_shixuns/index.html.erb
+++ /dev/null
@@ -1,45 +0,0 @@
-<% define_admin_breadcrumbs do %>
- <% add_admin_breadcrumb('云上实验室', admins_laboratories_path) %>
- <% add_admin_breadcrumb("#{current_laboratory.name} - 实训项目") %>
-<% end %>
-
-
-
- <%= form_tag(admins_laboratory_laboratory_shixuns_path(current_laboratory), method: :get, class: 'form-inline search-form', remote: true) do %>
-
- 状态:
- <% status_options = [['全部', ''], ['编辑中', 0], ['审核中', 1], ['已发布', 2], ['已关闭', 3]] %>
- <%= select_tag(:status, options_for_select(status_options), class: 'form-control') %>
-
-
-
- 技术平台:
- <%= select_tag(:tag_id, options_for_select(MirrorRepository.pluck(:type_name,:id).unshift(['']), params[:tag_id]), class: 'form-control') %>
-
-
- <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-12 col-md-2 mr-3', placeholder: '创建者/实训名称检索') %>
-
-
- <%= hidden_field_tag(:homepage, false, id:'') %>
- <%= check_box_tag(:homepage, true, params[:homepage].to_s == 'true', class: 'form-check-input') %>
- 只看首页展示
-
-
-
- <%= hidden_field_tag(:ownership, false, id:'') %>
- <%= check_box_tag(:ownership, true, params[:ownership].to_s == 'true', class: 'form-check-input') %>
- 只看自建
-
-
- <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %>
- <%= link_to '清空', admins_laboratory_laboratory_shixuns_path(current_laboratory), class: 'btn btn-default','data-disable-with': '清空中...' %>
- <% end %>
-
- <%= javascript_void_link('添加实训', class: 'btn btn-primary', data: { toggle: 'modal', target: '.admin-add-laboratory-shixun-modal' }) %>
-
-
-
- <%= render partial: 'admins/laboratory_shixuns/shared/list', locals: { laboratory_shixuns: @laboratory_shixuns } %>
-
-
-<%= render partial: 'admins/laboratory_shixuns/shared/add_laboratory_shixun_modal' %>
\ No newline at end of file
diff --git a/app/views/admins/laboratory_shixuns/index.js.erb b/app/views/admins/laboratory_shixuns/index.js.erb
deleted file mode 100644
index 0a51afa21..000000000
--- a/app/views/admins/laboratory_shixuns/index.js.erb
+++ /dev/null
@@ -1 +0,0 @@
-$('.laboratory-shixun-list-container').html("<%= j(render partial: 'admins/laboratory_shixuns/shared/list', locals: { laboratory_shixuns: @laboratory_shixuns }) %>");
\ No newline at end of file
diff --git a/app/views/admins/laboratory_shixuns/shared/_add_laboratory_shixun_modal.html.erb b/app/views/admins/laboratory_shixuns/shared/_add_laboratory_shixun_modal.html.erb
deleted file mode 100644
index 56d01d663..000000000
--- a/app/views/admins/laboratory_shixuns/shared/_add_laboratory_shixun_modal.html.erb
+++ /dev/null
@@ -1,28 +0,0 @@
-
\ No newline at end of file
diff --git a/app/views/admins/laboratory_shixuns/shared/_list.html.erb b/app/views/admins/laboratory_shixuns/shared/_list.html.erb
deleted file mode 100644
index 462486f4b..000000000
--- a/app/views/admins/laboratory_shixuns/shared/_list.html.erb
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
- 序号
- 实训名称
- 技术平台
- 技术体系
- 封面
- 创建者
- 状态
- 执行时间
- 操作
-
-
-
- <% if laboratory_shixuns.present? %>
- <% laboratory_shixuns.each_with_index do |laboratory_shixun, index| %>
-
- <%= render partial: 'admins/laboratory_shixuns/shared/td', locals: { laboratory_shixun: laboratory_shixun, index: index } %>
-
- <% end %>
- <% else %>
- <%= render 'admins/shared/no_data_for_table' %>
- <% end %>
-
-
-
-<%= render partial: 'admins/shared/paginate', locals: { objects: laboratory_shixuns } %>
\ No newline at end of file
diff --git a/app/views/admins/laboratory_shixuns/shared/_td.html.erb b/app/views/admins/laboratory_shixuns/shared/_td.html.erb
deleted file mode 100644
index d987b53b4..000000000
--- a/app/views/admins/laboratory_shixuns/shared/_td.html.erb
+++ /dev/null
@@ -1,33 +0,0 @@
-<%- shixun = laboratory_shixun.shixun -%>
-
-
<%= list_index_no((params[:page] || 1).to_i, index) %>
-
- <%= link_to "/shixuns/#{shixun.identifier}", target: '_blank' do %>
- <%= shixun.name %>
- 首页
- 自建
- <% end %>
-
-
<%= shixun.shixun_main_name %>
-
- <% shixun.tag_repertoires.each do |tag| %>
- <%= tag.name %>
- <% end %>
-
-
- <% imageExists = Util::FileManage.exists?(shixun) %>
- <% imageUrl = imageExists ? '/' + url_to_avatar(shixun) : '' %>
- <%= image_tag(imageUrl, width: 60, height: 40, class: "preview-image shixun-image-#{shixun.id}", data: { toggle: 'tooltip', title: '点击预览' }, style: imageExists ? '' : 'display:none') %>
-
-
<%= link_to shixun.user&.real_name, "/users/#{shixun.user&.login}", target:'_blank' %>
-
<%= t("shixun.status.#{shixun.status}") %>
-
<%= shixun.excute_time %>
-
- <%= link_to('去修改', admins_shixun_settings_path(id: laboratory_shixun.shixun_id)) %>
- <%= javascript_void_link('首页展示', class: 'action homepage-show-action', data: { id: laboratory_shixun.id }, style: laboratory_shixun.homepage? ? 'display:none' : '') %>
- <%= javascript_void_link('取消首页展示', class: 'action homepage-hide-action', data: { id: laboratory_shixun.id }, style: laboratory_shixun.homepage? ? '' : 'display:none') %>
-
- <% unless laboratory_shixun.ownership? %>
- <%= delete_link '删除', admins_laboratory_laboratory_shixun_path(current_laboratory, laboratory_shixun, element: ".laboratory-shixun-item-#{laboratory_shixun.id}") %>
- <% end %>
-
\ No newline at end of file
diff --git a/app/views/admins/laboratory_subjects/index.html.erb b/app/views/admins/laboratory_subjects/index.html.erb
deleted file mode 100644
index 211bf24f3..000000000
--- a/app/views/admins/laboratory_subjects/index.html.erb
+++ /dev/null
@@ -1,45 +0,0 @@
-<% define_admin_breadcrumbs do %>
- <% add_admin_breadcrumb('云上实验室', admins_laboratories_path) %>
- <% add_admin_breadcrumb("#{current_laboratory.name} - 实践课程") %>
-<% end %>
-
-
-
- <%= form_tag(admins_laboratory_laboratory_subjects_path(current_laboratory), method: :get, class: 'form-inline search-form', remote: true) do %>
-
- 状态:
- <% status_options = [['全部', ''], ['编辑中', 0], ['审核中', 1], ['已发布', 2]] %>
- <%= select_tag(:status, options_for_select(status_options), class: 'form-control') %>
-
-
-
- 单位:
- <%= select_tag :school_id, options_for_select([''], params[:school_id]), class: 'form-control school-select flex-1' %>
-
-
- <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-12 col-md-2 mr-3', placeholder: '创建者/课程名称检索') %>
-
-
- <%= hidden_field_tag(:homepage, false, id:'') %>
- <%= check_box_tag(:homepage, true, params[:homepage].to_s == 'true', class: 'form-check-input') %>
- 只看首页展示
-
-
-
- <%= hidden_field_tag(:ownership, false, id:'') %>
- <%= check_box_tag(:ownership, true, params[:ownership].to_s == 'true', class: 'form-check-input') %>
- 只看自建
-
-
- <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %>
- <%= link_to '清空', admins_laboratory_laboratory_subjects_path(current_laboratory), class: 'btn btn-default','data-disable-with': '清空中...' %>
- <% end %>
-
- <%= javascript_void_link('添加课程', class: 'btn btn-primary', data: { toggle: 'modal', target: '.admin-add-laboratory-subject-modal' }) %>
-
-
-
- <%= render partial: 'admins/laboratory_subjects/shared/list', locals: { laboratory_subjects: @laboratory_subjects } %>
-
-
-<%= render partial: 'admins/laboratory_subjects/shared/add_laboratory_subject_modal' %>
\ No newline at end of file
diff --git a/app/views/admins/laboratory_subjects/index.js.erb b/app/views/admins/laboratory_subjects/index.js.erb
deleted file mode 100644
index 3ebc1286c..000000000
--- a/app/views/admins/laboratory_subjects/index.js.erb
+++ /dev/null
@@ -1 +0,0 @@
-$('.laboratory-subject-list-container').html("<%= j(render partial: 'admins/laboratory_subjects/shared/list', locals: { laboratory_subjects: @laboratory_subjects }) %>");
\ No newline at end of file
diff --git a/app/views/admins/laboratory_subjects/shared/_add_laboratory_subject_modal.html.erb b/app/views/admins/laboratory_subjects/shared/_add_laboratory_subject_modal.html.erb
deleted file mode 100644
index 63ab3fdf1..000000000
--- a/app/views/admins/laboratory_subjects/shared/_add_laboratory_subject_modal.html.erb
+++ /dev/null
@@ -1,28 +0,0 @@
-
\ No newline at end of file
diff --git a/app/views/admins/laboratory_subjects/shared/_list.html.erb b/app/views/admins/laboratory_subjects/shared/_list.html.erb
deleted file mode 100644
index c40d02260..000000000
--- a/app/views/admins/laboratory_subjects/shared/_list.html.erb
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
-
- 序号
- 课程名称
- 技术体系
- 等级体系
- 封面
- 创建者
- 单位
- 状态
- 操作
-
-
-
- <% if laboratory_subjects.present? %>
- <% laboratory_subjects.each_with_index do |laboratory_subject, index| %>
-
- <%- subject = laboratory_subject.subject -%>
-
- <%= list_index_no((params[:page] || 1).to_i, index) %>
-
- <%= link_to(subject.name, "/paths/#{subject.id}", target: '_blank') %>
- 首页
- 自建
-
- <%= display_text subject.repertoire&.name %>
- <%= display_text subject.subject_level_system&.name %>
-
- <% image_exists = Util::FileManage.exists?(subject) %>
- <%= image_tag(image_exists ? Util::FileManage.source_disk_file_url(subject) : '', height: 40, class: "w-100 preview-image subject-image-#{subject.id}", style: image_exists ? '' : 'display:none') %>
-
- <%= subject.user.real_name %>
- <%= subject.user.school_name %>
- <%= display_subject_status(subject) %>
-
-
- <%= link_to('去修改', admins_subjects_path(id: laboratory_subject.subject_id)) %>
- <%= javascript_void_link('首页展示', class: 'action homepage-show-action', data: { id: laboratory_subject.id }, style: laboratory_subject.homepage? ? 'display:none' : '') %>
- <%= javascript_void_link('取消首页展示', class: 'action homepage-hide-action', data: { id: laboratory_subject.id }, style: laboratory_subject.homepage? ? '' : 'display:none') %>
-
- <% unless laboratory_subject.ownership? %>
- <%= delete_link '删除', admins_laboratory_laboratory_subject_path(current_laboratory, laboratory_subject, element: ".laboratory-subject-item-#{laboratory_subject.id}") %>
- <% end %>
-
-
- <% end %>
- <% else %>
- <%= render 'admins/shared/no_data_for_table' %>
- <% end %>
-
-
-
-<%= render partial: 'admins/shared/paginate', locals: { objects: laboratory_subjects } %>
\ No newline at end of file
diff --git a/app/views/admins/library_applies/index.html.erb b/app/views/admins/library_applies/index.html.erb
deleted file mode 100644
index 9d2ae8e66..000000000
--- a/app/views/admins/library_applies/index.html.erb
+++ /dev/null
@@ -1,32 +0,0 @@
-<% define_admin_breadcrumbs do %>
- <% add_admin_breadcrumb('教学案例发布') %>
-<% end %>
-
-
-
-
- <%= render(partial: 'admins/library_applies/shared/list', locals: { applies: @library_applies }) %>
-
-
-<%= render(partial: 'admins/shared/admin_common_refuse_modal') %>
\ No newline at end of file
diff --git a/app/views/admins/library_applies/index.js.erb b/app/views/admins/library_applies/index.js.erb
deleted file mode 100644
index 6f4c3e712..000000000
--- a/app/views/admins/library_applies/index.js.erb
+++ /dev/null
@@ -1 +0,0 @@
-$('.library-applies-list-container').html("<%= j( render partial: 'admins/library_applies/shared/list', locals: { applies: @library_applies } ) %>");
\ No newline at end of file
diff --git a/app/views/admins/library_applies/shared/_list.html.erb b/app/views/admins/library_applies/shared/_list.html.erb
deleted file mode 100644
index fde3d1d1a..000000000
--- a/app/views/admins/library_applies/shared/_list.html.erb
+++ /dev/null
@@ -1,58 +0,0 @@
-<% is_processed = params[:status].to_s != 'pending' %>
-
-
-
-
- 序号
- 头像
- 姓名
- 教学案例
- 案例描述
- 时间
- <% if is_processed %>
- 拒绝原因
- 状态
- <% else %>
- 操作
- <% end %>
-
-
-
- <% if applies.present? %>
- <% applies.each_with_index do |apply, index| %>
- <% user = apply.library.user %>
- <% library = apply.library %>
-
- <%= list_index_no((params[:page] || 1).to_i, index) %>
-
- <%= link_to "/users/#{user.login}", class: 'professional-authentication-avatar', target: '_blank', data: { toggle: 'tooltip', title: '个人主页' } do %>
-
- <% end %>
-
- <%= link_to user&.real_name,"/users/#{user&.login}", target: "_blank" %>
- <%= link_to library.title, library_path(library), :target => "_blank" %>
- <%= overflow_hidden_span library.content[0..50]%>
- <%= apply.updated_at.strftime('%Y-%m-%d %H:%M') %>
-
- <% if is_processed %>
- <%= overflow_hidden_span apply.reason %>
- <%= t("library_apply.status.#{apply.status}") %>
- <% else %>
-
- <%= agree_link '同意', agree_admins_library_apply_path(apply, element: ".library_applies-#{apply.id}"), 'data-confirm': '确认审核通过?' %>
- <%= javascript_void_link('拒绝', class: 'action refuse-action',
- data: {
- toggle: 'modal', target: '.admin-common-refuse-modal', id: apply.id,
- url: refuse_admins_library_apply_path(apply, element: ".library_applies-#{apply.id}")
- }) %>
-
- <% end %>
-
- <% end %>
- <% else %>
- <%= render 'admins/shared/no_data_for_table' %>
- <% end %>
-
-
-
-<%= render partial: 'admins/shared/paginate', locals: { objects: applies } %>
\ No newline at end of file
diff --git a/app/views/admins/message_templates/_form.html.erb b/app/views/admins/message_templates/_form.html.erb
new file mode 100644
index 000000000..4b2a6c767
--- /dev/null
+++ b/app/views/admins/message_templates/_form.html.erb
@@ -0,0 +1,65 @@
+
+
+
+ <%= form_for @message_template, url: {controller: "message_templates", action: "#{type}"} do |f| %>
+
+
+
+
+
+
+
+ <%= f.submit "确认", class: "btn btn-primary submit-btn" %>
+
+ <% end %>
+
\ No newline at end of file
diff --git a/app/views/admins/message_templates/_list.html.erb b/app/views/admins/message_templates/_list.html.erb
new file mode 100644
index 000000000..4082735f5
--- /dev/null
+++ b/app/views/admins/message_templates/_list.html.erb
@@ -0,0 +1,39 @@
+
+
+
+ 序号
+ 类型
+ 系统消息模版
+ 邮件模版
+ 通知地址
+ 操作
+
+
+
+ <% if message_templates.present? %>
+ <% message_templates.each_with_index do |message_template_type, index| %>
+ <% message_template = message_template_type.constantize.last%>
+
+ <%= list_index_no((params[:page] || 1).to_i, index) %>
+ <%= message_template.simple_type %>
+
+ <%= message_template.sys_notice.to_s.truncate(200) %>
+
+
+ <%= message_template.email.to_s.truncate(100) %>
+
+
+ <%= message_template.notification_url.to_s.truncate(200) %>
+
+
+ <%= link_to "编辑", edit_admins_message_template_path(message_template),remote: true, class: "action" %>
+
+
+ <% end %>
+ <% else %>
+ <%= render 'admins/shared/no_data_for_table' %>
+ <% end %>
+
+
+
+<%= render partial: 'admins/shared/paginate', locals: { objects: message_templates } %>
\ No newline at end of file
diff --git a/app/views/admins/message_templates/edit.js.erb b/app/views/admins/message_templates/edit.js.erb
new file mode 100644
index 000000000..5d797af2f
--- /dev/null
+++ b/app/views/admins/message_templates/edit.js.erb
@@ -0,0 +1,2 @@
+$("#admins-message-templates-content").html("<%= j render partial: 'admins/message_templates/form', locals:{type: 'update'} %>")
+createMDEditor('message-template-email-editor', { height: 500, placeholder: '请输入邮件模版' });
diff --git a/app/views/admins/message_templates/index.html.erb b/app/views/admins/message_templates/index.html.erb
new file mode 100644
index 000000000..54d273332
--- /dev/null
+++ b/app/views/admins/message_templates/index.html.erb
@@ -0,0 +1,12 @@
+<% define_admin_breadcrumbs do %>
+ <% add_admin_breadcrumb('消息模版') %>
+<% end %>
+
+
+ <%= link_to "初始化数据", init_data_admins_message_templates_path, class: "btn btn-primary pull-right", "data-disabled-with":"...初始化数据" %>
+
+
+ <%= render partial: 'admins/message_templates/list', locals: { message_templates: @message_templates } %>
+
+
+
diff --git a/app/views/admins/message_templates/index.js.erb b/app/views/admins/message_templates/index.js.erb
new file mode 100644
index 000000000..d8e0fdd26
--- /dev/null
+++ b/app/views/admins/message_templates/index.js.erb
@@ -0,0 +1 @@
+$('.message-templates-list-container').html("<%= j( render partial: 'admins/message_templates/list', locals: { message_templates: @message_templates } ) %>");
\ No newline at end of file
diff --git a/app/views/admins/project_categories/_form_modal.html.erb b/app/views/admins/project_categories/_form_modal.html.erb
index fd20936b6..fc58d3345 100644
--- a/app/views/admins/project_categories/_form_modal.html.erb
+++ b/app/views/admins/project_categories/_form_modal.html.erb
@@ -7,9 +7,33 @@
×
- <%= form_for @project_category, url: {controller: "project_categories", action: "#{type}"} do |p| %>
+ <%= form_for @project_category, url: {controller: "project_categories", action: "#{type}"}, html: { enctype: 'multipart/form-data' } do |p| %>
- <%= p.text_field :name,class: "form-control input-lg",placeholder: "分类名称",required: true, maxlength: 64%>
+
+
+ 分类名称 *
+
+ <%= p.text_field :name,class: "form-control input-lg",placeholder: "分类名称",required: true, maxlength: 64%>
+
+
+
+ 精选等级
+
+ <%= p.number_field :pinned_index,class: "form-control input-lg",placeholder: "精选等级",required: true%>
+
+
+ <% logo_img = @project_category.logo_url %>
+
+
+ <%= file_field_tag(:logo, accept: 'image/png,image/jpg,image/jpeg',style: "display: none", value: params[:logo]) %>
+
+
+
+
logo
+
格式:PNG、JPG
+
尺寸:高度38px以内,宽等比例缩放
+
+