You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

cloudbrain_image.go 14 kB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  1. package models
  2. import (
  3. "fmt"
  4. "strings"
  5. "unicode/utf8"
  6. "xorm.io/builder"
  7. "code.gitea.io/gitea/modules/timeutil"
  8. )
  9. const RECOMMOND_TYPE = 5
  10. const NORMAL_TYPE = 0
  11. const IMAGE_STATUS_COMMIT = 0
  12. const IMAGE_STATUS_SUCCESS = 1
  13. const IMAGE_STATUS_Failed = 2
  14. type Image struct {
  15. ID int64 `xorm:"pk autoincr" json:"id"`
  16. Type int `xorm:"INDEX NOT NULL" json:"type"` //0 normal 5官方推荐,中间值保留为后续扩展
  17. CloudbrainType int `xorm:"INDEX NOT NULL" json:"cloudbrainType"` //0 云脑一 1云脑二
  18. UID int64 `xorm:"INDEX NOT NULL" json:"uid"`
  19. IsPrivate bool `xorm:"INDEX NOT NULL" json:"isPrivate"`
  20. Tag string `xorm:"varchar(100) UNIQUE" json:"tag"`
  21. Description string `xorm:"varchar(765)" json:"description"`
  22. Topics []string `xorm:"TEXT JSON" json:"topics"`
  23. Place string `xorm:"varchar(300)" json:"place"`
  24. NumStars int `xorm:"NOT NULL DEFAULT 0" json:"numStars"`
  25. IsStar bool `xorm:"-" json:"isStar"`
  26. UserName string `xorm:"-" json:"userName"`
  27. RelAvatarLink string `xorm:"-" json:"relAvatarLink"`
  28. Status int `xorm:"NOT NULL DEFAULT 0" json:"status"` //0代表正在提交,1提交完成,2提交失败
  29. CreatedUnix timeutil.TimeStamp `xorm:"INDEX created" json:"createdUnix"`
  30. UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated" json:"updatedUnix"`
  31. }
  32. type ImageList []*Image
  33. type ImageStar struct {
  34. ID int64 `xorm:"pk autoincr"`
  35. UID int64 `xorm:"UNIQUE(s)"`
  36. ImageID int64 `xorm:"UNIQUE(s)"`
  37. CreatedUnix timeutil.TimeStamp `xorm:"created"`
  38. }
  39. type ImageTopic struct {
  40. ID int64
  41. Name string `xorm:"UNIQUE VARCHAR(105)"`
  42. ImageCount int
  43. CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
  44. UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
  45. }
  46. type ImageTopicRelation struct {
  47. ImageID int64 `xorm:"UNIQUE(s)"`
  48. TopicID int64 `xorm:"UNIQUE(s)"`
  49. }
  50. type SearchImageOptions struct {
  51. Keyword string
  52. UID int64
  53. IncludePublicOnly bool
  54. IncludeOfficialOnly bool
  55. IncludePrivateOnly bool
  56. IncludeStarByMe bool
  57. IncludeCustom bool
  58. IncludeOwnerOnly bool
  59. Topics string
  60. ListOptions
  61. SearchOrderBy
  62. }
  63. type ErrorImageTagExist struct {
  64. Tag string
  65. }
  66. type ErrorImageCommitting struct {
  67. Tag string
  68. }
  69. type ImagesPageResult struct {
  70. Count int64 `json:"count"`
  71. Images []*Image `json:"images"`
  72. }
  73. func (err ErrorImageTagExist) Error() string {
  74. return fmt.Sprintf("Image already exists [tag: %s]", err.Tag)
  75. }
  76. func (err ErrorImageCommitting) Error() string {
  77. return fmt.Sprintf("Image already exists [tag: %s]", err.Tag)
  78. }
  79. type ErrImageNotExist struct {
  80. ID int64
  81. Tag string
  82. }
  83. func (err ErrImageNotExist) Error() string {
  84. return fmt.Sprintf("Image does not exist [id: %d] [tag: %s]", err.ID, err.Tag)
  85. }
  86. func IsErrorImageCommitting(err error) bool {
  87. _, ok := err.(ErrorImageCommitting)
  88. return ok
  89. }
  90. func IsErrImageNotExist(err error) bool {
  91. _, ok := err.(ErrImageNotExist)
  92. return ok
  93. }
  94. func IsErrImageTagExist(err error) bool {
  95. _, ok := err.(ErrorImageTagExist)
  96. return ok
  97. }
  98. func IsImageExist(tag string) (bool, error) {
  99. return x.Exist(&Image{
  100. Tag: tag,
  101. })
  102. }
  103. func IsImageExistByUser(tag string, uid int64) (bool, error) {
  104. return x.Exist(&Image{
  105. Tag: tag,
  106. UID: uid,
  107. Status: IMAGE_STATUS_SUCCESS,
  108. })
  109. }
  110. type FindImageTopicOptions struct {
  111. ListOptions
  112. ImageID int64
  113. Keyword string
  114. }
  115. func (opts *FindImageTopicOptions) toConds() builder.Cond {
  116. var cond = builder.NewCond()
  117. if opts.ImageID > 0 {
  118. cond = cond.And(builder.Eq{"image_topic_relation.image_id": opts.ImageID})
  119. }
  120. if opts.Keyword != "" {
  121. cond = cond.And(builder.Like{"image_topic.name", strings.ToLower(opts.Keyword)})
  122. }
  123. return cond
  124. }
  125. func GetImageByID(id int64) (*Image, error) {
  126. rel := new(Image)
  127. has, err := x.
  128. ID(id).
  129. Get(rel)
  130. if err != nil {
  131. return nil, err
  132. } else if !has {
  133. return nil, ErrImageNotExist{ID: id}
  134. }
  135. return rel, nil
  136. }
  137. func GetImageByTag(tag string) (*Image, error) {
  138. image := &Image{Tag: tag}
  139. has, err := x.
  140. Get(image)
  141. if err != nil {
  142. return nil, err
  143. } else if !has {
  144. return nil, ErrImageNotExist{Tag: tag}
  145. }
  146. return image, nil
  147. }
  148. func SanitizeAndValidateImageTopics(topics []string) (validTopics []string, invalidTopics []string) {
  149. validTopics = make([]string, 0)
  150. mValidTopics := make(map[string]struct{})
  151. invalidTopics = make([]string, 0)
  152. for _, topic := range topics {
  153. topic = strings.TrimSpace(strings.ToLower(topic))
  154. // ignore empty string
  155. if len(topic) == 0 {
  156. continue
  157. }
  158. // ignore same topic twice
  159. if _, ok := mValidTopics[topic]; ok {
  160. continue
  161. }
  162. if utf8.RuneCountInString(topic) <= 35 {
  163. validTopics = append(validTopics, topic)
  164. mValidTopics[topic] = struct{}{}
  165. } else {
  166. invalidTopics = append(invalidTopics, topic)
  167. }
  168. }
  169. return validTopics, invalidTopics
  170. }
  171. func FindImageTopics(opts *FindImageTopicOptions) (topics []*ImageTopic, err error) {
  172. sess := x.Select("image_topic.*").Where(opts.toConds())
  173. if opts.ImageID > 0 {
  174. sess.Join("INNER", "image_topic_relation", "image_topic_relation.topic_id = image_topic.id")
  175. }
  176. if opts.PageSize != 0 && opts.Page != 0 {
  177. sess = opts.setSessionPagination(sess)
  178. }
  179. return topics, sess.Desc("image_topic.image_count").Find(&topics)
  180. }
  181. func SaveImageTopics(imageID int64, topicNames ...string) error {
  182. topics, err := FindImageTopics(&FindImageTopicOptions{
  183. ImageID: imageID,
  184. })
  185. if err != nil {
  186. return err
  187. }
  188. sess := x.NewSession()
  189. defer sess.Close()
  190. if err := sess.Begin(); err != nil {
  191. return err
  192. }
  193. var addedTopicNames []string
  194. for _, topicName := range topicNames {
  195. if strings.TrimSpace(topicName) == "" {
  196. continue
  197. }
  198. var found bool
  199. for _, t := range topics {
  200. if strings.EqualFold(topicName, t.Name) {
  201. found = true
  202. break
  203. }
  204. }
  205. if !found {
  206. addedTopicNames = append(addedTopicNames, topicName)
  207. }
  208. }
  209. var removeTopics []*ImageTopic
  210. for _, t := range topics {
  211. var found bool
  212. for _, topicName := range topicNames {
  213. if strings.EqualFold(topicName, t.Name) {
  214. found = true
  215. break
  216. }
  217. }
  218. if !found {
  219. removeTopics = append(removeTopics, t)
  220. }
  221. }
  222. for _, topicName := range addedTopicNames {
  223. _, err := addTopicByNameToImage(sess, imageID, topicName)
  224. if err != nil {
  225. return err
  226. }
  227. }
  228. for _, topic := range removeTopics {
  229. err := removeTopicFromImage(sess, imageID, topic)
  230. if err != nil {
  231. return err
  232. }
  233. }
  234. topicNames = make([]string, 0, 25)
  235. if err := sess.Table("image_topic").Cols("name").
  236. Join("INNER", "image_topic_relation", "image_topic_relation.topic_id = image_topic.id").
  237. Where("image_topic_relation.image_id = ?", imageID).Desc("image_topic.image_count").Find(&topicNames); err != nil {
  238. return err
  239. }
  240. if _, err := sess.ID(imageID).Cols("topics").Update(&Image{
  241. Topics: topicNames,
  242. }); err != nil {
  243. return err
  244. }
  245. return sess.Commit()
  246. }
  247. func addTopicByNameToImage(e Engine, imageID int64, topicName string) (*ImageTopic, error) {
  248. var topic ImageTopic
  249. has, err := e.Where("name = ?", topicName).Get(&topic)
  250. if err != nil {
  251. return nil, err
  252. }
  253. if !has {
  254. topic.Name = topicName
  255. topic.ImageCount = 1
  256. if _, err := e.Insert(&topic); err != nil {
  257. return nil, err
  258. }
  259. } else {
  260. topic.ImageCount++
  261. if _, err := e.ID(topic.ID).Cols("image_count").Update(&topic); err != nil {
  262. return nil, err
  263. }
  264. }
  265. if _, err := e.Insert(&ImageTopicRelation{
  266. ImageID: imageID,
  267. TopicID: topic.ID,
  268. }); err != nil {
  269. return nil, err
  270. }
  271. return &topic, nil
  272. }
  273. func removeTopicFromImage(e Engine, imageId int64, topic *ImageTopic) error {
  274. topic.ImageCount--
  275. if _, err := e.ID(topic.ID).Cols("image_count").Update(topic); err != nil {
  276. return err
  277. }
  278. if _, err := e.Delete(&ImageTopicRelation{
  279. ImageID: imageId,
  280. TopicID: topic.ID,
  281. }); err != nil {
  282. return err
  283. }
  284. return nil
  285. }
  286. func SearchImage(opts *SearchImageOptions) (ImageList, int64, error) {
  287. cond := SearchImageCondition(opts)
  288. return SearchImageByCondition(opts, cond)
  289. }
  290. func SearchImageCondition(opts *SearchImageOptions) builder.Cond {
  291. var cond = builder.NewCond()
  292. if len(opts.Keyword) > 0 {
  293. var subQueryCond = builder.NewCond()
  294. for _, v := range strings.Split(opts.Keyword, ",") {
  295. subQueryCond = subQueryCond.Or(builder.Like{"LOWER(image_topic.name)", strings.ToLower(v)})
  296. }
  297. subQuery := builder.Select("image_topic_relation.image_id").From("image_topic_relation").
  298. Join("INNER", "image_topic", "image_topic.id = image_topic_relation.image_id").
  299. Where(subQueryCond).
  300. GroupBy("image_topic_relation.image_id")
  301. var keywordCond = builder.In("id", subQuery)
  302. var likes = builder.NewCond()
  303. for _, v := range strings.Split(opts.Keyword, ",") {
  304. likes = likes.Or(builder.Like{"LOWER(tag)", strings.ToLower(v)})
  305. likes = likes.Or(builder.Like{"LOWER(description)", strings.ToLower(v)})
  306. }
  307. keywordCond = keywordCond.Or(likes)
  308. cond = cond.And(keywordCond)
  309. }
  310. if len(opts.Topics) > 0 { //标签精确匹配
  311. var subQueryCond = builder.NewCond()
  312. for _, v := range strings.Split(opts.Keyword, ",") {
  313. subQueryCond = subQueryCond.Or(builder.Eq{"LOWER(image_topic.name)": strings.ToLower(v)})
  314. subQuery := builder.Select("image_topic_relation.image_id").From("image_topic_relation").
  315. Join("INNER", "image_topic", "image_topic.id = image_topic_relation.image_id").
  316. Where(subQueryCond).
  317. GroupBy("image_topic_relation.image_id")
  318. var topicCond = builder.In("id", subQuery)
  319. cond = cond.And(topicCond)
  320. }
  321. }
  322. if opts.IncludePublicOnly {
  323. cond = cond.And(builder.Eq{"is_private": false})
  324. }
  325. if opts.IncludePrivateOnly {
  326. cond = cond.And(builder.Eq{"is_private": true})
  327. }
  328. if opts.IncludeOwnerOnly {
  329. cond = cond.And(builder.Eq{"uid": opts.UID})
  330. }
  331. if opts.IncludeOfficialOnly {
  332. cond = cond.And(builder.Eq{"type": RECOMMOND_TYPE})
  333. }
  334. if opts.IncludeStarByMe {
  335. subQuery := builder.Select("image_id").From("image_star").
  336. Where(builder.Eq{"uid": opts.UID})
  337. var starCond = builder.In("id", subQuery)
  338. cond = cond.And(starCond)
  339. }
  340. return cond
  341. }
  342. func SearchImageByCondition(opts *SearchImageOptions, cond builder.Cond) (ImageList, int64, error) {
  343. if opts.Page <= 0 {
  344. opts.Page = 1
  345. }
  346. var err error
  347. sess := x.NewSession()
  348. defer sess.Close()
  349. images := make(ImageList, 0, opts.PageSize)
  350. count, err := sess.Where(cond).Count(new(Image))
  351. if err != nil {
  352. return nil, 0, fmt.Errorf("Count: %v", err)
  353. }
  354. sess.Where(cond).OrderBy(opts.SearchOrderBy.String())
  355. if opts.PageSize > 0 {
  356. sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
  357. }
  358. if err = sess.Find(&images); err != nil {
  359. return nil, 0, fmt.Errorf("Images: %v", err)
  360. }
  361. if err = images.loadAttributes(sess, opts.UID); err != nil {
  362. return nil, 0, fmt.Errorf("LoadAttributes: %v", err)
  363. }
  364. return images, count, nil
  365. }
  366. func (images ImageList) loadAttributes(e Engine, uid int64) error {
  367. if len(images) == 0 {
  368. return nil
  369. }
  370. set := make(map[int64]struct{})
  371. for i := range images {
  372. set[images[i].UID] = struct{}{}
  373. }
  374. // Load creators.
  375. users := make(map[int64]*User, len(set))
  376. if err := e.Table("\"user\"").
  377. Cols("name", "lower_name", "avatar", "email").
  378. Where("id > 0").
  379. In("id", keysInt64(set)).
  380. Find(&users); err != nil {
  381. return fmt.Errorf("find users: %v", err)
  382. }
  383. for i := range images {
  384. images[i].UserName = users[images[i].UID].Name
  385. images[i].RelAvatarLink = users[images[i].UID].RelAvatarLink()
  386. if uid == -1 {
  387. images[i].IsStar = false
  388. } else {
  389. images[i].IsStar = isImageStaring(e, uid, images[i].ID)
  390. }
  391. }
  392. return nil
  393. }
  394. func GetCommittingImageCount() int {
  395. total, err := x.Where("status =?", 0).Count(new(Image))
  396. if err != nil {
  397. return 0
  398. }
  399. return int(total)
  400. }
  401. func CreateLocalImage(image *Image) error {
  402. _, err := x.Insert(image)
  403. return err
  404. }
  405. func UpdateLocalImage(image *Image) error {
  406. _, err := x.ID(image.ID).Cols("description", "is_private", "status").Update(image)
  407. return err
  408. }
  409. func UpdateLocalImageStatus(image *Image) error {
  410. _, err := x.ID(image.ID).Cols("status").Update(image)
  411. return err
  412. }
  413. func DeleteLocalImage(id int64) error {
  414. image := new(Image)
  415. _, err := x.ID(id).Delete(image)
  416. return err
  417. }
  418. //star or unstar Image
  419. func StarImage(userID, imageID int64, star bool) error {
  420. sess := x.NewSession()
  421. defer sess.Close()
  422. if err := sess.Begin(); err != nil {
  423. return err
  424. }
  425. if star {
  426. if isImageStaring(sess, userID, imageID) {
  427. return nil
  428. }
  429. if _, err := sess.Insert(&ImageStar{UID: userID, ImageID: imageID}); err != nil {
  430. return err
  431. }
  432. if _, err := sess.Exec("UPDATE `image` SET num_stars = num_stars + 1 WHERE id = ?", imageID); err != nil {
  433. return err
  434. }
  435. if _, err := sess.Exec("UPDATE `user` SET num_image_stars = num_image_stars + 1 WHERE id = ?", userID); err != nil {
  436. return err
  437. }
  438. } else {
  439. if !isImageStaring(sess, userID, imageID) {
  440. return nil
  441. }
  442. if _, err := sess.Delete(&ImageStar{0, userID, imageID, 0}); err != nil {
  443. return err
  444. }
  445. if _, err := sess.Exec("UPDATE `image` SET num_stars = num_stars - 1 WHERE id = ?", imageID); err != nil {
  446. return err
  447. }
  448. if _, err := sess.Exec("UPDATE `user` SET num_image_stars = num_image_stars - 1 WHERE id = ?", userID); err != nil {
  449. return err
  450. }
  451. }
  452. return sess.Commit()
  453. }
  454. func IsImageStaring(userID, datasetID int64) bool {
  455. return isImageStaring(x, userID, datasetID)
  456. }
  457. func isImageStaring(e Engine, userID, imageID int64) bool {
  458. has, _ := e.Get(&ImageStar{0, userID, imageID, 0})
  459. return has
  460. }
  461. func RecommendImage(imageId int64, recommond bool) error {
  462. image := Image{Type: getRecommondType(recommond)}
  463. _, err := x.ID(imageId).Cols("type").Update(image)
  464. return err
  465. }
  466. func getRecommondType(recommond bool) int {
  467. if recommond {
  468. return RECOMMOND_TYPE
  469. } else {
  470. return NORMAL_TYPE
  471. }
  472. }