package models import ( "strconv" "strings" "time" "code.gitea.io/gitea/modules/timeutil" "xorm.io/builder" ) const ( TechShow = 1 TechHide = 2 TechMigrating = 3 TechMigrateFailed = 4 TechNotExist = 5 ) const DefaultTechApprovedStatus = TechShow type TechConvergeBaseInfo struct { ID int64 `xorm:"pk autoincr"` ProjectNumber string `xorm:"UNIQUE NOT NULL"` //项目立项编号 ProjectName string //科技项目名称 Institution string //项目承担单位 ApplyYear int //申报年度 Province string //所属省(省市) Category string //单位性质 Recommend string //推荐单位 Owner string //项目负责人 Phone string //负责人电话 Email string //负责人邮箱 Contact string //项目联系人 ContactPhone string //联系人电话 ContactEmail string //联系人邮箱 ExecuteMonth int //执行周期(月) ExecuteStartYear int //执行开始年份 ExecuteEndYear int //执行结束年份 ExecutePeriod string //执行期限 Type string //项目类型 StartUp string //启动会时间 NumberTopic int Topic1 string Topic2 string Topic3 string Topic4 string Topic5 string Topic6 string Topic7 string AllInstitution string `xorm:"TEXT"` CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` } func (t *TechConvergeBaseInfo) Brief() *TechConvergeBrief { return &TechConvergeBrief{ ProjectNumber: t.ProjectNumber, ProjectName: t.ProjectName, Institution: t.Institution, AllInstitution: t.AllInstitution, } } func (t *TechConvergeBaseInfo) IsValidInstitution(institution string) bool { if t.AllInstitution == "" && t.Institution == "" { return false } allInstitution := make([]string, 0) if t.AllInstitution != "" { allInstitution = strings.Split(t.AllInstitution, ",") } if t.Institution != "" { allInstitution = append(allInstitution, t.Institution) } newInstitution := strings.Split(institution, ",") total := len(newInstitution) matched := 0 for _, n := range newInstitution { for _, s := range allInstitution { if s == n { matched++ break } } } if matched == total { return true } return false } type RepoConvergeInfo struct { ID int64 `xorm:"pk autoincr"` RepoID int64 Url string BaseInfoID int64 Institution string UID int64 Status int CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` User *User `xorm:"-"` Repo *Repository `xorm:"-"` BaseInfo *TechConvergeBaseInfo `xorm:"-"` } func (r *RepoConvergeInfo) InsertOrUpdate() error { if r.ID != 0 { _, err := x.ID(r.ID).Update(r) return err } else { _, err := x.InsertOne(r) return err } } func GetTechConvergeBaseInfoByProjectNumber(projectNumber string) (*TechConvergeBaseInfo, error) { tb := &TechConvergeBaseInfo{ProjectNumber: projectNumber} return getTechConvergeBaseInfo(tb) } func GetTechConvergeBaseInfoById(id int64) (*TechConvergeBaseInfo, error) { tb := &TechConvergeBaseInfo{ID: id} return getTechConvergeBaseInfo(tb) } func (baseInfo *TechConvergeBaseInfo) InsertOrUpdate() error { if baseInfo.ID != 0 { _, err := x.ID(baseInfo.ID).Update(baseInfo) return err } else { _, err := x.InsertOne(baseInfo) return err } } type ErrTechConvergeBaseInfoNotExist struct { ID string } func (err ErrTechConvergeBaseInfoNotExist) Error() string { return "tech.tech_not_exist" } func IsErrTechConvergeBaseInfoNotExist(err error) bool { _, ok := err.(ErrTechConvergeBaseInfoNotExist) return ok } func getTechConvergeBaseInfo(tb *TechConvergeBaseInfo) (*TechConvergeBaseInfo, error) { has, err := x.Get(tb) if err != nil { return nil, err } else if !has { if tb.ProjectNumber != "" { return nil, ErrTechConvergeBaseInfoNotExist{tb.ProjectNumber} } else { return nil, ErrTechConvergeBaseInfoNotExist{strconv.FormatInt(tb.ID, 10)} } } return tb, nil } func GetProjectNames() []string { var names []string x.Table("tech_converge_base_info").Distinct("project_name").Find(&names) return names } func GetIdByProjectName(name string) []string { var ids []int64 x.Table("tech_converge_base_info").Cols("id").Where("project_name=?", name).Find(&ids) idStrs := make([]string, 0, len(ids)) for _, id := range ids { idStrs = append(idStrs, strconv.FormatInt(id, 10)) } return idStrs } func GetSummitRepoIds() []int64 { var ids []int64 x.Table("repo_converge_info").Cols("repo_id").Find(&ids) return ids } func GetTechRepoTopics(limit int) []string { repoIds := GetSummitRepoIds() if len(repoIds) == 0 { return []string{} } //select name, repo_count from topic a, repo_topic b //where a.id=b.topic_id and repo_id in (1,3) order by repo_count desc inCondition := "repo_id in (" const MaxINItems = 1000 for i := 0; i < len(repoIds); i++ { if i == len(repoIds)-1 { inCondition += strconv.FormatInt(repoIds[i], 10) } else if (i+1)%MaxINItems == 0 { inCondition += strconv.FormatInt(repoIds[i], 10) + ") or repo_id in (" } else { inCondition += strconv.FormatInt(repoIds[i], 10) + "," } } inCondition += ")" sql := "select name, repo_count from topic a, repo_topic b where a.id=b.topic_id and (" + inCondition + ") order by repo_count desc" if limit > 0 { sql += "limit " + strconv.Itoa(limit) } result, err := x.QueryString(sql) if err != nil { return []string{} } var topics []string for _, record := range result { topics = append(topics, record["name"]) } return topics } func GetProjectTypes() []string { sql := "SELECT COUNT(id) AS theCount, type from tech_converge_base_info GROUP BY type ORDER BY theCount DESC" result, err := x.QueryString(sql) if err != nil { return []string{} } var projectTypes []string for _, record := range result { projectTypes = append(projectTypes, record["type"]) } return projectTypes } func GetApplyExecuteYears() ([]int, []int) { apply, executeStart, executeEnd := GetYearInfos() applyEnd := time.Now().Year() var applyArray []int var executeArray []int for i := apply; i <= applyEnd; i++ { applyArray = append(applyArray, i) } for i := executeStart; i <= executeEnd; i++ { executeArray = append(executeArray, i) } return applyArray, executeArray } func GetYearInfos() (int, int, int) { sql := "select min(apply_year) as apply_year,min(CASE WHEN execute_start_year != 0 THEN execute_start_year END) as execute_start_year,max(execute_end_year) as execute_end_year from tech_converge_base_info" result, err := x.QueryString(sql) if err != nil { return 2018, 2019, 2024 } for _, record := range result { apply, _ := strconv.Atoi(record["apply_year"]) executeStart, _ := strconv.Atoi(record["execute_start_year"]) executeEnd, _ := strconv.Atoi(record["execute_end_year"]) return apply, executeStart, executeEnd } return 2018, 2019, 2024 } func GetAllInstitutions() []string { var names []string x.Table("tech_converge_base_info").Cols("all_institution").Find(&names) var allNames []string for _, name := range names { singleNames := strings.Split(name, ",") for _, singleName := range singleNames { if singleName != "" { if !contains(allNames, singleName) { allNames = append(allNames, singleName) } } } } return allNames } func contains(s []string, e string) bool { for _, a := range s { if a == e { return true } } return false } type SearchTechOpt struct { Q string //科技项目名称 ProjectType string Institution string ApplyYear int ExecuteYear int OrderBy string ListOptions } type SearchRepoOpt struct { Q string //项目名称,简介 ProjectName string Topic string Institution string OrderBy string ListOptions } type SearchUserRepoOpt struct { User *User ListOptions } type TechRepoInfoUser struct { ID int64 `json:"id"` ProjectNumber string `json:"no"` ProjectName string `json:"name"` Institution string `json:"institution"` AllInstitution string `json:"all_institution"` Url string `json:"url"` RepoName string `json:"repo_name"` RepoOwnerName string `json:"repo_owner_name"` ContributionInstitution string `json:"contribution_institution"` UserName string `json:"user_name"` Status int `json:"status"` CreatedUnix timeutil.TimeStamp `json:"created_unix"` UpdatedUnix timeutil.TimeStamp `json:"updated_unix"` } type TechRepoInfoAdmin struct { ID int64 `json:"id"` Url string `json:"url"` ContributionInstitution string `json:"contribution_institution"` Status int `json:"status"` ProjectNumber string `json:"no"` ProjectName string `json:"name"` Institution string `json:"institution"` Province string `json:"province"` Category string `json:"category"` Recommend string `json:"recommend"` Owner string `json:"owner"` Phone string `json:"phone"` Email string `json:"email"` Contact string `json:"contact"` ContactPhone string `json:"contact_phone"` ContactEmail string `json:"contact_email"` ExecuteMonth int `json:"execute_month"` ExecutePeriod string `json:"execute_period"` Type string `json:"type"` StartUp string `json:"start_up"` NumberTopic int `json:"number_topic"` Topic1 string `json:"topic1"` Topic2 string `json:"topic2"` Topic3 string `json:"topic3"` Topic4 string `json:"topic4"` Topic5 string `json:"topic5"` AllInstitution string `json:"all_institution"` RepoName string `json:"repo_name"` RepoOwnerName string `json:"repo_owner_name"` UserName string `json:"user_name"` CreatedUnix timeutil.TimeStamp `json:"created_unix"` UpdatedUnix timeutil.TimeStamp `json:"updated_unix"` } type RepoWithInstitution struct { ID int64 `json:"id"` OwnerID int64 `json:"owner_id"` OwnerName string `json:"owner_name"` Name string `json:"name"` Alias string `json:"alias"` Topics []string `json:"topics"` Description string `json:"description"` Institution string `json:"institution"` RelAvatarLink string `json:"rel_avatar_link"` UpdatedUnix timeutil.TimeStamp `json:"updated_unix"` } type TechRepoInfo struct { ID int64 `json:"id"` ProjectName string `json:"project_name"` Institution string `json:"institution"` Type string `json:"type"` ApplyYear int `json:"apply_year"` ExecutePeriod string `json:"execute_period"` AllInstitution string `json:"all_institution"` RepoCount int `json:"repo_numer"` Repos []*RepoWithInstitution } func ShowTechRepo(ids []int64, show bool) error { status := TechShow if !show { status = TechHide } idStrs := make([]string, 0, len(ids)) for _, id := range ids { idStrs = append(idStrs, strconv.FormatInt(id, 10)) } sql := "update repo_converge_info set status=? where id in (" + strings.Join(idStrs, ",") + ") and (status=" + strconv.Itoa(TechShow) + " or status=" + strconv.Itoa(TechHide) + ")" _, err := x.Exec(sql, status) return err } func GetTechRepoInfoForUser(opts *SearchUserRepoOpt) ([]*RepoConvergeInfo, int64, error) { cond := buildTechRepoForUserCondition(opts) total, err := x.Where(cond).Count(new(RepoConvergeInfo)) if err != nil { return nil, 0, err } repoConvergeInfos := make([]*RepoConvergeInfo, 0) err = x.Where(cond).Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).Desc("status").Find(&repoConvergeInfos) if err != nil { return nil, 0, err } loadAttributes(repoConvergeInfos) return repoConvergeInfos, total, nil } func loadAttributes(infos []*RepoConvergeInfo) { for _, info := range infos { info.User, _ = GetUserByID(info.UID) info.Repo, _ = GetRepositoryByID(info.RepoID) info.BaseInfo, _ = GetTechConvergeBaseInfoById(info.BaseInfoID) } } func buildTechRepoForUserCondition(opts *SearchUserRepoOpt) builder.Cond { var cond = builder.NewCond() if opts.User != nil { cond = cond.And(builder.Eq{"uid": opts.User.ID}) } return cond } func GetAvailableRepoConvergeInfo(opt *SearchRepoOpt) ([]*RepoConvergeInfo, error) { repos := make([]*RepoConvergeInfo, 0) err := x.Table("repo_converge_info").Where(buildRepoFilterCond(opt)).Find(&repos) return repos, err } func buildRepoFilterCond(opt *SearchRepoOpt) string { sql := "status=" + strconv.Itoa(TechShow) if opt.Institution != "" { sql += " and (institution like '%" + opt.Institution + ",%'" + " or institution like '%," + opt.Institution + "%'" + " or institution = '" + opt.Institution + "')" } if opt.ProjectName != "" { baseInfoIds := GetIdByProjectName(opt.ProjectName) if len(baseInfoIds) > 0 { sql += " and base_info_id in (" + strings.Join(baseInfoIds, ",") + ")" } } return sql } func SearchTechRepoInfo(opt *SearchTechOpt) ([]*TechRepoInfo, int64, error) { sql := `select a.*,COALESCE(b.count,0) as repo_count, COALESCE(b.max,0) as max from tech_converge_base_info a left join (select base_info_id,count(id),max(updated_unix) from repo_converge_info where status=` + strconv.Itoa(TechShow) + ` GROUP BY base_info_id ) b on a.id=b.base_info_id` totalSql := "select count(*) from (" + sql + ") c" + buildTechFilterCond(opt) total, err := x.SQL(totalSql).Count(new(TechConvergeBaseInfo)) resultList := make([]*TechRepoInfo, 0) if err != nil { return resultList, total, err } resultSql := "select id,project_name, institution,type,apply_year,execute_period,all_institution,repo_count from (" + sql + ") c " + buildTechFilterCond(opt) + opt.OrderBy + " offset " + strconv.Itoa((opt.Page-1)*opt.PageSize) + " limit " + strconv.Itoa(opt.PageSize) resultMap, err := x.QueryInterface(resultSql) if err == nil { for _, record := range resultMap { resultList = append(resultList, &TechRepoInfo{ ID: record["id"].(int64), ProjectName: record["project_name"].(string), Institution: record["institution"].(string), Type: record["type"].(string), ApplyYear: int(record["apply_year"].(int64)), ExecutePeriod: record["execute_period"].(string), AllInstitution: record["all_institution"].(string), RepoCount: int(record["repo_count"].(int64)), }) } } loadRepoInfoForTech(resultList) return resultList, total, err } func buildTechFilterCond(opt *SearchTechOpt) string { sql := "" if opt.Q != "" { sql += getWherePrefix(sql) + " project_name like '%" + opt.Q + "%'" } if opt.ProjectType != "" { sql += getWherePrefix(sql) + " type ='" + opt.ProjectType + "'" } if opt.ApplyYear != 0 { sql += getWherePrefix(sql) + " apply_year =" + strconv.Itoa(opt.ApplyYear) } if opt.Institution != "" { sql += getWherePrefix(sql) + " (all_institution like '%" + opt.Institution + ",%'" + " or all_institution like '%," + opt.Institution + "%'" + " or all_institution = '" + opt.Institution + "')" } if opt.ExecuteYear != 0 { sql += getWherePrefix(sql) + " execute_start_year <=" + strconv.Itoa(opt.ExecuteYear) + " and execute_end_year >=" + strconv.Itoa(opt.ExecuteYear) } return sql } func getWherePrefix(sql string) string { if sql == "" { return " where " } return " and " } func loadRepoInfoForTech(list []*TechRepoInfo) { for _, techRepo := range list { techRepo.Repos = []*RepoWithInstitution{} if techRepo.RepoCount > 0 { var repoIds []int64 x.Table("repo_converge_info").Cols("repo_id").Where("base_info_id=? and status=?", techRepo.ID, TechShow).Limit(2).Desc("updated_unix").Find(&repoIds) resultMap, err := GetRepositoriesMapByIDs(repoIds) if err == nil { for _, repoId := range repoIds { repo, ok := resultMap[repoId] if ok { techRepo.Repos = append(techRepo.Repos, &RepoWithInstitution{ ID: repo.ID, Institution: techRepo.Institution, OwnerID: repo.OwnerID, OwnerName: repo.OwnerName, Name: repo.Name, Alias: repo.Alias, Topics: repo.Topics, Description: repo.Description, RelAvatarLink: repo.RelAvatarLink(), UpdatedUnix: repo.UpdatedUnix, }) } } } } } } type TechConvergeBrief struct { ProjectNumber string `json:"no"` //项目立项编号 ProjectName string `json:"name"` //科技项目名称 Institution string `json:"institution"` //项目承担单位 AllInstitution string `json:"all_institution"` } type FindTechOpt struct { TechNo string ProjectName string Institution string } func FindTech(opt FindTechOpt) ([]*TechConvergeBaseInfo, error) { var cond = builder.NewCond() if opt.TechNo != "" { cond = cond.And(builder.Like{"project_number", opt.TechNo}) } if opt.ProjectName != "" { cond = cond.And(builder.Like{"project_name", opt.ProjectName}) } if opt.Institution != "" { cond = cond.And(builder.Like{"institution", opt.Institution}.Or(builder.Like{"all_institution", opt.Institution})) } r := make([]*TechConvergeBaseInfo, 0) err := x.Where(cond).OrderBy("updated_unix desc").Find(&r) if err != nil { return nil, err } return r, nil } func GetTechByTechNo(techNo string) (*TechConvergeBaseInfo, error) { var tech = &TechConvergeBaseInfo{} has, err := x.Where("project_number = ?", techNo).Get(tech) if err != nil { return nil, err } else if !has { return nil, ErrTechConvergeBaseInfoNotExist{} } return tech, nil } type GetRepoConvergeOpts struct { RepoId int64 BaseInfoId int64 Status []int } func GetRepoConverge(opts GetRepoConvergeOpts) ([]*RepoConvergeInfo, error) { r := make([]*RepoConvergeInfo, 0) cond := builder.NewCond() if opts.RepoId > 0 { cond = cond.And(builder.Eq{"repo_id": opts.RepoId}) } if opts.BaseInfoId > 0 { cond = cond.And(builder.Eq{"base_info_id": opts.BaseInfoId}) } if len(opts.Status) > 0 { cond = cond.And(builder.In("status", opts.Status)) } err := x.Where(cond).Find(&r) if err != nil { return nil, err } return r, nil } func UpdateRepoConvergeStatus(id int64, status int) (int64, error) { return x.ID(id).Update(&RepoConvergeInfo{ Status: status, }) }