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. 其他说明 +通过页面注册都第一个用户为平台管理员用户 ## 页面展示 - 代码库 -![](docs/figs/code.png) +![](docs/figs/code.png?raw=true) - 任务管理 -![](docs/figs/issue_manage.png) +![](docs/figs/issue_manage.png?raw=true) - 任务查看 -![](docs/figs/issue_view.png) +![](docs/figs/issue_view.png?raw=true) - 任务指派 -![](docs/figs/issue_assign2.png) +![](docs/figs/issue_assign2.png?raw=true) - 里程碑 -![](docs/figs/milestone.png) +![](docs/figs/milestone.png?raw=true) ### 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" + } + ] +} +``` + + +## 创建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" +} +``` + + + +## 删除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" +} +``` + + 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 %> - -
- <%= form_tag(admins_course_lists_path, method: :get, class: 'form-inline search-form flex-1', remote: true) do %> -
- - <% auto_trial_options = [['创建者姓名', 0], ['课程名称', 1]] %> - <%= select_tag(:search_type, options_for_select(auto_trial_options), class: 'form-control') %> -
- <%= 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_course_lists_path,class: "btn btn-default",id:"course-lists-clear-search",'data-disable-with': '清除中...' %> - <% 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 @@ - - - - - - - - - - - - <% if courses.present? %> - <% courses.each_with_index do |course_list,index| %> - - - - - <% course_count = course_list.courses.size %> - - - - - - <% end %> - <% else %> - <%= render 'admins/shared/no_data_for_table' %> - <% end %> - -
序号ID课程名称课堂数创建者<%= sort_tag('创建时间', name: 'created_at', path: admins_course_lists_path) %>操作
<%= list_index_no(@params_page.to_i, index) %><%= course_list.id %><%= course_list.name %><%= 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 } %> -
- -<%= 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 %> - -
- <%= form_tag(admins_courses_path, method: :get, class: 'form-inline search-form flex-1', remote: true) do %> -
- - <% status_options = [['全部', ''], ["正在进行(#{@processed_courses})", 'processing'], ["已结束#{@ended_courses}", 'ended']] %> - <%= 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_show, false, id:'') %> - <%= check_box_tag(:homepage_show, true, params[:homepage_show].to_s == 'true', class: 'form-check-input course-homepage-show') %> - -
- - <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> - - <% 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 @@ - - - - - - - - - - - - - - - - - - - - - - - - - <% 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 %> - -
序号ID课堂名称成员资源普通作业分组作业实训作业试卷评测次数私有状态单位创建者<%= sort_tag('创建时间', name: 'created_at', path: admins_courses_path) %>首页邮件通知操作
- -<%= 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 @@ - - - - - - - - - - - <% if customers.present? %> - <% customers.each_with_index do |customer, index| %> - - - - - - - <% end %> - <% else %> - <%= render 'admins/shared/no_data_for_table' %> - <% end %> - -
序号客户名称<%= sort_tag('添加时间', name: 'created_at', path: admins_partner_customers_path(current_partner)) %>操作
<%= 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' %> -
- -<%= 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 @@ - - - - - - - - - - - - - - - - - - <% if statistics.present? %> - <% statistics.each_with_index do |statistic, index| %> - - - - - - - - - - - - - - <% end %> - <% else %> - <%= render 'admins/shared/no_data_for_table' %> - <% end %> - -
序号单位名称<%= 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) %>
<%= 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') %>
- -<%= 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 @@ - - - - - - - - - - - - - - <% if applies.present? %> - <% applies.each_with_index do |apply, index| %> - - - - - - - - - - <% end %> - <% else %> - <%= render 'admins/shared/no_data_for_table' %> - <% end %> - -
序号ID部门名称单位名称创建者<%= sort_tag('创建于', name: 'created_at', path: admins_department_applies_path) %>操作
<%= 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 } %> -
- -<%= 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 %> - -
- <%= form_tag(admins_departments_path, method: :get, class: 'form-inline search-form flex-1', remote: true) do %> -
- <%= hidden_field_tag(:with_member, false,id:'') %> - <%= check_box_tag(:with_member, true, params[:with_member].to_s == 'true', class: 'form-check-input') %> - -
- -
- <%= hidden_field_tag(:with_identifier, false,id:'') %> - <%= check_box_tag(:with_identifier, true, params[:with_identifier].to_s == 'true', class: 'form-check-input') %> - -
- - <%= 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 %> - - <%= javascript_void_link '新建部门', class: 'btn btn-primary', data: { toggle: 'modal', target: '.admin-create-department-modal' } %> -
- -
- <%= 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 @@ - \ 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 @@ - - - - - - - - - - - - - - - - - <% 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 %> - -
序号部门名称单位名称用户数已职业认证部门管理员统计链接云主机数<%= sort_tag('创建时间', name: 'created_at', path: admins_departments_path) %>操作
- -<%= 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 %> - -
-
- <%= javascript_void_link '新增', class: 'btn btn-primary', data: { toggle: 'modal', target: '.admin-create-discipline-modal' } %> -
- <%= javascript_void_link '导入数据', class: 'btn btn-primary', data: { toggle: 'modal', target: '.admin-import-discipline-modal'} %> -
- -
- <%= 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 @@ - \ 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| %> - - - - - - - - - <% end %> - <% else %> - <%= render 'admins/shared/no_data_for_table' %> - <% end %> - -
序号课程方向实践课程实训题库操作
<%= 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' %> -
\ 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 @@ + \ 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 @@ + + + + + + + + + + + + + <% if edu_settings.present? %> + <% edu_settings.each_with_index do |edu_setting, index| %> + + + + + + + + + + <% end %> + <% else %> + <%= render 'admins/shared/no_data_for_table' %> + <% end %> + +
序号变量名变量值备注说明<%= sort_tag('创建时间', name: 'created_at', path: admins_edu_settings_path) %>操作
<%= 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" %> +
+ +<%= 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 %> - -
- - - <%= form_tag(admins_examination_authentications_path(unsafe_params), method: :get, class: 'form-inline search-form justify-content-end mt-3', remote: true) do %> -
- - <% status_options = [['全部', 'processed'], ['已同意', 'agreed'], ['已拒绝', 'refused']] %> - <%= select_tag(:status, options_for_select(status_options), class: 'form-control') %> -
- <%= 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 %> -
- -
- <%= 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 @@ - \ 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 %> - - - - - - - - - - - - <% end %> - <% else %> - <%= render 'admins/shared/no_data_for_table' %> - <% end %> - -
序号头像创建者学校试卷提交时间操作审批结果
<%= 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 %> -
- -<%= 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 @@ + \ 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 @@ + + + + + + + + + + + + + <% if faqs.present? %> + <% faqs.each_with_index do |faq, index| %> + + + + + + + + + <% end %> + <% else %> + <%= render 'admins/shared/no_data_for_table' %> + <% end %> + +
序号标题url<%= sort_tag('创建于', name: 'created_at', path: admins_faqs_path) %><%= sort_tag('更新于', name: 'updated_at', path: admins_faqs_path) %>操作
<%= 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" %> +
+ +<%= 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 @@
-
+ <% +=begin%> +
Banner设置
@@ -144,7 +146,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 -%> - - - - - - - - - - - - - <% end %> - <% else %> - <%= render 'admins/shared/no_data_for_table' %> - <% end %> - -
序号课程名称技术体系等级体系封面创建者单位状态操作
<%= 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 %> -
- -<%= 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 %> - -
- - - <%= form_tag(admins_library_applies_path(unsafe_params), method: :get, class: 'form-inline search-form justify-content-end mt-3', remote: true) do %> -
- - <% status_options = [['全部', 'processed'], ['已同意', 'agreed'], ['已拒绝', 'refused']] %> - <%= select_tag(:status, options_for_select(status_options) ,class: 'form-control') %> -
- <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: '编号/名称检索') %> - <%= submit_tag('搜索', class: 'btn btn-primary ml-3') %> - <% 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 %> - - - - - - - - - <% if is_processed %> - - - <% else %> - - <% end %> - - <% end %> - <% else %> - <%= render 'admins/shared/no_data_for_table' %> - <% end %> - -
序号头像姓名教学案例案例描述时间拒绝原因状态操作
<%= 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') %><%= overflow_hidden_span apply.reason %><%= t("library_apply.status.#{apply.status}") %> - <%= 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}") - }) %> -
- -<%= 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 @@ +
+
<%= type == "create" ? "新建" : "编辑" %><%= @message_template.simple_type %>消息模版
+ <%= link_to "返回", admins_message_templates_path, class: "btn btn-default pull-right" %> +
+ +
+ <%= form_for @message_template, url: {controller: "message_templates", action: "#{type}"} do |f| %> + +
+ +
+ <%= f.text_area :sys_notice, class:"form-control", rows: "10", cols: "20",placeholer: "请输入站内信" %> +
+ + + +
+
+ +
+ <%= f.text_field :notification_url, class: "form-control input-lg", maxlength: "60", placeholder: "请输入站内信跳转地址" %> +
+ +
+
+ +
+ <%= f.text_field :email_title, class: "form-control input-lg", maxlength: "60", placeholder: "请输入邮件标题" %> +
+ +
+
+ +
+
+ <%= f.text_area :email, class:"form-control", style: 'display: none;', rows: "10", cols: "20", placeholer: "请输入邮件正文" %> +
+
+
+ +
+ <%= 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%> + + + + + + + + + <% end %> + <% else %> + <%= render 'admins/shared/no_data_for_table' %> + <% end %> + +
序号类型系统消息模版邮件模版通知地址操作
<%= 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" %> +
+ +<%= 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| %> + +
+
diff --git a/app/views/admins/projects/shared/_form_modal.html.erb b/app/views/admins/projects/shared/_form_modal.html.erb new file mode 100644 index 000000000..b9796756a --- /dev/null +++ b/app/views/admins/projects/shared/_form_modal.html.erb @@ -0,0 +1,26 @@ + \ No newline at end of file diff --git a/app/views/admins/projects/shared/_list.html.erb b/app/views/admins/projects/shared/_list.html.erb index d4fa5a423..44a26ec07 100644 --- a/app/views/admins/projects/shared/_list.html.erb +++ b/app/views/admins/projects/shared/_list.html.erb @@ -8,13 +8,12 @@ 特殊协议 issue 资源 - 版本库 - PullRequest + Pulls 里程碑 - 成员 - 管理员 - <%= sort_tag('创建时间', name: 'created_on', path: admins_projects_path) %> - 操作 + 成员 + 管理员 + <%= sort_tag('创建时间', name: 'created_on', path: admins_projects_path) %> + 操作 @@ -30,17 +29,23 @@ <%= project.is_secret ? '√' : '' %> <%= project.issues.size %> <%= project.attachments.size %> - <%= project&.project_score.try(:changeset_num).to_i %> - <%= project&.project_score.try(:pull_request_num).to_i %> + <%= project&.pull_requests_count %> <%= project.versions.size %> <%= project.members.size %> - <%= project.owner ? link_to(project.owner&.real_name, "/users/#{project.owner&.login}", target: '_blank') : "" %> + <%= link_to_project(project) %> <%= project.created_on&.strftime('%Y-%m-%d %H:%M') %> + <% if project.is_public %> + <%= javascript_void_link '精选', class: 'action pinned-action', data: { id: project.id }, style: project.is_pinned ? 'display: none;' : '' %> + <%= javascript_void_link '取消精选', class: 'action unpinned-action', data: { id: project.id }, style: project.is_pinned ? '' : 'display: none;' %> + <%= javascript_void_link '推荐', class: 'action recommend-action', data: { id: project.id }, style: project.recommend ? 'display: none;' : '' %> + <%= javascript_void_link '取消推荐', class: 'action unrecommend-action', data: { id: project.id }, style: project.recommend ? '' : 'display: none;' %> + <%= link_to "设置推荐等级", edit_admins_project_path(project.id), remote: true, class: "action edit-recommend-action", style: project.recommend ? '' : 'display: none;' %> + <% end %> <%= link_to "删除", admins_project_path(project.id), method: :delete, data:{confirm: "确认删除的吗?"}, class: "delete-project-action" %> - + <% end %> <% else %> diff --git a/app/views/admins/reversed_keywords/_form_modal.html.erb b/app/views/admins/reversed_keywords/_form_modal.html.erb new file mode 100644 index 000000000..7dd2f2ba4 --- /dev/null +++ b/app/views/admins/reversed_keywords/_form_modal.html.erb @@ -0,0 +1,32 @@ + \ No newline at end of file diff --git a/app/views/admins/reversed_keywords/_list.html.erb b/app/views/admins/reversed_keywords/_list.html.erb new file mode 100644 index 000000000..5577c9999 --- /dev/null +++ b/app/views/admins/reversed_keywords/_list.html.erb @@ -0,0 +1,37 @@ + + + + + + + + + + + + + <% if keywords.present? %> + <% keywords.each_with_index do |keyword, index| %> + + + + + + + + + <% end %> + <% else %> + <%= render 'admins/shared/no_data_for_table' %> + <% end %> + +
序号<%= sort_tag('标识', name: 'identifier', path: admins_reversed_keywords_path) %>描述<%= sort_tag('限制是否开启', name: 'closed', path: admins_reversed_keywords_path) %><%= sort_tag('创建时间', name: 'created_at', path: admins_reversed_keywords_path) %>操作
<%= list_index_no((params[:page] || 1).to_i, index) %> + <%= link_to(keyword.identifier, "javascript:void(0)") %> + <%= keyword.description %><%= !keyword.closed ? '√' : '' %><%= keyword.created_at&.strftime('%Y-%m-%d %H:%M') %> + <%= javascript_void_link '开启限制', class: 'action unclose-action', data: { id: keyword.id }, style: keyword.closed? ? '' : 'display: none;' %> + <%= javascript_void_link '关闭限制', class: 'action close-action', data: { id: keyword.id }, style: keyword.closed? ? 'display: none;' : '' %> + <%= link_to "编辑", edit_admins_reversed_keyword_path(keyword), remote: true, class: "action" %> + <%= link_to "删除", admins_reversed_keyword_path(keyword), method: :delete, data:{confirm: "确认删除的吗?"}, class: "action" %> +
+ +<%= render partial: 'admins/shared/paginate', locals: { objects: keywords } %> \ No newline at end of file diff --git a/app/views/admins/reversed_keywords/edit.js.erb b/app/views/admins/reversed_keywords/edit.js.erb new file mode 100644 index 000000000..0cb61e6fd --- /dev/null +++ b/app/views/admins/reversed_keywords/edit.js.erb @@ -0,0 +1,2 @@ +$("#reversed-keyword-modals").html("<%= j render(partial: 'admins/reversed_keywords/form_modal', locals: {type: 'update'}) %>") +$(".reversed-keyword-change-modal").modal('show'); \ No newline at end of file diff --git a/app/views/admins/reversed_keywords/index.html.erb b/app/views/admins/reversed_keywords/index.html.erb new file mode 100644 index 000000000..a5218c9c9 --- /dev/null +++ b/app/views/admins/reversed_keywords/index.html.erb @@ -0,0 +1,22 @@ +<% define_admin_breadcrumbs do %> + <% add_admin_breadcrumb('系统保留关键词') %> +<% end %> + +
+ <%= form_tag(admins_reversed_keywords_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_reversed_keyword_path, remote: true, class: "btn btn-primary pull-right", "data-disabled-with":"...新增" %> +
+ +
+ 旨在为特殊用户群体、特殊场景、路由而保留的关键词,如:Gitlink、Trustie等;目前主要在用户注册、创建组织、创建项目等场景下做关键词的验证. +
+ +
+ <%= render partial: 'admins/reversed_keywords/list', locals: { keywords: @keywords } %> +
+
+
diff --git a/app/views/admins/reversed_keywords/index.js.erb b/app/views/admins/reversed_keywords/index.js.erb new file mode 100644 index 000000000..859d7be89 --- /dev/null +++ b/app/views/admins/reversed_keywords/index.js.erb @@ -0,0 +1 @@ +$('.reversed-keyword-list-container').html("<%= j( render partial: 'admins/reversed_keywords/list', locals: { keywords: @keywords } ) %>"); \ No newline at end of file diff --git a/app/views/admins/reversed_keywords/new.js.erb b/app/views/admins/reversed_keywords/new.js.erb new file mode 100644 index 000000000..91af4adc5 --- /dev/null +++ b/app/views/admins/reversed_keywords/new.js.erb @@ -0,0 +1,2 @@ +$("#reversed-keyword-modals").html("<%= j render(partial: 'admins/reversed_keywords/form_modal', locals: {type: 'create'}) %>") +$(".reversed-keyword-change-modal").modal('show'); \ No newline at end of file diff --git a/app/views/admins/salesman_channels/destroy.js.erb b/app/views/admins/salesman_channels/destroy.js.erb deleted file mode 100644 index d00e4fa08..000000000 --- a/app/views/admins/salesman_channels/destroy.js.erb +++ /dev/null @@ -1,2 +0,0 @@ -$.notify({ message: '操作成功' },{ type: 'success' }); -$("<%= params[:element]%>").remove(); \ No newline at end of file diff --git a/app/views/admins/salesman_channels/index.html.erb b/app/views/admins/salesman_channels/index.html.erb deleted file mode 100644 index 75847a542..000000000 --- a/app/views/admins/salesman_channels/index.html.erb +++ /dev/null @@ -1,36 +0,0 @@ -<% define_admin_breadcrumbs do %> - <% add_admin_breadcrumb("#{@salesman.name}的渠道", admins_salesmans_path) %> -<% end %> - -<% define_admin_breadcrumbs do %> - <% add_admin_breadcrumb('数据变化报表', admins_school_statistics_path) %> -<% end %> - -
-
-
-
-
- <%= text_field_tag :start_date, params[:start_date], class: 'form-control start-date mx-0', placeholder: '开始时间' %> -
- <%= text_field_tag :end_date, params[:end_date], class: 'form-control end-date mx-0', placeholder: '结束时间' %> -
-
- -
- <%= text_field_tag :keyword, params[:keyword], placeholder: 'ID/单位名称检索', class: 'form-control mx-3 search-input' %> - - <%= javascript_void_link '搜索', class: 'btn btn-primary search-btn', target: '' %> - -
-
- <%= javascript_void_link '新增渠道', class: 'btn btn-primary', data: {toggle: 'modal', target: '.admin-add-salesman-channel-user-modal' } %> -
-
- - -
- <%= render(partial: 'admins/salesman_channels/shared/list') %> -
- -<%= render 'admins/salesman_channels/shared/add_salesman_channels_user_modal' %> diff --git a/app/views/admins/salesman_channels/index.js.erb b/app/views/admins/salesman_channels/index.js.erb deleted file mode 100644 index 422cfe5cb..000000000 --- a/app/views/admins/salesman_channels/index.js.erb +++ /dev/null @@ -1 +0,0 @@ - $(".salesman-channel-list-container").html("<%= j(render partial: 'admins/salesman_channels/shared/list') %>") \ No newline at end of file diff --git a/app/views/admins/salesman_channels/shared/_add_salesman_channels_user_modal.html.erb b/app/views/admins/salesman_channels/shared/_add_salesman_channels_user_modal.html.erb deleted file mode 100644 index 121e711be..000000000 --- a/app/views/admins/salesman_channels/shared/_add_salesman_channels_user_modal.html.erb +++ /dev/null @@ -1,29 +0,0 @@ - \ No newline at end of file diff --git a/app/views/admins/salesman_channels/shared/_list.html.erb b/app/views/admins/salesman_channels/shared/_list.html.erb deleted file mode 100644 index 8116e6cf6..000000000 --- a/app/views/admins/salesman_channels/shared/_list.html.erb +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - <% if @channels.present? %> - <% @channels.each_with_index do |channel, index| %> - - - - - - - - - - <% end %> - <% else %> - <%= render 'admins/shared/no_data_for_table' %> - <% end %> - -
序号名称教师数学生数课堂数实训数操作
<%= index + 1 %> - <%= channel.school_name %> - - <%= channel.teacher_count(@start_time, @end_time) %> - - <%= channel.student_count(@start_time, @end_time) %> - - <%= channel.course_count(@start_time, @end_time) %> - - <%= channel.shixuns_count(@start_time, @end_time) %> - - <%= delete_link '删除', admins_salesman_channel_path(channel, salesman_id: channel.salesman_id, element: ".salesman-channel-item-#{channel.id}"), class: 'delete-salesman-action' %> -
\ No newline at end of file diff --git a/app/views/admins/salesman_customers/destroy.js.erb b/app/views/admins/salesman_customers/destroy.js.erb deleted file mode 100644 index d00e4fa08..000000000 --- a/app/views/admins/salesman_customers/destroy.js.erb +++ /dev/null @@ -1,2 +0,0 @@ -$.notify({ message: '操作成功' },{ type: 'success' }); -$("<%= params[:element]%>").remove(); \ No newline at end of file diff --git a/app/views/admins/salesman_customers/index.html.erb b/app/views/admins/salesman_customers/index.html.erb deleted file mode 100644 index f7948b0f0..000000000 --- a/app/views/admins/salesman_customers/index.html.erb +++ /dev/null @@ -1,15 +0,0 @@ -<% define_admin_breadcrumbs do %> - <% add_admin_breadcrumb("#{@salesman.name}的客户", admins_salesmans_path) %> -<% end %> - -
-
- <%= javascript_void_link '新增客户', class: 'btn btn-primary', data: {salesman_id: @salesman.id, toggle: 'modal', target: '.admin-add-salesman-customer-user-modal' } %> -
-
- -
- <%= render(partial: 'admins/salesman_customers/shared/list') %> -
- -<%= render 'admins/salesman_customers/shared/add_salesman_customers_user_modal' %> diff --git a/app/views/admins/salesman_customers/index.js.erb b/app/views/admins/salesman_customers/index.js.erb deleted file mode 100644 index ed90f5394..000000000 --- a/app/views/admins/salesman_customers/index.js.erb +++ /dev/null @@ -1 +0,0 @@ - $(".salesman-customer-list-container").html("<%= j(render partial: 'admins/salesman_customers/shared/list') %>") \ No newline at end of file diff --git a/app/views/admins/salesman_customers/shared/_add_salesman_customers_user_modal.html.erb b/app/views/admins/salesman_customers/shared/_add_salesman_customers_user_modal.html.erb deleted file mode 100644 index f31499f52..000000000 --- a/app/views/admins/salesman_customers/shared/_add_salesman_customers_user_modal.html.erb +++ /dev/null @@ -1,30 +0,0 @@ - \ No newline at end of file diff --git a/app/views/admins/salesman_customers/shared/_list.html.erb b/app/views/admins/salesman_customers/shared/_list.html.erb deleted file mode 100644 index 21e639865..000000000 --- a/app/views/admins/salesman_customers/shared/_list.html.erb +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - <% if @customers.present? %> - <% @customers.each_with_index do |customer, index| %> - - - - - - - - - <% end %> - <% else %> - <%= render 'admins/shared/no_data_for_table' %> - <% end %> - -
序号名称单位课堂数实训数操作
<%= index + 1 %> - <%= link_to customer.name, user_path(customer), :title => customer.name %> - - <%= customer.school_name %> - - <%= customer.courses_count %> - - <%= customer.shixuns_count %> - - <%= delete_link '删除', admins_salesman_customer_path(customer, salesman_id: customer.salesman_id, element: ".salesman-customer-item-#{customer.id}"), class: 'delete-salesman--action' %> -
\ No newline at end of file diff --git a/app/views/admins/salesmans/destroy.js.erb b/app/views/admins/salesmans/destroy.js.erb deleted file mode 100644 index 0a9fa8518..000000000 --- a/app/views/admins/salesmans/destroy.js.erb +++ /dev/null @@ -1,2 +0,0 @@ -$.notify({ message: '操作成功' },{ type: 'success' }); -$(".salesman-item-<%= params[:id]%>").remove(); \ No newline at end of file diff --git a/app/views/admins/salesmans/index.html.erb b/app/views/admins/salesmans/index.html.erb deleted file mode 100644 index 2c5a095ef..000000000 --- a/app/views/admins/salesmans/index.html.erb +++ /dev/null @@ -1,15 +0,0 @@ -<% define_admin_breadcrumbs do %> - <% add_admin_breadcrumb('销售数据列表', admins_salesmans_path) %> -<% end %> - -
-
- <%= javascript_void_link '新增销售人员', class: 'btn btn-primary', data: { toggle: 'modal', target: '.admin-add-salesman-user-modal' } %> -
-
- -
- <%= render(partial: 'admins/salesmans/shared/list') %> -
- -<%= render 'admins/salesmans/shared/add_salesman_user_modal' %> diff --git a/app/views/admins/salesmans/shared/_add_salesman_user_modal.html.erb b/app/views/admins/salesmans/shared/_add_salesman_user_modal.html.erb deleted file mode 100644 index 20e2befda..000000000 --- a/app/views/admins/salesmans/shared/_add_salesman_user_modal.html.erb +++ /dev/null @@ -1,30 +0,0 @@ - \ No newline at end of file diff --git a/app/views/admins/salesmans/shared/_list.html.erb b/app/views/admins/salesmans/shared/_list.html.erb deleted file mode 100644 index 8293b4575..000000000 --- a/app/views/admins/salesmans/shared/_list.html.erb +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - <% if @salesmans.present? %> - <% @salesmans.each_with_index do |salesman, index| %> - - - - - - - - <% end %> - <% else %> - <%= render 'admins/shared/no_data_for_table' %> - <% end %> - -
序号业务员重点跟踪客户(点击数字客户列表)重点跟踪院校(点击数字渠道列表)操作
<%= index + 1 %> - <%= link_to salesman.name, admins_salesman_path(salesman), :title => salesman.name %> - - <%= link_to salesman.salesman_customers_count.to_i, - admins_salesman_customers_path(salesman_id: salesman), :title => "点击进入客户列表" %> - - <%= link_to salesman.salesman_channels_count.to_i, admins_salesman_channels_path(salesman_id: salesman), - :title => "点击进入渠道列表" %> - - <%= delete_link '删除', admins_salesman_path(salesman, element: ".salesman-item-#{salesman.id}"), - class: 'delete-salesman-action' %> -
\ No newline at end of file diff --git a/app/views/admins/school_statistics/contrast.js.erb b/app/views/admins/school_statistics/contrast.js.erb deleted file mode 100644 index bc9916c12..000000000 --- a/app/views/admins/school_statistics/contrast.js.erb +++ /dev/null @@ -1 +0,0 @@ -$(".school-statistic-list-container").html("<%= j(render partial: 'admins/school_statistics/shared/contrast_list', locals: { statistics: @statistics, select_options: @select_options }) %>") \ No newline at end of file diff --git a/app/views/admins/school_statistics/index.html.erb b/app/views/admins/school_statistics/index.html.erb deleted file mode 100644 index d726adb22..000000000 --- a/app/views/admins/school_statistics/index.html.erb +++ /dev/null @@ -1,49 +0,0 @@ -<% define_admin_breadcrumbs do %> - <% add_admin_breadcrumb('数据变化报表', admins_school_statistics_path) %> -<% end %> - -
-
-
- - <%= hidden_field_tag :contrast_column, params[:contrast_column] %> - -
-
- <%= text_field_tag :grow_begin_date, params[:grow_begin_date], class: 'form-control start-date mx-0', placeholder: '开始时间' %> -
- <%= text_field_tag :grow_end_date, params[:grow_end_date], class: 'form-control end-date mx-0', placeholder: '结束时间' %> -
-
-
-
- <%= text_field_tag :begin_date, params[:begin_date], class: 'form-control start-date mx-0', placeholder: '开始时间' %> -
- <%= text_field_tag :end_date, params[:end_date], class: 'form-control end-date mx-0', placeholder: '结束时间' %> -
-
VS
-
- <%= text_field_tag :other_begin_date, params[:other_begin_date], class: 'form-control start-date mx-0', placeholder: '开始时间' %> -
- <%= text_field_tag :other_end_date, params[:other_end_date], class: 'form-control end-date mx-0', placeholder: '结束时间' %> -
-
-
- -
- <%= hidden_field_tag :data_type, params[:data_type] || 'grow' %> - <%= javascript_void_link '时段对比', class: "btn btn-outline-info btn-sm contrast-btn #{params[:data_type] == 'contrast' ? 'active' : ''}", - data: { toggle: 'tooltip', trigger: 'hover', title: '请在左侧分别选择需进行对比的两个时段,具体从当日5:00开始计算,下表显示两时段选定指标两时段变化情况对比' } %> - <%= javascript_void_link '数据变化', class: "btn btn-outline-info btn-sm grow-btn #{params[:data_type] == 'contrast' ? '' : 'active'}", - data: { toggle: 'tooltip', trigger: 'hover', title: '请在左侧选择时间段,具体从当日5:00开始计算,下表显示选定时间段内各项指标数据变化情况' } %> -
- - <%= text_field_tag :keyword, params[:keyword], placeholder: 'ID/单位名称检索', class: 'form-control mx-3 search-input' %> - - <%= javascript_void_link '搜索', class: 'btn btn-primary search-btn' %> -
-
- -
- <%= render partial: 'admins/school_statistics/shared/list', locals: { statistics: @statistics } %> -
\ No newline at end of file diff --git a/app/views/admins/school_statistics/index.js.erb b/app/views/admins/school_statistics/index.js.erb deleted file mode 100644 index 3f5f7334b..000000000 --- a/app/views/admins/school_statistics/index.js.erb +++ /dev/null @@ -1 +0,0 @@ -$(".school-statistic-list-container").html("<%= j(render partial: 'admins/school_statistics/shared/list', locals: { statistics: @statistics }) %>") \ No newline at end of file diff --git a/app/views/admins/school_statistics/shared/_contrast_list.html.erb b/app/views/admins/school_statistics/shared/_contrast_list.html.erb deleted file mode 100644 index 928340c80..000000000 --- a/app/views/admins/school_statistics/shared/_contrast_list.html.erb +++ /dev/null @@ -1,72 +0,0 @@ -
-
- 学校数据统计(<%= I18n.t("school_daily_report.#{params[:contrast_column]}") %>变化统计情况) -
- <%= select_tag :contrast_column, - options_for_select(select_options, params[:contrast_column]), - class: 'form-control contrast-column-select' %> - - <% if statistics.present? %> -
- 说明:新增数=时段二-时段一;新增百分比=(新增数/时段一)*100%(保留小数点后五位) -
- <% end %> -
- -<% if statistics.present? %> - - - - - - - - - - - <% statistics.each do |statistic| %> - - - - - <% - increase = statistic['other_total'] - statistic['total'] - percentage = statistic['total'].zero? ? increase.to_f * 100 : (increase / statistic['total'].to_f) * 100 - %> - <% if increase > 0 %> - - <% if statistic['total'].zero? %> - - <% else %> - - <% end %> - <% elsif increase.zero? %> - - - <% else %> - - - <% end %> - - <% end %> - -
单位名称时段一
<%= "(#{params[:begin_date]} 05:00至#{(Time.zone.parse(params[:end_date]) + 1.days).strftime('%Y-%m-%d')} 05:00)" %>
时段二
<%= "(#{params[:other_begin_date]} 05:00至#{(Time.zone.parse(params[:other_end_date]) + 1.days).strftime('%Y-%m-%d')} 05:00)" %>
- <%= sort_tag('变化情况', name: 'percentage', path: contrast_admins_school_statistics_path) %> -
( 新 增 数 | 新增百分比) -
- <%= link_to statistic.school_name, "/colleges/#{statistic.school_id}/statistics", - target: '_blank', data: { toggle: 'tooltip', title: '点击查看学校统计概况' } %> - <%= statistic['total'] %><%= statistic['other_total'] %> - +<%= increase %> - -+<%= percentage.round(5) %>% - <%= increase %> - <%= percentage.round(5) %>% - <%= increase %> - <%= percentage.round(5) %>%
- - <%= render partial: 'admins/shared/paginate', locals: { objects: statistics } %> -<% else %> -
- 暂无数据,请选择时间段对比 -
-<% end %> \ No newline at end of file diff --git a/app/views/admins/school_statistics/shared/_list.html.erb b/app/views/admins/school_statistics/shared/_list.html.erb deleted file mode 100644 index 9d2a1c209..000000000 --- a/app/views/admins/school_statistics/shared/_list.html.erb +++ /dev/null @@ -1,58 +0,0 @@ -
- 统计总计: - <% if params[:grow_begin_date].present? %> - <%= params[:grow_begin_date] %> 05:00至<%= (Time.zone.parse(params[:grow_end_date]) + 1.days).strftime('%Y-%m-%d') %> 05:00 - <% else %> - <%= (Time.current - 5.hour).beginning_of_day.ago(1.days).strftime('%Y-%m-%d') %> 05:00至 - <%= (Time.current - 5.hour).beginning_of_day.strftime('%Y-%m-%d') %> 05:00, - <% end %> - 新增教师<%= @grow_summary.teacher_increase_count || 0 %>人, - 新增学生<%= @grow_summary.student_increase_count || 0 %>人, - 新增课堂<%= @grow_summary.course_increase_count || 0 %>个, - 新增实训<%= @grow_summary.shixun_increase_count || 0 %>个, - 新增实训作业<%= @grow_summary.shixun_homework_count || 0 %>个, - 新增实训评测<%= @grow_summary.shixun_evaluate_count || 0 %>个, - 活跃用户 - <%= @grow_summary.uniq_active_user_count.to_i.zero? ? @grow_summary.active_user_count.to_i : @grow_summary.uniq_active_user_count.to_i %> - 个 -
- - - - - - - - - - - - - - - - - <% if statistics.present? %> - <% statistics.each_with_index do |statistic, index| %> - - - - - - - - - - - - <% end %> - <% else %> - <%= render 'admins/shared/no_data_for_table' %> - <% end %> - -
序号单位名称<%= sort_tag('新增教师', name: 'teacher_increase_count', path: admins_school_statistics_path) %><%= sort_tag('新增学生', name: 'student_increase_count', path: admins_school_statistics_path) %><%= sort_tag('新增课堂', name: 'course_increase_count', path: admins_school_statistics_path) %><%= sort_tag('新增实训', name: 'shixun_increase_count', path: admins_school_statistics_path) %><%= sort_tag('新增实训作业', name: 'shixun_homework_count', path: admins_school_statistics_path) %><%= sort_tag('新增实训评测', name: 'shixun_evaluate_count', path: admins_school_statistics_path) %><%= sort_tag('活跃用户', name: 'uniq_active_user_count', path: admins_school_statistics_path) %>
<%= list_index_no(@params_page.to_i, index) %> - <%= link_to statistic.school_name, "/colleges/#{statistic.school_id}/statistics", - target: '_blank', data: { toggle: 'tooltip', title: '点击查看学校统计概况' } %> - <%= statistic.teacher_increase_count.to_i %><%= statistic.student_increase_count.to_i %><%= statistic.course_increase_count.to_i %><%= statistic.shixun_increase_count.to_i %><%= statistic.shixun_homework_count.to_i %><%= statistic.shixun_evaluate_count.to_i %><%= statistic.uniq_active_user_count.to_i.zero? ? statistic.active_user_count.to_i : statistic.uniq_active_user_count.to_i %>
- -<%= render partial: 'admins/shared/paginate', locals: { objects: statistics } %> \ No newline at end of file diff --git a/app/views/admins/schools/index.html.erb b/app/views/admins/schools/index.html.erb deleted file mode 100644 index 56cbadd9d..000000000 --- a/app/views/admins/schools/index.html.erb +++ /dev/null @@ -1,16 +0,0 @@ -<% define_admin_breadcrumbs do %> - <% add_admin_breadcrumb('单位列表') %> -<% end %> - -
- <%= form_tag(admins_schools_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 %> - - <%#= javascript_void_link '新建单位', class: 'btn btn-primary', data: { toggle: 'modal', target: '.admin-create-school-modal' } %> -
- -
- <%= render partial: 'admins/schools/shared/list', locals: { schools: @schools } %> -
\ No newline at end of file diff --git a/app/views/admins/schools/index.js.erb b/app/views/admins/schools/index.js.erb deleted file mode 100644 index 41a2454be..000000000 --- a/app/views/admins/schools/index.js.erb +++ /dev/null @@ -1 +0,0 @@ -$('.school-list-container').html("<%= j(render partial: 'admins/schools/shared/list', locals: { schools: @schools }) %>"); \ No newline at end of file diff --git a/app/views/admins/schools/index.json.jbuilder b/app/views/admins/schools/index.json.jbuilder deleted file mode 100644 index 3f358191f..000000000 --- a/app/views/admins/schools/index.json.jbuilder +++ /dev/null @@ -1,6 +0,0 @@ -json.count @total_count -json.schools do - json.array! @schools.each do |school| - json.extract! school, :id, :name - end -end \ No newline at end of file diff --git a/app/views/admins/schools/shared/_list.html.erb b/app/views/admins/schools/shared/_list.html.erb deleted file mode 100644 index dd0a054e3..000000000 --- a/app/views/admins/schools/shared/_list.html.erb +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - - - - - <% if schools.present? %> - <% schools.each_with_index do |school, index| %> - - - - - - - - - - - - - - - <% end %> - <% else %> - <%= render 'admins/shared/no_data_for_table' %> - <% end %> - -
序号IDLOGO标识码单位名称地区城市详细地址<%= sort_tag('用户数', name: 'users_count', path: admins_schools_path) %>部门数<%= sort_tag('创建时间', name: 'created_at', path: admins_schools_path) %>操作
<%= list_index_no((params[:page] || 1).to_i, index) %><%= school.id %> - <% if Util::FileManage.exists?(school) %> - <%= image_tag(Util::FileManage.source_disk_file_url(school).to_s + "?#{Time.now.to_i}", width: 40, height: 40, class: 'preview-image') %> - <% else %> - <%= content_tag(:span, '--', class: 'text-secondary') %> - <% end %> - <%= display_text school.identifier %><%= school.name %><%= school.province %><%= school.city %><%= school.address %><%= school.users_count %><%= @department_count.fetch(school.id, 0) %><%= school.created_at&.strftime('%Y-%m-%d %H:%M') %> - <%= delete_link '删除', admins_school_path(school, element: ".school-item-#{school.id}"), class: 'delete-school-action' %> -
- -<%= render partial: 'admins/shared/paginate', locals: { objects: schools } %> \ No newline at end of file diff --git a/app/views/admins/shared/_sidebar.html.erb b/app/views/admins/shared/_sidebar.html.erb index 221bbbc11..dbf7932e1 100644 --- a/app/views/admins/shared/_sidebar.html.erb +++ b/app/views/admins/shared/_sidebar.html.erb @@ -49,21 +49,30 @@ <% end %> + +
  • <%= sidebar_item(admins_reversed_keywords_path, '系统保留关键词', icon: 'key', controller: 'admins-reversed_keywords') %>
  • <%= sidebar_item(admins_laboratories_path, '云上实验室', icon: 'cloud', controller: 'admins-laboratories') %>
  • - <%= sidebar_item('/admins/sidekiq', '定时任务', icon: 'bell', controller: 'root') %> + <%= sidebar_item_group('#setting-system', '系统配置', icon: 'wrench') do %> +
  • <%= sidebar_item(admins_sites_path, 'setting接口配置', icon: 'deaf', controller: 'admins-sites') %>
  • +
  • <%= sidebar_item(admins_edu_settings_path, '全局变量配置', icon: 'pencil-square', controller: 'admins-edu_settings') %>
  • +
  • <%= sidebar_item(admins_system_notifications_path, '系统通知配置', icon: 'bell', controller: 'admins-system_notifications') %>
  • +
  • <%= sidebar_item(admins_message_templates_path, '消息模版配置', icon: 'folder', controller: 'admins-message_templates') %>
  • + <% end %> +
  • + <%= sidebar_item('/admins/sidekiq', '定时任务', icon: 'bell', controller: 'root') %> +
  • + + <%= render_admin_statistics_item %> +
  • <%= sidebar_item('/', '返回主站', icon: 'sign-out', controller: 'root') %>
  • diff --git a/app/views/admins/sites/_form.html.erb b/app/views/admins/sites/_form.html.erb new file mode 100644 index 000000000..1776bcd03 --- /dev/null +++ b/app/views/admins/sites/_form.html.erb @@ -0,0 +1,42 @@ + \ No newline at end of file diff --git a/app/views/admins/sites/_list.html.erb b/app/views/admins/sites/_list.html.erb new file mode 100644 index 000000000..bef39cf1f --- /dev/null +++ b/app/views/admins/sites/_list.html.erb @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + <% if sites.present? %> + <% sites.each_with_index do |site, index| %> + + + + + + + + + + + <% end %> + <% else %> + <%= render 'admins/shared/no_data_for_table' %> + <% end %> + +
    序号名称路由标识类型<%= sort_tag('创建时间', name: 'created_at', path: admins_sites_path) %>操作
    <%= list_index_no((params[:page] || 1).to_i, index) %> + <%= overflow_hidden_span display_text(site.name), width: 150 %> + <%= site.url %><%= overflow_hidden_span display_text(site.key), width: 150 %><%= site.site_type.humanize %><%= site.created_at&.strftime('%Y-%m-%d %H:%M') %> + <%= link_to "编辑", edit_admins_site_path(site), remote: true, class: "action" %> + <%= link_to "删除", admins_site_path(site), method: :delete, data:{confirm: "确认删除的吗?"}, class: "action" %> +
    + +<%= render partial: 'admins/shared/paginate', locals: { objects: sites } %> \ No newline at end of file diff --git a/app/views/admins/sites/edit.js.erb b/app/views/admins/sites/edit.js.erb new file mode 100644 index 000000000..6711ffc1f --- /dev/null +++ b/app/views/admins/sites/edit.js.erb @@ -0,0 +1,2 @@ +$("#site-modals").html("<%= j render(partial: 'admins/sites/form', locals: {type: 'update'}) %>") +$(".site-change-modal").modal('show'); \ No newline at end of file diff --git a/app/views/admins/sites/index.html.erb b/app/views/admins/sites/index.html.erb new file mode 100644 index 000000000..8a5fa1bfc --- /dev/null +++ b/app/views/admins/sites/index.html.erb @@ -0,0 +1,24 @@ +<% define_admin_breadcrumbs do %> + <% add_admin_breadcrumb('Setting接口配置') %> +<% end %> + +
    + <%= form_tag(admins_sites_path, method: :get, class: 'form-inline search-form flex-1', remote: true) do %> +
    + + <% type_options = [['全部', ''], ['Add', Site.site_types[:add]], ['Personal', Site.site_types[:personal]], ['Common', Site.site_types[:common]]] %> + <%= select_tag(:site_type, options_for_select(type_options), class: 'form-control') %> +
    + + <%= 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_site_path, remote: true, class: "btn btn-primary pull-right", "data-disabled-with":"...新增" %> +
    + +
    + <%= render partial: 'admins/sites/list', locals: { sites: @sites } %> +
    +
    +
    diff --git a/app/views/admins/sites/index.js.erb b/app/views/admins/sites/index.js.erb new file mode 100644 index 000000000..797d58296 --- /dev/null +++ b/app/views/admins/sites/index.js.erb @@ -0,0 +1 @@ +$('.sites-list-container').html("<%= j( render partial: 'admins/sites/list', locals: { sites: @sites } ) %>"); \ No newline at end of file diff --git a/app/views/admins/sites/new.js.erb b/app/views/admins/sites/new.js.erb new file mode 100644 index 000000000..6978fed32 --- /dev/null +++ b/app/views/admins/sites/new.js.erb @@ -0,0 +1,2 @@ +$("#site-modals").html("<%= j render(partial: 'admins/sites/form', locals: {type: 'create'}) %>") +$(".site-change-modal").modal('show'); \ No newline at end of file diff --git a/app/views/admins/sub_disciplines/adjust_position.js.erb b/app/views/admins/sub_disciplines/adjust_position.js.erb deleted file mode 100644 index 0dae991c1..000000000 --- a/app/views/admins/sub_disciplines/adjust_position.js.erb +++ /dev/null @@ -1,5 +0,0 @@ -<% if @message.present? %> -$.notify({ message: "<%= @message %>" }); -<% else %> -$(".sub-discipline-list-container").html("<%= j(render :partial => 'admins/sub_disciplines/shared/list') %>"); -<% end %> \ No newline at end of file diff --git a/app/views/admins/sub_disciplines/destroy.js.erb b/app/views/admins/sub_disciplines/destroy.js.erb deleted file mode 100644 index bb03cdcf1..000000000 --- a/app/views/admins/sub_disciplines/destroy.js.erb +++ /dev/null @@ -1,2 +0,0 @@ -$.notify({ message: '删除成功' }); -$(".sub-discipline-item-<%= @sub_discipline_id %>").remove(); \ No newline at end of file diff --git a/app/views/admins/sub_disciplines/edit.js.erb b/app/views/admins/sub_disciplines/edit.js.erb deleted file mode 100644 index aa7219de5..000000000 --- a/app/views/admins/sub_disciplines/edit.js.erb +++ /dev/null @@ -1,2 +0,0 @@ -$('.admin-modal-container').html("<%= j( render partial: 'admins/sub_disciplines/shared/edit_sub_discipline_modal', locals: { sub_discipline: @sub_discipline } ) %>"); -$('.modal.admin-edit-sub-discipline-modal').modal('show'); \ No newline at end of file diff --git a/app/views/admins/sub_disciplines/index.html.erb b/app/views/admins/sub_disciplines/index.html.erb deleted file mode 100644 index d782e00ae..000000000 --- a/app/views/admins/sub_disciplines/index.html.erb +++ /dev/null @@ -1,14 +0,0 @@ -<% define_admin_breadcrumbs do %> - <% add_admin_breadcrumb('课程方向', admins_disciplines_path) %> - <% add_admin_breadcrumb(@discipline.name) %> -<% end %> - -
    - <%= javascript_void_link '新增', class: 'btn btn-primary', data: { toggle: 'modal', target: '.admin-create-sub-discipline-modal' } %> -
    - -
    - <%= render(partial: 'admins/sub_disciplines/shared/list') %> -
    - -<%= render 'admins/sub_disciplines/shared/create_sub_discipline_modal' %> diff --git a/app/views/admins/sub_disciplines/shared/_create_sub_discipline_modal.html.erb b/app/views/admins/sub_disciplines/shared/_create_sub_discipline_modal.html.erb deleted file mode 100644 index a8b00b051..000000000 --- a/app/views/admins/sub_disciplines/shared/_create_sub_discipline_modal.html.erb +++ /dev/null @@ -1,28 +0,0 @@ - \ No newline at end of file diff --git a/app/views/admins/sub_disciplines/shared/_edit_sub_discipline_modal.html.erb b/app/views/admins/sub_disciplines/shared/_edit_sub_discipline_modal.html.erb deleted file mode 100644 index b51c267bb..000000000 --- a/app/views/admins/sub_disciplines/shared/_edit_sub_discipline_modal.html.erb +++ /dev/null @@ -1,23 +0,0 @@ - \ No newline at end of file diff --git a/app/views/admins/sub_disciplines/shared/_list.html.erb b/app/views/admins/sub_disciplines/shared/_list.html.erb deleted file mode 100644 index cd0b02f88..000000000 --- a/app/views/admins/sub_disciplines/shared/_list.html.erb +++ /dev/null @@ -1,37 +0,0 @@ -<% max_position = @sub_disciplines.pluck(:position).max %> - - - - - - - - - - - - - <% if @sub_disciplines.present? %> - <% @sub_disciplines.each do |sub| %> - - - - - - - - - <% end %> - <% else %> - <%= render 'admins/shared/no_data_for_table' %> - <% end %> - -
    序号课程实践课程实训题库操作
    <%= sub.position %> - <%= link_to sub.name, admins_tag_disciplines_path(sub_discipline_id: sub), :title => sub.name %> - <%= check_box_tag :subject,!sub.subject,sub.subject,disabled:!sub.discipline&.subject,remote:true,data:{id:sub.id},class:"sub-discipline-source-form" %><%= check_box_tag :shixun,!sub.shixun,sub.shixun,disabled:!sub.discipline&.shixun,remote:true,data:{id:sub.id},class:"sub-discipline-source-form" %><%= check_box_tag :question,!sub.question,sub.question,disabled:!sub.discipline&.question,remote:true,data:{id:sub.id},class:"sub-discipline-source-form" %> - <%= javascript_void_link('上移', class: 'move-action', data: { id: sub.id, opr: "up" }, style: sub.position == 1 ? 'display:none' : '') %> - <%= javascript_void_link('下移', class: 'move-action', data: { id: sub.id, opr: "down" }, style: sub.position == max_position ? 'display:none' : '') %> - - <%= link_to '编辑', edit_admins_sub_discipline_path(sub), remote: true, class: 'action' %> - <%= delete_link '删除', admins_sub_discipline_path(sub, element: ".sub-discipline-item-#{sub.id}"), class: 'delete-sub-discipline-action' %> -
    \ No newline at end of file diff --git a/app/views/admins/sub_disciplines/update.js.erb b/app/views/admins/sub_disciplines/update.js.erb deleted file mode 100644 index 395e06778..000000000 --- a/app/views/admins/sub_disciplines/update.js.erb +++ /dev/null @@ -1,6 +0,0 @@ -<% if @message.present? %> -$.notify({ message: "<%= @message %>" }); -<% else %> -$('.modal.admin-edit-sub-discipline-modal').modal("hide"); -$(".sub-discipline-list-container").html("<%= j(render :partial => 'admins/sub_disciplines/shared/list') %>"); -<% end %> \ No newline at end of file diff --git a/app/views/admins/sub_repertoires/destroy.js.erb b/app/views/admins/sub_repertoires/destroy.js.erb deleted file mode 100644 index 4575f811c..000000000 --- a/app/views/admins/sub_repertoires/destroy.js.erb +++ /dev/null @@ -1,2 +0,0 @@ -$.notify({ message: '删除成功' }); -$(".sub-repertoire-item-<%= @sub_repertoire_id %>").remove(); \ No newline at end of file diff --git a/app/views/admins/sub_repertoires/edit.js.erb b/app/views/admins/sub_repertoires/edit.js.erb deleted file mode 100644 index 10fe19697..000000000 --- a/app/views/admins/sub_repertoires/edit.js.erb +++ /dev/null @@ -1,2 +0,0 @@ -$('.admin-modal-container').html("<%= j( render partial: 'admins/sub_repertoires/shared/edit_sub_repertoire_modal', locals: { sub_repertoire: @sub_repertoire } ) %>"); -$('.modal.admin-edit-sub-repertoire-modal').modal('show'); \ No newline at end of file diff --git a/app/views/admins/sub_repertoires/index.html.erb b/app/views/admins/sub_repertoires/index.html.erb deleted file mode 100644 index be8ec98f9..000000000 --- a/app/views/admins/sub_repertoires/index.html.erb +++ /dev/null @@ -1,14 +0,0 @@ -<% define_admin_breadcrumbs do %> - <% add_admin_breadcrumb('技术体系', admins_repertoires_path) %> - <% add_admin_breadcrumb(@repertoire.name) %> -<% end %> - -
    - <%= javascript_void_link '新增', class: 'btn btn-primary', data: { toggle: 'modal', target: '.admin-create-sub-repertoire-modal' } %> -
    - -
    - <%= render(partial: 'admins/sub_repertoires/shared/list') %> -
    - -<%= render 'admins/sub_repertoires/shared/create_sub_repertoire_modal' %> diff --git a/app/views/admins/sub_repertoires/shared/_create_sub_repertoire_modal.html.erb b/app/views/admins/sub_repertoires/shared/_create_sub_repertoire_modal.html.erb deleted file mode 100644 index 435f2fb32..000000000 --- a/app/views/admins/sub_repertoires/shared/_create_sub_repertoire_modal.html.erb +++ /dev/null @@ -1,28 +0,0 @@ - \ No newline at end of file diff --git a/app/views/admins/sub_repertoires/shared/_edit_sub_repertoire_modal.html.erb b/app/views/admins/sub_repertoires/shared/_edit_sub_repertoire_modal.html.erb deleted file mode 100644 index 28ff8a169..000000000 --- a/app/views/admins/sub_repertoires/shared/_edit_sub_repertoire_modal.html.erb +++ /dev/null @@ -1,23 +0,0 @@ - diff --git a/app/views/admins/sub_repertoires/shared/_list.html.erb b/app/views/admins/sub_repertoires/shared/_list.html.erb deleted file mode 100644 index 77d20ef4c..000000000 --- a/app/views/admins/sub_repertoires/shared/_list.html.erb +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - <% if @sub_repertoires.present? %> - <% @sub_repertoires.each_with_index do |sub, index| %> - - - - - - <% end %> - <% else %> - <%= render 'admins/shared/no_data_for_table' %> - <% end %> - -
    序号课程操作
    <%= index + 1 %> - <%= link_to sub.name, admins_tag_repertoires_path(sub_repertoire_id: sub), :title => sub.name %> - - <%= link_to '编辑', edit_admins_sub_repertoire_path(sub), remote: true, class: 'action' %> - <%= delete_link '删除', admins_sub_repertoire_path(sub, element: ".sub-repertoire-item-#{sub.id}"), class: 'delete-sub-repertoire-action' %> -
    \ No newline at end of file diff --git a/app/views/admins/sub_repertoires/update.js.erb b/app/views/admins/sub_repertoires/update.js.erb deleted file mode 100644 index 04d282c5a..000000000 --- a/app/views/admins/sub_repertoires/update.js.erb +++ /dev/null @@ -1,2 +0,0 @@ -$('.modal.admin-edit-sub-repertoire-modal').modal("hide"); -$(".sub-repertoire-list-container").html("<%= j(render :partial => 'admins/sub_repertoires/shared/list') %>"); \ No newline at end of file diff --git a/app/views/admins/subject_authorizations/index.html.erb b/app/views/admins/subject_authorizations/index.html.erb deleted file mode 100644 index f3ae7bf56..000000000 --- a/app/views/admins/subject_authorizations/index.html.erb +++ /dev/null @@ -1,33 +0,0 @@ -<% define_admin_breadcrumbs do %> - <% add_admin_breadcrumb('实践课程发布') %> -<% end %> - -
    - - - <%= form_tag(admins_subject_authorizations_path(unsafe_params), method: :get, class: 'form-inline search-form justify-content-end mt-3', remote: true) do %> -
    - - <% status_options = [['全部', 'processed'], ['已同意', 'agreed'], ['已拒绝', 'refused']] %> - <%= select_tag(:status, options_for_select(status_options), class: 'form-control') %> -
    - <%= 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 %> -
    - -
    - <%= render(partial: 'admins/subject_authorizations/shared/list', - locals: { applies: @applies, subject_map: @subject_map, challenge_count_map: @challenge_count_map }) %> -
    - -<%= render(partial: 'admins/shared/admin_common_refuse_modal') %> \ No newline at end of file diff --git a/app/views/admins/subject_authorizations/index.js.erb b/app/views/admins/subject_authorizations/index.js.erb deleted file mode 100644 index a21809041..000000000 --- a/app/views/admins/subject_authorizations/index.js.erb +++ /dev/null @@ -1 +0,0 @@ -$('.subject-authorization-list-container').html("<%= j( render partial: 'admins/subject_authorizations/shared/list', locals: { applies: @applies, subject_map: @subject_map, challenge_count_map: @challenge_count_map } ) %>"); \ No newline at end of file diff --git a/app/views/admins/subject_authorizations/shared/_list.html.erb b/app/views/admins/subject_authorizations/shared/_list.html.erb deleted file mode 100644 index 94503dad7..000000000 --- a/app/views/admins/subject_authorizations/shared/_list.html.erb +++ /dev/null @@ -1,66 +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 %> - <% subject = subject_map[apply.container_id] %> - - - - - - - - - - - <% if is_processed %> - - - <% else %> - - <% end %> - - <% end %> - <% else %> - <%= render 'admins/shared/no_data_for_table' %> - <% end %> - -
    序号头像创建者实践课程名称阶段数实训数关卡数时间拒绝原因状态操作
    <%= list_index_no((params[:page] || 1).to_i, index) %> - <%= link_to "/users/#{user.login}", class: 'subject-authorization-avatar', target: '_blank', data: { toggle: 'tooltip', title: '个人主页' } do %> - - <% end %> - <%= link_to user&.real_name,"/users/#{user&.login}", target: "_blank" %> - <%= link_to "/paths/#{subject.id}", target: '_blank' do %> - <%= overflow_hidden_span subject.name, width: 300 %> - <% end %> - <%= subject.stages_count %><%= subject.shixuns_count %><%= challenge_count_map.fetch(subject.id, 0) %><%= apply.updated_at.strftime('%Y-%m-%d %H:%M') %><%= overflow_hidden_span apply.reason, width: 140 %><%= apply.status_text %> - <%= agree_link '同意', agree_admins_subject_authorization_path(apply, element: ".subject-authorization-#{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_subject_authorization_path(apply, element: ".subject-authorization-#{apply.id}") - }) %> -
    - -<%= render partial: 'admins/shared/paginate', locals: { objects: applies } %> \ No newline at end of file diff --git a/app/views/admins/subject_settings/index.html.erb b/app/views/admins/subject_settings/index.html.erb deleted file mode 100644 index b46c1d90d..000000000 --- a/app/views/admins/subject_settings/index.html.erb +++ /dev/null @@ -1,41 +0,0 @@ -<% define_admin_breadcrumbs do %> - <% add_admin_breadcrumb('课程配置') %> -<% end %> - -
    - <%= form_tag(admins_subject_settings_path, method: :get, class: 'form-inline search-form flex-1', remote: true) do %> -
    - - <% status_options = [['全部', ''], ['编辑中', 'pending'], ['审核中', 'applying'], ['已公开', 'published']] %> - <%= 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_show, false, id:'') %> - <%= check_box_tag(:homepage_show, true, params[:homepage_show].to_s == 'true', class: 'form-check-input') %> - -
    - -
    - <%= hidden_field_tag(:excellent, false, id:'') %> - <%= check_box_tag(:excellent, true, params[:excellent].to_s == 'true', class: 'form-check-input') %> - -
    - - <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> - - <% end %> -
    - -
    - <%= render partial: 'admins/subject_settings/shared/list', locals: { subjects: @subjects } %> -
    - -<%= render partial: 'admins/shared/modal/upload_file_modal', locals: { title: '上传图片', accept: 'image/*' } %> \ No newline at end of file diff --git a/app/views/admins/subject_settings/index.js.erb b/app/views/admins/subject_settings/index.js.erb deleted file mode 100644 index c3f403462..000000000 --- a/app/views/admins/subject_settings/index.js.erb +++ /dev/null @@ -1 +0,0 @@ -$('.subject-setting-list-container').html("<%= j( render partial: 'admins/subject_settings/shared/list', locals: { subjects: @subjects } ) %>"); \ No newline at end of file diff --git a/app/views/admins/subject_settings/mobile.js.erb b/app/views/admins/subject_settings/mobile.js.erb deleted file mode 100644 index e69de29bb..000000000 diff --git a/app/views/admins/subject_settings/shared/_edit_subject_modal.html.erb b/app/views/admins/subject_settings/shared/_edit_subject_modal.html.erb deleted file mode 100644 index 6e9aa871a..000000000 --- a/app/views/admins/subject_settings/shared/_edit_subject_modal.html.erb +++ /dev/null @@ -1,33 +0,0 @@ - \ No newline at end of file diff --git a/app/views/admins/subject_settings/shared/_list.html.erb b/app/views/admins/subject_settings/shared/_list.html.erb deleted file mode 100644 index 79411e892..000000000 --- a/app/views/admins/subject_settings/shared/_list.html.erb +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - <% if subjects.present? %> - <% subjects.each_with_index do |subject, index| %> - - <% page_no = ((params[:page] || 1).to_i - 1) * 20 + index + 1 %> - <%= render partial: "admins/subject_settings/shared/td",locals: {subject: subject,page_no:page_no} %> - - <% end %> - <% else %> - <%= render 'admins/shared/no_data_for_table' %> - <% end %> - -
    序号名称老版技术体系状态课程体系封面开课人数操作
    - -<%= render partial: 'admins/shared/paginate', locals: { objects: subjects } %> \ No newline at end of file diff --git a/app/views/admins/subject_settings/shared/_td.html.erb b/app/views/admins/subject_settings/shared/_td.html.erb deleted file mode 100644 index 94d34f42d..000000000 --- a/app/views/admins/subject_settings/shared/_td.html.erb +++ /dev/null @@ -1,47 +0,0 @@ -<%= page_no %> - - <%= link_to(subject.name, "/paths/#{subject.id}", target: '_blank') %> - 首页 - 金课 - -<%= display_text subject.repertoire&.name %> -<%= display_text subject.public == 2 ? "已公开" : ((subject.public == 1 && subject.status == 2) ? "审核中" : "未发布") %> - - <%= select_tag(:sub_disciplines, options_for_select(@sub_disciplines, subject.sub_disciplines.pluck(:id)),multiple:true,class:"form-control subject-setting-form",data:{id:subject.id},id:"tags-chosen-#{subject.id}") %> - - - <% 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') %> - <%= javascript_void_link image_exists ? '重新上传' : '上传图片', class: 'action upload-image-action', data: { source_id: subject.id, source_type: 'Subject', toggle: 'modal', target: '.admin-upload-file-modal' } %> - -<%= subject.student_count %> - - <%= check_box_tag :show_mobile, !subject.show_mobile, subject.show_mobile, remote: true, - data: {id: subject.id, toggle: "tooltip", placement: "top"}, class: "subject-mobile-form mr10", title: "小程序端显示" %> - <%= link_to('编辑', edit_admins_subject_path(subject), remote: true, class: 'edit-action') %> - - - \ No newline at end of file diff --git a/app/views/admins/subject_settings/update.js.erb b/app/views/admins/subject_settings/update.js.erb deleted file mode 100644 index c95a41ec3..000000000 --- a/app/views/admins/subject_settings/update.js.erb +++ /dev/null @@ -1 +0,0 @@ -$.notify({ message: '更新成功' }); \ No newline at end of file diff --git a/app/views/admins/subject_settings/update_mobile_show.js.erb b/app/views/admins/subject_settings/update_mobile_show.js.erb deleted file mode 100644 index e69de29bb..000000000 diff --git a/app/views/admins/subjects/edit.js.erb b/app/views/admins/subjects/edit.js.erb deleted file mode 100644 index b2e12f9bb..000000000 --- a/app/views/admins/subjects/edit.js.erb +++ /dev/null @@ -1,2 +0,0 @@ -$('.admin-modal-container').html("<%= j( render partial: 'admins/subjects/shared/edit_subject_modal', locals: { subject: @subject } ) %>"); -$('.modal.admin-edit-subject-modal').modal('show'); \ No newline at end of file diff --git a/app/views/admins/subjects/index.html.erb b/app/views/admins/subjects/index.html.erb deleted file mode 100644 index 39c4a349b..000000000 --- a/app/views/admins/subjects/index.html.erb +++ /dev/null @@ -1,39 +0,0 @@ -<% define_admin_breadcrumbs do %> - <% add_admin_breadcrumb('课程列表') %> -<% end %> - -
    - <%= form_tag(admins_subjects_path, method: :get, class: 'form-inline search-form flex-1', remote: true) do %> -
    - - <% status_options = [['全部', ''], ["编辑中", "editing"], ["已发布", 'processed'], ["待审核", 'pending'], ["已公开", 'publiced']] %> - <%= 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_show, false, id:'') %> - <%= check_box_tag(:homepage_show, true, params[:homepage_show].to_s == 'true', class: 'form-check-input') %> - -
    - -
    - <%= hidden_field_tag(:excellent, false, id:'') %> - <%= check_box_tag(:excellent, true, params[:excellent].to_s == 'true', class: 'form-check-input') %> - -
    - - <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> - - <% end %> -
    - -
    - <%= render partial: 'admins/subjects/shared/list', locals: { subjects: @subjects } %> -
    \ No newline at end of file diff --git a/app/views/admins/subjects/index.js.erb b/app/views/admins/subjects/index.js.erb deleted file mode 100644 index 20e07f13a..000000000 --- a/app/views/admins/subjects/index.js.erb +++ /dev/null @@ -1 +0,0 @@ -$('.subject-list-container').html("<%= j( render partial: 'admins/subjects/shared/list', locals: { subjects: @subjects } ) %>"); \ No newline at end of file diff --git a/app/views/admins/subjects/shared/_edit_subject_modal.html.erb b/app/views/admins/subjects/shared/_edit_subject_modal.html.erb deleted file mode 100644 index 6e9aa871a..000000000 --- a/app/views/admins/subjects/shared/_edit_subject_modal.html.erb +++ /dev/null @@ -1,33 +0,0 @@ - \ No newline at end of file diff --git a/app/views/admins/subjects/shared/_list.html.erb b/app/views/admins/subjects/shared/_list.html.erb deleted file mode 100644 index ee136ddf5..000000000 --- a/app/views/admins/subjects/shared/_list.html.erb +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - <% if subjects.present? %> - <% subjects.each_with_index do |subject, index| %> - - - - - - - - - - - - <% end %> - <% else %> - <%= render 'admins/shared/no_data_for_table' %> - <% end %> - -
    序号名称阶段数实训数创建者单位<%= sort_tag('创建时间', name: 'created_at', path: admins_subjects_path) %>状态操作
    <%= list_index_no((params[:page] || 1).to_i, index) %> - <%= link_to(subject.name, "/paths/#{subject.id}", target: '_blank') %> - 首页 - 金课 - <%= subject.stages_count %><%= subject.shixuns_count %><%= subject.user.real_name %><%= subject.user.school_name %><%= subject.created_at&.strftime('%Y-%m-%d %H:%M') %> - <%= display_subject_status(subject) %> - - <%= javascript_void_link('隐藏', class: 'hide-action', data: { id: subject.id }, style: subject.hidden? ? 'display:none' : '') %> - <%= javascript_void_link('取消隐藏', class: 'active-action', data: { id: subject.id }, style: subject.hidden? ? '' : 'display:none') %> - - <%= delete_link '删除', admins_subject_path(subject, element: ".subject-item-#{subject.id}"), class: 'delete-subject-action' %> - - <% if subject.published? %> -
    - <%= javascript_void_link('更多', class: 'action dropdown-toggle', 'data-toggle': 'dropdown', 'aria-haspopup': true, 'aria-expanded': false) %> - -
    - <% end %> -
    - -<%= render partial: 'admins/shared/paginate', locals: { objects: subjects } %> \ No newline at end of file diff --git a/app/views/admins/system_notifications/_form.html.erb b/app/views/admins/system_notifications/_form.html.erb new file mode 100644 index 000000000..33c35747a --- /dev/null +++ b/app/views/admins/system_notifications/_form.html.erb @@ -0,0 +1,57 @@ +
    +
    <%= type == "create" ? "新建" : "编辑" %>系统通知
    + <%= link_to "返回", admins_system_notifications_path, class: "btn btn-default pull-right" %> +
    + +
    + <%= form_for @notification, url: {controller: "system_notifications", action: "#{type}"} do |p| %> +
    + +
    + <%= p.text_field :subject, class: "form-control input-lg", placeholder: "请输入系统通知标题" %> +
    + +
    +
    + +
    + <%= p.text_field :sub_subject, class: "form-control input-lg", placeholder: "请输入系统通知副标题" %> +
    + +
    +
    + +
    + <%= p.text_area :content, class:"form-control", style: 'display: none;', rows: "10", cols: "20", placeholer: "请输入系统通知正文" %> +
    + +
    + +
    +
    + <%= p.check_box :is_top, class: "form-check-input", value:"true"%> + +
    +
    +
    + <%= p.submit "确认", class: "btn btn-primary submit-btn" %> +
    + <% end %> +
    \ No newline at end of file diff --git a/app/views/admins/system_notifications/_form_modal.html.erb b/app/views/admins/system_notifications/_form_modal.html.erb new file mode 100644 index 000000000..658bbc9c0 --- /dev/null +++ b/app/views/admins/system_notifications/_form_modal.html.erb @@ -0,0 +1,40 @@ + \ No newline at end of file diff --git a/app/views/admins/system_notifications/_list.html.erb b/app/views/admins/system_notifications/_list.html.erb new file mode 100644 index 000000000..0d86ab035 --- /dev/null +++ b/app/views/admins/system_notifications/_list.html.erb @@ -0,0 +1,35 @@ + + + + + + + + + + + + + <% if notifications.present? %> + <% notifications.each_with_index do |notification, index| %> + + + + + + + + + <% end %> + <% else %> + <%= render 'admins/shared/no_data_for_table' %> + <% end %> + +
    序号标题副标题<%= sort_tag('是否置顶', name: 'is_top', path: admins_system_notifications_path) %><%= sort_tag('创建时间', name: 'created_at', path: admins_system_notifications_path) %>操作
    <%= list_index_no((params[:page] || 1).to_i, index) %><%= notification.subject %><%= notification.sub_subject %><%= notification.is_top ? '√' : '' %><%= notification.created_at&.strftime('%Y-%m-%d %H:%M') %> + <%= javascript_void_link '置顶', class: 'action unclose-action', data: { id: notification.id }, style: notification.is_top ? 'display: none;' : '' %> + <%= javascript_void_link '取消置顶', class: 'action close-action', data: { id: notification.id }, style: notification.is_top ? '' : 'display: none;' %> + <%= link_to "编辑", edit_admins_system_notification_path(notification), remote: true, class: "action" %> + <%= link_to "删除", admins_system_notification_path(notification), method: :delete, data:{confirm: "确认删除的吗?"}, class: "action" %> +
    + +<%= render partial: 'admins/shared/paginate', locals: { objects: notifications } %> \ No newline at end of file diff --git a/app/views/admins/system_notifications/edit.js.erb b/app/views/admins/system_notifications/edit.js.erb new file mode 100644 index 000000000..e5929f876 --- /dev/null +++ b/app/views/admins/system_notifications/edit.js.erb @@ -0,0 +1,2 @@ +$("#admins-system-notification-content").html("<%= j render(partial: 'admins/system_notifications/form', locals: {type: 'update'}) %>") +createMDEditor('system-notification-content-editor', { height: 500, placeholder: '请输入邮件模版' }); \ No newline at end of file diff --git a/app/views/admins/system_notifications/index.html.erb b/app/views/admins/system_notifications/index.html.erb new file mode 100644 index 000000000..01a35f216 --- /dev/null +++ b/app/views/admins/system_notifications/index.html.erb @@ -0,0 +1,21 @@ +<% define_admin_breadcrumbs do %> + <% add_admin_breadcrumb('系统通知模版') %> +<% end %> + +
    +
    + <%= form_tag(admins_system_notifications_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_system_notification_path, remote: true, class: "btn btn-primary pull-right", "data-disabled-with":"...新增" %> +
    + +
    +
    + +
    + <%= render partial: 'admins/system_notifications/list', locals: { notifications: @notifications } %> +
    +
    \ No newline at end of file diff --git a/app/views/admins/system_notifications/index.js.erb b/app/views/admins/system_notifications/index.js.erb new file mode 100644 index 000000000..a5cfec841 --- /dev/null +++ b/app/views/admins/system_notifications/index.js.erb @@ -0,0 +1 @@ +$('.system-notification-list-container').html("<%= j( render partial: 'admins/system_notifications/list', locals: { notifications: @notifications } ) %>"); \ No newline at end of file diff --git a/app/views/admins/system_notifications/new.js.erb b/app/views/admins/system_notifications/new.js.erb new file mode 100644 index 000000000..885357b5a --- /dev/null +++ b/app/views/admins/system_notifications/new.js.erb @@ -0,0 +1,2 @@ +$("#admins-system-notification-content").html("<%= j render(partial: 'admins/system_notifications/form', locals: {type: 'create'}) %>") +createMDEditor('system-notification-content-editor', { height: 500, placeholder: '请输入邮件模版' }); diff --git a/app/views/admins/tag_disciplines/adjust_position.js.erb b/app/views/admins/tag_disciplines/adjust_position.js.erb deleted file mode 100644 index 32e066698..000000000 --- a/app/views/admins/tag_disciplines/adjust_position.js.erb +++ /dev/null @@ -1,5 +0,0 @@ -<% if @message.present? %> -$.notify({ message: "<%= @message %>" }); -<% else %> -$(".tag-discipline-list-container").html("<%= j(render :partial => 'admins/tag_disciplines/shared/list') %>"); -<% end %> \ No newline at end of file diff --git a/app/views/admins/tag_disciplines/destroy.js.erb b/app/views/admins/tag_disciplines/destroy.js.erb deleted file mode 100644 index 0b61b7eb7..000000000 --- a/app/views/admins/tag_disciplines/destroy.js.erb +++ /dev/null @@ -1,2 +0,0 @@ -$.notify({ message: '删除成功' }); -$(".tag-discipline-item-<%= @tag_discipline_id %>").remove(); \ No newline at end of file diff --git a/app/views/admins/tag_disciplines/edit.js.erb b/app/views/admins/tag_disciplines/edit.js.erb deleted file mode 100644 index 90a4d28d5..000000000 --- a/app/views/admins/tag_disciplines/edit.js.erb +++ /dev/null @@ -1,2 +0,0 @@ -$('.admin-modal-container').html("<%= j( render partial: 'admins/tag_disciplines/shared/edit_tag_discipline_modal', locals: { tag_discipline: @tag_discipline } ) %>"); -$('.modal.admin-edit-tag-discipline-modal').modal('show'); \ No newline at end of file diff --git a/app/views/admins/tag_disciplines/index.html.erb b/app/views/admins/tag_disciplines/index.html.erb deleted file mode 100644 index 199137612..000000000 --- a/app/views/admins/tag_disciplines/index.html.erb +++ /dev/null @@ -1,15 +0,0 @@ -<% define_admin_breadcrumbs do %> - <% add_admin_breadcrumb('课程方向', admins_disciplines_path) %> - <% add_admin_breadcrumb(@sub_discipline&.discipline&.name, admins_sub_disciplines_path(discipline_id: @sub_discipline&.discipline_id)) %> - <% add_admin_breadcrumb(@sub_discipline.name) %> -<% end %> - -
    - <%= javascript_void_link '新增', class: 'btn btn-primary', data: { toggle: 'modal', target: '.admin-create-tag-discipline-modal' } %> -
    - -
    - <%= render(partial: 'admins/tag_disciplines/shared/list') %> -
    - -<%= render 'admins/tag_disciplines/shared/create_tag_discipline_modal' %> diff --git a/app/views/admins/tag_disciplines/shared/_create_tag_discipline_modal.html.erb b/app/views/admins/tag_disciplines/shared/_create_tag_discipline_modal.html.erb deleted file mode 100644 index 33132285b..000000000 --- a/app/views/admins/tag_disciplines/shared/_create_tag_discipline_modal.html.erb +++ /dev/null @@ -1,28 +0,0 @@ - \ No newline at end of file diff --git a/app/views/admins/tag_disciplines/shared/_edit_tag_discipline_modal.html.erb b/app/views/admins/tag_disciplines/shared/_edit_tag_discipline_modal.html.erb deleted file mode 100644 index 9eb50658c..000000000 --- a/app/views/admins/tag_disciplines/shared/_edit_tag_discipline_modal.html.erb +++ /dev/null @@ -1,23 +0,0 @@ - \ No newline at end of file diff --git a/app/views/admins/tag_disciplines/shared/_list.html.erb b/app/views/admins/tag_disciplines/shared/_list.html.erb deleted file mode 100644 index 59420c42c..000000000 --- a/app/views/admins/tag_disciplines/shared/_list.html.erb +++ /dev/null @@ -1,52 +0,0 @@ -<% max_position = @tag_disciplines.pluck(:position).max %> - - - - - - - - - - - - - - <% if @tag_disciplines.present? %> - <% @tag_disciplines.each do |tag| %> - - - - - - - - - - <% end %> - <% else %> - <%= render 'admins/shared/no_data_for_table' %> - <% end %> - -
    序号知识点创建者实践课程实训题库操作
    <%= tag.position %><%= tag.name %> - <% if tag.user.present? %> - <%= link_to tag.user.real_name, user_path(tag.user), :target => "_blank" %> - <% else %> - -- - <% end %> - - <% disabled = !(tag.sub_discipline&.subject && tag.discipline&.subject) %> - <%= check_box_tag :subject,!tag.subject,tag.subject,disabled:disabled,remote:true,data:{id:tag.id},class:"tag-discipline-source-form" %> - - <% disabled = !(tag.sub_discipline&.shixun && tag.discipline&.shixun) %> - <%= check_box_tag :shixun,!tag.shixun,tag.shixun,disabled:disabled,remote:true,data:{id:tag.id},class:"tag-discipline-source-form" %> - - <% disabled = !(tag.sub_discipline&.question && tag.discipline&.question) %> - <%= check_box_tag :question,!tag.question,tag.question,disabled:disabled,remote:true,data:{id:tag.id},class:"tag-discipline-source-form" %> - - <%= javascript_void_link('上移', class: 'move-action', data: { id: tag.id, opr: "up" }, style: tag.position == 1 ? 'display:none' : '') %> - <%= javascript_void_link('下移', class: 'move-action', data: { id: tag.id, opr: "down" }, style: tag.position == max_position ? 'display:none' : '') %> - - <%= link_to '编辑', edit_admins_tag_discipline_path(tag), remote: true, class: 'action' %> - <%= delete_link '删除', admins_tag_discipline_path(tag, element: ".tag-discipline-item-#{tag.id}"), class: 'delete-tag-discipline-action' %> -
    \ No newline at end of file diff --git a/app/views/admins/tag_disciplines/update.js.erb b/app/views/admins/tag_disciplines/update.js.erb deleted file mode 100644 index b03f10477..000000000 --- a/app/views/admins/tag_disciplines/update.js.erb +++ /dev/null @@ -1,6 +0,0 @@ -<% if @message.present? %> -$.notify({ message: "<%= @message %>" }); -<% else %> -$('.modal.admin-edit-tag-discipline-modal').modal("hide"); -$(".tag-discipline-list-container").html("<%= j(render :partial => 'admins/tag_disciplines/shared/list') %>"); -<% end %> \ No newline at end of file diff --git a/app/views/admins/tag_repertoires/destroy.js.erb b/app/views/admins/tag_repertoires/destroy.js.erb deleted file mode 100644 index 710f10ca9..000000000 --- a/app/views/admins/tag_repertoires/destroy.js.erb +++ /dev/null @@ -1,2 +0,0 @@ -$.notify({ message: '删除成功' }); -$(".tag-repertoire-item-<%= @tag_repertoire_id %>").remove(); \ No newline at end of file diff --git a/app/views/admins/tag_repertoires/edit.js.erb b/app/views/admins/tag_repertoires/edit.js.erb deleted file mode 100644 index 5969eedf7..000000000 --- a/app/views/admins/tag_repertoires/edit.js.erb +++ /dev/null @@ -1,2 +0,0 @@ -$('.admin-modal-container').html("<%= j( render partial: 'admins/tag_repertoires/shared/edit_tag_repertoire_modal', locals: { tag_repertoire: @tag_repertoire } ) %>"); -$('.modal.admin-edit-tag-repertoire-modal').modal('show'); \ No newline at end of file diff --git a/app/views/admins/tag_repertoires/index.html.erb b/app/views/admins/tag_repertoires/index.html.erb deleted file mode 100644 index 122cfc6f6..000000000 --- a/app/views/admins/tag_repertoires/index.html.erb +++ /dev/null @@ -1,15 +0,0 @@ -<% define_admin_breadcrumbs do %> - <% add_admin_breadcrumb('技术体系', admins_repertoires_path) %> - <% add_admin_breadcrumb(@sub_repertoire&.repertoire&.name, admins_sub_repertoires_path(repertoire_id: @sub_repertoire&.repertoire_id)) %> - <% add_admin_breadcrumb(@sub_repertoire.name) %> -<% end %> - -
    - <%= javascript_void_link '新增', class: 'btn btn-primary', data: { toggle: 'modal', target: '.admin-create-tag-repertoire-modal' } %> -
    - -
    - <%= render(partial: 'admins/tag_repertoires/shared/list') %> -
    - -<%= render 'admins/tag_repertoires/shared/create_tag_repertoire_modal' %> diff --git a/app/views/admins/tag_repertoires/shared/_create_tag_repertoire_modal.html.erb b/app/views/admins/tag_repertoires/shared/_create_tag_repertoire_modal.html.erb deleted file mode 100644 index f952762e4..000000000 --- a/app/views/admins/tag_repertoires/shared/_create_tag_repertoire_modal.html.erb +++ /dev/null @@ -1,28 +0,0 @@ - \ No newline at end of file diff --git a/app/views/admins/tag_repertoires/shared/_edit_tag_repertoire_modal.html.erb b/app/views/admins/tag_repertoires/shared/_edit_tag_repertoire_modal.html.erb deleted file mode 100644 index 3be94dd05..000000000 --- a/app/views/admins/tag_repertoires/shared/_edit_tag_repertoire_modal.html.erb +++ /dev/null @@ -1,23 +0,0 @@ - \ No newline at end of file diff --git a/app/views/admins/tag_repertoires/shared/_list.html.erb b/app/views/admins/tag_repertoires/shared/_list.html.erb deleted file mode 100644 index 3d8047f8d..000000000 --- a/app/views/admins/tag_repertoires/shared/_list.html.erb +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - <% if @tag_repertoires.present? %> - <% @tag_repertoires.each_with_index do |tag, index| %> - - - - - - <% end %> - <% else %> - <%= render 'admins/shared/no_data_for_table' %> - <% end %> - -
    序号知识标签操作
    <%= index + 1 %><%= tag.name %> - <%= link_to '编辑', edit_admins_tag_repertoire_path(tag), remote: true, class: 'action' %> - <%= delete_link '删除', admins_tag_repertoire_path(tag, element: ".tag-repertoire-item-#{tag.id}"), class: 'delete-tag-repertoire-action' %> -
    \ No newline at end of file diff --git a/app/views/admins/tag_repertoires/update.js.erb b/app/views/admins/tag_repertoires/update.js.erb deleted file mode 100644 index a55f7f3b9..000000000 --- a/app/views/admins/tag_repertoires/update.js.erb +++ /dev/null @@ -1,2 +0,0 @@ -$('.modal.admin-edit-tag-repertoire-modal').modal("hide"); -$(".tag-repertoire-list-container").html("<%= j(render :partial => 'admins/tag_repertoires/shared/list') %>"); \ No newline at end of file diff --git a/app/views/admins/unit_applies/edit.js.erb b/app/views/admins/unit_applies/edit.js.erb deleted file mode 100644 index 15fc1f4c2..000000000 --- a/app/views/admins/unit_applies/edit.js.erb +++ /dev/null @@ -1,56 +0,0 @@ -$("body").append("<%= j render partial: "admins/unit_applies/shared/edit_modal",locals: {apply: @unit_apply, schools: @all_schools} %>") -var uni_edit_modal = $(".admin-unit-edit-modal") -uni_edit_modal.modal("show") - -uni_edit_modal.on("hidden.bs.modal",function () { - $(".admin-unit-edit-modal").remove() - $("body").removeClass("modal-open") -}) - -// 初始化学校选择器 -var matcherFunc = function(params, data){ - if ($.trim(params.term) === '') { - return data; - } - if (typeof data.text === 'undefined') { - return null; - } - if (data.name && data.name.indexOf(params.term) > -1) { - var modifiedData = $.extend({}, data, true); - return modifiedData; - } - - // Return `null` if the term should not be displayed - return null; -} -$.ajax({ - url: '/api/schools/for_option.json', - dataType: 'json', - type: 'GET', - success: function(data) { - $("#all-schools").select2({ - theme: 'bootstrap4', - placeholder: '查询学校/单位', - minimumInputLength: 1, - data: data.schools, - templateResult: function (item) { - if(!item.id || item.id === '') return item.text; - return item.name; - }, - templateSelection: function(item){ - return item.name || item.text; - }, - matcher: matcherFunc - }) - } -}); - -$("#all-schools").select2({}) -$("#show-province-<%= @unit_apply.id %>").select2() -$("#schoolCity_<%= @unit_apply.id %>").select2() - -// **************** 地区选择 **************** -$('.province-city-select').cxSelect({ - url: '/javascripts/educoder/province-data.json', - selects: ['province-select', 'city-select'] -}); \ No newline at end of file diff --git a/app/views/admins/unit_applies/index.html.erb b/app/views/admins/unit_applies/index.html.erb deleted file mode 100644 index 2b715b87c..000000000 --- a/app/views/admins/unit_applies/index.html.erb +++ /dev/null @@ -1,17 +0,0 @@ -<% define_admin_breadcrumbs do %> - <% add_admin_breadcrumb('单位审批') %> -<% end %> - -
    - <%= form_tag(admins_unit_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_unit_applies_path(keyword:nil),class:"btn btn-default",remote:true %> - <% end %> -
    - -
    - <%= render(partial: 'admins/unit_applies/shared/list', locals: { applies: @unit_applies }) %> -
    - -<%= render(partial: 'admins/shared/admin_common_refuse_modal') %> diff --git a/app/views/admins/unit_applies/index.js.erb b/app/views/admins/unit_applies/index.js.erb deleted file mode 100644 index d9297d51a..000000000 --- a/app/views/admins/unit_applies/index.js.erb +++ /dev/null @@ -1 +0,0 @@ -$(".unit-applies-list-container").html("<%= j render partial: "admins/unit_applies/shared/list", locals: {applies: @unit_applies} %>") diff --git a/app/views/admins/unit_applies/shared/_apply_item.html.erb b/app/views/admins/unit_applies/shared/_apply_item.html.erb deleted file mode 100644 index b7d38b776..000000000 --- a/app/views/admins/unit_applies/shared/_apply_item.html.erb +++ /dev/null @@ -1,18 +0,0 @@ -<%= list_index_no((params[:page] || 1).to_i, index) %> -<%= apply.id %> -<%= overflow_hidden_span apply.name %> - - <%= "#{apply&.province.to_s}"+"#{apply&.city.to_s}" %> - -<%= overflow_hidden_span apply.address %> -<%= link_to apply&.user&.real_name, "/users/#{apply&.user&.login}", target: "_blank" %> -<%= format_time apply.created_at %> - - <%= agree_link '批准', agree_admins_unit_apply_path(apply, element: ".unit-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_unit_apply_path(apply, tip: "unapplied", element: ".unit-apply-#{apply.id}") - }) %> - <%= link_to "更改",edit_admins_unit_apply_path(apply), remote: true, class:"action",'data-disable-with': '打开中...' %> - \ No newline at end of file diff --git a/app/views/admins/unit_applies/shared/_edit_modal.html.erb b/app/views/admins/unit_applies/shared/_edit_modal.html.erb deleted file mode 100644 index 6f8995ae0..000000000 --- a/app/views/admins/unit_applies/shared/_edit_modal.html.erb +++ /dev/null @@ -1,43 +0,0 @@ - \ No newline at end of file diff --git a/app/views/admins/unit_applies/shared/_list.html.erb b/app/views/admins/unit_applies/shared/_list.html.erb deleted file mode 100644 index 75b9c720b..000000000 --- a/app/views/admins/unit_applies/shared/_list.html.erb +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - <% if applies.present? %> - <% applies.each_with_index do |apply, index| %> - - <%= render partial: "admins/unit_applies/shared/apply_item", locals: {apply: apply, index: index} %> - - <% end %> - <% else %> - <%= render 'admins/shared/no_data_for_table' %> - <% end %> - -
    序号ID单位名称地区详细地址申请者<%= sort_tag('创建于', name: 'created_at', path: admins_unit_applies_path) %>操作
    - -<%= render partial: 'admins/shared/paginate', locals: { objects: applies } %> \ No newline at end of file diff --git a/app/views/admins/unit_applies/update.js.erb b/app/views/admins/unit_applies/update.js.erb deleted file mode 100644 index c710f46b5..000000000 --- a/app/views/admins/unit_applies/update.js.erb +++ /dev/null @@ -1,3 +0,0 @@ -$('.modal.admin-unit-edit-modal').modal('hide'); -$('.unit-applies-list-table .unit-apply-<%= @unit_apply.id %>').remove() -$.notify({ message: '操作成功' }); \ No newline at end of file diff --git a/app/views/admins/users/index.html.erb b/app/views/admins/users/index.html.erb index 7e37be5c4..3e4c7b74d 100644 --- a/app/views/admins/users/index.html.erb +++ b/app/views/admins/users/index.html.erb @@ -26,13 +26,8 @@ <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> <% end %> - <%#= link_to '新增', new_admins_user_path, class: 'btn btn-primary ml-3' %> - <%#= javascript_void_link '导入用户', class: 'btn btn-secondary btn-sm', data: { toggle: 'modal', target: '.admin-import-user-modal'} %> - <% -=begin%> - <%= javascript_void_link '导入课堂成员', class: 'btn btn-secondary btn-sm ml-2', data: { toggle: 'modal', target: '.admin-import-course-member-modal'} %> -<% -=end%> + <%= javascript_void_link '导入用户', class: 'btn btn-secondary btn-sm', data: { toggle: 'modal', target: '.admin-import-user-modal'} %> +
    @@ -41,6 +36,3 @@ <%= render partial: 'admins/users/shared/reward_grade_modal' %> <%= render partial: 'admins/users/shared/import_user_modal' %> - - -<%= render partial: 'admins/courses/shared/import_course_member_modal' %> diff --git a/app/views/admins/users/shared/_user_list.html.erb b/app/views/admins/users/shared/_user_list.html.erb index 5d9042462..ae4657b5e 100644 --- a/app/views/admins/users/shared/_user_list.html.erb +++ b/app/views/admins/users/shared/_user_list.html.erb @@ -18,7 +18,7 @@ <%= list_index_no((params[:page] || 1).to_i, index) %> - <%= link_to "/accounts/#{user.login}", target: '_blank' do %> + <%= link_to "/#{user.login}", target: '_blank' do %> <%= overflow_hidden_span user.real_name, width: 100 %> <% end %> diff --git a/app/views/admins/video_applies/index.html.erb b/app/views/admins/video_applies/index.html.erb deleted file mode 100644 index f70f308fe..000000000 --- a/app/views/admins/video_applies/index.html.erb +++ /dev/null @@ -1,32 +0,0 @@ -<% define_admin_breadcrumbs do %> - <% add_admin_breadcrumb('视频发布') %> -<% end %> - -
    - - - <%= form_tag(admins_video_applies_path(unsafe_params), method: :get, class: 'form-inline search-form justify-content-end mt-3', remote: true) do %> -
    - - <% status_options = [['全部', 'all'], ['已同意', 'agreed'], ['已拒绝', 'refused']] %> - <%= select_tag(:status, options_for_select(status_options), class: 'form-control') %> -
    - <%= 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 %> -
    - -
    - <%= render(partial: 'admins/video_applies/shared/list', locals: { applies: @video_applies}) %> -
    - -<%= render(partial: 'admins/shared/admin_common_refuse_modal') %> \ No newline at end of file diff --git a/app/views/admins/video_applies/index.js.erb b/app/views/admins/video_applies/index.js.erb deleted file mode 100644 index 570f88c62..000000000 --- a/app/views/admins/video_applies/index.js.erb +++ /dev/null @@ -1 +0,0 @@ -$('.video-applies-list-container').html("<%= j( render partial: 'admins/video_applies/shared/list', locals: { applies: @video_applies } ) %>"); \ No newline at end of file diff --git a/app/views/admins/video_applies/shared/_list.html.erb b/app/views/admins/video_applies/shared/_list.html.erb deleted file mode 100644 index 3466d21ba..000000000 --- a/app/views/admins/video_applies/shared/_list.html.erb +++ /dev/null @@ -1,59 +0,0 @@ -<% is_processed = params[:status].to_s != 'pending' %> - - - - - - - - - - - - <% if is_processed %> - - - <% else %> - - <% end %> - - - - <% if applies.present? %> - <% applies.each_with_index do |v, index| %> - <% video = v.video %> - <% user = video.user %> - - - - - - - - - <% if is_processed %> - - - <% else %> - - <% end %> - - <% end %> - <% else %> - <%= render 'admins/shared/no_data_for_table' %> - <% end %> - -
    序号头像姓名视频名称视频大小播放链接时间拒绝原因状态操作
    <%= 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 video.title, video.file_url, :target => "_blank" %><%= video.filesize&.to_s(:human_size) %><%= link_to "播放视频",video.file_url, target: "_blank" %><%= v.updated_at.strftime('%Y-%m-%d %H:%M') %><%= overflow_hidden_span v.reason %><%= t("admins_apply_status.status.#{v.status}") %> - <%= agree_link '同意', agree_admins_video_apply_path(v, element: ".video_applies-#{v.id}"), 'data-confirm': '确认审核通过?' %> - <%= javascript_void_link('拒绝', class: 'action refuse-action', - data: { - toggle: 'modal', target: '.admin-common-refuse-modal', id: v.id, - url: refuse_admins_video_apply_path(v, element: ".video_applies-#{v.id}") - }) %> -
    - -<%= render partial: 'admins/shared/paginate', locals: { objects: applies } %> \ No newline at end of file diff --git a/app/views/admins/weapp_adverts/index.html.erb b/app/views/admins/weapp_adverts/index.html.erb deleted file mode 100644 index c560fdb8a..000000000 --- a/app/views/admins/weapp_adverts/index.html.erb +++ /dev/null @@ -1,42 +0,0 @@ -<% - define_admin_breadcrumbs do - add_admin_breadcrumb('广告栏') - end -%> - -
    -
    - 广告栏 - <%= javascript_void_link '添加', class: 'btn btn-primary btn-sm add-btn', data: { toggle: 'modal', target: '.admin-add-weapp-advert-modal' } %> -
    -
    - <% @adverts.each_with_index do |advert, index| %> - - <% end %> -
    -
    - - -<%= render partial: 'admins/weapp_adverts/shared/add_weapp_advert_modal' %> -<%= render partial: 'admins/shared/modal/upload_file_modal' %> \ No newline at end of file diff --git a/app/views/admins/weapp_adverts/shared/_add_weapp_advert_modal.html.erb b/app/views/admins/weapp_adverts/shared/_add_weapp_advert_modal.html.erb deleted file mode 100644 index 3937054b5..000000000 --- a/app/views/admins/weapp_adverts/shared/_add_weapp_advert_modal.html.erb +++ /dev/null @@ -1,35 +0,0 @@ - \ No newline at end of file diff --git a/app/views/admins/weapp_carousels/index.html.erb b/app/views/admins/weapp_carousels/index.html.erb deleted file mode 100644 index ec6cfa57c..000000000 --- a/app/views/admins/weapp_carousels/index.html.erb +++ /dev/null @@ -1,42 +0,0 @@ -<% - define_admin_breadcrumbs do - add_admin_breadcrumb('轮播图') - end -%> - -
    -
    - 顶部轮播图(拖动排序) - <%= javascript_void_link '添加', class: 'btn btn-primary btn-sm add-btn', data: { toggle: 'modal', target: '.admin-add-weapp-carousel-modal' } %> -
    -
    - <% @carousels.each_with_index do |carousel, index| %> - - <% end %> -
    -
    - - -<%= render partial: 'admins/weapp_carousels/shared/add_weapp_carousel_modal' %> -<%= render partial: 'admins/shared/modal/upload_file_modal' %> \ No newline at end of file diff --git a/app/views/admins/weapp_carousels/shared/_add_weapp_carousel_modal.html.erb b/app/views/admins/weapp_carousels/shared/_add_weapp_carousel_modal.html.erb deleted file mode 100644 index 123ce4a62..000000000 --- a/app/views/admins/weapp_carousels/shared/_add_weapp_carousel_modal.html.erb +++ /dev/null @@ -1,35 +0,0 @@ - \ No newline at end of file diff --git a/app/views/applied_projects/_detail.json.jbuilder b/app/views/applied_projects/_detail.json.jbuilder new file mode 100644 index 000000000..e76875dc2 --- /dev/null +++ b/app/views/applied_projects/_detail.json.jbuilder @@ -0,0 +1,19 @@ +project = object.project +json.project do + json.id project.id + json.identifier project.identifier + json.name project.name + json.description project.description + json.is_public project.is_public + json.owner do + json.partial! "/users/user_simple", locals: {user: project.owner} + end +end +json.user do + json.partial! "/users/user_simple", locals: {user: object.user} +end +json.id object.id +json.status object.status +json.role object.role +json.created_at format_time(object.created_at) +json.time_ago time_from_now(object.created_at) diff --git a/app/views/applied_projects/create.json.jbuilder b/app/views/applied_projects/create.json.jbuilder new file mode 100644 index 000000000..e2512880c --- /dev/null +++ b/app/views/applied_projects/create.json.jbuilder @@ -0,0 +1 @@ +json.partial! "detail", locals: {object: @applied_project} diff --git a/app/views/compare/show.json.jbuilder b/app/views/compare/show.json.jbuilder index 8257cc7fa..deca41037 100644 --- a/app/views/compare/show.json.jbuilder +++ b/app/views/compare/show.json.jbuilder @@ -3,19 +3,11 @@ json.commits_count @compare_result['Commits']&.size json.commits do json.array! @compare_result['Commits'] do |commit| json.author do - # TODO: 获取头像地址待优化 - forge_user = User.includes(:user_extension).select(:id, :login).find_by(login: commit['Author']['Name']) - json.login commit['Author']['Name'] - json.name commit['Author']['Name'] - json.image_url forge_user.nil? ? '' : url_to_avatar(forge_user) + json.partial! 'repositories/commit_author', locals: { user: render_cache_commit_author(commit['Committer']), name: commit['Committer']['Name'] } end json.committer do - # TODO: 获取头像地址待优化 - forge_user = User.includes(:user_extension).select(:id, :login).find_by(login: commit['Committer']['Name']) - json.login commit['Committer']['Name'] - json.name commit['Committer']['Name'] - json.image_url forge_user.nil? ? '' : url_to_avatar(forge_user) + json.partial! 'repositories/commit_author', locals: { user: render_cache_commit_author(commit['Committer']), name: commit['Committer']['Name'] } end json.timestamp render_unix_time(commit['Committer']['When']) json.time_from_now time_from_now(commit['Committer']['When']) @@ -83,3 +75,5 @@ json.diff do end end +json.status @merge_status +json.message @merge_message \ No newline at end of file diff --git a/app/views/edu_datas/code_lines.json.jbuilder b/app/views/edu_datas/code_lines.json.jbuilder deleted file mode 100644 index d4cccf0a7..000000000 --- a/app/views/edu_datas/code_lines.json.jbuilder +++ /dev/null @@ -1 +0,0 @@ -json.content @content \ No newline at end of file diff --git a/app/views/edu_settings/_form.html.erb b/app/views/edu_settings/_form.html.erb deleted file mode 100644 index 3b7f09f1c..000000000 --- a/app/views/edu_settings/_form.html.erb +++ /dev/null @@ -1,27 +0,0 @@ -<%= form_with(model: edu_setting, local: true) do |form| %> - <% if edu_setting.errors.any? %> -
    -

    <%= pluralize(edu_setting.errors.count, "error") %> prohibited this edu_setting from being saved:

    - -
      - <% edu_setting.errors.full_messages.each do |message| %> -
    • <%= message %>
    • - <% end %> -
    -
    - <% end %> - -
    - <%= form.label :name %> - <%= form.text_field :name %> -
    - -
    - <%= form.label :value %> - <%= form.text_field :value %> -
    - -
    - <%= form.submit %> -
    -<% end %> diff --git a/app/views/edu_settings/edit.html.erb b/app/views/edu_settings/edit.html.erb deleted file mode 100644 index 2a0796842..000000000 --- a/app/views/edu_settings/edit.html.erb +++ /dev/null @@ -1,6 +0,0 @@ -

    Editing Edu Setting

    - -<%= render 'form', edu_setting: @edu_setting %> - -<%= link_to 'Show', @edu_setting %> | -<%= link_to 'Back', edu_settings_path %> diff --git a/app/views/edu_settings/index.html.erb b/app/views/edu_settings/index.html.erb deleted file mode 100644 index d49977254..000000000 --- a/app/views/edu_settings/index.html.erb +++ /dev/null @@ -1,30 +0,0 @@ -

    <%= notice %>

    - -

    EduCoder公共配置

    -

    说明:该界面适用于存储全局变量

    - - - - - - - - - - - - <% @edu_settings.each do |edu_setting| %> - - - - - - - - <% end %> - -
    变量名变量值
    <%= edu_setting.name %><%= edu_setting.value %><%= link_to 'Show', edu_setting %><%= link_to 'Edit', edit_edu_setting_path(edu_setting) %><%= link_to 'Destroy', edu_setting, method: :delete, data: { confirm: 'Are you sure?' } %>
    - -
    - -<%= link_to 'New Edu Setting', new_edu_setting_path %> diff --git a/app/views/edu_settings/new.html.erb b/app/views/edu_settings/new.html.erb deleted file mode 100644 index 8a70e3a63..000000000 --- a/app/views/edu_settings/new.html.erb +++ /dev/null @@ -1,5 +0,0 @@ -

    New Edu Setting

    - -<%= render 'form', edu_setting: @edu_setting %> - -<%= link_to 'Back', edu_settings_path %> diff --git a/app/views/edu_settings/show.html.erb b/app/views/edu_settings/show.html.erb deleted file mode 100644 index 9d027373f..000000000 --- a/app/views/edu_settings/show.html.erb +++ /dev/null @@ -1,14 +0,0 @@ -

    <%= notice %>

    - -

    - Name: - <%= @edu_setting.name %> -

    - -

    - Value: - <%= @edu_setting.value %> -

    - -<%= link_to 'Edit', edit_edu_setting_path(@edu_setting) %> | -<%= link_to 'Back', edu_settings_path %> diff --git a/app/views/issues/_simple_issue_item.json.jbuilder b/app/views/issues/_simple_issue_item.json.jbuilder index f711253a4..d21c9fcae 100644 --- a/app/views/issues/_simple_issue_item.json.jbuilder +++ b/app/views/issues/_simple_issue_item.json.jbuilder @@ -1,4 +1,10 @@ json.name issue.try(:subject) +json.issue_type issue.issue_type == "1" ? "普通" : "悬赏" +json.status_id issue.try(:status_id) +json.issue_status issue.issue_status.try(:name) +json.priority issue.priority.try(:name) +json.priority_id issue.try(:priority_id) +json.version issue.version.try(:name) json.created_at format_time(issue.try(:created_on)) json.updated_at format_time(issue.try(:updated_on)) json.assign_user_name issue&.get_assign_user.try(:show_real_name) diff --git a/app/views/issues/edit.json.jbuilder b/app/views/issues/edit.json.jbuilder index eae63c3c7..5b8ff23ba 100644 --- a/app/views/issues/edit.json.jbuilder +++ b/app/views/issues/edit.json.jbuilder @@ -5,6 +5,7 @@ json.done_ratio @issue.done_ratio.to_s + "%" json.issue_tags @issue.get_issue_tags json.cannot_edit_tags @cannot_edit_tags json.issue_current_user @issue.author_id == current_user.try(:id) +json.assigned_to_id @issue.is_collaborators? ? @issue.assigned_to_id : nil # json.issue_chosen @issue_chosen # json.branches @all_branches json.attachments do diff --git a/app/views/issues/index.json.jbuilder b/app/views/issues/index.json.jbuilder index 13b68eb69..8db3524ed 100644 --- a/app/views/issues/index.json.jbuilder +++ b/app/views/issues/index.json.jbuilder @@ -1,12 +1,13 @@ json.partial! "commons/success" -json.all_count @all_issues_size -json.open_count @open_issues_size -json.close_count @close_issues_size -json.assign_me_count @assign_to_me_size -json.my_published_count @my_published_size +json.all_count @all_issues.size +json.open_count @open_issues.size +json.close_count @close_issues.size +json.assign_me_count @assign_to_me.size +json.my_published_count @my_published.size json.search_count @issues_size json.limit @limit json.user_admin_or_member @user_admin_or_member +json.user_operate_issue @user_operate_issue json.issues do json.array! @issues.to_a do |issue| diff --git a/app/views/issues/show.json.jbuilder b/app/views/issues/show.json.jbuilder index be6de14e0..5c8cbc410 100644 --- a/app/views/issues/show.json.jbuilder +++ b/app/views/issues/show.json.jbuilder @@ -13,6 +13,7 @@ json.tracker @issue.tracker.try(:name) json.issue_status @issue.issue_status.try(:name) json.priority @issue.priority.try(:name) json.version @issue.version.try(:name) +json.version_id @issue.version.try(:id) json.issue_tags @issue.get_issue_tags json.done_ratio @issue.done_ratio.to_s + "%" json.issue_type @issue.issue_type diff --git a/app/views/layouts/cooperative.html.erb b/app/views/layouts/cooperative.html.erb index b186c6245..93be04708 100644 --- a/app/views/layouts/cooperative.html.erb +++ b/app/views/layouts/cooperative.html.erb @@ -1,7 +1,7 @@ - <%= current_laboratory.laboratory_setting&.name || 'EduCoder' %>-后台管理 + <%= current_laboratory.laboratory_setting&.name || 'GitLink' %>-后台管理 diff --git a/app/views/organizations/organizations/_detail.json.jbuilder b/app/views/organizations/organizations/_detail.json.jbuilder index e7aafd4e9..4a8b512b9 100644 --- a/app/views/organizations/organizations/_detail.json.jbuilder +++ b/app/views/organizations/organizations/_detail.json.jbuilder @@ -1,5 +1,6 @@ json.id organization.id json.name organization.login +json.nickname organization.nickname.blank? ? organization.name : organization.nickname json.description organization.description json.website organization.website json.location organization.location diff --git a/app/views/organizations/organizations/_simple.json.jbuilder b/app/views/organizations/organizations/_simple.json.jbuilder new file mode 100644 index 000000000..7fed762a2 --- /dev/null +++ b/app/views/organizations/organizations/_simple.json.jbuilder @@ -0,0 +1,9 @@ +if organization.present? + json.id organization.id + json.name organization.login + json.nickname organization.nickname.blank? ? organization.name : organization.nickname + json.description organization.description + json.avatar_url url_to_avatar(organization) +else + nil +end \ No newline at end of file diff --git a/app/views/organizations/organizations/recommend.json.jbuilder b/app/views/organizations/organizations/recommend.json.jbuilder new file mode 100644 index 000000000..340634e02 --- /dev/null +++ b/app/views/organizations/organizations/recommend.json.jbuilder @@ -0,0 +1,12 @@ +json.organizations do + + json.array! @organizations.each do |group| + json.array! group.each do |organization| + json.id organization.id + json.name organization.login + json.nickname organization.real_name + json.avatar_url url_to_avatar(organization) + json.website organization.website + end + end +end \ No newline at end of file diff --git a/app/views/organizations/team_projects/_detail.json.jbuilder b/app/views/organizations/team_projects/_detail.json.jbuilder index 9af32d1df..dde707002 100644 --- a/app/views/organizations/team_projects/_detail.json.jbuilder +++ b/app/views/organizations/team_projects/_detail.json.jbuilder @@ -1,6 +1,7 @@ json.id team_project.id json.project do - json.owner_name team_project&.project&.owner&.login + json.owner_login team_project&.project&.owner&.login + json.owner_name team_project&.project&.owner&.real_name json.owner_image_url url_to_avatar(team_project&.project&.owner) json.name team_project&.project&.name json.identifier team_project&.project&.identifier diff --git a/app/views/organizations/teams/_detail.json.jbuilder b/app/views/organizations/teams/_detail.json.jbuilder index 4f3588cb9..68572814f 100644 --- a/app/views/organizations/teams/_detail.json.jbuilder +++ b/app/views/organizations/teams/_detail.json.jbuilder @@ -1,5 +1,6 @@ json.id team.id json.name team.name +json.nickname team.nickname.blank? ? team.name : team.nickname json.description team.description json.authorize team.authorize json.includes_all_project team.includes_all_project diff --git a/app/views/organizations/teams/_simple_detail.json.jbuilder b/app/views/organizations/teams/_simple_detail.json.jbuilder new file mode 100644 index 000000000..5e9a6246d --- /dev/null +++ b/app/views/organizations/teams/_simple_detail.json.jbuilder @@ -0,0 +1,3 @@ +json.id team.id +json.name team.name +json.nickname team.nickname.blank? ? team.name : team.nickname \ No newline at end of file diff --git a/app/views/organizations/teams/index.json.jbuilder b/app/views/organizations/teams/index.json.jbuilder index ad3479ec4..189047fc4 100644 --- a/app/views/organizations/teams/index.json.jbuilder +++ b/app/views/organizations/teams/index.json.jbuilder @@ -1,4 +1,8 @@ -json.total_count @teams.total_count +json.total_count params[:is_full].present? ? @teams.count : @teams.total_count json.teams @teams do |team| - json.partial! "detail", team: team, organization: @organization + if params[:is_full].present? + json.partial! "simple_detail", team: team, organization: @organization + else + json.partial! "detail", team: team, organization: @organization + end end diff --git a/app/views/organizations/teams/show.json.jbuilder b/app/views/organizations/teams/show.json.jbuilder index d0290897a..c86f9b546 100644 --- a/app/views/organizations/teams/show.json.jbuilder +++ b/app/views/organizations/teams/show.json.jbuilder @@ -1,3 +1,6 @@ json.partial! "detail", team: @team, organization: @organization json.is_admin @is_admin -json.is_member @is_member \ No newline at end of file +json.is_member @is_member +json.organization do + json.partial! "organizations/organizations/simple", organization: @organization +end \ No newline at end of file diff --git a/app/views/owners/index.json.jbuilder b/app/views/owners/index.json.jbuilder index 677aff71a..80b6b5909 100644 --- a/app/views/owners/index.json.jbuilder +++ b/app/views/owners/index.json.jbuilder @@ -2,6 +2,7 @@ json.total_count @owners.size json.owners @owners.each do |owner| json.id owner.id json.type owner.type - json.name owner.login + json.login owner.login + json.name owner&.show_real_name json.avatar_url url_to_avatar(owner) end \ No newline at end of file diff --git a/app/views/owners/show.json.jbuilder b/app/views/owners/show.json.jbuilder new file mode 100644 index 000000000..db0a80854 --- /dev/null +++ b/app/views/owners/show.json.jbuilder @@ -0,0 +1,26 @@ +json.type @owner.type +if @owner.is_a?(Organization) + json.partial! "organizations/organizations/detail", organization: @owner + json.can_create_project @can_create_project + json.is_admin @is_admin + json.is_member @is_member +elsif @owner.is_a?(User) + json.partial! 'users/user', locals: { user: @owner } + json.undo_messages @waiting_applied_messages.size + json.undo_transfer_projects @common_applied_transfer_projects.size + json.undo_join_projects @common_applied_projects.size + json.undo_events @undo_events + json.user_composes_count @user_composes_count + json.user_org_count @user_org_count + json.common_projects_count @projects_common_count + json.mirror_projects_count @projects_mirrior_count + json.sync_mirror_projects_count @projects_sync_mirrior_count + json.created_time format_time(@owner.created_on) + json.email @owner.show_email ? @owner.mail : nil + json.province @owner.show_location ? @owner.province : nil + json.city @owner.show_location ? @owner.city : nil + json.custom_department @owner.show_department ? @owner.custom_department : nil + json.description @owner.description +elsif + json.nil +end \ No newline at end of file diff --git a/app/views/project_categories/pinned_index.json.jbuilder b/app/views/project_categories/pinned_index.json.jbuilder new file mode 100644 index 000000000..db3997ebd --- /dev/null +++ b/app/views/project_categories/pinned_index.json.jbuilder @@ -0,0 +1 @@ +json.project_categories @project_categories, :id, :name, :logo_url diff --git a/app/views/project_rank/_detail.json.jbuilder b/app/views/project_rank/_detail.json.jbuilder new file mode 100644 index 000000000..5e45f518e --- /dev/null +++ b/app/views/project_rank/_detail.json.jbuilder @@ -0,0 +1,31 @@ +project_common = $redis_cache.hgetall("v2-project-common:#{item[0]}") +owner_common = $redis_cache.hgetall("v2-owner-common:#{project_common["owner_id"]}") +json.id item[0] +json.score item[1] +json.name project_common["name"] +if project_common['identifier'].include?("/") + json.identifier project_common["identifier"].split('/')[1] + json.owner do + json.id nil + json.type 'User' + json.name project_common["identifier"].split('/')[0] + json.login project_common["identifier"].split('/')[0] + json.avatar_url User::Avatar.get_letter_avatar_url(project_common["identifier"].split('/')[0]) + end +else + json.identifier project_common["identifier"] + json.owner do + json.id project_common["owner_id"] + json.type owner_common["type"] + json.name owner_common["name"] + json.login owner_common["login"] + json.avatar_url owner_common["avatar_url"] + end +end +json.description project_common["description"] +json.visits project_common["visits"] +json.forks project_common["forks"] +json.watchers project_common["watchers"] +json.praises project_common["praises"] +json.issues project_common["issues"] +json.pulls project_common["pullrequests"] \ No newline at end of file diff --git a/app/views/project_rank/index.json.jbuilder b/app/views/project_rank/index.json.jbuilder new file mode 100644 index 000000000..84792ca7d --- /dev/null +++ b/app/views/project_rank/index.json.jbuilder @@ -0,0 +1,8 @@ +json.partial! "commons/success" +json.projects do + + json.array! @project_rank.each do |item| + json.partial! "detail", locals: {item: item} + end + +end \ No newline at end of file diff --git a/app/views/project_trends/_detail.json.jbuilder b/app/views/project_trends/_detail.json.jbuilder new file mode 100644 index 000000000..04de10f6a --- /dev/null +++ b/app/views/project_trends/_detail.json.jbuilder @@ -0,0 +1,24 @@ +json.id trend.id +json.trend_type trend.trend_type +json.action_type l("trend.#{trend.action_type}") + l("trend.#{trend.trend_type}") +json.trend_id trend.trend_id +json.user_name trend.user.try(:show_real_name) +json.user_login trend.user.login +json.user_avatar url_to_avatar(trend.user) +json.action_time time_from_now(trend.created_at) +json.project do + json.owner do + json.partial! 'users/user_simple', locals: {user: trend&.project&.owner} + end + json.identifier trend&.project&.identifier + json.description trend&.project&.description +end + +if trend.trend_type == "Issue" + json.partial! "issues/simple_issue_item", locals: {issue: trend.trend} +elsif trend.trend_type == "VersionRelease" + json.partial! "version_releases/simple_version_release", locals: {version: trend.trend} +else + json.name trend.trend&.title + json.created_at format_time(trend.trend&.created_at) +end diff --git a/app/views/project_trends/index.json.jbuilder b/app/views/project_trends/index.json.jbuilder index 159a3bdb8..6dca7bdb5 100644 --- a/app/views/project_trends/index.json.jbuilder +++ b/app/views/project_trends/index.json.jbuilder @@ -9,25 +9,9 @@ json.limit @limit json.project_trends_size @project_trends_size json.project_trends do json.array! @project_trends.to_a.each do |trend| - json.id trend.id - json.trend_type trend.trend_type - json.action_type l("trend.#{trend.action_type}") + l("trend.#{trend.trend_type}") - json.trend_id trend.trend_id - json.user_name trend.user.try(:show_real_name) - json.user_login trend.user.login - json.user_avatar url_to_avatar(trend.user) - - if trend.trend_type == "Issue" - json.partial! "issues/simple_issue_item", locals: {issue: trend.trend} - elsif trend.trend_type == "VersionRelease" - json.partial! "version_releases/simple_version_release", locals: {version: trend.trend} - else - json.name trend.trend.title - json.created_at format_time(trend.trend.created_at) - end - + #后续需要天际pullrequest 和 版本的内容 - + json.partial! "detail", trend: trend end end diff --git a/app/views/projects/_project_detail.json.jbuilder b/app/views/projects/_project_detail.json.jbuilder index 59f6e5f4f..d6f6717e3 100644 --- a/app/views/projects/_project_detail.json.jbuilder +++ b/app/views/projects/_project_detail.json.jbuilder @@ -5,6 +5,9 @@ json.name project.name json.description Nokogiri::HTML(project.description).text json.visits project.visits json.praises_count project.praises_count.to_i +json.watchers_count project.watchers_count.to_i +json.issues_count project.issues_count.to_i +json.pull_requests_count project.pull_requests_count.to_i json.forked_count project.forked_count.to_i json.is_public project.is_public json.is_secret project.is_secret @@ -15,6 +18,7 @@ json.time_ago time_from_now(project.updated_on) json.forked_from_project_id project.forked_from_project_id json.open_devops project.open_devops? json.platform project.platform +json.is_pinned project.has_pinned_users.include?(current_user) json.author do if project.educoder? project_educoder = project.project_educoder @@ -27,7 +31,7 @@ json.author do json.name user.try(:show_real_name) json.type user&.type json.login user.login - json.image_url render_avatar_url(user) + json.image_url url_to_avatar(user) end end diff --git a/app/views/projects/applied_transfer_projects/_detail.json.jbuilder b/app/views/projects/applied_transfer_projects/_detail.json.jbuilder new file mode 100644 index 000000000..9e8cf2286 --- /dev/null +++ b/app/views/projects/applied_transfer_projects/_detail.json.jbuilder @@ -0,0 +1,25 @@ +project = object.project +if project.present? + json.project do + json.id project.id + json.identifier project.identifier + json.name project.name + json.description project.description + json.is_public project.is_public + json.owner do + json.partial! "/users/user_simple", locals: {user: project.owner} + end + end +else + json.project nil +end +json.user do + json.partial! "/users/user_simple", locals: {user: object.user} +end +json.owner do + json.partial! "/users/user_simple", locals: {user: object.owner} +end +json.id object.id +json.status object.status +json.created_at format_time(object.created_at) +json.time_ago time_from_now(object.created_at) diff --git a/app/views/projects/applied_transfer_projects/cancel.json.jbuilder b/app/views/projects/applied_transfer_projects/cancel.json.jbuilder new file mode 100644 index 000000000..59da42d4e --- /dev/null +++ b/app/views/projects/applied_transfer_projects/cancel.json.jbuilder @@ -0,0 +1 @@ +json.partial! "/projects/applied_transfer_projects/detail", locals: {object: @applied_transfer_project} diff --git a/app/views/projects/applied_transfer_projects/create.json.jbuilder b/app/views/projects/applied_transfer_projects/create.json.jbuilder new file mode 100644 index 000000000..59da42d4e --- /dev/null +++ b/app/views/projects/applied_transfer_projects/create.json.jbuilder @@ -0,0 +1 @@ +json.partial! "/projects/applied_transfer_projects/detail", locals: {object: @applied_transfer_project} diff --git a/app/views/projects/applied_transfer_projects/organizations.json.jbuilder b/app/views/projects/applied_transfer_projects/organizations.json.jbuilder new file mode 100644 index 000000000..772910f0b --- /dev/null +++ b/app/views/projects/applied_transfer_projects/organizations.json.jbuilder @@ -0,0 +1,4 @@ +json.total_count @organizations.size +json.organizations @organizations do |org| + json.partial! "/organizations/organizations/simple", locals: {organization: org} +end diff --git a/app/views/projects/banner_recommend.json.jbuilder b/app/views/projects/banner_recommend.json.jbuilder new file mode 100644 index 000000000..fe9861f85 --- /dev/null +++ b/app/views/projects/banner_recommend.json.jbuilder @@ -0,0 +1,36 @@ +json.partial! "commons/success" +json.projects do + json.array! @projects do |project| + owner = project.owner + json.id project.id + json.identifier project.identifier + json.name project.name + json.description project.description + json.visits project.visits + json.author do + json.name owner.try(:show_real_name) + json.type owner.type + json.login owner.login + json.image_url url_to_avatar(owner) + end + + json.category do + if project.project_category.blank? + json.nil! + else + json.id project.project_category.id + json.name project.project_category.name + end + end + + json.language do + if project.project_language.blank? + json.nil! + else + json.id project.project_language.id + json.name project.project_language.name + end + end + end +end + diff --git a/app/views/projects/branches.json.jbuilder b/app/views/projects/branches.json.jbuilder index dd722c9d9..3bfc9f46b 100644 --- a/app/views/projects/branches.json.jbuilder +++ b/app/views/projects/branches.json.jbuilder @@ -1,17 +1,22 @@ json.array! @branches do |branch| - json.name branch['name'] - json.user_can_push branch['user_can_push'] - json.user_can_merge branch['user_can_merge'] - json.protected branch['protected'] + branch_name = branch.is_a?(Hash) ? branch['name'] : branch + json.name branch_name + # json.user_can_push branch['user_can_push'] + # json.user_can_merge branch['user_can_merge'] + # json.protected branch['protected'] json.http_url render_http_url(@project) - json.zip_url render_zip_url(@owner, @repository, branch['name']) - json.tar_url render_tar_url(@owner, @repository, branch['name']) - json.last_commit do - json.sha branch['commit']['id'] - json.message branch['commit']['message'] - json.timestamp render_unix_time(branch['commit']['timestamp']) - json.time_from_now time_from_now(branch['commit']['timestamp']) - json.author branch['commit']['author'] - json.committer branch['commit']['committer'] - end + json.zip_url render_zip_url(@owner, @repository, branch_name) + json.tar_url render_tar_url(@owner, @repository, branch_name) + # json.last_commit do + # json.sha branch['commit']['id'] + # json.message branch['commit']['message'] + # json.timestamp render_unix_time(branch['commit']['timestamp']) + # json.time_from_now time_from_now(branch['commit']['timestamp']) + # json.author do + # json.partial! 'repositories/commit_author', user: render_cache_commit_author(branch['commit']['author']), name: branch['commit']['author']['name'] + # end + # json.committer do + # json.partial! 'repositories/commit_author', user: render_cache_commit_author(branch['commit']['committer']), name: branch['commit']['committer']['name'] + # end + # end end diff --git a/app/views/projects/branches_slice.json.jbuilder b/app/views/projects/branches_slice.json.jbuilder new file mode 100644 index 000000000..963bca0a8 --- /dev/null +++ b/app/views/projects/branches_slice.json.jbuilder @@ -0,0 +1,24 @@ +json.array! @branches_slice do |branch_slice| + json.branch_type branch_slice['branch_name'] + json.list branch_slice['branches'].each do |branch| + json.name branch['name'] + json.user_can_push branch['user_can_push'] + json.user_can_merge branch['user_can_merge'] + json.protected branch['protected'] + json.http_url render_http_url(@project) + json.zip_url render_zip_url(@owner, @repository, branch['name']) + json.tar_url render_tar_url(@owner, @repository, branch['name']) + json.last_commit do + json.sha branch['commit']['id'] + json.message branch['commit']['message'] + json.timestamp render_unix_time(branch['commit']['timestamp']) + json.time_from_now time_from_now(branch['commit']['timestamp']) + json.author do + json.partial! 'repositories/commit_author', user: render_cache_commit_author(branch['commit']['author']), name: branch['commit']['author']['name'] + end + json.committer do + json.partial! 'repositories/commit_author', user: render_cache_commit_author(branch['commit']['committer']), name: branch['commit']['committer']['name'] + end + end + end +end \ No newline at end of file diff --git a/app/views/projects/create.json.jbuilder b/app/views/projects/create.json.jbuilder index 0a586a654..4b4c63fe4 100644 --- a/app/views/projects/create.json.jbuilder +++ b/app/views/projects/create.json.jbuilder @@ -1 +1,2 @@ json.extract! @project, :id, :name,:identifier +json.login @project&.owner.login diff --git a/app/views/projects/index.json.jbuilder b/app/views/projects/index.json.jbuilder index d66c44a30..ca7a41e29 100644 --- a/app/views/projects/index.json.jbuilder +++ b/app/views/projects/index.json.jbuilder @@ -1,4 +1,4 @@ -json.total_count @total_count +json.total_count @projects.total_count json.projects @projects do |project| # json.partial! "/projects/project_detail", project: project json.id project.id @@ -26,9 +26,10 @@ json.projects @projects do |project| json.image_url render_educoder_avatar_url(project.project_educoder) else user = project.owner + json.type user.type json.name user.try(:show_real_name) json.login user.login - json.image_url render_avatar_url(user) + json.image_url url_to_avatar(user) end end diff --git a/app/views/projects/members/index.json.jbuilder b/app/views/projects/members/index.json.jbuilder new file mode 100644 index 000000000..0f605b69c --- /dev/null +++ b/app/views/projects/members/index.json.jbuilder @@ -0,0 +1,4 @@ +json.total_count @users.total_count +json.users do + json.partial! 'users/user_small', users: @users +end diff --git a/app/views/projects/migrate.json.jbuilder b/app/views/projects/migrate.json.jbuilder index c9ae044ee..1e8ddc4d5 100644 --- a/app/views/projects/migrate.json.jbuilder +++ b/app/views/projects/migrate.json.jbuilder @@ -1 +1,2 @@ json.extract! @project, :id, :name, :identifier +json.login @project&.owner&.login \ No newline at end of file diff --git a/app/views/projects/project_units/index.json.jbuilder b/app/views/projects/project_units/index.json.jbuilder new file mode 100644 index 000000000..73a558a78 --- /dev/null +++ b/app/views/projects/project_units/index.json.jbuilder @@ -0,0 +1 @@ +json.array! @project_units.pluck(:unit_type) \ No newline at end of file diff --git a/app/views/projects/teams/index.json.jbuilder b/app/views/projects/teams/index.json.jbuilder index 0a48e884d..0b74a37a4 100644 --- a/app/views/projects/teams/index.json.jbuilder +++ b/app/views/projects/teams/index.json.jbuilder @@ -1,5 +1,6 @@ json.total_count @teams.total_count +json.can_add @owner.is_owner?(current_user.id) || @owner&.repo_admin_change_team_access json.teams @teams.each do |team| json.(team, :id, :name, :authorize) - json.can_remove !team.includes_all_project && team&.organization&.repo_admin_change_team_access + json.can_remove !team.includes_all_project && (@owner.is_owner?(current_user.id) || team&.organization&.repo_admin_change_team_access) end \ No newline at end of file diff --git a/app/views/projects/update.json.jbuilder b/app/views/projects/update.json.jbuilder index 20fa3e07d..01e70377e 100644 --- a/app/views/projects/update.json.jbuilder +++ b/app/views/projects/update.json.jbuilder @@ -5,3 +5,5 @@ json.description @project.description json.project_category_id @project.project_category_id json.project_language_id @project.project_language_id json.is_public @project.is_public +json.website @project.website +json.lesson_url @project.lesson_url \ No newline at end of file diff --git a/app/views/projects/webhooks/_detail.json.jbuilder b/app/views/projects/webhooks/_detail.json.jbuilder new file mode 100644 index 000000000..41a91f4ad --- /dev/null +++ b/app/views/projects/webhooks/_detail.json.jbuilder @@ -0,0 +1,4 @@ +json.(webhook, :id, :url, :http_method, :is_active) +json.type webhook.type +json.last_status webhook.last_status +json.create_time Time.at(webhook.created_unix).strftime("%Y-%m-%d %H:%M:%S") \ No newline at end of file diff --git a/app/views/projects/webhooks/create.json.jbuilder b/app/views/projects/webhooks/create.json.jbuilder new file mode 100644 index 000000000..6d6dde31f --- /dev/null +++ b/app/views/projects/webhooks/create.json.jbuilder @@ -0,0 +1,7 @@ +json.id @webhook["id"] +json.type @webhook["type"] +json.content_type @webhook["config"]["content_type"] +json.url @webhook["config"]["url"] +json.events @webhook["events"] +json.active @webhook["active"] +json.create_time @webhook["created_at"].to_time.strftime("%Y-%m-%d %H:%M:%S") \ No newline at end of file diff --git a/app/views/projects/webhooks/edit.json.jbuilder b/app/views/projects/webhooks/edit.json.jbuilder new file mode 100644 index 000000000..c54d10306 --- /dev/null +++ b/app/views/projects/webhooks/edit.json.jbuilder @@ -0,0 +1,11 @@ +json.id @webhook.id +json.(@webhook, :id, :http_method, :content_type, :url, :secret, :last_status, :is_active) +json.type @webhook.type +json.create_time Time.at(@webhook.created_unix).strftime("%Y-%m-%d %H:%M:%S") +event = @webhook.events +json.branch_filter event["branch_filter"] +if event["send_everything"] + json.events event["events"].keys.collect{|i| i == "pull_request" ? i + "_only" : i} +else + json.events event["events"].select{|k, v| v}.keys.collect{|i| i == "pull_request" ? i + "_only" : i} +end diff --git a/app/views/projects/webhooks/index.json.jbuilder b/app/views/projects/webhooks/index.json.jbuilder new file mode 100644 index 000000000..62722acbe --- /dev/null +++ b/app/views/projects/webhooks/index.json.jbuilder @@ -0,0 +1,4 @@ +json.total_count @webhooks.total_count +json.webhooks @webhooks.each do |webhook| + json.partial! 'detail', webhook: webhook +end \ No newline at end of file diff --git a/app/views/projects/webhooks/tasks.json.jbuilder b/app/views/projects/webhooks/tasks.json.jbuilder new file mode 100644 index 000000000..82b2eae4a --- /dev/null +++ b/app/views/projects/webhooks/tasks.json.jbuilder @@ -0,0 +1,6 @@ +json.total_count @tasks.total_count +json.tasks @tasks.each do |task| + json.(task, :id, :event_type, :type, :uuid, :is_succeed, :is_delivered, :payload_content, :request_content) + json.response_content task.response_content_json + json.delivered_time Time.at(task.delivered*10**-9).strftime("%Y-%m-%d %H:%M:%S") +end \ No newline at end of file diff --git a/app/views/public_keys/create.json.jbuilder b/app/views/public_keys/create.json.jbuilder new file mode 100644 index 000000000..9296413ab --- /dev/null +++ b/app/views/public_keys/create.json.jbuilder @@ -0,0 +1,8 @@ +if @public_key.present? + json.status 0 + json.id @public_key["id"] + json.name @public_key["title"] + json.content @public_key["key"] + json.fingerprint @public_key["fingerprint"] + json.created_time @public_key["created_at"].to_time.strftime("%Y/%m/%d %H:%M") +end \ No newline at end of file diff --git a/app/views/public_keys/index.json.jbuilder b/app/views/public_keys/index.json.jbuilder new file mode 100644 index 000000000..6710529bb --- /dev/null +++ b/app/views/public_keys/index.json.jbuilder @@ -0,0 +1,5 @@ +json.total_count @public_keys.total_count +json.public_keys @public_keys do |public_key| + json.(public_key, :id, :name, :content, :fingerprint, :created_unix) + json.created_time Time.at(public_key.created_unix).strftime("%Y/%m/%d %H:%M") +end \ No newline at end of file diff --git a/app/views/pull_requests/_commit.json.jbuilder b/app/views/pull_requests/_commit.json.jbuilder index 0b7db5893..2cd2bab78 100644 --- a/app/views/pull_requests/_commit.json.jbuilder +++ b/app/views/pull_requests/_commit.json.jbuilder @@ -1,14 +1,27 @@ -json.author do - author = User.find_by(login: commit['Author']['Name']) - json.partial! 'repositories/commit_author', locals: { user: author } -end +if commit['Status'].present? + json.author do + json.partial! 'repositories/commit_author', locals: { user: render_cache_commit_author(commit['Author']), name: commit['Author']['Name'] } + end + + json.committer do + json.partial! 'repositories/commit_author', locals: { user: render_cache_commit_author(commit['Committer']), name: commit['Committer']['Name'] } + end + json.timestamp render_unix_time(commit['Committer']['When']) + json.time_from_now time_from_now(commit['Committer']['When']) + json.created_at render_format_time_with_date(commit['Committer']['When']) + json.message commit['CommitMessage'] + json.sha commit['Sha'] +else + json.author do + json.partial! 'repositories/commit_author', locals: { user: render_cache_commit_author(commit['commit']['author']), name: commit['commit']['author']['name'] } + end -json.committer do - author = User.find_by(login: commit['Committer']['Name']) - json.partial! 'repositories/commit_author', locals: { user: author } -end -json.timestamp render_unix_time(commit['Committer']['When']) -json.time_from_now time_from_now(commit['Committer']['When']) -json.created_at render_format_time_with_date(commit['Committer']['When']) -json.message commit['CommitMessage'] -json.sha commit['Sha'] + json.committer do + json.partial! 'repositories/commit_author', locals: { user: render_cache_commit_author(commit['commit']['committer']), name: commit['commit']['committer']['name'] } + end + json.timestamp render_unix_time(commit['commit']['committer']['date']) + json.time_from_now time_from_now(commit['commit']['committer']['date']) + json.created_at render_format_time_with_date(commit['commit']['committer']['date']) + json.message commit['commit']['message'] + json.sha commit['sha'] +end \ No newline at end of file diff --git a/app/views/pull_requests/_detail.json.jbuilder b/app/views/pull_requests/_detail.json.jbuilder new file mode 100644 index 000000000..5c9347420 --- /dev/null +++ b/app/views/pull_requests/_detail.json.jbuilder @@ -0,0 +1,25 @@ +pr = issue.pull_request +json.pull_request_id pr.id +json.pull_request_number pr.gitea_number +json.pull_request_status pr.status +json.pull_request_head pr.head +json.pull_request_base pr.base +json.pull_request_staus pr.status == 1 ? "merged" : (pr.status == 2 ? "closed" : "open") +json.is_original pr.is_original +json.fork_project_id pr&.fork_project_id +json.fork_project_identifier pr&.fork_project&.identifier +json.fork_project_user pr&.fork_project&.owner.try(:login) + + +json.id issue.id +json.name issue.subject +json.pr_time time_from_now(pr.status == 1 ? pr.updated_at : issue.updated_on) +json.assign_user_name issue.get_assign_user.try(:show_real_name) +json.assign_user_login issue.get_assign_user.try(:login) +json.author_name issue.user.try(:show_real_name) +json.author_login issue.user.try(:login) +json.avatar_url url_to_avatar(issue.user) +json.priority issue.priority.try(:name) +json.version issue.version.try(:name) +json.journals_count issue.get_journals_size +json.issue_tags issue.get_issue_tags \ No newline at end of file diff --git a/app/views/pull_requests/_merge_item.json.jbuilder b/app/views/pull_requests/_merge_item.json.jbuilder index 8475c468d..0db17e265 100644 --- a/app/views/pull_requests/_merge_item.json.jbuilder +++ b/app/views/pull_requests/_merge_item.json.jbuilder @@ -5,10 +5,10 @@ json.issue_priories @project_priories json.project_author @project.owner.try(:show_real_name) json.project_name @project.try(:name) json.members do - json.array! @project_members.to_a.each do |member| - json.id member.user_id - json.login member.user.try(:login) - json.name member.user.try(:show_real_name) - json.avatar_url url_to_avatar(member.user) + json.array! @project_members.to_a.each do |user| + json.id user.id + json.login user.try(:login) + json.name user.try(:show_real_name) + json.avatar_url url_to_avatar(user) end end \ No newline at end of file diff --git a/app/views/pull_requests/create.json.jbuilder b/app/views/pull_requests/create.json.jbuilder new file mode 100644 index 000000000..1483420d7 --- /dev/null +++ b/app/views/pull_requests/create.json.jbuilder @@ -0,0 +1,2 @@ +json.partial! "commons/success" +json.partial! 'detail', pr: @pull_request, issue: @pull_request.issue \ No newline at end of file diff --git a/app/views/pull_requests/index.json.jbuilder b/app/views/pull_requests/index.json.jbuilder index b6a7ff0d1..d37901ef8 100644 --- a/app/views/pull_requests/index.json.jbuilder +++ b/app/views/pull_requests/index.json.jbuilder @@ -1,18 +1,20 @@ json.partial! "commons/success" -json.all_count @all_issues_size -json.open_count @open_issues_size -json.close_count @close_issues_size -json.merged_issues_size @merged_issues_size +json.open_count @open_issues.size +json.close_count @close_issues.size +json.merged_issues_size @merged_issues.size json.search_count @issues_size json.limit @limit json.user_admin_or_member @user_admin_or_member +json.user_admin_or_developer @user_admin_or_developer json.project_name @project.name -json.project_author_name @project.owner.try(:login) +json.project_author @project.owner.try(:login) +json.project_author_name @project.owner.try(:show_real_name) json.issues do json.array! @issues.to_a do |issue| pr = issue.pull_request json.pull_request_id pr.id + json.pull_request_number pr.gitea_number json.pull_request_status pr.status json.pull_request_head pr.head json.pull_request_base pr.base @@ -21,6 +23,8 @@ json.issues do json.fork_project_id pr&.fork_project_id json.fork_project_identifier pr&.fork_project&.identifier json.fork_project_user pr&.fork_project&.owner.try(:login) + json.fork_project_user_name pr&.fork_project&.owner.try(:show_real_name) + json.id issue.id json.name issue.subject json.pr_time time_from_now(pr.status == 1 ? pr.updated_at : issue.updated_on) diff --git a/app/views/pull_requests/show.json.jbuilder b/app/views/pull_requests/show.json.jbuilder index c3f781f73..739bfd74e 100644 --- a/app/views/pull_requests/show.json.jbuilder +++ b/app/views/pull_requests/show.json.jbuilder @@ -3,20 +3,25 @@ json.project_name @project.name json.identifier @project.identifier json.project_identifier @project.identifier json.pr_time time_from_now(@pull_request.updated_at) -json.commits_count @pull_request.commits_count -json.files_count @pull_request.files_count -json.comments_count @pull_request.comments_count +json.commits_count @gitea_pull["commit_num"] +json.files_count @gitea_pull["changed_files"] +json.comments_count @issue.journals.parent_journals.size +json.comments_total_count @issue.get_journals_size json.pull_request do json.extract! @pull_request, :id,:base, :head, :status,:fork_project_id, :is_original json.pull_request_staus @pull_request.status == 1 ? "merged" : (@pull_request.status == 2 ? "closed" : "open") json.fork_project_user @pull_request&.fork_project&.owner.try(:login) + json.fork_project_user_name @pull_request&.fork_project&.owner.try(:show_real_name) json.create_user @pull_request&.user&.login + json.mergeable @gitea_pull["mergeable"] + json.state @gitea_pull["state"] end json.issue do json.extract! @issue, :id,:subject,:description,:is_private, :branch_name - json.project_author_name @project.owner.try(:login) + json.project_author @project.owner.try(:login) + json.project_author_name @project.owner.try(:show_real_name) #json.user_permission @user_permission json.closed_on @issue.closed_on.present? ? format_time(@issue.closed_on) : "" json.created_at format_time(@issue.created_on) @@ -30,3 +35,5 @@ json.issue do json.version @issue.version.try(:name) json.issue_tags @issue.get_issue_tags end + +json.conflict_files @pull_request.conflict_files diff --git a/app/views/repositories/_author.json.jbuilder b/app/views/repositories/_author.json.jbuilder index 1dbcc4ebc..e37a50e44 100644 --- a/app/views/repositories/_author.json.jbuilder +++ b/app/views/repositories/_author.json.jbuilder @@ -1,5 +1,6 @@ json.author do if @project.forge? + json.id user.id json.login user.login json.type user&.type json.name user.real_name diff --git a/app/views/repositories/_commit.json.jbuilder b/app/views/repositories/_commit.json.jbuilder index 079473b3d..892353ea7 100644 --- a/app/views/repositories/_commit.json.jbuilder +++ b/app/views/repositories/_commit.json.jbuilder @@ -7,11 +7,22 @@ if @project.educoder? json.timestamp 0 json.time_from_now commit[0]['time'] end - json.author do - {} - # json.partial! '/projects/author', user: render_commit_author(commit['author']) - end - json.committer {} + if commit[0]['author'].present? + json.author do + json.id nil + json.login commit[0]['author']['username'] + json.name commit[0]['author']['username'] + json.type nil + json.image_url commit[0]['author']['image_url'] + end + json.committer do + json.id nil + json.login commit[0]['author']['username'] + json.name commit[0]['author']['username'] + json.type nil + json.image_url commit[0]['author']['image_url'] + end + end end if @project.forge? @@ -26,9 +37,9 @@ if @project.forge? end json.author do - json.partial! 'commit_author', user: render_commit_author(commit['author']) + json.partial! 'commit_author', user: render_cache_commit_author(commit['commit']['author']), name: commit['commit']['author']['name'] end json.committer do - json.partial! 'commit_author', user: render_commit_author(commit['committer']) + json.partial! 'commit_author', user: render_cache_commit_author(commit['commit']['committer']), name: commit['commit']['committer']['name'] end end diff --git a/app/views/repositories/_commit_author.json.jbuilder b/app/views/repositories/_commit_author.json.jbuilder index 77d9b655c..83935709a 100644 --- a/app/views/repositories/_commit_author.json.jbuilder +++ b/app/views/repositories/_commit_author.json.jbuilder @@ -1,8 +1,21 @@ -if user - json.id user.id - json.login user.login - json.name user.real_name - json.image_url url_to_avatar(user) +if user.present? + if user.is_a?(Hash) + json.id user["id"] + json.login user["login"] + json.name user["name"] + json.type user["type"] + json.image_url user["avatar_url"] + else + json.id user.id + json.login user.login + json.name user.real_name + json.type user&.type + json.image_url url_to_avatar(user) + end else - json.nil! + json.id nil + json.login name + json.name name + json.type nil + json.image_url User::Avatar.get_letter_avatar_url(name) end diff --git a/app/views/repositories/_contributor.json.jbuilder b/app/views/repositories/_contributor.json.jbuilder new file mode 100644 index 000000000..56fa9ce86 --- /dev/null +++ b/app/views/repositories/_contributor.json.jbuilder @@ -0,0 +1,16 @@ +user = $redis_cache.hgetall("v2-owner-common:#{contributor["login"]}-#{contributor["email"]}") +if user.blank? + json.contributions contributor["contributions"] + # json.gid contributor["id"] + json.login contributor["login"] + json.type nil + json.name contributor["login"] + json.image_url User::Avatar.get_letter_avatar_url(contributor["login"]) +else + json.contributions contributor["contributions"] + # json.gid contributor["id"] + json.login user["login"] + json.type user["type"] + json.name user["name"] + json.image_url user["avatar_url"] +end diff --git a/app/views/repositories/_simple_entry.json.jbuilder b/app/views/repositories/_simple_entry.json.jbuilder index ec2a045bb..9d0998c82 100644 --- a/app/views/repositories/_simple_entry.json.jbuilder +++ b/app/views/repositories/_simple_entry.json.jbuilder @@ -1,6 +1,6 @@ if @project.forge? file_name = entry['name'] - file_type = file_name.to_s.split(".").last + file_type = File.extname(file_name.to_s)[1..-1] direct_download = download_type(file_type) image_type = image_type?(file_type) json.name file_name @@ -9,9 +9,19 @@ if @project.forge? json.type entry['type'] json.size entry['size'] - json.content decode64_content(entry, @owner, @repository, @ref) + json.content direct_download ? nil : decode64_content(entry, @owner, @repository, @ref, @path) json.target entry['target'] - json.download_url entry['download_url'] + + download_url = + if image_type + dir_path = [@owner.login, @repository.identifier, "raw/branch", @ref].join('/') + render_download_image_url(dir_path, entry['path'], decode64_content(entry, @owner, @repository, @ref)) + else + # entry['download_url'] + render_download_file_url(@owner, @repository, entry['path'].to_s, @ref) + end + json.download_url download_url + json.direct_download direct_download json.image_type image_type json.is_readme_file is_readme?(entry['type'], entry['name']) @@ -22,6 +32,8 @@ end if @project.educoder? file_path = params[:filepath].present? ? [params[:filepath], entry['name']].join('/') : entry['name'] + file_type = File.extname(entry['name'].to_s)[1..-1] + image_type = image_type?(file_type) json.name entry['name'] json.sha nil @@ -32,7 +44,7 @@ if @project.educoder? json.target nil json.download_url nil json.direct_download false - json.image_type false + json.image_type image_type json.is_readme_file false json.commit do json.message entry['title'] diff --git a/app/views/repositories/commit.json.jbuilder b/app/views/repositories/commit.json.jbuilder index 77f6e3f1b..4c1074c8b 100644 --- a/app/views/repositories/commit.json.jbuilder +++ b/app/views/repositories/commit.json.jbuilder @@ -1,11 +1,17 @@ # json.key_format! camelize: :lower -json.files_count @commit_diff['NumFiles'] -json.total_addition @commit_diff['TotalAddition'] -json.total_deletion @commit_diff['TotalDeletion'] -json.files @commit_diff['Files'], partial: 'pull_requests/diff_file', as: :file, locals: {sha: @sha} +if @commit.blank? #如果有状态值,则表示报错了 + json.total_count 0 + json.commits [] +else + json.files_count @commit_diff['NumFiles'] + json.total_addition @commit_diff['TotalAddition'] + json.total_deletion @commit_diff['TotalDeletion'] + json.files @commit_diff['Files'], partial: 'pull_requests/diff_file', as: :file, locals: {sha: @sha} -json.partial! 'commit', commit: @commit, project: @project -json.parents @commit['parents'] do |parent| - json.sha parent['sha'] - # json.url EduSetting.get('host_name') + commit_repository_path(@repo, parent['sha']) + json.partial! 'commit', commit: @commit, project: @project + json.parents @commit['parents'] do |parent| + json.sha parent['sha'] + # json.url EduSetting.get('host_name') + commit_repository_path(@repo, parent['sha']) + end + json.branch @commit['branch'] end diff --git a/app/views/repositories/commits.json.jbuilder b/app/views/repositories/commits.json.jbuilder index 7cbf34c4d..14b9f1b9e 100644 --- a/app/views/repositories/commits.json.jbuilder +++ b/app/views/repositories/commits.json.jbuilder @@ -1,23 +1,57 @@ -if @hash_commit.blank? #如果有状态值,则表示报错了 +if @hash_commit.blank? || @project.educoder? #如果有状态值,则表示报错了 json.total_count 0 - json.commits [] + json.commits do + json.array! @commits do |commit| + json.sha commit['id'] + json.message commit['title'] + json.time_from_now commit['time'] + json.author do + json.id nil + json.login commit['author']['username'] + json.name commit['author']['username'] + json.type nil + json.image_url commit['author']['image_url'] + end + json.committer do + json.id nil + json.login commit['author']['username'] + json.name commit['author']['username'] + json.type nil + json.image_url commit['author']['image_url'] + end + end + end else json.total_count @hash_commit[:total_count] json.commits do json.array! @hash_commit[:body] do |commit| commiter = commit['committer'] - if commiter.present? - commit_user_id = commiter['id'] - forge_user = User.simple_select.find_by(gitea_uid: commit_user_id) - end + + # forge_user = + # if commiter.present? + # User.simple_select.find_by(gitea_uid: commiter['id']) + # end + json.sha commit['sha'] json.message commit['commit']['message'] json.timestamp render_unix_time(commit['commit']['author']['date']) json.time_from_now time_from_now(commit['commit']['author']['date']) - if forge_user - json.partial! 'author', user: forge_user - else - json.author nil + # if forge_user + # json.partial! 'author', user: forge_user + # else + # json.author do + # json.id nil + # json.login commit['commit']['author']['name'] + # json.type nil + # json.name commit['commit']['author']['name'] + # json.image_url User::Avatar.get_letter_avatar_url(commit['commit']['author']['name']) + # end + # end + json.author do + json.partial! 'commit_author', user: render_cache_commit_author(commit['commit']['author']), name: commit['commit']['author']['name'] + end + json.committer do + json.partial! 'commit_author', user: render_cache_commit_author(commit['commit']['committer']), name: commit['commit']['committer']['name'] end end end diff --git a/app/views/repositories/commits_slice.json.jbuilder b/app/views/repositories/commits_slice.json.jbuilder new file mode 100644 index 000000000..e252cda69 --- /dev/null +++ b/app/views/repositories/commits_slice.json.jbuilder @@ -0,0 +1,38 @@ +if @hash_commit.blank? #如果有状态值,则表示报错了 + json.total_count 0 + json.commits [] +else + json.total_count @hash_commit[:total_count] + json.commit_dates do + json.array! @hash_commit[:body] do |commit_date| + json.commit_date commit_date["commit_date"] + json.commits do + json.array! commit_date["Commits"] do |commit| + commiter = commit['committer'] + + forge_user = + if commiter.present? + User.simple_select.find_by(gitea_uid: commiter['id']) + end + + json.sha commit['sha'] + json.message commit['commit']['message'] + json.timestamp render_unix_time(commit['commit']['author']['date']) + json.time_from_now time_from_now(commit['commit']['author']['date']) + if forge_user + json.partial! 'author', user: forge_user + else + json.author do + json.id nil + json.login commit['commit']['author']['name'] + json.type nil + json.name commit['commit']['author']['name'] + json.image_url User::Avatar.get_letter_avatar_url(commit['commit']['author']['name']) + end + end + end + end + end + end + +end diff --git a/app/views/repositories/contributors.json.jbuilder b/app/views/repositories/contributors.json.jbuilder new file mode 100644 index 000000000..2fb6abae8 --- /dev/null +++ b/app/views/repositories/contributors.json.jbuilder @@ -0,0 +1,7 @@ +total_count = @contributors.size +json.list @contributors.each do |contributor| + json.partial! 'contributor', locals: { contributor: contributor } +end +json.total_count total_count + + diff --git a/app/views/repositories/detail.json.jbuilder b/app/views/repositories/detail.json.jbuilder new file mode 100644 index 000000000..9381c4737 --- /dev/null +++ b/app/views/repositories/detail.json.jbuilder @@ -0,0 +1,65 @@ +json.content @project.content +json.website @project.website +json.lesson_url @project.lesson_url +json.identifier render_identifier(@project) +json.invite_code @project.invite_code +json.name @project.name +json.description @project.description +json.project_id @project.id +json.repo_id @repository.id +json.issues_count @project.issues.issue_issue.size - @project.issues.issue_issue.closed.size +json.pull_requests_count @project.pull_requests.opening.size +json.project_identifier render_identifier(@project) +json.praises_count @project.praises_count.to_i +json.forked_count @project.forked_count.to_i +json.watchers_count @project.watchers_count.to_i +json.versions_count @project.versions.opening.size #里程碑数量 +json.version_releases_count @project.releases_size(@user.try(:id), "all") +json.version_releasesed_count @project.releases_size(@user.try(:id), "released") #已发行的版本 +json.permission render_permission(@user, @project) +json.mirror_url @project&.repository.remote_mirror_url +json.mirror @project&.repository.mirror_url.present? +json.type @project.numerical_for_project_type +json.open_devops @project.open_devops? + +unless @project.common? + json.mirror_status @repository.mirror_status + json.mirror_num @repository.mirror_num + json.first_sync @repository.first_sync? +end + +json.watched @project.watched_by? @user +json.praised @project.praised_by? @user +json.status @project.status +json.forked_from_project_id @project_fork_id +json.fork_info do + if @fork_project.present? + json.fork_form_name @fork_project.try(:name) + json.fork_project_user_login @fork_project_user.try(:login) + json.fork_project_identifier @fork_project.identifier + json.fork_project_user_name @fork_project_user.try(:show_real_name) + json.fork_project_user_type @fork_project_user.try(:type) + end +end +if @result[:repo] + json.size replace_bytes_to_b(number_to_human_size(@result[:repo]['size'].to_i*1024)) + json.ssh_url @result[:repo]['ssh_url'] + json.clone_url @project.educoder? ? "#{Rails.application.config_for(:configuration)['educoder']['git_site']}/#{@project&.project_educoder&.repo_name}.git" : @result[:repo]['clone_url'] + json.default_branch @project.educoder? ? "master" : @result[:repo]['default_branch'] + json.empty @result[:repo]['empty'] + json.full_name @result[:repo]['full_name'] + json.private @result[:repo]['private'] +end +json.license_name @project.license_name +json.branches_count @result[:branch_tag_total_count].present? ? (@result[:branch_tag_total_count]['branch_count'] || 0) : 0 +json.tags_count @result[:branch_tag_total_count].present? ? (@result[:branch_tag_total_count]['tag_count'] || 0) : 0 +# json.contributors do +# total_count = @result[:contributor].size +# json.list @result[:contributor].each do |contributor| +# json.partial! 'contributor', locals: { contributor: contributor } +# end +# json.total_count total_count +# end +# json.languages @result[:language].blank? ? nil : @result[:language] + +json.partial! 'author', locals: { user: @project.owner } diff --git a/app/views/repositories/edit.json.jbuilder b/app/views/repositories/edit.json.jbuilder index 45f88858e..d0586d92d 100644 --- a/app/views/repositories/edit.json.jbuilder +++ b/app/views/repositories/edit.json.jbuilder @@ -6,3 +6,12 @@ json.project_category_id @project.project_category_id json.project_language_id @project.project_language_id json.private !@project.is_public json.is_secret @project.is_secret +json.website @project.website +json.project_units @project.project_units.pluck(:unit_type) +json.lesson_url @project.lesson_url +json.permission render_permission(current_user, @project) +json.is_transfering @project.is_transfering +json.transfer do + json.partial! "/users/user_simple", locals: {user: @project&.applied_transfer_project&.owner} +end +json.is_pinned @project.has_pinned_users.include?(current_user) diff --git a/app/views/repositories/entries.json.jbuilder b/app/views/repositories/entries.json.jbuilder index 9b1d07cb2..6dea93b32 100644 --- a/app/views/repositories/entries.json.jbuilder +++ b/app/views/repositories/entries.json.jbuilder @@ -7,7 +7,7 @@ if @project.educoder? end end json.commits_count @entries['commit_count'] - json.zip_url @entries['git_url'] + json.zip_url '' json.tar_url '' json.entries do json.array! @entries['trees'] do |entry| @@ -41,7 +41,8 @@ if @project.forge? end #json.tags_count @tags_count #json.branches_count @branches_count - #json.commits_count @commits_count + json.commits_count @commits_count + # json.zip_url archive_repositories_path(@owner&.login, @repository, @ref) json.zip_url render_zip_url(@owner, @repository, @ref) json.tar_url render_tar_url(@owner, @repository, @ref) json.entries do @@ -59,4 +60,5 @@ if @project.forge? end end end + end diff --git a/app/views/repositories/sub_entries.json.jbuilder b/app/views/repositories/sub_entries.json.jbuilder index 3276ed0dd..296e307f4 100644 --- a/app/views/repositories/sub_entries.json.jbuilder +++ b/app/views/repositories/sub_entries.json.jbuilder @@ -24,8 +24,12 @@ if @project.educoder? end end json.entries do - json.array! @sub_entries['trees'] do |entry| - json.partial! 'repositories/simple_entry', locals: { entry: entry } + if @sub_entries['trees'].is_a?(Array) + json.array! @sub_entries['trees'] do |entry| + json.partial! 'repositories/simple_entry', locals: { entry: entry } + end + elsif @sub_entries['trees'].is_a?(Hash) + json.partial! 'repositories/simple_entry', locals: { entry: @sub_entries['trees'] } end end end diff --git a/app/views/repositories/tags.json.jbuilder b/app/views/repositories/tags.json.jbuilder index 9db3ff93e..eaf11058e 100644 --- a/app/views/repositories/tags.json.jbuilder +++ b/app/views/repositories/tags.json.jbuilder @@ -4,8 +4,23 @@ json.array! @tags do |tag| json.id tag['id'] json.zipball_url render_zip_url(@owner, @repository, tag['name']) json.tarball_url render_tar_url(@owner, @repository, tag['name']) + json.tagger do + json.partial! 'commit_author', user: render_cache_commit_author(tag['tagger']), name: tag['tagger']['name'] + end + json.time_ago time_from_now(tag['tagger']['date'].to_time) + json.created_at_unix tag['tagger']['date'].to_time.to_i + json.message tag['message'] json.commit do json.sha tag['commit']['sha'] + json.message tag['commit']['message'] + json.time_ago time_from_now(tag['commit']['commiter']['date'].to_time) + json.created_at_unix tag['commit']['commiter']['date'].to_time.to_i + json.committer do + json.partial! 'commit_author', user: render_cache_commit_author(tag['commit']['commiter']), name: tag['commit']['commiter']['name'] + end + json.author do + json.partial! 'commit_author', user: render_cache_commit_author(tag['commit']['author']), name: tag['commit']['author']['name'] + end end end end diff --git a/app/views/settings/show.json.jbuilder b/app/views/settings/show.json.jbuilder index 0bdeccd84..019e3688e 100644 --- a/app/views/settings/show.json.jbuilder +++ b/app/views/settings/show.json.jbuilder @@ -49,4 +49,24 @@ json.setting do json.current_main_site_url current_main_site_url json.api_urls Rails.application.config_for(:configuration)["api_urls"] + + + json.add do + json.array! @add + end + + json.personal do + json.array! @personal + end + + json.common @common + + if @top_system_notification.present? + json.system_notification do + json.(@top_system_notification, :id, :subject, :sub_subject, :content) + json.is_read @top_system_notification.read_member?(current_user&.id) + end + else + json.system_notification nil + end end diff --git a/app/views/template_message_settings/_detail.json.jbuilder b/app/views/template_message_settings/_detail.json.jbuilder new file mode 100644 index 000000000..d85a4c4ea --- /dev/null +++ b/app/views/template_message_settings/_detail.json.jbuilder @@ -0,0 +1,8 @@ +json.type type +json.type_name type.constantize.type_name +json.total_settings_count count +json.settings do + json.array! type.constantize.openning.limit(100).each do |setting| + json.(setting, :name, :key, :notification_disabled, :email_disabled) + end +end \ No newline at end of file diff --git a/app/views/template_message_settings/index.json.jbuilder b/app/views/template_message_settings/index.json.jbuilder new file mode 100644 index 000000000..4f863ce0f --- /dev/null +++ b/app/views/template_message_settings/index.json.jbuilder @@ -0,0 +1,11 @@ +json.partial! "commons/success" +json.setting_types do + + json.array! @group_settings.keys.sort_by{|i| i.constantize.order_index}.each do |k| + json.partial! "detail", type: k, count: @group_settings[k] + end + + # json.array! @group_settings, partial: 'detail', as: :type + + +end \ No newline at end of file diff --git a/app/views/user_mailer/register_email.html.erb b/app/views/user_mailer/register_email.html.erb index 6f88177c5..431db06f3 100644 --- a/app/views/user_mailer/register_email.html.erb +++ b/app/views/user_mailer/register_email.html.erb @@ -1,7 +1,7 @@ - 验证码发送 + GitLink-验证码发送 @@ -296,7 +296,7 @@
    - +
    Shell JavaScript @@ -325,26 +325,127 @@ +
  • + PublicKeys + +
  • Users
  • Projects
  • @@ -368,6 +478,12 @@
  • 仓库详情(简版)
  • +
  • + 仓库详情(新版) +
  • +
  • + 仓库标签列表 +
  • 编辑仓库信息
  • @@ -389,16 +505,63 @@
  • 获取仓库成员列表
  • +
  • + 获取仓库所有文件 +
  • 获取仓库代码目录
  • 获取仓库代码子目录或者文件
  • +
  • + 获取仓库README文件 +
  • +
  • + 获取仓库贡献者 +
  • +
  • + 获取仓库webhooks列表 +
  • +
  • + 获取仓库单个webhook +
  • +
  • + 添加仓库webhook +
  • +
  • + 更新仓库webhook +
  • +
  • + 删除仓库webhook +
  • +
  • + 获取仓库webhook的历史推送列表 +
  • +
  • + 仓库webhook测试推送 +
  • Pulls +
  • Issues @@ -548,6 +711,257 @@ http://localhost:3000/api/ignores.json + +

    PublicKeys

    public_keys列表

    +

    获取public_keys列表,支持分页

    + +
    +

    示例:

    +
    +
    curl -X GET \
    +http://localhost:3000/api/public_keys.json
    +
    await octokit.request('GET /api/public_keys.json')
    +

    HTTP 请求

    +

    GET api/public_keys.json

    +

    请求参数

    + + + + + + + + + + + + + + + + + + + + + + + +
    参数必选默认类型字段说明
    page1int页码
    limit15int每页数量
    +

    返回字段说明

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    total_countint总数
    public_keys.idintID
    public_keys.namestring密钥标题
    public_keys.contentstring密钥内容
    public_keys.fingerprintstring密钥标识
    public_keys.created_timestring密钥创建时间
    + +
    +

    返回的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"
    +        }
    +    ]
    +}
    +
    + +

    创建public_key

    +

    创建public_key

    + +
    +

    示例:

    +
    +
    curl -X POST \
    +http://localhost:3000/api/public_keys.json
    +
    await octokit.request('POST /api/public_keys.json')
    +

    HTTP 请求

    +

    POST api/public_keys.json

    +

    请求参数

    + + + + + + + + + + + + + + + + + + + + + + + +
    参数必选默认类型字段说明
    keystring密钥
    titlestring密钥标题
    + +
    +

    请求的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_countint总数
    idintID
    namestring密钥标题
    contentstring密钥内容
    fingerprintstring密钥标识
    created_timestring密钥创建时间
    + +
    +

    返回的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"
    +}
    +
    + +

    删除public_key

    +

    删除public_key

    + +
    +

    示例:

    +
    +
    curl -X DELETE \
    +http://localhost:3000/api/public_keys/:id.json
    +
    await octokit.request('DELETE /api/public_keys/:id.json')
    +

    HTTP 请求

    +

    DELETE api/public_keys/:id.json

    +

    请求参数

    + + + + + + + + + + + + + + + + +
    参数必选默认类型字段说明
    idint密钥ID
    + +
    +

    返回的JSON示例:

    +
    +
    {
    +    "status": 0,
    +    "message": "success"
    +}
    +
    + +

    Users

    获取当前登陆用户信息

    获取当前登陆用户信息

    @@ -607,7 +1021,3548 @@ Success — a happy kitten is an authenticated kitten! -

    Projects

    获取项目列表

    +

    用户消息列表

    +

    获取用户消息列表

    + +
    +

    示例:

    +
    +
    curl -X GET http://localhost:3000/api/users/:login/messages.json
    +
    await octokit.request('GET /api/users/:login/messages.json')
    +

    HTTP 请求

    +

    GET api/users/yystopf/messages.json

    +

    请求字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    typestring消息类型,不传为所有消息,notification为系统消息,atme为@我消息
    statusinteger是否已读,不传为所有消息,1为未读,2为已读
    limitinteger每页个数
    pageinteger页码
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    total_countinteger消息总数
    typestring消息类型
    unread_notificationinteger未读系统通知数量
    unread_atmeinteger未读@我数量
    messages.idinteger消息id
    messages.statusinteger消息是否已读,1为未读,2为已读
    messages.contentstring消息内容
    messages.notification_urlstring消息跳转地址
    messages.sourcestring消息来源
    messages.timeagostring消息时间
    messages.typestring消息类型,notification为系统消息,atme为@我消息
    senderobject消息发送者
    +

    消息来源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示例:

    +
    +
    {
    +    "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"
    +        }
    +    ]
    +}
    +
    + +

    用户阅读系统通知

    +

    用户阅读系统通知

    + +
    +

    示例:

    +
    +
    curl -X POST http://localhost:3000/api/users/yystopf/system_notification_histories.json
    +
    await octokit.request('GET /api/users/:login/system_notification_histories.json')
    +

    HTTP 请求

    +

    POST /api/users/:login/system_notification_histories.json

    +

    请求字段说明:

    + + + + + + + + + + + + +
    参数类型字段说明
    system_notification_idinteger阅读的系统通知id
    + +
    +

    返回的JSON示例:

    +
    +
    {
    +    "status": 0,
    +    "message": "success"
    +}
    +

    发送消息

    +

    发送消息, 目前只支持atme

    + +
    +

    示例:

    +
    +
    curl -X POST http://localhost:3000/api/users/:login/messages.json
    +
    await octokit.request('POST /api/users/:login/messages.json')
    +

    HTTP 请求

    +

    POST api/users/yystopf/messages.json

    +

    请求字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    typestring消息类型
    receivers_loginarray需要发送消息的用户名数组
    atmeable_typestringatme消息对象,是从哪里@我的,比如评论:Journal、疑修:Issue、合并请求:PullRequest
    atmeable_idintegeratme消息对象id
    + +
    +

    请求的JSON示例:

    +
    +
    {
    +    "type": "atme",
    +    "receivers_login": ["yystopf", "testforge1"],
    +    "atmeable_type": "Journal",
    +    "atmeable_id": 67
    +}
    +
    +
    +

    返回的JSON示例:

    +
    +
    {
    +    "status": 0,
    +    "message": "success"
    +}
    +
    + +

    阅读消息

    +

    阅读消息

    + +
    +

    示例:

    +
    +
    curl -X POST http://localhost:3000/api/users/:login/messages/read.json
    +
    await octokit.request('POST /api/users/:login/messages/read.json')
    +

    HTTP 请求

    +

    POST api/users/yystopf/messages/read.json

    +

    请求字段说明:

    + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    typestring消息类型,不传为所有消息,notification为系统消息,atme为@我消息
    idsarray消息id数组,包含-1则把所有未读消息标记为已读
    + +
    +

    返回的JSON示例:

    +
    +
    {
    +    "status": 0,
    +    "message": "success"
    +}
    +
    + +

    删除消息

    +

    删除消息

    + +
    +

    示例:

    +
    +
    curl -X DELETE http://localhost:3000/api/users/:login/messages.json
    +
    await octokit.request('DELETE /api/users/:login/messages.json')
    +

    HTTP 请求

    +

    DELETE api/users/yystopf/messages.json

    +

    请求字段说明:

    + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    typestring消息类型,atme为@我消息
    idsarray消息id数组,包含-1则把所有消息删除
    + +
    +

    返回的JSON示例:

    +
    +
    {
    +    "status": 0,
    +    "message": "success"
    +}
    +
    + +

    更改用户信息

    +

    更改用户信息

    + +
    +

    示例:

    +
    +
    curl -X PATCH/PUT http://localhost:3000/api/users/yystopf.json
    +
    await octokit.request('PATCH/PUT /api/users/:login.json')
    +

    HTTP 请求

    +

    PATCH/PUT /api/users/:login.json

    +

    请求字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    user.nicknamestring用户昵称
    user.imagebase64/file用户头像
    user.user_extension_attributes.genderint性别, 0男 1女
    user.user_extension_attributes.provincestring省份
    user.user_extension_attributes.citystring城市
    user.user_extension_attributes.descriptionstring简介
    user.user_extension_attributes.custom_departmentstring单位名称
    user.user_extension_attributes.technical_titlestring职业
    user.user_extension_attributes.show_emailbool是否展示邮箱
    user.user_extension_attributes.show_locationbool是否展示位置
    user.user_extension_attributes.show_departmentbool是否展示公司
    + +
    +

    请求的JSON示例:

    +
    +
    {
    +    "user": {
    +        "nickname": "xxx",
    +        "user_extension_attributes": {
    +            "gender": 0,
    +            "province": "湖南",
    +            "city": "长沙",
    +            "description": "个性签名",
    +            "custom_department": "湖南智擎科技有限公司",
    +        }
    +    }
    +}
    +
    +
    +

    返回的JSON示例:

    +
    +
    {
    +    "status": 0,
    +    "message": "success"
    +}
    +

    获取平台消息设置配置信息

    +

    获取平台消息设置配置信息

    + +
    +

    示例:

    +
    +
    curl -X GET http://localhost:3000/api/template_message_settings.json
    +
    await octokit.request('GET /api/template_message_settings.json')
    +

    HTTP 请求

    +

    GET /api/template_message_settings.json

    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    typestring消息配置类型
    type_namestring消息配置类型含义
    total_settings_countint配置条数
    settings.namestring配置名称
    settings.keystring配置标识
    settings.notification_disabledboolean站内信设置是否禁用
    settings.email_disabledboolean邮件设置是否禁用
    + +
    +

    返回的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
    +                }
    +            ]
    +        }
    +    ]
    +}
    +
    + +

    获取用户消息设置配置信息

    +

    获取用户消息设置配置信息

    + +
    +

    示例:

    +
    +
    curl -X GET http://localhost:3000/api/users/yystopf/template_message_settings.json
    +
    await octokit.request('GET /api/uses/yystopf/template_message_settings.json')
    +

    HTTP 请求

    +

    GET /api/users/:user_id/template_message_settings.json

    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    notification_bodystring站内信配置
    email_bodystring邮件配置
    + +
    +

    返回的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
    +    }
    +}
    +
    + +

    重新设置用户消息设置配置信息

    +

    重新设置用户消息设置配置信息

    + +
    +

    示例:

    +
    +
    curl -X POST http://localhost:3000/api/users/yystopf/template_message_settings/update_setting.json
    +
    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_bodystring站内信配置
    email_bodystring邮件配置
    + +
    +

    请求的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_bodystring站内信配置
    email_bodystring邮件配置
    + +
    +

    返回的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
    +    }
    +}
    +
    + +

    获取用户星标项目

    +

    获取用户星标项目

    + +
    +

    示例:

    +
    +
    curl -X GET http://localhost:3000/api/users/yystopf/is_pinned_projects.json
    +
    await octokit.request('GET /api/users/:login/is_pinned_projects.json')
    +

    HTTP 请求

    +

    GET api/users/:login/is_pinned_projects.json

    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    total_countint星标项目数量
    identifierstring项目标识
    namestring项目名称
    descriptionstring项目描述
    visitsint项目访问数量
    praises_countint项目点赞数量
    watchers_countint项目关注数量
    issues_countint项目issue数量
    pull_requests_countint项目合并请求数量
    forked_countint项目复刻数量
    is_publicbool项目是否公开
    mirror_urlstring镜像地址
    typeint项目类型 0 普通项目 1 普通镜像项目 2 同步镜像项目
    time_agostring上次更新时间
    open_devopsint是否开启devops
    forked_from_project_idintfork项目id
    platformstring项目平台
    author.namestring项目拥有者名称
    author.typestring项目拥有者类型
    author.loginstring项目拥有者用户名
    author.image_urlstring项目拥有者头像
    category.namestring项目分类名称
    language.namestring项目语言名称
    positionint项目排序
    + +
    +

    返回的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"
    +            }
    +        }
    +    ]
    +}
    +
    + +

    用户添加星标项目

    +

    用户添加星标项目

    + +
    +

    示例:

    +
    +
    curl -X POST http://localhost:3000/api/users/yystopf/is_pinned_projects/pin.json
    +
    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_idsarray设定为星标项目的id
    +

    只设定一个星标项目

    + + + + + + + + + + + + +
    参数类型字段说明
    is_pinned_project_idinteger设定为星标项目的id
    + +
    +

    返回的JSON示例:

    +
    +
    {
    +    "status": 0,
    +    "message": "success"
    +}
    +

    星标项目展示排序

    +

    星标项目展示排序

    + +
    +

    示例:

    +
    +
    curl -X PATCH http://localhost:3000/api/users/yystopf/is_pinned_projects/11.json
    +
    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.positionint排序,数字越大排名越前
    + +
    +

    请求的JSON示例:

    +
    +
    {
    +    "pinned_project": {
    +        "position": 1
    +    }
    +}
    +
    +
    +

    返回的JSON示例:

    +
    +
    {
    +    "status": 0,
    +    "message": "success"
    +}
    +

    用户近期活动统计

    +

    用户近期活动统计, 默认显示近一周的数据

    + +
    +

    示例:

    +
    +
    curl -X GET http://localhost:3000/api/users/yystopf/statistics/activity.json
    +
    await octokit.request('GET /api/users/:login/statistics/activity.json')
    +

    HTTP 请求

    +

    GET /api/users/:login/statistics/activity.json

    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    datesarray时间
    issues_countarray疑修数量
    pull_requests_countarray合并请求数量
    commtis_countarray贡献数量
    + +
    +

    返回的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
    +    ]
    +}
    +
    + +

    获取用户贡献度

    +

    获取用户贡献度

    + +
    +

    示例:

    +
    +
    curl -X GET http://localhost:3000/api/users/yystopf/headmaps.json
    +
    await octokit.request('GET /api/users/:login/headmaps.json')
    +

    HTTP 请求

    +

    GET api/users/:login/headmaps.json

    +

    请求字段说明:

    + + + + + + + + + + + + +
    参数类型字段说明
    yearstring年份
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    total_contributionsint所选时间内的总贡献度
    headmaps.datestring时间
    headmaps.contributionsint贡献度
    + +
    +

    返回的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
    +        }
    +    ]
    +}
    +
    + +

    获取用户动态

    +

    获取用户动态

    + +
    +

    示例:

    +
    +
    curl -X GET http://localhost:3000/api/users/yystopf/project_trends.json
    +
    await octokit.request('GET /api/users/:login/project_trends.json')
    +

    HTTP 请求

    +

    GET api/users/:login/project_trends.json

    +

    请求字段说明:

    + + + + + + + + + + + + +
    参数类型字段说明
    datestring日期,格式: 2021-05-28
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    total_countint所选时间内的总动态数
    project_trends.trend_typestring动态类型,Issue:疑修,VersionRelease:版本发布,PullRequest:合并请求
    project_trends.action_typestring操作类型
    project_trends.trend_idinteger动态id
    project_trends.user_namestring用户名称
    project_trends.user_loginstring用户用户名
    project_trends.user_avatarstring用户头像
    project_trends.action_timestring操作时间
    project_trends.namestring动态标题
    + +
    +

    返回的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"
    +        }
    +    ]
    +}
    +
    + +

    用户开发能力

    +

    用户开发能力, 默认为所有时间下的开发能力

    + +
    +

    示例:

    +
    +
    curl -X GET http://localhost:3000/api/users/yystopf/statistics/develop.json
    +
    await octokit.request('GET /api/users/:login/statistics/develop.json')
    +

    HTTP 请求

    +

    GET /api/users/:login/statistics/develop.json

    +

    请求字段说明:

    + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    start_timeinteger时间戳,开始时间,格式:1621526400
    end_timeinteger时间戳,结束时间,格式:1622131200
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    influenceint影响力
    contributionint贡献度
    activityint活跃度
    experienceint项目经验
    languageint语言能力
    languages_percentfloat语言百分比
    each_language_scoreint各门语言分数
    + +
    +

    返回的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
    +        }
    +    }
    +}
    +
    + +

    用户角色定位

    +

    用户角色定位,默认显示所有时间下的角色定位数据

    + +
    +

    示例:

    +
    +
    curl -X GET http://localhost:3000/api/users/yystopf/statistics/role.json
    +
    await octokit.request('GET /api/users/:login/statistics/role.json')
    +

    HTTP 请求

    +

    GET /api/users/:login/statistics/role.json

    +

    请求字段说明:

    + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    start_timeinteger时间戳,开始时间,格式:1621526400
    end_timeinteger时间戳,结束时间,格式:1622131200
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    total_projects_countint用户所有的项目数量
    role.object.countint用户该语言下的项目数量
    role.object.percentfloat用户该语言下的项目占比
    + +
    +

    返回的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
    +        }
    +    }
    +}
    +
    + +

    用户专业定位

    +

    用户专业定位,默认显示所有时间下的专业定位数据

    + +
    +

    示例:

    +
    +
    curl -X GET http://localhost:3000/api/users/yystopf/statistics/major.json
    +
    await octokit.request('GET /api/users/:login/statistics/major.json')
    +

    HTTP 请求

    +

    GET /api/users/:login/statistics/major.json

    +

    请求字段说明:

    + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    start_timeinteger时间戳,开始时间,格式:1621526400
    end_timeinteger时间戳,结束时间,格式:1622131200
    +

    返回字段说明:

    + + + + + + + + + + + + +
    参数类型字段说明
    categoriesint用户项目分类
    + +
    +

    返回的JSON示例:

    +
    +
    {
    +    "categories": [
    +        "大数据",
    +        "机器学习",
    +        "深度学习",
    +        "人工智能",
    +        "智慧医疗",
    +        "云计算"
    +    ]
    +}
    +
    + +

    待办事项-用户通知信息

    +

    待办事项-用户通知信息

    + +
    +

    示例:

    +
    +
    curl -X GET http://localhost:3000/api/users/yystopf/applied_messages.json
    +
    await octokit.request('GET /api/users/:login/applied_messages.json')
    +

    HTTP 请求

    +

    GET /api/users/:login/applied_messages.json

    +

    请求字段说明:

    + + + + + + + + + + + + +
    参数类型字段说明
    loginstring用户标识
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    appliedobject通知主体
    applied.idint通知主体的迁移id
    applied.statusstring通知主体的迁移状态,canceled:取消,common:正在迁移, accept:已接受,refuse:已拒绝
    applied.time_agostring通知主体的迁移创建的时间
    applied.project.idint通知主体的迁移项目的id
    applied.project.identifierstring通知主体的迁移项目的标识
    applied.project.namestring通知主体的迁移项目的名称
    applied.project.descriptionstring通知主体的迁移项目的描述
    applied.project.is_publicbool通知主体的迁移项目是否公开
    applied.project.owner.idbool通知主体的迁移项目拥有者id
    applied.project.owner.typestring通知主体的迁移项目拥有者类型
    applied.project.owner.namestring通知主体的迁移项目拥有者昵称
    applied.project.owner.loginstring通知主体的迁移项目拥有者标识
    applied.project.owner.image_urlstring通知主体的迁移项目拥有者头像
    applied.user.idint通知主体的迁移创建者的id
    applied.user.typestring通知主体的迁移创建者的类型
    applied.user.namestring通知主体的迁移创建者的名称
    applied.user.loginstring通知主体的迁移创建者的标识
    applied.user.image_urlstring通知主体的迁移创建者头像
    applied_user.idint通知发起者的id
    applied_user.typestring通知发起者的类型
    applied_user.namestring通知发起者的名称
    applied_user.loginstring通知发起者的标识
    applied_user.image_urlstring通知发起者头像
    applied_typestring通知类型
    namestring通知内容
    viewedstring是否已读,waiting:未读,viewed:已读
    statusstring通知状态, canceled:已取消,common: 正常,successed:成功,failure:失败
    time_agostring通知时间
    + +
    +

    返回的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分钟前"
    +        },
    +        ...
    +    ]
    +}
    +

    待办事项-接受仓库

    +

    待办事项-接受仓库

    + +
    +

    示例:

    +
    +
    curl -X GET http://localhost:3000/api/users/yystopf/applied_transfer_projects.json
    +
    await octokit.request('GET /api/users/:login/applied_transfer_projects.json')
    +

    HTTP 请求

    +

    GET /api/users/:login/applied_transfer_projects.json

    +

    请求字段说明:

    + + + + + + + + + + + + +
    参数类型字段说明
    loginstring用户标识
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    idint迁移id
    statusstring迁移状态,canceled:取消,common:正在迁移, accept:已接受,refuse:已拒绝
    time_agostring迁移创建的时间
    project.idint迁移项目的id
    project.identifierstring迁移项目的标识
    project.namestring迁移项目的名称
    project.descriptionstring迁移项目的描述
    project.is_publicbool迁移项目是否公开
    project.owner.idbool迁移项目拥有者id
    project.owner.typestring迁移项目拥有者类型
    project.owner.namestring迁移项目拥有者昵称
    project.owner.loginstring迁移项目拥有者标识
    project.owner.image_urlstring迁移项目拥有者头像
    user.idint迁移创建者的id
    user.typestring迁移创建者的类型
    user.namestring迁移创建者的名称
    user.loginstring迁移创建者的标识
    user.image_urlstring迁移创建者头像
    owner.idint迁移接受者的id
    owner.typestring迁移接受者的类型
    owner.namestring迁移接受者的名称
    owner.loginstring迁移接受者的标识
    owner.image_urlstring迁移接受者头像
    + +
    +

    返回的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小时前"
    +        },
    +        ...
    +    ]
    +}
    +

    用户接受迁移

    +

    用户接受迁移

    + +
    +

    示例:

    +
    +
    curl -X POST http://localhost:3000/api/users/yystopf/applied_transfer_projects/2/accept.json
    +
    await octokit.request('GET /api/users/:login/applied_transfer_projects/:id/accept.json')
    +

    HTTP 请求

    +

    GET /api/users/:login/applied_transfer_projects/:id/accept.json

    +

    请求字段说明:

    + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    loginstring用户标识
    idint迁移id
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    idint迁移id
    statusstring迁移状态,canceled:取消,common:正在迁移, accept:已接受,refuse:已拒绝
    time_agostring迁移创建的时间
    project.idint迁移项目的id
    project.identifierstring迁移项目的标识
    project.namestring迁移项目的名称
    project.descriptionstring迁移项目的描述
    project.is_publicbool迁移项目是否公开
    project.owner.idbool迁移项目拥有者id
    project.owner.typestring迁移项目拥有者类型
    project.owner.namestring迁移项目拥有者昵称
    project.owner.loginstring迁移项目拥有者标识
    project.owner.image_urlstring迁移项目拥有者头像
    user.idint迁移创建者的id
    user.typestring迁移创建者的类型
    user.namestring迁移创建者的名称
    user.loginstring迁移创建者的标识
    user.image_urlstring迁移创建者头像
    owner.idint迁移接受者的id
    owner.typestring迁移接受者的类型
    owner.namestring迁移接受者的名称
    owner.loginstring迁移接受者的标识
    owner.image_urlstring迁移接受者头像
    + +
    +

    返回的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小时前"
    +}
    +

    用户拒绝迁移

    +

    用户拒绝迁移

    + +
    +

    示例:

    +
    +
    curl -X POST http://localhost:3000/api/users/yystopf/applied_transfer_projects/2/refuse.json
    +
    await octokit.request('GET /api/users/:login/applied_transfer_projects/:id/refuse.json')
    +

    HTTP 请求

    +

    GET /api/users/:login/applied_transfer_projects/:id/refuse.json

    +

    请求字段说明:

    + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    loginstring用户标识
    idint迁移id
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    idint迁移id
    statusstring迁移状态,canceled:取消,common:正在迁移, accept:已接受,refuse:已拒绝
    time_agostring迁移创建的时间
    project.idint迁移项目的id
    project.identifierstring迁移项目的标识
    project.namestring迁移项目的名称
    project.descriptionstring迁移项目的描述
    project.is_publicbool迁移项目是否公开
    project.owner.idbool迁移项目拥有者id
    project.owner.typestring迁移项目拥有者类型
    project.owner.namestring迁移项目拥有者昵称
    project.owner.loginstring迁移项目拥有者标识
    project.owner.image_urlstring迁移项目拥有者头像
    user.idint迁移创建者的id
    user.typestring迁移创建者的类型
    user.namestring迁移创建者的名称
    user.loginstring迁移创建者的标识
    user.image_urlstring迁移创建者头像
    owner.idint迁移接受者的id
    owner.typestring迁移接受者的类型
    owner.namestring迁移接受者的名称
    owner.loginstring迁移接受者的标识
    owner.image_urlstring迁移接受者头像
    + +
    +

    返回的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小时前"
    +}
    +

    待办事项-项目申请

    +

    待办事项-项目申请

    + +
    +

    示例:

    +
    +
    curl -X GET http://localhost:3000/api/users/yystopf/applied_projects.json
    +
    await octokit.request('GET /api/users/:login/applied_projects.json')
    +

    HTTP 请求

    +

    GET /api/users/:login/applied_projects.json

    +

    请求字段说明:

    + + + + + + + + + + + + +
    参数类型字段说明
    loginstring用户标识
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    idint申请id
    statusstring申请状态,canceled:取消,common:正在申请, accept:已接受,refuse:已拒绝
    time_agostring申请创建的时间
    project.idint申请项目的id
    project.identifierstring申请项目的标识
    project.namestring申请项目的名称
    project.descriptionstring申请项目的描述
    project.is_publicbool申请项目是否公开
    project.owner.idbool申请项目拥有者id
    project.owner.typestring申请项目拥有者类型
    project.owner.namestring申请项目拥有者昵称
    project.owner.loginstring申请项目拥有者标识
    project.owner.image_urlstring申请项目拥有者头像
    user.idint申请创建者的id
    user.typestring申请创建者的类型
    user.namestring申请创建者的名称
    user.loginstring申请创建者的标识
    user.image_urlstring申请创建者头像
    + +
    +

    返回的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分钟前"
    +        },
    +        ...
    +    ]
    +}
    +

    用户接受申请

    +

    用户接受申请

    + +
    +

    示例:

    +
    +
    curl -X POST http://localhost:3000/api/users/yystopf/applied_projects/2/accept.json
    +
    await octokit.request('GET /api/users/:login/applied_projects/:id/accept.json')
    +

    HTTP 请求

    +

    GET /api/users/:login/applied_projects/:id/accept.json

    +

    请求字段说明:

    + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    loginstring用户标识
    idint申请id
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    idint申请id
    statusstring申请状态,canceled:取消,common:正在申请, accept:已接受,refuse:已拒绝
    time_agostring申请创建的时间
    project.idint申请项目的id
    project.identifierstring申请项目的标识
    project.namestring申请项目的名称
    project.descriptionstring申请项目的描述
    project.is_publicbool申请项目是否公开
    project.owner.idbool申请项目拥有者id
    project.owner.typestring申请项目拥有者类型
    project.owner.namestring申请项目拥有者昵称
    project.owner.loginstring申请项目拥有者标识
    project.owner.image_urlstring申请项目拥有者头像
    user.idint申请创建者的id
    user.typestring申请创建者的类型
    user.namestring申请创建者的名称
    user.loginstring申请创建者的标识
    user.image_urlstring申请创建者头像
    + +
    +

    返回的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分钟前"
    +}
    +

    用户拒绝申请

    +

    用户拒绝申请

    + +
    +

    示例:

    +
    +
    curl -X POST http://localhost:3000/api/users/yystopf/applied_projects/2/refuse.json
    +
    await octokit.request('GET /api/users/:login/applied_projects/:id/refuse.json')
    +

    HTTP 请求

    +

    GET /api/users/:login/applied_projects/:id/refuse.json

    +

    请求字段说明:

    + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    loginstring用户标识
    idint申请id
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    idint申请id
    statusstring申请状态,canceled:取消,common:正在申请, accept:已接受,refuse:已拒绝
    time_agostring申请创建的时间
    project.idint申请项目的id
    project.identifierstring申请项目的标识
    project.namestring申请项目的名称
    project.descriptionstring申请项目的描述
    project.is_publicbool申请项目是否公开
    project.owner.idbool申请项目拥有者id
    project.owner.typestring申请项目拥有者类型
    project.owner.namestring申请项目拥有者昵称
    project.owner.loginstring申请项目拥有者标识
    project.owner.image_urlstring申请项目拥有者头像
    user.idint申请创建者的id
    user.typestring申请创建者的类型
    user.namestring申请创建者的名称
    user.loginstring申请创建者的标识
    user.image_urlstring申请创建者头像
    + +
    +

    返回的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分钟前"
    +}
    +

    Projects

    申请加入项目

    +

    申请加入项目

    + +
    +

    示例:

    +
    +
    curl -X POST http://localhost:3000/api/applied_projects.json
    +
    await octokit.request('POST /api/appliedr_projects.json')
    +

    HTTP 请求

    +

    POST /api/applied_projects.json

    +

    请求参数

    + + + + + + + + + + + + + + + + + + + + + + + +
    参数必选默认类型字段说明
    applied_project.codestring邀请码
    applied_project.rolestring项目权限,reporter: 报告者, developer: 开发者,manager:管理员
    + +
    +

    请求的JSON示例

    +
    +
    {
    +  "applied_project": {
    +    "code": "1una34",
    +    "role": "developer"
    +  }
    +}
    +

    返回字段说明

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    idint申请id
    statusstring申请状态,canceled:取消,common:正在申请, accept:已接受,refuse:已拒绝
    time_agostring项目申请创建的时间
    project.idint申请项目的id
    project.identifierstring申请项目的标识
    project.namestring申请项目的名称
    project.descriptionstring申请项目的描述
    project.is_publicbool申请项目是否公开
    project.owner.idbool申请项目拥有者id
    project.owner.typestring申请项目拥有者类型
    project.owner.namestring申请项目拥有者昵称
    project.owner.loginstring申请项目拥有者标识
    project.owner.image_urlstring申请项目拥有者头像
    user.idint申请创建者的id
    user.typestring申请创建者的类型
    user.namestring申请创建者的名称
    user.loginstring申请创建者的标识
    user.image_urlstring申请创建者头像
    + +
    +

    返回的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分钟前"
    +}
    +

    获取项目列表

    获取项目列表,也可以更加相关条件过滤搜素

    @@ -618,9 +4573,9 @@ Success — a happy kitten is an authenticated kitten! -d "limit=5" \ http://localhost:3000/api/projects | jq
  • await octokit.request('GET /api/projects')
    -

    HTTP 请求

    +

    HTTP 请求

    GET api/projects

    -

    请求参数

    +

    请求参数

    @@ -687,7 +4642,7 @@ http://localhost:3000/api/projects | jq
    参数项目类型, 取值为:common、mirror; common:开源托管项目, mirror:开源镜像项目
    -

    返回字段说明

    +

    返回字段说明

    @@ -839,9 +4794,9 @@ Remember — a happy kitten is an authenticated kitten!
    curl -X GET \
     http://localhost:3000/api/projects/recommend  | jq
     
    await octokit.request('GET /api/projects/recommend.json')
    -

    HTTP 请求

    +

    HTTP 请求

    GET api/projects/recommend

    -

    返回字段说明

    +

    返回字段说明

    参数
    @@ -966,18 +4921,18 @@ http://localhost:3000/api/projects/recommend | jq -

    项目主页

    -

    获取项目主页信息

    +

    项目导航

    +

    获取项目导航信息

    示例:

    curl -X GET \
    -http://localhost:3000/api/jasder/forgeplus/about  | jq
    -
    await octokit.request('GET /api/jasder/forgeplus/about')
    -

    HTTP 请求

    -

    GET api/:owner/:repo/about

    -

    请求参数

    +http://localhost:3000/api/yystopf/ceshi/menu_list | jq +
    await octokit.request('GET /api/yystopf/ceshi/menu_list')
    +

    HTTP 请求

    +

    GET api/:owner/:repo/menu_list

    +

    请求参数

    参数
    @@ -1002,7 +4957,75 @@ http://localhost:3000/api/jasder/forgeplus/about | jq
    参数项目标识identifier
    -

    返回字段说明

    +

    返回字段说明

    + + + + + + + + + + + + +
    参数类型字段说明
    menu_namestring导航名称, home:主页,code:代码库,issues:疑修,pulls:合并请求,devops:工作流,versions:里程碑,activity:动态,setting:仓库设置
    + +
    +

    返回的JSON示例:

    +
    +
    [
    +    {
    +        "menu_name": "home"
    +    },
    +    {
    +        "menu_name": "code"
    +    },
    +    {
    +        "menu_name": "pulls"
    +    },
    +    {
    +        "menu_name": "activity"
    +    }
    +]
    +

    项目主页

    +

    获取项目主页信息

    + +
    +

    示例:

    +
    +
    curl -X GET \
    +http://localhost:3000/api/jasder/forgeplus/about  | jq
    +
    await octokit.request('GET /api/jasder/forgeplus/about')
    +

    HTTP 请求

    +

    GET api/:owner/:repo/about

    +

    请求参数

    + + + + + + + + + + + + + + + + + + + + + + + +
    参数必选默认类型字段说明
    ownerstring用户登录名
    repostring项目标识identifier
    +

    返回字段说明

    @@ -1039,7 +5062,106 @@ http://localhost:3000/api/jasder/forgeplus/about | jq -

    创建项目

    +

    项目模块信息

    +

    项目模块信息

    + +
    +

    示例:

    +
    +
    curl -X GET \
    +http://localhost:3000/api/yystopf/ceshi/project_units.json
    +
    await octokit.request('GET /api/yystopf/ceshi/project_units')
    +

    HTTP 请求

    +

    GET /api/yystopf/ceshi/project_units

    +

    返回字段说明:

    +
    参数
    + + + + + + + + + + + +
    参数类型字段说明
    typestring模块名称
    + +
    +

    返回的JSON示例:

    +
    +
    [
    +    {
    +        "type": "code"
    +    },
    +    {
    +        "type": "pulls"
    +    },
    +    {
    +        "type": "issues"
    +    }
    +]
    +

    更改项目模块展示

    +

    更改项目模块展示

    + +
    +

    示例:

    +
    +
    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
    +
    await octokit.request('POST /api/yystopf/ceshi/project_units')
    +

    HTTP 请求

    +

    POST /api/yystopf/ceshi/project_units

    +

    请求参数

    + + + + + + + + + + + + + + + + +
    参数必选默认类型字段说明
    unit_typesarray项目模块内容, 支持以下参数:code:代码库,issues:疑修,pulls:合并请求,devops:工作流,versions:里程碑
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    statusint返回状态, 0: 表示操作成功
    messagestring返回信息说明
    + +
    +

    返回的JSON示例:

    +
    +
    {
    +    "status": 0,
    +    "message": "success"
    +}
    +

    创建项目

    创建项目

    @@ -1056,9 +5178,9 @@ Remember — a happy kitten is an authenticated kitten! -d "license_id=1" \ http://localhost:3000/api/projects.json
    await octokit.request('GET /api/projects.json')
    -

    HTTP 请求

    +

    HTTP 请求

    POST api/projects

    -

    请求参数

    +

    请求参数

    @@ -1132,7 +5254,7 @@ http://localhost:3000/api/projects.json
    参数项目是否私有, true:为私有,false: 公开,默认为公开
    -

    返回字段说明

    +

    返回字段说明

    @@ -1174,9 +5296,9 @@ http://localhost:3000/api/projects.json -d"project_language_id=2"\ http://localhost:3000/api/projects/migrate.json
    await octokit.request('GET /api/projects/migrate.json')
    -

    HTTP 请求

    +

    HTTP 请求

    POST api/projects/migrate.json

    -

    请求参数

    +

    请求参数

    参数
    @@ -1264,7 +5386,7 @@ http://localhost:3000/api/projects/migrate.json
    参数项目是否私有, true:为私有,false: 非私有,默认为公开
    -

    返回字段说明

    +

    返回字段说明

    @@ -1299,9 +5421,9 @@ http://localhost:3000/api/projects/migrate.json
    curl -X POST http://localhost:3000/api/repositories/1244/sync_mirror.json
     
    await octokit.request('POST /api/repositories/1244/sync_mirror.json')
    -

    HTTP 请求

    +

    HTTP 请求

    POST api/repositories/:id/sync_mirror.json

    -

    请求参数

    +

    请求参数

    参数
    @@ -1319,7 +5441,7 @@ http://localhost:3000/api/projects/migrate.json
    参数仓库id
    -

    返回字段说明

    +

    返回字段说明

    @@ -1354,9 +5476,9 @@ http://localhost:3000/api/projects/migrate.json
    curl -X POST http://localhost:3000/api/jasder/forgeplus/forks.json
     
    await octokit.request('POST /api/jaser/jasder_test/forks.json')
    -

    HTTP 请求

    +

    HTTP 请求

    POST api/:owner/:repo/forks.json

    -

    请求参数

    +

    请求参数

    参数
    @@ -1381,7 +5503,7 @@ http://localhost:3000/api/projects/migrate.json
    参数项目标识identifier
    -

    返回字段说明

    +

    返回字段说明

    @@ -1408,6 +5530,502 @@ http://localhost:3000/api/projects/migrate.json "id": 3290, "identifier": "newadm" } +

    用户管理的组织列表

    +

    用户管理的组织列表

    + +
    +

    示例:

    +
    +
    curl -X GET \
    +http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizations.json  | jq
    +
    await octokit.request('GET /api/:owner/:repo/applied_transfer_projects/organizations')
    +

    HTTP 请求

    +

    GET api/:owner/:repo/applied_transfer_projects/organizations

    +

    请求参数

    +
    参数
    + + + + + + + + + + + + + + + + + + + + + + +
    参数必选默认类型字段说明
    ownerstring用户登录名
    repostring项目标识identifier
    +

    返回字段说明

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    namestring组织标识
    nicknamestring组织名称
    descriptionstring组织描述
    avatar_urlstring|组织头像
    + +
    +

    返回的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表示正在迁移

    + +
    +

    示例:

    +
    +
    curl -X POST http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects.json
    +
    await octokit.request('POST /api/:owner/:repo/applied_transfer_projects.json')
    +

    HTTP 请求

    +

    POST /api/:owner/:repo/applied_transfer_projects.json

    +

    请求参数

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数必选默认类型字段说明
    ownerstring用户登录名
    repostring项目标识identifier
    owner_namestring迁移对象标识
    +

    返回字段说明

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    idint项目id
    statusstring项目迁移状态,canceled:取消,common:正在迁移, accept:已接受,refuse:已拒绝
    time_agostring项目迁移创建的时间
    project.idint迁移项目的id
    project.identifierstring迁移项目的标识
    project.namestring迁移项目的名称
    project.descriptionstring迁移项目的描述
    project.is_publicbool迁移项目是否公开
    project.owner.idbool迁移项目拥有者id
    project.owner.typestring迁移项目拥有者类型
    project.owner.namestring迁移项目拥有者昵称
    project.owner.loginstring迁移项目拥有者标识
    project.owner.image_urlstring迁移项目拥有者头像
    user.idint迁移创建者的id
    user.typestring迁移创建者的类型
    user.namestring迁移创建者的名称
    user.loginstring迁移创建者的标识
    user.image_urlstring迁移创建者头像
    owner.idint迁移接受者的id
    owner.typestring迁移接受者的类型
    owner.namestring迁移接受者的名称
    owner.loginstring迁移接受者的标识
    owner.image_urlstring迁移接受者头像
    + +
    +

    返回的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表示正在迁移

    + +
    +

    示例:

    +
    +
    curl -X POST http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/cancel.json
    +
    await octokit.request('POST /api/:owner/:repo/applied_transfer_projects/cancel.json')
    +

    HTTP 请求

    +

    POST /api/:owner/:repo/applied_transfer_projects/cancel.json

    +

    请求参数

    + + + + + + + + + + + + + + + + + + + + + + + +
    参数必选默认类型字段说明
    ownerstring用户登录名
    repostring项目标识identifier
    +

    返回字段说明

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    idint迁移id
    statusstring迁移状态,canceled:取消,common:正在迁移, accept:已接受,refuse:已拒绝
    time_agostring迁移创建的时间
    project.idint迁移项目的id
    project.identifierstring迁移项目的标识
    project.namestring迁移项目的名称
    project.descriptionstring迁移项目的描述
    project.is_publicbool迁移项目是否公开
    project.owner.idbool迁移项目拥有者id
    project.owner.typestring迁移项目拥有者类型
    project.owner.namestring迁移项目拥有者昵称
    project.owner.loginstring迁移项目拥有者标识
    project.owner.image_urlstring迁移项目拥有者头像
    user.idint迁移创建者的id
    user.typestring迁移创建者的类型
    user.namestring迁移创建者的名称
    user.loginstring迁移创建者的标识
    user.image_urlstring迁移创建者头像
    owner.idint迁移接受者的id
    owner.typestring迁移接受者的类型
    owner.namestring迁移接受者的名称
    owner.loginstring迁移接受者的标识
    owner.image_urlstring迁移接受者头像
    + +
    +

    返回的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分钟前"
    +}
     

    Repositories

    仓库详情

    仓库详情

    @@ -1590,16 +6208,16 @@ http://localhost:3000/api/projects/migrate.json "repo_id": 75073, "repo_identifier": "mirror_demo" } -

    编辑仓库信息

    -

    编辑仓库信息

    +

    仓库详情(新版)

    +

    仓库详情

    示例:

    -
    curl -X GET http://localhost:3000/api/jasder/jasder_test/edit.json
    -
    await octokit.request('GET /api/jasder/jasder_test/edit.json')
    +
    curl -X GET http://localhost:3000/api/yystopf/ceshi/detail.json
    +
    await octokit.request('GET /api/yystopf/ceshi/detail.json')
     

    HTTP 请求

    -

    GET /api/:owner/:repo/edit.json

    +

    GET /api/:owner/:repo/detail

    请求参数:

    @@ -1634,6 +6252,584 @@ http://localhost:3000/api/projects/migrate.json + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    contentstring仓库简介
    websitestring仓库网址
    readmestringreadme文件|
    identifierstring项目标识
    namestring项目名称
    issues_countint项目issue数量
    pull_requests_countint项目合并请求数量
    project_identifierint项目标识
    praises_countint项目点赞数量
    forked_countint项目复刻数量
    watchers_countint项目关注数量
    versions_countint项目里程碑数量
    version_releases_countint项目发行版数量
    version_releasesed_countint项目发行版已发行数量
    permissionstring项目权限
    mirror_urlstring镜像地址
    mirrorbool是否为镜像项目
    typeint项目类型 0 普通项目 1 普通镜像项目 2 同步镜像项目
    open_devopsint是否开启devops
    watchedbool是否关注
    praisedbool是否点赞
    statusint项目状态
    forked_from_project_idintfork项目id
    fork_infoobjectfork项目信息
    sizestring仓库大小
    ssh_urlstring项目ssh地址
    clone_urlstring项目克隆地址
    default_branchstring仓库默认分支
    emptybool仓库是否为空
    full_namestring仓库全称
    privatebool仓库是否为私有项目
    license_namestring许可证名称
    release_versions.list.namestring项目issue数量
    release_versions.list.tag_namestring发行版标签名称
    release_versions.list.created_atstring发行版创建时间
    release_versions.total_countint发行版数量
    branches.list.namestring分支名称
    branches.total_countint分支数量
    tags.list.namestring标签名称
    tags.total_countint标签数量
    contributors.list.contributionsint贡献数量
    contributors.list.loginstring贡献者登录名
    contributors.list.namestring贡献者用户名称
    contributors.list.image_urlstring贡献者头像
    languagesobject项目语言占比
    + +
    +

    返回的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%"
    +    }
    +}
    +

    仓库标签列表

    +

    仓库标签列表

    + +
    +

    示例:

    +
    +
    curl -X GET http://localhost:3000/api/yystopf/csfjkkj/tags.json
    +
    await octokit.request('GET /api/yystopf/csfjkkj/tags.json')
    +

    HTTP 请求

    +

    GET /api/:owner/:repo/tags.json

    +

    请求参数:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数必选默认类型字段说明
    ownerstring用户登录名
    repostring项目标识identifier
    page1integer页码
    limit20integer每页个数
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    idint标签id
    namestring标签名称
    zipball_urlstring标签zip包下载地址
    tarball_urlstring标签tar包下载地址
    taggerobject打标签的人
    time_agostring打标签的时间
    created_at_unixstring打标签的时间戳
    messagestring标签信息
    commitobject标签最后一个commit
    commit.shastringcommit的id
    commit.messagestringcommit的提交信息
    commit.time_agostringcommit的提交时间
    commit.created_at_unixstringcommit的提交时间戳
    commit.committerobjectcommit的提交者
    commit.authorobjectcommit的作者
    + +
    +

    返回的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"
    +            }
    +        }
    +    }
    +]
    +

    编辑仓库信息

    +

    编辑仓库信息

    + +
    +

    示例:

    +
    +
    curl -X GET http://localhost:3000/api/jasder/jasder_test/edit.json
    +
    await octokit.request('GET /api/jasder/jasder_test/edit.json')
    +

    HTTP 请求

    +

    GET /api/:owner/:repo/edit.json

    +

    请求参数:

    + + + + + + + + + + + + + + + + + + + + + + + +
    参数必选默认类型字段说明
    ownerstring用户登录名
    repostring项目标识identifier
    +

    返回字段说明:

    + + + + + + + + @@ -1702,9 +6898,9 @@ http://localhost:3000/api/projects/migrate.json -d"private=true"\ http://localhost:3000/api/jasder/jasder_test.json
    await octokit.request('PATCH /api/jasder/jasder_test.json')
    -

    HTTP 请求

    +

    HTTP 请求

    PATCH /api/:owner/:repo

    -

    请求参数:

    +

    请求参数:

    参数类型字段说明
    identifier string 仓库标识
    @@ -1764,7 +6960,7 @@ http://localhost:3000/api/jasder/jasder_test.json
    参数项目是否私有, true:为私有,false: 公开,默认为公开
    -

    返回字段说明:

    +

    返回字段说明:

    @@ -1829,9 +7025,9 @@ http://localhost:3000/api/jasder/jasder_test.json
    curl -X DELETE http://localhost:3000/api/jasder/jasder_test.json
     
    await octokit.request('DELETE /api/jasder/jasder_test.json')
    -

    HTTP 请求

    +

    HTTP 请求

    PATCH /api/:owner/:repo

    -

    请求参数:

    +

    请求参数:

    参数
    @@ -1856,7 +7052,7 @@ http://localhost:3000/api/jasder/jasder_test.json
    参数项目标识identifier
    -

    返回字段说明:

    +

    返回字段说明:

    @@ -1897,9 +7093,9 @@ http://localhost:3000/api/jasder/jasder_test.json -d"user_id=12"\ http://localhost:3000/api/jasder/jasder_test/collaborators.json
    await octokit.request('POST /api/jasder/jasder_test/collaborators.json')
    -

    HTTP 请求

    +

    HTTP 请求

    POST /api/:owner/:repo/collaborators.json

    -

    请求参数:

    +

    请求参数:

    参数
    @@ -1931,7 +7127,7 @@ http://localhost:3000/api/jasder/jasder_test/collaborators.json
    参数用户id
    -

    返回字段说明:

    +

    返回字段说明:

    @@ -1972,9 +7168,9 @@ http://localhost:3000/api/jasder/jasder_test/collaborators.json -d"user_id=12"\ http://localhost:3000/api/jasder/jasder_test/collaborators.json
    await octokit.request('DELETE /api/jasder/jasder_test/collaborators.json')
    -

    HTTP 请求

    +

    HTTP 请求

    DELETE /api/:owner/:repo/collaborators.json

    -

    请求参数:

    +

    请求参数:

    参数
    @@ -2006,7 +7202,7 @@ http://localhost:3000/api/jasder/jasder_test/collaborators.json
    参数用户id
    -

    返回字段说明:

    +

    返回字段说明:

    @@ -2048,9 +7244,9 @@ http://localhost:3000/api/jasder/jasder_test/collaborators.json -d"role=Developer"\ http://localhost:3000/api/jasder/jasder_test/change_role.json
    await octokit.request('PUT /api/jasder/jasder_test/change_role.json')
    -

    HTTP 请求

    +

    HTTP 请求

    PUT /api/:owner/:repo/change_role.json

    -

    请求参数:

    +

    请求参数:

    参数
    @@ -2089,7 +7285,7 @@ http://localhost:3000/api/jasder/jasder_test/change_role.json
    参数取值范围:"Manager", "Developer", "Reporter";分别为项目管理人员(拥有所有操作权限)、项目开发人员(只拥有读写权限)、项目报告人员(只拥有读权限)
    -

    返回字段说明:

    +

    返回字段说明:

    @@ -2131,9 +7327,9 @@ http://localhost:3000/api/jasder/jasder_test/change_role.json -d"limit=5"\ http://localhost:3000/api/jasder/jasder_test/collaborators.json
    await octokit.request('GET /api/jasder/jasder_test/collaborators.json')
    -

    HTTP 请求

    +

    HTTP 请求

    GET /api/:owner/:repo/collaborators.json

    -

    请求参数:

    +

    请求参数:

    参数
    @@ -2172,7 +7368,7 @@ http://localhost:3000/api/jasder/jasder_test/collaborators.json
    参数每页多少条数据,默认15条
    -

    返回字段说明:

    +

    返回字段说明:

    @@ -2250,7 +7446,130 @@ http://localhost:3000/api/jasder/jasder_test/collaborators.json -

    获取仓库代码目录

    +

    获取仓库所有文件

    +

    获取仓库所有文件

    + +
    +

    示例:

    +
    +
    curl -X GET \
    +-d "ref=develop" \
    +http://localhost:3000/api/yystopf/ceshi/files.json
    +
    await octokit.request('GET /api/yystopf/ceshi/files.json')
    +

    HTTP 请求

    +

    GET /api/:owner/:repo/files

    +

    请求参数:

    +
    参数
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数必选默认类型字段说明
    ownerstring用户登录名
    repostring项目标识identifier
    searchstring文件搜索关键词
    refstring分支名,默认为仓库默认分支
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    namestring文件名称
    pathstring文件路径
    shastring文件标识
    typestring文件类型
    sizeint文件大小
    urlstring文件地址
    + +
    +

    返回的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"
    +    }
    +]
    +

    获取仓库代码目录

    获取仓库代码目录

    @@ -2260,9 +7579,9 @@ http://localhost:3000/api/jasder/jasder_test/collaborators.json -d "ref=develop" \ http://localhost:3000//api/jasder/jasder_test/entries.json
    await octokit.request('GET /api/jasder/jasder_test/entries.json')
    -

    HTTP 请求

    +

    HTTP 请求

    GET /api/:owner/:repo/entries.json

    -

    请求参数:

    +

    请求参数:

    @@ -2294,7 +7613,7 @@ http://localhost:3000//api/jasder/jasder_test/entries.json
    参数分支名称、tag名称或是提交记录id,默认为master分支
    -

    返回字段说明:

    +

    返回字段说明:

    @@ -2408,9 +7727,9 @@ http://localhost:3000//api/jasder/jasder_test/entries.json -d"filepath=file"\ http://localhost:3000//api/jasder/jasder_test/sub_entries.json
    await octokit.request('GET /api/jasder/jasder_test/sub_entries.json')
    -

    HTTP 请求

    +

    HTTP 请求

    GET /api/:owner/:repo/sub_entries.json

    -

    请求参数:

    +

    请求参数:

    参数
    @@ -2449,7 +7768,7 @@ http://localhost:3000//api/jasder/jasder_test/sub_entries.json
    参数分支名称、tag名称或是提交记录id,默认为master分支
    -

    返回字段说明:

    +

    返回字段说明:

    @@ -2535,7 +7854,2397 @@ http://localhost:3000//api/jasder/jasder_test/sub_entries.json -

    Pulls

    Issues

    Organizations

    Teams

    Errors

    +

    获取仓库README文件

    +

    获取仓库README文件

    + +
    +

    示例:

    +
    +
    curl -X GET \
    +-d "ref=master" \
    +-d "filepath=lib" \
    +http://localhost:3000/api/yystopf/csfjkkj/readme.json
    +
    await octokit.request('GET /api/yystopf/csfjkkj/readme.json')
    +

    HTTP 请求

    +

    GET /api/:owner/:repo/readme.json

    +

    请求参数:

    +
    参数
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数必选默认类型字段说明
    ownerstring用户登录名
    repostring项目标识identifier
    refstring分支名称、tag名称或是提交记录id,默认为默认分支
    filepathstring子目录名称,默认为空
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    typestring文件类型, file:文件,dir:文件目录
    encodingstring编码
    sizeint文件夹或文件大小 单位B
    namestring文件夹或文件名称
    pathstring文件夹或文件相对路径
    contentstring文件内容
    shastring文件commitid
    + +
    +

    返回的JSON示例:

    +
    +
    {
    +    "type": "file",
    +    "encoding": "base64",
    +    "size": 24,
    +    "name": "README.md",
    +    "path": "lib/README.md",
    +    "content": "ZGZhc2RhZGpmIGRrZnNsCgpzZGZkZnMK",
    +    "sha": "860962cd21c60b1a9e07d723080c87c32c18d44a"
    +}
    +
    + +

    获取仓库贡献者

    +

    获取仓库贡献者

    + +
    +

    示例:

    +
    +
    curl -X GET \
    +-d "ref=master" \
    +-d "filepath=lib" \
    +http://localhost:3000/api/yystopf/csfjkkj/contributors.json
    +
    await octokit.request('GET /api/yystopf/csfjkkj/contributors.json')
    +

    HTTP 请求

    +

    GET /api/:owner/:repo/contributors.json

    +

    请求参数:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数必选默认类型字段说明
    ownerstring用户登录名
    repostring项目标识identifier
    refstring分支名称、tag名称或是提交记录id,默认为整个仓库
    filepathstring子目录名称,默认为空
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    total_countinteger贡献者数量
    contributionsinteger贡献数量
    loginstring用户登录名
    typestring用户类型
    namestring用户昵称
    image_urlstring用户头像
    + +
    +

    返回的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列表

    + +
    +

    示例:

    +
    +
    curl -X GET \
    +http://localhost:3000/api/yystopf/ceshi/webhooks.json
    +
    await octokit.request('GET /api/yystopf/ceshi/webhooks.json')
    +

    HTTP 请求

    +

    GET /api/:owner/:repo/webhooks.json

    +

    请求参数:

    + + + + + + + + + + + + + + + + + + + + + + + +
    参数必选默认类型字段说明
    ownerstring用户登录名
    repostring项目标识identifier
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    idintid
    urlstring地址
    http_methodstring请求方式
    is_activebool是否激活
    typestring类型
    last_statusstring最后一次推送的状态
    create_timestring创建时间
    + +
    +

    返回的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

    + +
    +

    示例:

    +
    +
    curl -X GET \
    +http://localhost:3000/api/yystopf/ceshi/webhooks/3/edit.json
    +
    await octokit.request('GET /api/yystopf/ceshi/webhooks/3/edit.json')
    +

    HTTP 请求

    +

    GET /api/:owner/:repo/webhooks/:id/edit.json

    +

    请求参数:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数必选默认类型字段说明
    ownerstring用户登录名
    repostring项目标识identifier
    idintegerwebhook ID
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    idintid
    urlstring地址
    content_typestringPOST Content Type
    http_methodstring请求方式
    secretstring
    is_activebool是否激活
    typestring类型
    last_statusstring最后一次推送的状态, waiting 等待,fail 失败,succeed 成功
    branch_filterstring分支过滤
    eventsstring触发条件
    create_timestring创建时间
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数含义
    create创建分支或标签
    delete分支或标签删除
    fork仓库被fork
    pushgit仓库推送
    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示例:

    +
    +
    {
    +    "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

    + +
    +

    示例:

    +
    +
    curl -X POST \
    +http://localhost:3000/api/yystopf/ceshi/webhooks.json
    +
    await octokit.request('POST /api/yystopf/ceshi/webhooks.json')
    +

    HTTP 请求

    +

    POST /api/:owner/:repo/webhooks.json

    +

    请求参数:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数必选默认类型字段说明
    ownerstring用户登录名
    repostring项目标识identifier
    webhook.urlstring目标url
    webhook.typestring类型
    webhook.http_methodstringhttp方法, POST和GET
    webhook.content_typestringPOST Content Type
    webhook.secretstring密钥文本
    webhook.activebool是否激活
    webhook.branch_filterstring分支过滤
    webhook.eventsarray触发事件
    + +

    触发事件字段说明

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数含义
    create创建分支或标签
    delete分支或标签删除
    fork仓库被fork
    pushgit仓库推送
    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示例:

    +
    +
    {
    +    "active": true, 
    +    "content_type": "json",
    +    "http_method": "GET",
    +    "secret": "123456",
    +    "url": "http://localhost:10000",
    +    "branch_filter": "*",
    +    "events": ["push"]
    +}
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    idintid
    urlstring地址
    content_typestringPOST Content Type
    is_activebool是否激活
    typestring类型
    eventsarray触发事件
    create_timestring创建时间
    + +
    +

    返回的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

    + +
    +

    示例:

    +
    +
    curl -X PATCH \
    +http://localhost:3000/api/yystopf/ceshi/webhooks/7.json
    +
    await octokit.request('PATCH /api/yystopf/ceshi/webhooks/7.json')
    +

    HTTP 请求

    +

    PATCH /api/:owner/:repo/webhooks/:id.json

    +

    请求参数:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数必选默认类型字段说明
    ownerstring用户登录名
    repostring项目标识identifier
    idstringwebhook id
    webhook.urlstring目标url
    webhook.typestring类型
    webhook.http_methodstringhttp方法, POST和GET
    webhook.content_typestringPOST Content Type
    webhook.secretstring密钥文本
    webhook.activebool是否激活
    webhook.branch_filterstring分支过滤
    webhook.eventsarray触发事件
    + +

    触发事件字段说明

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数含义
    create创建分支或标签
    delete分支或标签删除
    fork仓库被fork
    pushgit仓库推送
    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示例:

    +
    +
    {
    +    "active": true, 
    +    "content_type": "json",
    +    "http_method": "GET",
    +    "secret": "123456",
    +    "url": "http://localhost:10000",
    +    "branch_filter": "*",
    +    "events": ["push"]
    +}
    +

    返回字段说明:

    +
    +

    返回的JSON示例:

    +
    +
    {
    +    "status": 0,
    +    "message": "success"
    +}
    +
    + +

    删除仓库webhook

    +

    删除仓库webhook

    + +
    +

    示例:

    +
    +
    curl -X DELETE \
    +http://localhost:3000/api/yystopf/ceshi/webhooks/7.json
    +
    await octokit.request('DELETE /api/yystopf/ceshi/webhooks/7.json')
    +

    HTTP 请求

    +

    DELETE /api/:owner/:repo/webhooks/:id.json

    +

    请求参数:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数必选默认类型字段说明
    ownerstring用户登录名
    repostring项目标识identifier
    idstringwebhook id
    +

    返回字段说明:

    +
    +

    返回的JSON示例:

    +
    +
    {
    +    "status": 0,
    +    "message": "success"
    +}
    +
    + +

    获取仓库webhook的历史推送列表

    +

    获取仓库webhook的历史推送列表

    + +
    +

    示例:

    +
    +
    curl -X GET \
    +http://localhost:3000/api/yystopf/ceshi/webhooks/3/tasks.json
    +
    await octokit.request('GET /api/yystopf/ceshi/webhooks/3/tasks.json')
    +

    HTTP 请求

    +

    GET /api/:owner/:repo/webhooks/:id/tasks.json

    +

    请求参数:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数必选默认类型字段说明
    ownerstring用户登录名
    repostring项目标识identifier
    idintegerwebhook ID
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    idintid
    uuidstring推送uuid
    typestring类型
    is_succeedbool是否推送成功
    is_deliveredbool是否完成推送
    payload_contentjson请求主体内容
    request_contentjson请求内容,头部等等
    reponse_contentjson响应内容,状态,头部,主体等等
    delivered_timestring推送时间
    + +
    +

    返回的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": "<!doctype html><html lang=\"zh-CN\" class=\"notranslate translated-ltr\" translate=\"no\"><head><meta charset=\"utf-8\"><meta name=\"”Keywords”\" content=\"”trustie,trustieforge,forge,确实让创建更美好,协同开发平台″\"><meta name=\"”Keywords”\" content=\"”TrustieOpenSourceProject″\"><meta name=\"”Keywords”\" content=\"”issue,bug,tracker,软件工程,课程实践″\"><meta name=\"”Description”\" content=\"”持续构建协同、共享、可信的软件创建生态开源创作与软件生产相结合,支持大规模群体开展软件协同创新活动”\"><meta name=\"theme-color\" content=\"#000000\"><link rel=\"manifest\" href=\"/react/build//manifest.json\"><link rel=\"stylesheet\" href=\"/react/build/css/iconfont.css\"><link rel=\"stylesheet\" href=\"/react/build/css/edu-purge.css\"><link rel=\"stylesheet\" href=\"/react/build/css/editormd.min.css\"><link rel=\"stylesheet\" href=\"/react/build/css/merge.css\"><link href=\"/react/build/static/css/main.07f7e90c.chunk.css\" rel=\"stylesheet\"></head><body><div id=\"md_div\" style=\"display:none\"></div><div id=\"root\" class=\"page -layout-v -fit widthunit\"></div><div id=\"picture_display\" style=\"display:none\"></div><script src=\"/react/build/js/jquery-1.8.3.min.js\"></script><script src=\"/react/build/js/js_min_all.js\"></script><script src=\"/react/build/js/codemirror/codemirror.js\"></script><script src=\"/react/build/js/editormd/editormd.min.js\"></script><script src=\"/react/build/js/codemirror/merge/merge.js\"></script><script src=\"/react/build/./static/js/runtime~main.3d644966.js\"></script><script src=\"/react/build/./static/js/main.e46872e3.chunk.js\"></script><script async type=\"text/javascript\" id=\"mini-profiler\" src=\"/mini-profiler-resources/includes.js?v=67dd1c2571ced7fc74ae7f1813e47bdf\" data-version=\"67dd1c2571ced7fc74ae7f1813e47bdf\" data-path=\"/mini-profiler-resources/\" data-current-id=\"9ynvpncz5xm0rpgorb5y\" data-ids=\"9ynvpncz5xm0rpgorb5y,hgggd9mv6lr4a9drcrlr,j7zqlx2vy5aji2vtgoba,f1ktsmh3jxvq0z2hf612,mih3dvgvlqhi3zy8lf2x,5k1qbkvbnru8mye9cest,tj6ern8w6awqf2zsimbr,9isaehvubivd52wo5p9v,1rzfhtq1nhuwbgy9p76g,z0xzidzyywna0y7a69m0,hzoklky92ycjqt42gi0s,y0ai7y0t28mcn8x0py2x,322il7nadinp51mw2r5m,m6dukftfsh6tjcxzp1gq,667wlqbytfwbrirnmma1,jcehj3dl8lkw8gk510cr\" data-horizontal-position=\"left\" data-vertical-position=\"top\" data-trivial=\"false\" data-children=\"false\" data-max-traces=\"20\" data-controls=\"false\" data-total-sql-count=\"false\" data-authorized=\"true\" data-toggle-shortcut=\"alt+p\" data-start-hidden=\"false\" data-collapse-results=\"true\" data-html-container=\"body\"></script>\n</body></html>"
    +      },
    +      "delivered_time": "2021-07-28 11:47:29"
    +    }
    +  ]
    +}
    +
    + +

    仓库webhook测试推送

    +

    仓库webhook测试推送

    + +
    +

    示例:

    +
    +
    curl -X POST \
    +http://localhost:3000/api/yystopf/ceshi/webhooks/3/test.json
    +
    await octokit.request('POST /api/yystopf/ceshi/webhooks/3/test.json')
    +

    HTTP 请求

    +

    POST /api/:owner/:repo/webhooks/:id/test.json

    +

    请求参数:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数必选默认类型字段说明
    ownerstring用户登录名
    repostring项目标识identifier
    idintegerwebhook ID
    +

    返回字段说明:

    +
    +

    返回的JSON示例:

    +
    +
    {
    +    "status": 0,
    +    "message": "success"
    +}
    +
    + +

    Pulls

    Get a pull request

    +

    获取合并请求详情接口

    + +
    +

    示例:

    +
    +
    curl -X GET http://localhost:3000/api/Jasder/gitlink/pulls/88.json
    +
    await octokit.request('GET /api/Jasder/gitlink/pulls/88.json')
    +

    HTTP 请求

    +

    GET /api/:owner/:repo/pulls/:id.json

    +

    请求参数:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数必选默认类型字段说明
    ownerstring用户登录名
    repostring项目标识identifier
    idintegerpull id值
    + +
    +

    返回的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文件列表

    + +
    +

    示例:

    +
    +
    curl -X GET \
    +http://localhost:3000/api/Jason/gitlink/pulls/1/files.json 
    +
    await octokit.request('GET /api/jasder/gitlink/pulls/1/files.json')
    +

    HTTP 请求

    +

    GET /api/:owner/:repo/pulls/:id/files.json

    +

    请求参数:

    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数名必选类型说明
    ownerstring用户登录名
    repostringproject's identifier
    idintpull request's id
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    ---
    files_countint文件更改的总数量
    total_additionint添加代码总行数
    total_deletionint删除代码总行数
    filesarray
    -- shastringcommit's sha value
    -- namestring当前文件名
    -- old_namestring修改之前的文件名称,与name相同的话,说明文件名未更改
    -- additionint文件添加的行数
    -- deletionint文件删除的行数
    -- typeint文件类型, 1: 表示该文件只添加了内容,2: 表示该文件内容有修改, 3: 表示文件被删除或者改文件只删除了内容
    -- isCreatedboolean当前文件是否为新增文件, true: 是, false: 否
    -- isDeletedboolean当前文件是否被删除, true: 是,false: 否
    -- isBinboolean当前文件是否为二进制文件,true: 是,false: 否
    -- isLFSFileboolean当前文件是否为LFS文件,true: 是,false: 否
    -- isRenamedboolean当前文件是否被重命名,true: 是,false: 否
    -- sectionsarray
    ---- fileNamestring文件名称
    ---- linesarray
    ------ leftIdxstring文件变动之前所在行数
    ------ rightIdxstring文件更改后所在行数
    ------ typestring文件变更类型,1: 新增,2: 修改, 3: 删除, 4: diff统计信息
    ------ contentstring文件变更的内容
    ------ sectionInfoobject
    -------- pathstring文件相对仓库的路径
    -------- lastLeftIdxint
    -------- lastRightIdxint
    -------- leftHunkSizeint文件变更之前的行数
    -------- rightHunkSizeint文件变更之后的行数(及当前页面编辑器显示的总行数)
    -------- leftIdxint文件变更之前所在行数
    -------- rightIdxint文件变更之后所在行数(即:页面编辑器开始显示的行数)
    + +
    +

    返回的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列表

    + +
    +

    示例:

    +
    +
    curl -X GET http://localhost:3000/api/jasder/jasder_test/pulls/1/commits.json
    +
    await octokit.request('GET /api/jasder/jasder_test/pulls/1/commits.json')
    +

    HTTP 请求

    +

    GET /api/:owner/:repo/pulls/:id/commits.json

    +

    请求参数:

    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数名必选类型说明
    ownerstring用户登录名
    repostringproject's identifier
    idintpull request's id
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    commits_countintcommits总数量
    commitsarray
    -- authorobject项目作者
    ---- loginstring用户login
    ---- namestring用户姓名
    ---- image_urlstring用户头像
    -- committerobjectcommit提交用户
    ---- loginstring用户login
    ---- namestring用户姓名
    ---- image_urlstring用户头像
    -- timestampintcommit的unix时间戳
    -- time_from_nowstringcommit’s 提交时间距当前时间的时间值
    -- messagestringcommit说明信息
    -- shastringcommit’s sha值
    + +
    +

    返回的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

    + +
    +

    示例:

    +
    +
    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
    +
    await octokit.request('GET /api/Jason/test-txt/compare/master...Jason/test-txt:develop')
    +

    HTTP 请求

    +

    GET /api/:owner/:repo/compare/{base}...{head}.json

    +

    请求参数:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数名必选类型说明
    ownerstring用户登录名
    repostringproject's identifier
    basestringpull request's id
    headstringpull request's id
    +

    返回字段说明:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    参数类型字段说明
    ---
    commits_countintcommits总数量
    commitsarray
    -- authorobject项目作者
    ---- loginstring用户login
    ---- namestring用户姓名
    ---- image_urlstring用户头像
    -- committerobjectcommit提交用户
    ---- loginstring用户login
    ---- namestring用户姓名
    ---- image_urlstring用户头像
    -- timestampintcommit的unix时间戳
    -- time_from_nowstringcommit’s 提交时间距当前时间的时间值
    -- messagestringcommit说明信息
    -- shastringcommit’s sha值
    diffobject
    -- files_countint文件更改的总数量
    -- total_additionint添加代码总行数
    -- total_deletionint删除代码总行数
    -- filesArray
    -- shastringcommit's sha
    -- namestring当前文件名
    -- old_namestring修改之前的文件名称,与name相同的话,说明文件名未更改
    -- additionint文件添加的行数
    -- deletionint文件删除的行数
    -- typeint文件类型, 1: 表示该文件只添加了内容,2: 表示该文件内容有修改, 3: 表示文件被删除或者改文件只删除了内容
    -- isCreatedboolean当前文件是否为新增文件, true: 是, false: 否
    -- isDeletedboolean当前文件是否被删除, true: 是,false: 否
    -- isBinboolean当前文件是否为二进制文件,true: 是,false: 否
    -- isLFSFileboolean当前文件是否为LFS文件,true: 是,false: 否
    -- isRenamedboolean当前文件是否被重命名,true: 是,false: 否
    -- sectionsarray
    ---- fileNamestring文件名称
    ---- linesarray
    ------ leftIdxstring文件变动之前所在行数
    ------ rightIdxstring文件更改后所在行数
    ------ typestring文件变更类型,1: 内容未改动,2: 添加, 3: 删除, 4: diff统计信息
    ------ contentstring文件变更的内容
    ------ sectionInfoobject
    -------- pathstring文件相对仓库的路径
    -------- lastLeftIdxint
    -------- lastRightIdxint
    -------- leftHunkSizeint文件变更之前的行数
    -------- rightHunkSizeint文件变更之后的行数(及当前页面编辑器显示的总行数)
    -------- leftIdxint文件变更之前所在行数
    -------- rightIdxint文件变更之后所在行数
    + +
    +

    返回的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

    +

    获取合并请求列表

    + +
    +

    示例:

    +
    +
    curl -X GET http://localhost:3000/api/Jasder/gitlink/pulls.json
    +
    await octokit.request('GET /api/Jasder/gitlink/pulls.json')
    +

    HTTP 请求

    +

    GET /api/:owner/:repo/pulls.json

    +

    请求参数:

    + + + + + + + + + + + + + + + + + + + + + + + +
    参数必选默认类型字段说明
    ownerstring用户登录名
    repostring项目标识identifier
    + +
    +

    返回的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
    +    }
    +  ]
    +}
    +

    Issues

    Organizations

    Teams

    Errors

    diff --git a/public/docs/images/logo-8bc02cd4.png b/public/docs/images/logo-8bc02cd4.png new file mode 100644 index 000000000..4326948c1 Binary files /dev/null and b/public/docs/images/logo-8bc02cd4.png differ diff --git a/public/docs/images/trustie_logo-b38b63e6.png b/public/docs/images/trustie_logo-b38b63e6.png new file mode 100644 index 000000000..30affed0b Binary files /dev/null and b/public/docs/images/trustie_logo-b38b63e6.png differ diff --git a/public/docs/stylesheets/screen-69a70330.css b/public/docs/stylesheets/screen-69a70330.css new file mode 100644 index 000000000..3e8e88be4 --- /dev/null +++ b/public/docs/stylesheets/screen-69a70330.css @@ -0,0 +1 @@ +/*! normalize.css v3.0.2 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-webkit-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}.content h1,.content h2,.content h3,.content h4,.content h5,.content h6,html,body{font-family:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";font-size:14px}.content h1,.content h2,.content h3,.content h4,.content h5,.content h6{font-weight:bold}.content code,.content pre{font-family:Consolas, Menlo, Monaco, "Lucida Console", "Liberation Mono", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Courier New", monospace, serif;font-size:12px;line-height:1.5}.content code{word-break:break-all;-webkit-hyphens:auto;-ms-hyphens:auto;hyphens:auto}@font-face{font-family:'slate';src:url(../fonts/slate-cfc9d06b.eot?-syv14m);src:url(../fonts/slate-cfc9d06b.eot?#iefix-syv14m) format("embedded-opentype"),url(../fonts/slate.woff2?-syv14m) format("woff2"),url(../fonts/slate.woff?-syv14m) format("woff"),url(../fonts/slate-7b7da4fe.ttf?-syv14m) format("truetype"),url(../fonts/slate-e55b8307.svg?-syv14m#slate) format("svg");font-weight:normal;font-style:normal}.content aside.warning:before,.content aside.notice:before,.content aside.success:before,.toc-wrapper>.search:before{font-family:'slate';speak:none;font-style:normal;font-weight:normal;font-variant:normal;text-transform:none;line-height:1}.content aside.warning:before{content:"\e600"}.content aside.notice:before{content:"\e602"}.content aside.success:before{content:"\e606"}.toc-wrapper>.search:before{content:"\e607"}html,body{color:#333;padding:0;margin:0;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;background-color:#F3F7F9;height:100%;-webkit-text-size-adjust:none}#toc>ul>li>a>span{float:right;background-color:#2484FF;border-radius:40px;width:20px}.toc-wrapper{-webkit-transition:left 0.3s ease-in-out;transition:left 0.3s ease-in-out;overflow-y:auto;overflow-x:hidden;position:fixed;z-index:30;top:0;left:0;bottom:0;width:230px;background-color:#2E3336;font-size:13px;font-weight:bold}.toc-wrapper .lang-selector{display:none}.toc-wrapper .lang-selector a{padding-top:0.5em;padding-bottom:0.5em}.toc-wrapper .logo{display:block;margin:15px auto;max-width:100%;margin-bottom:0px;max-width:80%}.toc-wrapper>.search{position:relative}.toc-wrapper>.search input{background:#2E3336;border-width:0 0 1px 0;border-color:#666;padding:6px 0 6px 20px;-webkit-box-sizing:border-box;box-sizing:border-box;margin:10px 15px;width:200px;outline:none;color:#fff;border-radius:0}.toc-wrapper>.search:before{position:absolute;top:17px;left:15px;color:#fff}.toc-wrapper .search-results{margin-top:0;-webkit-box-sizing:border-box;box-sizing:border-box;height:0;overflow-y:auto;overflow-x:hidden;-webkit-transition-property:height, margin;transition-property:height, margin;-webkit-transition-duration:180ms;transition-duration:180ms;-webkit-transition-timing-function:ease-in-out;transition-timing-function:ease-in-out;background:#1E2224}.toc-wrapper .search-results.visible{height:30%;margin-bottom:1em}.toc-wrapper .search-results li{margin:1em 15px;line-height:1}.toc-wrapper .search-results a{color:#fff;text-decoration:none}.toc-wrapper .search-results a:hover{text-decoration:underline}.toc-wrapper ul,.toc-wrapper li{list-style:none;margin:0;padding:0;line-height:28px}.toc-wrapper li{color:#fff;-webkit-transition-property:background;transition-property:background;-webkit-transition-timing-function:linear;transition-timing-function:linear;-webkit-transition-duration:200ms;transition-duration:200ms}.toc-wrapper .toc-link.active{background-color:#0F75D4;color:#fff}.toc-wrapper .toc-link.active-parent{background-color:#1E2224;color:#fff}.toc-wrapper .toc-list-h2{display:none;background-color:#1E2224;font-weight:500}.toc-wrapper .toc-h2{padding-left:25px;font-size:12px}.toc-wrapper .toc-footer{padding:1em 0;margin-top:1em;border-top:1px dashed #666}.toc-wrapper .toc-footer li,.toc-wrapper .toc-footer a{color:#fff;text-decoration:none}.toc-wrapper .toc-footer a:hover{text-decoration:underline}.toc-wrapper .toc-footer li{font-size:0.8em;line-height:1.7;text-decoration:none}.toc-link,.toc-footer li{padding:0 15px 0 15px;display:block;overflow-x:hidden;white-space:nowrap;text-overflow:ellipsis;text-decoration:none;color:#fff;-webkit-transition-property:background;transition-property:background;-webkit-transition-timing-function:linear;transition-timing-function:linear;-webkit-transition-duration:130ms;transition-duration:130ms}#nav-button{padding:0 1.5em 5em 0;display:none;position:fixed;top:0;left:0;z-index:100;color:#000;text-decoration:none;font-weight:bold;opacity:0.7;line-height:16px;-webkit-transition:left 0.3s ease-in-out;transition:left 0.3s ease-in-out}#nav-button span{display:block;padding:6px 6px 6px;background-color:rgba(243,247,249,0.7);-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:rotate(-90deg) translate(-100%, 0);transform:rotate(-90deg) translate(-100%, 0);border-radius:0 0 0 5px}#nav-button img{height:16px;vertical-align:bottom}#nav-button:hover{opacity:1}#nav-button.open{left:230px}.page-wrapper{margin-left:230px;position:relative;z-index:10;background-color:#F3F7F9;min-height:100%;padding-bottom:1px}.page-wrapper .dark-box{width:50%;background-color:#2E3336;position:absolute;right:0;top:0;bottom:0}.page-wrapper .lang-selector{position:fixed;z-index:50;border-bottom:5px solid #2E3336}.lang-selector{display:flex;background-color:#1E2224;width:100%;font-weight:bold;overflow-x:auto}.lang-selector a{display:inline;color:#fff;text-decoration:none;padding:0 10px;line-height:30px;outline:0}.lang-selector a:active,.lang-selector a:focus{background-color:#111;color:#fff}.lang-selector a.active{background-color:#2E3336;color:#fff}.lang-selector:after{content:'';clear:both;display:block}.content{-webkit-transform:translateZ(0);position:relative;z-index:30}.content:after{content:'';display:block;clear:both}.content>h1,.content>h2,.content>h3,.content>h4,.content>h5,.content>h6,.content>p,.content>table,.content>ul,.content>ol,.content>aside,.content>dl{margin-right:50%;padding:0 28px;-webkit-box-sizing:border-box;box-sizing:border-box;display:block}.content>ul,.content>ol{padding-left:43px}.content>h1,.content>h2,.content>div{clear:both}.content h1{font-size:25px;padding-top:0.5em;padding-bottom:0.5em;margin-bottom:21px;margin-top:2em;border-top:1px solid #ccc;border-bottom:1px solid #ccc;background-color:#fdfdfd}.content h1:first-child,.content div:first-child+h1{border-top-width:0;margin-top:0}.content h2{font-size:19px;margin-top:4em;margin-bottom:0;border-top:1px solid #ccc;padding-top:1.2em;padding-bottom:1.2em;background-image:-webkit-gradient(linear, left top, left bottom, from(rgba(255,255,255,0.2)), to(rgba(255,255,255,0)));background-image:linear-gradient(to bottom, rgba(255,255,255,0.2), rgba(255,255,255,0))}.content h1+h2,.content h1+div+h2{margin-top:-21px;border-top:none}.content h3,.content h4,.content h5,.content h6{font-size:15px;margin-top:2.5em;margin-bottom:0.8em}.content h4,.content h5,.content h6{font-size:10px}.content hr{margin:2em 0;border-top:2px solid #2E3336;border-bottom:2px solid #F3F7F9}.content table{margin-bottom:1em;overflow:auto}.content table th,.content table td{text-align:left;vertical-align:top;line-height:1.6}.content table th code,.content table td code{white-space:nowrap}.content table th{padding:5px 10px;border-bottom:1px solid #ccc;vertical-align:bottom}.content table td{padding:10px}.content table tr:last-child{border-bottom:1px solid #ccc}.content table tr:nth-child(odd)>td{background-color:white}.content table tr:nth-child(even)>td{background-color:#fbfcfd}.content dt{font-weight:bold}.content dd{margin-left:15px}.content p,.content li,.content dt,.content dd{line-height:1.6;margin-top:0}.content img{max-width:100%}.content code{background-color:rgba(0,0,0,0.05);padding:3px;border-radius:3px}.content pre>code{background-color:transparent;padding:0}.content aside{padding-top:1em;padding-bottom:1em;margin-top:1.5em;margin-bottom:1.5em;background:#8fbcd4;line-height:1.6}.content aside.warning{background-color:#c97a7e}.content aside.success{background-color:#6ac174}.content aside:before{vertical-align:middle;padding-right:0.5em;font-size:14px}.content .search-highlight{padding:2px;margin:-3px;border-radius:4px;border:1px solid #F7E633;background:-webkit-gradient(linear, right bottom, left top, from(#F7E633), to(#F1D32F));background:linear-gradient(to top left, #F7E633 0%, #F1D32F 100%)}.content>div.highlight{clear:none}.content pre,.content blockquote{background-color:#1E2224;color:#fff;margin:0;width:50%;float:right;clear:right;-webkit-box-sizing:border-box;box-sizing:border-box}.content pre>p,.content blockquote>p{margin:0}.content pre a,.content blockquote a{color:#fff;text-decoration:none;border-bottom:dashed 1px #ccc}.content pre{padding-top:2em;padding-bottom:2em;padding:2em 28px}.content blockquote>p{background-color:#191D1F;padding:13px 2em;color:#eee}@media (max-width: 930px){.toc-wrapper{left:-230px}.toc-wrapper.open{left:0}.page-wrapper{margin-left:0}#nav-button{display:block}.toc-link{padding-top:0.3em;padding-bottom:0.3em}}@media (max-width: 700px){.dark-box{display:none}.content>h1,.content>h2,.content>h3,.content>h4,.content>h5,.content>h6,.content>p,.content>table,.content>ul,.content>ol,.content>aside,.content>dl{margin-right:0}.toc-wrapper .lang-selector{display:block}.page-wrapper .lang-selector{display:none}.content pre,.content blockquote{width:auto;float:none}.content>pre+h1,.content>blockquote+h1,.content>pre+h2,.content>blockquote+h2,.content>pre+h3,.content>blockquote+h3,.content>pre+h4,.content>blockquote+h4,.content>pre+h5,.content>blockquote+h5,.content>pre+h6,.content>blockquote+h6,.content>pre+p,.content>blockquote+p,.content>pre+table,.content>blockquote+table,.content>pre+ul,.content>blockquote+ul,.content>pre+ol,.content>blockquote+ol,.content>pre+aside,.content>blockquote+aside,.content>pre+dl,.content>blockquote+dl{margin-top:28px}}.highlight .c,.highlight .cm,.highlight .c1,.highlight .cs{color:#909090}.highlight,.highlight .w{background-color:#1E2224}.copy-clipboard{float:right;fill:#9DAAB6;cursor:pointer;opacity:0.4;height:18px;width:18px}.copy-clipboard:hover{opacity:0.8} \ No newline at end of file diff --git a/public/docs/stylesheets/screen-cf7c3c82.css b/public/docs/stylesheets/screen-cf7c3c82.css deleted file mode 100644 index d1e3a372c..000000000 --- a/public/docs/stylesheets/screen-cf7c3c82.css +++ /dev/null @@ -1 +0,0 @@ -/*! normalize.css v3.0.2 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-webkit-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}.content h1,.content h2,.content h3,.content h4,.content h5,.content h6,html,body{font-family:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";font-size:14px}.content h1,.content h2,.content h3,.content h4,.content h5,.content h6{font-weight:bold}.content code,.content pre{font-family:Consolas, Menlo, Monaco, "Lucida Console", "Liberation Mono", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Courier New", monospace, serif;font-size:12px;line-height:1.5}.content code{word-break:break-all;-webkit-hyphens:auto;-ms-hyphens:auto;hyphens:auto}@font-face{font-family:'slate';src:url(../fonts/slate-cfc9d06b.eot?-syv14m);src:url(../fonts/slate-cfc9d06b.eot?#iefix-syv14m) format("embedded-opentype"),url(../fonts/slate.woff2?-syv14m) format("woff2"),url(../fonts/slate.woff?-syv14m) format("woff"),url(../fonts/slate-7b7da4fe.ttf?-syv14m) format("truetype"),url(../fonts/slate-e55b8307.svg?-syv14m#slate) format("svg");font-weight:normal;font-style:normal}.content aside.warning:before,.content aside.notice:before,.content aside.success:before,.toc-wrapper>.search:before{font-family:'slate';speak:none;font-style:normal;font-weight:normal;font-variant:normal;text-transform:none;line-height:1}.content aside.warning:before{content:"\e600"}.content aside.notice:before{content:"\e602"}.content aside.success:before{content:"\e606"}.toc-wrapper>.search:before{content:"\e607"}html,body{color:#333;padding:0;margin:0;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;background-color:#F3F7F9;height:100%;-webkit-text-size-adjust:none}#toc>ul>li>a>span{float:right;background-color:#2484FF;border-radius:40px;width:20px}.toc-wrapper{-webkit-transition:left 0.3s ease-in-out;transition:left 0.3s ease-in-out;overflow-y:auto;overflow-x:hidden;position:fixed;z-index:30;top:0;left:0;bottom:0;width:230px;background-color:#2E3336;font-size:13px;font-weight:bold}.toc-wrapper .lang-selector{display:none}.toc-wrapper .lang-selector a{padding-top:0.5em;padding-bottom:0.5em}.toc-wrapper .logo{display:block;margin:15px auto;max-width:100%;margin-bottom:0px}.toc-wrapper>.search{position:relative}.toc-wrapper>.search input{background:#2E3336;border-width:0 0 1px 0;border-color:#666;padding:6px 0 6px 20px;-webkit-box-sizing:border-box;box-sizing:border-box;margin:10px 15px;width:200px;outline:none;color:#fff;border-radius:0}.toc-wrapper>.search:before{position:absolute;top:17px;left:15px;color:#fff}.toc-wrapper .search-results{margin-top:0;-webkit-box-sizing:border-box;box-sizing:border-box;height:0;overflow-y:auto;overflow-x:hidden;-webkit-transition-property:height, margin;transition-property:height, margin;-webkit-transition-duration:180ms;transition-duration:180ms;-webkit-transition-timing-function:ease-in-out;transition-timing-function:ease-in-out;background:#1E2224}.toc-wrapper .search-results.visible{height:30%;margin-bottom:1em}.toc-wrapper .search-results li{margin:1em 15px;line-height:1}.toc-wrapper .search-results a{color:#fff;text-decoration:none}.toc-wrapper .search-results a:hover{text-decoration:underline}.toc-wrapper ul,.toc-wrapper li{list-style:none;margin:0;padding:0;line-height:28px}.toc-wrapper li{color:#fff;-webkit-transition-property:background;transition-property:background;-webkit-transition-timing-function:linear;transition-timing-function:linear;-webkit-transition-duration:200ms;transition-duration:200ms}.toc-wrapper .toc-link.active{background-color:#0F75D4;color:#fff}.toc-wrapper .toc-link.active-parent{background-color:#1E2224;color:#fff}.toc-wrapper .toc-list-h2{display:none;background-color:#1E2224;font-weight:500}.toc-wrapper .toc-h2{padding-left:25px;font-size:12px}.toc-wrapper .toc-footer{padding:1em 0;margin-top:1em;border-top:1px dashed #666}.toc-wrapper .toc-footer li,.toc-wrapper .toc-footer a{color:#fff;text-decoration:none}.toc-wrapper .toc-footer a:hover{text-decoration:underline}.toc-wrapper .toc-footer li{font-size:0.8em;line-height:1.7;text-decoration:none}.toc-link,.toc-footer li{padding:0 15px 0 15px;display:block;overflow-x:hidden;white-space:nowrap;text-overflow:ellipsis;text-decoration:none;color:#fff;-webkit-transition-property:background;transition-property:background;-webkit-transition-timing-function:linear;transition-timing-function:linear;-webkit-transition-duration:130ms;transition-duration:130ms}#nav-button{padding:0 1.5em 5em 0;display:none;position:fixed;top:0;left:0;z-index:100;color:#000;text-decoration:none;font-weight:bold;opacity:0.7;line-height:16px;-webkit-transition:left 0.3s ease-in-out;transition:left 0.3s ease-in-out}#nav-button span{display:block;padding:6px 6px 6px;background-color:rgba(243,247,249,0.7);-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:rotate(-90deg) translate(-100%, 0);transform:rotate(-90deg) translate(-100%, 0);border-radius:0 0 0 5px}#nav-button img{height:16px;vertical-align:bottom}#nav-button:hover{opacity:1}#nav-button.open{left:230px}.page-wrapper{margin-left:230px;position:relative;z-index:10;background-color:#F3F7F9;min-height:100%;padding-bottom:1px}.page-wrapper .dark-box{width:50%;background-color:#2E3336;position:absolute;right:0;top:0;bottom:0}.page-wrapper .lang-selector{position:fixed;z-index:50;border-bottom:5px solid #2E3336}.lang-selector{display:flex;background-color:#1E2224;width:100%;font-weight:bold;overflow-x:auto}.lang-selector a{display:inline;color:#fff;text-decoration:none;padding:0 10px;line-height:30px;outline:0}.lang-selector a:active,.lang-selector a:focus{background-color:#111;color:#fff}.lang-selector a.active{background-color:#2E3336;color:#fff}.lang-selector:after{content:'';clear:both;display:block}.content{-webkit-transform:translateZ(0);position:relative;z-index:30}.content:after{content:'';display:block;clear:both}.content>h1,.content>h2,.content>h3,.content>h4,.content>h5,.content>h6,.content>p,.content>table,.content>ul,.content>ol,.content>aside,.content>dl{margin-right:50%;padding:0 28px;-webkit-box-sizing:border-box;box-sizing:border-box;display:block}.content>ul,.content>ol{padding-left:43px}.content>h1,.content>h2,.content>div{clear:both}.content h1{font-size:25px;padding-top:0.5em;padding-bottom:0.5em;margin-bottom:21px;margin-top:2em;border-top:1px solid #ccc;border-bottom:1px solid #ccc;background-color:#fdfdfd}.content h1:first-child,.content div:first-child+h1{border-top-width:0;margin-top:0}.content h2{font-size:19px;margin-top:4em;margin-bottom:0;border-top:1px solid #ccc;padding-top:1.2em;padding-bottom:1.2em;background-image:-webkit-gradient(linear, left top, left bottom, from(rgba(255,255,255,0.2)), to(rgba(255,255,255,0)));background-image:linear-gradient(to bottom, rgba(255,255,255,0.2), rgba(255,255,255,0))}.content h1+h2,.content h1+div+h2{margin-top:-21px;border-top:none}.content h3,.content h4,.content h5,.content h6{font-size:15px;margin-top:2.5em;margin-bottom:0.8em}.content h4,.content h5,.content h6{font-size:10px}.content hr{margin:2em 0;border-top:2px solid #2E3336;border-bottom:2px solid #F3F7F9}.content table{margin-bottom:1em;overflow:auto}.content table th,.content table td{text-align:left;vertical-align:top;line-height:1.6}.content table th code,.content table td code{white-space:nowrap}.content table th{padding:5px 10px;border-bottom:1px solid #ccc;vertical-align:bottom}.content table td{padding:10px}.content table tr:last-child{border-bottom:1px solid #ccc}.content table tr:nth-child(odd)>td{background-color:white}.content table tr:nth-child(even)>td{background-color:#fbfcfd}.content dt{font-weight:bold}.content dd{margin-left:15px}.content p,.content li,.content dt,.content dd{line-height:1.6;margin-top:0}.content img{max-width:100%}.content code{background-color:rgba(0,0,0,0.05);padding:3px;border-radius:3px}.content pre>code{background-color:transparent;padding:0}.content aside{padding-top:1em;padding-bottom:1em;margin-top:1.5em;margin-bottom:1.5em;background:#8fbcd4;line-height:1.6}.content aside.warning{background-color:#c97a7e}.content aside.success{background-color:#6ac174}.content aside:before{vertical-align:middle;padding-right:0.5em;font-size:14px}.content .search-highlight{padding:2px;margin:-3px;border-radius:4px;border:1px solid #F7E633;background:-webkit-gradient(linear, right bottom, left top, from(#F7E633), to(#F1D32F));background:linear-gradient(to top left, #F7E633 0%, #F1D32F 100%)}.content>div.highlight{clear:none}.content pre,.content blockquote{background-color:#1E2224;color:#fff;margin:0;width:50%;float:right;clear:right;-webkit-box-sizing:border-box;box-sizing:border-box}.content pre>p,.content blockquote>p{margin:0}.content pre a,.content blockquote a{color:#fff;text-decoration:none;border-bottom:dashed 1px #ccc}.content pre{padding-top:2em;padding-bottom:2em;padding:2em 28px}.content blockquote>p{background-color:#191D1F;padding:13px 2em;color:#eee}@media (max-width: 930px){.toc-wrapper{left:-230px}.toc-wrapper.open{left:0}.page-wrapper{margin-left:0}#nav-button{display:block}.toc-link{padding-top:0.3em;padding-bottom:0.3em}}@media (max-width: 700px){.dark-box{display:none}.content>h1,.content>h2,.content>h3,.content>h4,.content>h5,.content>h6,.content>p,.content>table,.content>ul,.content>ol,.content>aside,.content>dl{margin-right:0}.toc-wrapper .lang-selector{display:block}.page-wrapper .lang-selector{display:none}.content pre,.content blockquote{width:auto;float:none}.content>pre+h1,.content>blockquote+h1,.content>pre+h2,.content>blockquote+h2,.content>pre+h3,.content>blockquote+h3,.content>pre+h4,.content>blockquote+h4,.content>pre+h5,.content>blockquote+h5,.content>pre+h6,.content>blockquote+h6,.content>pre+p,.content>blockquote+p,.content>pre+table,.content>blockquote+table,.content>pre+ul,.content>blockquote+ul,.content>pre+ol,.content>blockquote+ol,.content>pre+aside,.content>blockquote+aside,.content>pre+dl,.content>blockquote+dl{margin-top:28px}}.highlight .c,.highlight .cm,.highlight .c1,.highlight .cs{color:#909090}.highlight,.highlight .w{background-color:#1E2224}.copy-clipboard{float:right;fill:#9DAAB6;cursor:pointer;opacity:0.4;height:18px;width:18px}.copy-clipboard:hover{opacity:0.8} \ No newline at end of file diff --git a/public/favicon.ico b/public/favicon.ico index 75a4c3dfd..ad428fa25 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/public/favicon.ico.bak b/public/favicon.ico.bak new file mode 100644 index 000000000..75a4c3dfd Binary files /dev/null and b/public/favicon.ico.bak differ diff --git a/public/images/email_logo.png b/public/images/email_logo.png new file mode 100644 index 000000000..ac07686b7 Binary files /dev/null and b/public/images/email_logo.png differ diff --git a/public/images/warn/pic_403.jpg b/public/images/warn/pic_403.jpg index 5db2f3236..def2ed775 100644 Binary files a/public/images/warn/pic_403.jpg and b/public/images/warn/pic_403.jpg differ diff --git a/public/images/warn/pic_403.jpg.bak b/public/images/warn/pic_403.jpg.bak new file mode 100644 index 000000000..5db2f3236 Binary files /dev/null and b/public/images/warn/pic_403.jpg.bak differ diff --git a/public/images/warn/pic_404.jpg b/public/images/warn/pic_404.jpg index b85751094..7724c7f6f 100644 Binary files a/public/images/warn/pic_404.jpg and b/public/images/warn/pic_404.jpg differ diff --git a/public/images/warn/pic_404.jpg.bak b/public/images/warn/pic_404.jpg.bak new file mode 100644 index 000000000..b85751094 Binary files /dev/null and b/public/images/warn/pic_404.jpg.bak differ diff --git a/public/images/warn/pic_500.jpg b/public/images/warn/pic_500.jpg index f46c1c370..7d7d4cb42 100644 Binary files a/public/images/warn/pic_500.jpg and b/public/images/warn/pic_500.jpg differ diff --git a/public/images/warn/pic_500.jpg.bak b/public/images/warn/pic_500.jpg.bak new file mode 100644 index 000000000..f46c1c370 Binary files /dev/null and b/public/images/warn/pic_500.jpg.bak differ diff --git a/public/images/warn/pic_bg.png b/public/images/warn/pic_bg.png new file mode 100644 index 000000000..91ceec7f2 Binary files /dev/null and b/public/images/warn/pic_bg.png differ diff --git a/public/message_template/issue_assigned.html b/public/message_template/issue_assigned.html new file mode 100755 index 000000000..c45e8729f --- /dev/null +++ b/public/message_template/issue_assigned.html @@ -0,0 +1,47 @@ + + + 有新的疑修指派给我 + + + + +
    +
    +
    + +

    确实开源,协同创新

    +
    +
    +
    +

    + {receiver},您好!
    + {nickname1}在 {nickname2}/{repository} 指派给你一个疑修:{title}
    +

    +
    +
    +
    +

    如果您在使用中有任何的疑问和建议,欢迎您给我们反馈意见
    + QQ群:1071514693

    +

    GitLink团队

    +
    +
    +
    +
    + + \ No newline at end of file diff --git a/public/message_template/issue_changed.html b/public/message_template/issue_changed.html new file mode 100755 index 000000000..537a12dc1 --- /dev/null +++ b/public/message_template/issue_changed.html @@ -0,0 +1,57 @@ + + + 有新的疑修指派给我 + + + + +
    +
    +
    + +

    确实开源,协同创新

    +
    +
    +
    +

    + {receiver},您好!
    + 在项目 {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} +

    +
    +
    +
    +

    如果您在使用中有任何的疑问和建议,欢迎您给我们反馈意见
    + QQ群:1071514693

    +

    GitLink团队

    +
    +
    +
    +
    + + \ No newline at end of file diff --git a/public/message_template/issue_deleted.html b/public/message_template/issue_deleted.html new file mode 100755 index 000000000..4207144c8 --- /dev/null +++ b/public/message_template/issue_deleted.html @@ -0,0 +1,47 @@ + + + 疑修状态改变 + + + + +
    +
    +
    + +

    确实开源,协同创新

    +
    +
    +
    +

    + {receiver},您好!
    + {nickname}已将疑修 {title} 删除 +

    +
    +
    +
    +

    如果您在使用中有任何的疑问和建议,欢迎您给我们反馈意见
    + QQ群:1071514693

    +

    GitLink团队

    +
    +
    +
    +
    + + \ No newline at end of file diff --git a/public/message_template/organization_joined.html b/public/message_template/organization_joined.html new file mode 100755 index 000000000..e3848f473 --- /dev/null +++ b/public/message_template/organization_joined.html @@ -0,0 +1,47 @@ + + + 加入组织 + + + + +
    +
    +
    + +

    确实开源,协同创新

    +
    +
    +
    +

    + {receiver},您好!
    + 你已加入 {organization} 组织
    +

    +
    +
    +
    +

    如果您在使用中有任何的疑问和建议,欢迎您给我们反馈意见
    + QQ群:1071514693

    +

    GitLink团队

    +
    +
    +
    +
    + + \ No newline at end of file diff --git a/public/message_template/organization_left.html b/public/message_template/organization_left.html new file mode 100755 index 000000000..32b1dc30e --- /dev/null +++ b/public/message_template/organization_left.html @@ -0,0 +1,47 @@ + + + 移出组织 + + + + +
    +
    +
    + +

    确实开源,协同创新

    +
    +
    +
    +

    + {receiver},您好!
    + 你已被移出 {organization} 组织
    +

    +
    +
    +
    +

    如果您在使用中有任何的疑问和建议,欢迎您给我们反馈意见
    + QQ群:1071514693

    +

    GitLink团队

    +
    +
    +
    +
    + + \ No newline at end of file diff --git a/public/message_template/organization_role.html b/public/message_template/organization_role.html new file mode 100755 index 000000000..b411c7340 --- /dev/null +++ b/public/message_template/organization_role.html @@ -0,0 +1,47 @@ + + + 组织权限被更改 + + + + +
    +
    +
    + +

    确实开源,协同创新

    +
    +
    +
    +

    + {receiver},您好!
    + 组织 {organization} 已把你的角色修改为 {role} +

    +
    +
    +
    +

    如果您在使用中有任何的疑问和建议,欢迎您给我们反馈意见
    + QQ群:1071514693

    +

    GitLink团队

    +
    +
    +
    +
    + + \ No newline at end of file diff --git a/public/message_template/project_issue.html b/public/message_template/project_issue.html new file mode 100755 index 000000000..653bd7a3d --- /dev/null +++ b/public/message_template/project_issue.html @@ -0,0 +1,47 @@ + + + 管理的仓库有新的疑修 + + + + +
    +
    +
    + +

    确实开源,协同创新

    +
    +
    +
    +

    + {receiver},您好!
    + {nickname1}在 {nickname2}/{repository} 新建了一个疑修:{title}
    +

    +
    +
    +
    +

    如果您在使用中有任何的疑问和建议,欢迎您给我们反馈意见
    + QQ群:1071514693

    +

    GitLink团队

    +
    +
    +
    +
    + + \ No newline at end of file diff --git a/public/message_template/project_joined.html b/public/message_template/project_joined.html new file mode 100755 index 000000000..dfafd5d92 --- /dev/null +++ b/public/message_template/project_joined.html @@ -0,0 +1,47 @@ + + + 加入项目 + + + + +
    +
    +
    + +

    确实开源,协同创新

    +
    +
    +
    +

    + {receiver},您好!
    + 你已加入 {nickname}/{repository} 项目
    +

    +
    +
    +
    +

    如果您在使用中有任何的疑问和建议,欢迎您给我们反馈意见
    + QQ群:1071514693

    +

    GitLink团队

    +
    +
    +
    +
    + + \ No newline at end of file diff --git a/public/message_template/project_left.html b/public/message_template/project_left.html new file mode 100755 index 000000000..94a0e9862 --- /dev/null +++ b/public/message_template/project_left.html @@ -0,0 +1,47 @@ + + + 移出项目 + + + + +
    +
    +
    + +

    确实开源,协同创新

    +
    +
    +
    +

    + {receiver},您好!
    + 你已被移出 {nickname}/{repository} 项目
    +

    +
    +
    +
    +

    如果您在使用中有任何的疑问和建议,欢迎您给我们反馈意见
    + QQ群:1071514693

    +

    GitLink团队

    +
    +
    +
    +
    + + \ No newline at end of file diff --git a/public/message_template/project_member_joined.html b/public/message_template/project_member_joined.html new file mode 100755 index 000000000..d2fec456d --- /dev/null +++ b/public/message_template/project_member_joined.html @@ -0,0 +1,47 @@ + + + 管理的仓库有成员变更 + + + + +
    +
    +
    + +

    确实开源,协同创新

    +
    +
    +
    +

    + {receiver},您好!
    + {nickname1} 已加入项目 {nickname2}/{repository}
    +

    +
    +
    +
    +

    如果您在使用中有任何的疑问和建议,欢迎您给我们反馈意见
    + QQ群:1071514693

    +

    GitLink团队

    +
    +
    +
    +
    + + \ No newline at end of file diff --git a/public/message_template/project_member_left.html b/public/message_template/project_member_left.html new file mode 100755 index 000000000..8dfca4a2e --- /dev/null +++ b/public/message_template/project_member_left.html @@ -0,0 +1,47 @@ + + + 管理的仓库有成员变更 + + + + +
    +
    +
    + +

    确实开源,协同创新

    +
    +
    +
    +

    + {receiver},您好!
    + {nickname1} 已被移出项目 {nickname2}/{repository}
    +

    +
    +
    +
    +

    如果您在使用中有任何的疑问和建议,欢迎您给我们反馈意见
    + QQ群:1071514693

    +

    GitLink团队

    +
    +
    +
    +
    + + \ No newline at end of file diff --git a/public/message_template/project_pull_request.html b/public/message_template/project_pull_request.html new file mode 100755 index 000000000..78aa5c2d4 --- /dev/null +++ b/public/message_template/project_pull_request.html @@ -0,0 +1,47 @@ + + + 管理的仓库有新的合并请求 + + + + +
    +
    +
    + +

    确实开源,协同创新

    +
    +
    +
    +

    + {receiver},您好!
    + {nickname1}在 {nickname2}/{repository} 提交了一个合并请求:{title}
    +

    +
    +
    +
    +

    如果您在使用中有任何的疑问和建议,欢迎您给我们反馈意见
    + QQ群:1071514693

    +

    GitLink团队

    +
    +
    +
    +
    + + \ No newline at end of file diff --git a/public/message_template/project_role.html b/public/message_template/project_role.html new file mode 100755 index 000000000..17f53e37b --- /dev/null +++ b/public/message_template/project_role.html @@ -0,0 +1,47 @@ + + + 移出项目 + + + + +
    +
    +
    + +

    确实开源,协同创新

    +
    +
    +
    +

    + {receiver},您好!
    + 项目 {nickname}/{repository} 已把你的角色修改为 {role} +

    +
    +
    +
    +

    如果您在使用中有任何的疑问和建议,欢迎您给我们反馈意见
    + QQ群:1071514693

    +

    GitLink团队

    +
    +
    +
    +
    + + \ No newline at end of file diff --git a/public/message_template/project_setting_changed.html b/public/message_template/project_setting_changed.html new file mode 100755 index 000000000..4d1f9762d --- /dev/null +++ b/public/message_template/project_setting_changed.html @@ -0,0 +1,54 @@ + + + 管理的仓库设置被更改 + + + + +
    +
    +
    + +

    确实开源,协同创新

    +
    +
    +
    +

    + {receiver},您好!
    + {nickname1} 更改 {nickname2}/{repository} 的仓库设置: + {ifname}更改项目名称为"{name}"{endname} + {ifidentifier}更改项目标识为"{identifier}"{endidentifier} + {ifdescription}更改项目简介为"{description}"{enddescription} + {ifcategory}更改项目类别为"{category}"{endcategory} + {iflanguage}更改项目语言为"{language}"{endlanguage} + {ifpermission}将仓库设为"{permission}"{endpermission} + {ifnavbar}将项目导航更改为"{navbar}"{endnavbar} +

    +
    +
    +
    +

    如果您在使用中有任何的疑问和建议,欢迎您给我们反馈意见
    + QQ群:1071514693

    +

    GitLink团队

    +
    +
    +
    +
    + + \ No newline at end of file diff --git a/public/message_template/pull_request_assigned.html b/public/message_template/pull_request_assigned.html new file mode 100755 index 000000000..c12924d1b --- /dev/null +++ b/public/message_template/pull_request_assigned.html @@ -0,0 +1,47 @@ + + + 有新的合并请求指派给我 + + + + +
    +
    +
    + +

    确实开源,协同创新

    +
    +
    +
    +

    + {receiver},您好!
    + {nickname1}在 {nickname2}/{repository} 指派给你一个合并请求:{title}
    +

    +
    +
    +
    +

    如果您在使用中有任何的疑问和建议,欢迎您给我们反馈意见
    + QQ群:1071514693

    +

    GitLink团队

    +
    +
    +
    +
    + + \ No newline at end of file diff --git a/public/message_template/pull_request_changed.html b/public/message_template/pull_request_changed.html new file mode 100755 index 000000000..ab9c3f6a1 --- /dev/null +++ b/public/message_template/pull_request_changed.html @@ -0,0 +1,51 @@ + + + 合并请求被更改 + + + + +
    +
    +
    + +

    确实开源,协同创新

    +
    +
    +
    +

    + {receiver},您好!
    + 在项目 {nickname2}/{repository} 的合并请求 {title} 中: + {ifassigner}{nickname1}将负责人从 {assigner1} 修改为 {assigner2}{endassigner} + {ifmilestone}{nickname1}将里程碑从 {milestone1} 修改为 {milestone2}{endmilestone} + {iftag}{nickname1}将标记从 {tag1} 修改为 {tag2}{endtag} + {ifpriority}{nickname1}将优先级从 {priority1} 修改为 {priority2}{endpriority} +

    +
    +
    +
    +

    如果您在使用中有任何的疑问和建议,欢迎您给我们反馈意见
    + QQ群:1071514693

    +

    GitLink团队

    +
    +
    +
    +
    + + \ No newline at end of file diff --git a/public/message_template/pull_request_closed.html b/public/message_template/pull_request_closed.html new file mode 100755 index 000000000..dca5e1c7d --- /dev/null +++ b/public/message_template/pull_request_closed.html @@ -0,0 +1,47 @@ + + + 合并请求被关闭 + + + + +
    +
    +
    + +

    确实开源,协同创新

    +
    +
    +
    +

    + {receiver},您好!
    + 你提交的合并请求:{title} 被拒绝; +

    +
    +
    +
    +

    如果您在使用中有任何的疑问和建议,欢迎您给我们反馈意见
    + QQ群:1071514693

    +

    GitLink团队

    +
    +
    +
    +
    + + \ No newline at end of file diff --git a/public/message_template/pull_request_merged.html b/public/message_template/pull_request_merged.html new file mode 100755 index 000000000..8c6e3469c --- /dev/null +++ b/public/message_template/pull_request_merged.html @@ -0,0 +1,47 @@ + + + 合并请求被合并 + + + + +
    +
    +
    + +

    确实开源,协同创新

    +
    +
    +
    +

    + {receiver},您好!
    + 你提交的合并请求:{title} 已通过; +

    +
    +
    +
    +

    如果您在使用中有任何的疑问和建议,欢迎您给我们反馈意见
    + QQ群:1071514693

    +

    GitLink团队

    +
    +
    +
    +
    + + \ No newline at end of file diff --git a/public/message_template/team_joined.html b/public/message_template/team_joined.html new file mode 100755 index 000000000..231f8b795 --- /dev/null +++ b/public/message_template/team_joined.html @@ -0,0 +1,47 @@ + + + 被拉入组织团队 + + + + +
    +
    +
    + +

    确实开源,协同创新

    +
    +
    +
    +

    + {receiver},您好!
    + 你已被拉入组织 {organization}{team} 团队,拥有{role}权限 +

    +
    +
    +
    +

    如果您在使用中有任何的疑问和建议,欢迎您给我们反馈意见
    + QQ群:1071514693

    +

    GitLink团队

    +
    +
    +
    +
    + + \ No newline at end of file diff --git a/public/message_template/team_left.html b/public/message_template/team_left.html new file mode 100755 index 000000000..1321e7b4d --- /dev/null +++ b/public/message_template/team_left.html @@ -0,0 +1,47 @@ + + + 被移出组织团队 + + + + +
    +
    +
    + +

    确实开源,协同创新

    +
    +
    +
    +

    + {receiver},您好!
    + 你已被移出组织 {organization}{team} 团队 +

    +
    +
    +
    +

    如果您在使用中有任何的疑问和建议,欢迎您给我们反馈意见
    + QQ群:1071514693

    +

    GitLink团队

    +
    +
    +
    +
    + + \ No newline at end of file diff --git a/public/mulan_repo.xlsx b/public/mulan_repo.xlsx new file mode 100644 index 000000000..1c9e45851 Binary files /dev/null and b/public/mulan_repo.xlsx differ diff --git a/spec/controllers/admins/edu_settings_controller_spec.rb b/spec/controllers/admins/edu_settings_controller_spec.rb new file mode 100644 index 000000000..ae5164879 --- /dev/null +++ b/spec/controllers/admins/edu_settings_controller_spec.rb @@ -0,0 +1,47 @@ +require 'rails_helper' + +RSpec.describe Admins::EduSettingsController, type: :controller do + + describe "GET #index" do + it "returns http success" do + get :index + expect(response).to have_http_status(:success) + end + end + + describe "GET #new" do + it "returns http success" do + get :new + expect(response).to have_http_status(:success) + end + end + + describe "GET #update" do + it "returns http success" do + get :update + expect(response).to have_http_status(:success) + end + end + + describe "GET #edit" do + it "returns http success" do + get :edit + expect(response).to have_http_status(:success) + end + end + + describe "GET #create" do + it "returns http success" do + get :create + expect(response).to have_http_status(:success) + end + end + + describe "GET #destroy" do + it "returns http success" do + get :destroy + expect(response).to have_http_status(:success) + end + end + +end diff --git a/spec/controllers/admins/sites_controller_spec.rb b/spec/controllers/admins/sites_controller_spec.rb new file mode 100644 index 000000000..0c5f79b3d --- /dev/null +++ b/spec/controllers/admins/sites_controller_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe Admins::SitesController, type: :controller do + +end diff --git a/spec/models/message_template_spec.rb b/spec/models/message_template_spec.rb new file mode 100644 index 000000000..8f1d26fcd --- /dev/null +++ b/spec/models/message_template_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe MessageTemplate, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/pinned_project_spec.rb b/spec/models/pinned_project_spec.rb new file mode 100644 index 000000000..cda43562e --- /dev/null +++ b/spec/models/pinned_project_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe PinnedProject, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/project_unit_spec.rb b/spec/models/project_unit_spec.rb new file mode 100644 index 000000000..d95607ca6 --- /dev/null +++ b/spec/models/project_unit_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe ProjectUnit, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/reversed_keyword_spec.rb b/spec/models/reversed_keyword_spec.rb new file mode 100644 index 000000000..6b71faeb9 --- /dev/null +++ b/spec/models/reversed_keyword_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe ReversedKeyword, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/views/admins/edu_settings/create.html.erb_spec.rb b/spec/views/admins/edu_settings/create.html.erb_spec.rb new file mode 100644 index 000000000..bc53b9f18 --- /dev/null +++ b/spec/views/admins/edu_settings/create.html.erb_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe "edu_settings/create.html.erb", type: :view do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/views/admins/edu_settings/destroy.html.erb_spec.rb b/spec/views/admins/edu_settings/destroy.html.erb_spec.rb new file mode 100644 index 000000000..51acd4901 --- /dev/null +++ b/spec/views/admins/edu_settings/destroy.html.erb_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe "edu_settings/destroy.html.erb", type: :view do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/views/admins/edu_settings/edit.html.erb_spec.rb b/spec/views/admins/edu_settings/edit.html.erb_spec.rb new file mode 100644 index 000000000..e381cbb04 --- /dev/null +++ b/spec/views/admins/edu_settings/edit.html.erb_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe "edu_settings/edit.html.erb", type: :view do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/views/admins/edu_settings/index.html.erb_spec.rb b/spec/views/admins/edu_settings/index.html.erb_spec.rb new file mode 100644 index 000000000..ba6e16ebc --- /dev/null +++ b/spec/views/admins/edu_settings/index.html.erb_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe "edu_settings/index.html.erb", type: :view do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/views/admins/edu_settings/new.html.erb_spec.rb b/spec/views/admins/edu_settings/new.html.erb_spec.rb new file mode 100644 index 000000000..00bc830cd --- /dev/null +++ b/spec/views/admins/edu_settings/new.html.erb_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe "edu_settings/new.html.erb", type: :view do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/views/admins/edu_settings/update.html.erb_spec.rb b/spec/views/admins/edu_settings/update.html.erb_spec.rb new file mode 100644 index 000000000..89ae0feb8 --- /dev/null +++ b/spec/views/admins/edu_settings/update.html.erb_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe "edu_settings/update.html.erb", type: :view do + pending "add some examples to (or delete) #{__FILE__}" +end