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