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