mysql.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. package dao
  2. import (
  3. "context"
  4. "fmt"
  5. "strings"
  6. "sync"
  7. "time"
  8. "go-common/app/interface/openplatform/article/model"
  9. "go-common/library/database/sql"
  10. "go-common/library/log"
  11. "go-common/library/xstr"
  12. "go-common/library/sync/errgroup"
  13. )
  14. const (
  15. _sharding = 100
  16. _mysqlBulkSize = 50
  17. // article
  18. _articleMetaSQL = "SELECT article_id,category_id,title,summary,banner_url, template_id, state, mid, reprint, image_urls, publish_time, ctime, attributes,words,dynamic_intro, origin_image_urls, media_id, spoiler FROM filtered_articles WHERE article_id = ?"
  19. _allArticleMetaSQL = "SELECT id,category_id,title,summary,banner_url, template_id, state, mid, reprint, image_urls, publish_time, ctime, attributes,words,dynamic_intro, origin_image_urls, media_id, spoiler FROM articles WHERE id = ?"
  20. _articlesMetaSQL = "SELECT article_id,category_id,title,summary,banner_url, template_id, state, mid, reprint, image_urls, publish_time, ctime, attributes,words,dynamic_intro, origin_image_urls, media_id, spoiler FROM filtered_articles WHERE article_id in (%s)"
  21. _upperPassedSQL = "SELECT article_id, publish_time, attributes FROM filtered_articles WHERE mid = ? ORDER BY publish_time desc"
  22. _uppersPassedSQL = "SELECT article_id, mid, publish_time, attributes FROM filtered_articles WHERE mid in (%s) ORDER BY publish_time desc"
  23. _articleContentSQL = "SELECT content FROM filtered_article_contents_%s WHERE article_id = ?"
  24. _articleKeywordsSQL = "SELECT tags FROM article_contents_%s WHERE article_id = ?"
  25. _articleUpperCountSQL = "SELECT count(*) FROM filtered_articles WHERE mid = ?"
  26. _articleUpCntTodaySQL = "SELECT count(*) FROM articles WHERE mid = ? and ctime >= ?"
  27. _delFilteredArtMetaSQL = "DELETE FROM filtered_articles where article_id = ?"
  28. _delFilteredArtContentSQL = "DELETE FROM filtered_article_contents_%s where article_id = ?"
  29. // stat
  30. _statSQL = "SELECT view,favorite,likes,dislike,reply,share,coin,dynamic FROM article_stats_%s WHERE article_id = ? and deleted_time = 0"
  31. _statsSQL = "SELECT article_id, view,favorite,likes,dislike,reply,share,coin,dynamic FROM article_stats_%s WHERE article_id in (%s) and deleted_time = 0"
  32. // category
  33. _categoriesSQL = "SELECT id,parent_id,name,position,banner_url FROM article_categories WHERE state = 1 and deleted_time = 0"
  34. // authors
  35. _authorsSQL = "SELECT mid, daily_limit, state FROM article_authors WHERE deleted_time=0"
  36. _authorSQL = "SELECT state,rtime, daily_limit FROM article_authors WHERE mid=? AND deleted_time=0"
  37. _applyCountSQL = "SELECT count(*) FROM article_authors WHERE atime >= ?"
  38. _applySQL = "INSERT INTO article_authors (mid,atime,count,content,category) VALUES (?,?,1,?,?) ON DUPLICATE KEY UPDATE atime=?,rtime=0,state=0,count=count+1,content=?,category=?,deleted_time=0"
  39. _addAuthorSQL = "INSERT INTO article_authors (mid,state,type) VALUES (?,1,5) ON DUPLICATE KEY UPDATE state=1,deleted_time=0"
  40. // recommends
  41. _recommendCategorySQL = "SELECT article_id, big_banner_url, show_recommend, position, end_time, big_banner_start_time, big_banner_end_time FROM article_recommends WHERE start_time <= ? and (end_time >= ? or end_time = 0) and category_id = ? and deleted_time = 0 ORDER BY position ASC"
  42. _allRecommendSQL = "SELECT article_id FROM article_recommends WHERE start_time <= ? and (end_time >= ? or end_time = 0) and deleted_time = 0 and category_id = 0 ORDER BY mtime DESC LIMIT ?,?"
  43. _allRecommendCountSQL = "SELECT COUNT(*) FROM article_recommends WHERE start_time <= ? and (end_time >= ? or end_time = 0) and deleted_time = 0 and category_id = 0"
  44. _deleteRecommendSQL = "UPDATE article_recommends SET deleted_time=? WHERE article_id=? and deleted_time = 0"
  45. // setting
  46. _settingsSQL = "SELECT name,value FROM article_settings WHERE deleted_time=0"
  47. // sort
  48. _newestArtsMetaSQL = "SELECT article_id, publish_time, attributes FROM filtered_articles ORDER BY publish_time DESC LIMIT ?"
  49. //notice
  50. _noticeSQL = "SELECT id, title, url, plat, condi, build from article_notices where state = 1 and stime <= ? and etime > ?"
  51. // users
  52. _userNoticeSQL = "SELECT notice_state from users where mid = ?"
  53. _updateUserNoticeSQL = "INSERT INTO users (mid,notice_state) VALUES (?,?) ON DUPLICATE KEY UPDATE notice_state=?"
  54. // hotspots
  55. _hotspotsSQL = "select id, title, tag, icon, top_articles from hotspots where deleted_time = 0 and `order` != 0 order by `order` asc"
  56. // search articles
  57. _searchArticles = "select article_id, publish_time, tags, stats_view, stats_reply from search_articles where publish_time >= ? and publish_time < ?"
  58. _addCheatSQL = "INSERT INTO stats_filters(article_id, lv) VALUES(?,?) ON DUPLICATE KEY UPDATE lv=?, deleted_time = 0"
  59. _delCheatSQL = "UPDATE stats_filters SET deleted_time = ? WHERE article_id = ? and deleted_time = 0"
  60. _tagArticlesSQL = "select tid, oid, log_date FROM article_tags where tid in (%s) and is_deleted = 0"
  61. _mediaArticleSQL = "select id from articles where mid = ? and media_id = ? and deleted_time = 0 and state > -10"
  62. _mediaByIDSQL = "select media_id from articles where id = ?"
  63. )
  64. var _searchInterval = int64(3 * 24 * 3600)
  65. func (d *Dao) hit(id int64) string {
  66. return fmt.Sprintf("%02d", id%_sharding)
  67. }
  68. // Categories get Categories
  69. func (d *Dao) Categories(c context.Context) (res map[int64]*model.Category, err error) {
  70. var rows *sql.Rows
  71. if rows, err = d.categoriesStmt.Query(c); err != nil {
  72. PromError("db:分区查询")
  73. log.Error("mysql: db.Categories.Query error(%+v)", err)
  74. return
  75. }
  76. defer rows.Close()
  77. res = make(map[int64]*model.Category)
  78. for rows.Next() {
  79. ca := &model.Category{}
  80. if err = rows.Scan(&ca.ID, &ca.ParentID, &ca.Name, &ca.Position, &ca.BannerURL); err != nil {
  81. PromError("分区Scan")
  82. log.Error("mysql: rows.Categories.Scan error(%+v)", err)
  83. return
  84. }
  85. res[ca.ID] = ca
  86. }
  87. err = rows.Err()
  88. promErrorCheck(err)
  89. return
  90. }
  91. // ArticleStats get article stats
  92. func (d *Dao) ArticleStats(c context.Context, id int64) (res *model.Stats, err error) {
  93. res = new(model.Stats)
  94. row := d.articleDB.QueryRow(c, fmt.Sprintf(_statSQL, d.hit(id)), id)
  95. if err = row.Scan(&res.View, &res.Favorite, &res.Like, &res.Dislike, &res.Reply, &res.Share, &res.Coin, &res.Dynamic); err != nil {
  96. if err == sql.ErrNoRows {
  97. res = nil
  98. err = nil
  99. } else {
  100. PromError("Stat scan")
  101. log.Error("mysql: ArticleStats row.Scan(%d) error(%+v)", id, err)
  102. }
  103. }
  104. return
  105. }
  106. // ArticlesStats get articles stats
  107. func (d *Dao) ArticlesStats(c context.Context, ids []int64) (res map[int64]*model.Stats, err error) {
  108. var (
  109. shardings = make(map[int64][]int64)
  110. group = &errgroup.Group{}
  111. mutex = &sync.Mutex{}
  112. )
  113. res = make(map[int64]*model.Stats)
  114. for _, id := range ids {
  115. shardings[id%_sharding] = append(shardings[id%_sharding], id)
  116. }
  117. for sharding, subIDs := range shardings {
  118. keysLen := len(subIDs)
  119. sharding := sharding
  120. subIDs := subIDs
  121. for i := 0; i < keysLen; i += _mysqlBulkSize {
  122. var keys []int64
  123. if (i + _mysqlBulkSize) > keysLen {
  124. keys = subIDs[i:]
  125. } else {
  126. keys = subIDs[i : i+_mysqlBulkSize]
  127. }
  128. group.Go(func() error {
  129. statsSQL := fmt.Sprintf(_statsSQL, d.hit(sharding), xstr.JoinInts(keys))
  130. rows, e := d.articleDB.Query(c, statsSQL)
  131. if e != nil {
  132. return e
  133. }
  134. defer rows.Close()
  135. for rows.Next() {
  136. s := &model.Stats{}
  137. var aid int64
  138. e = rows.Scan(&aid, &s.View, &s.Favorite, &s.Like, &s.Dislike, &s.Reply, &s.Share, &s.Coin, &s.Dynamic)
  139. if e != nil {
  140. return e
  141. }
  142. mutex.Lock()
  143. res[aid] = s
  144. mutex.Unlock()
  145. }
  146. return rows.Err()
  147. })
  148. }
  149. }
  150. err = group.Wait()
  151. if err != nil {
  152. PromError("stats Scan")
  153. log.Error("mysql: rows.ArticleStats.Scan error(%+v)", err)
  154. }
  155. if len(res) == 0 {
  156. res = nil
  157. }
  158. return
  159. }
  160. // Settings gets article settings.
  161. func (d *Dao) Settings(c context.Context) (res map[string]string, err error) {
  162. var rows *sql.Rows
  163. if rows, err = d.settingsStmt.Query(c); err != nil {
  164. PromError("db:文章配置查询")
  165. log.Error("mysql: db.settingsStmt.Query error(%+v)", err)
  166. return
  167. }
  168. defer rows.Close()
  169. res = make(map[string]string)
  170. for rows.Next() {
  171. var name, value string
  172. if err = rows.Scan(&name, &value); err != nil {
  173. PromError("文章配置scan")
  174. log.Error("mysql: rows.Scan error(%+v)", err)
  175. return
  176. }
  177. res[name] = value
  178. }
  179. err = rows.Err()
  180. promErrorCheck(err)
  181. return
  182. }
  183. // Notices notice .
  184. func (d *Dao) Notices(c context.Context, t time.Time) (res []*model.Notice, err error) {
  185. var rows *sql.Rows
  186. if rows, err = d.noticeStmt.Query(c, t, t); err != nil {
  187. PromError("db:notice")
  188. log.Error("mysql: notice Query() error(%+v)", err)
  189. return
  190. }
  191. defer rows.Close()
  192. for rows.Next() {
  193. ba := &model.Notice{}
  194. if err = rows.Scan(&ba.ID, &ba.Title, &ba.URL, &ba.Plat, &ba.Condition, &ba.Build); err != nil {
  195. PromError("db:notice")
  196. log.Error("mysql: notice Scan() error(%+v)", err)
  197. return
  198. }
  199. res = append(res, ba)
  200. }
  201. err = rows.Err()
  202. promErrorCheck(err)
  203. return
  204. }
  205. // NoticeState .
  206. func (d *Dao) NoticeState(c context.Context, mid int64) (res int64, err error) {
  207. if err = d.userNoticeStmt.QueryRow(c, mid).Scan(&res); err != nil {
  208. if err == sql.ErrNoRows {
  209. err = nil
  210. } else {
  211. PromError("db:notice_state")
  212. log.Error("mysql: notice state row.Scan error(%+v)", err)
  213. }
  214. }
  215. return
  216. }
  217. // UpdateNoticeState update notice state
  218. func (d *Dao) UpdateNoticeState(c context.Context, mid int64, state int64) (err error) {
  219. if _, err = d.updateUserNoticeStmt.Exec(c, mid, state, state); err != nil {
  220. PromError("db:修改用户引导状态")
  221. log.Error("mysql: update_notice state(mid: %v, state: %v) error(%+v)", mid, state, err)
  222. }
  223. return
  224. }
  225. // Hotspots .
  226. func (d *Dao) Hotspots(c context.Context) (res []*model.Hotspot, err error) {
  227. var rows *sql.Rows
  228. if rows, err = d.hotspotsStmt.Query(c); err != nil {
  229. PromError("db:hotspots")
  230. log.Error("mysql: hotspot Query() error(%+v)", err)
  231. return
  232. }
  233. defer rows.Close()
  234. for rows.Next() {
  235. ba := &model.Hotspot{}
  236. var ic int
  237. var arts string
  238. if err = rows.Scan(&ba.ID, &ba.Title, &ba.Tag, &ic, &arts); err != nil {
  239. PromError("db:hostspot")
  240. log.Error("mysql: hotspot Scan() error(%+v)", err)
  241. return
  242. }
  243. if ic != 0 {
  244. ba.Icon = true
  245. }
  246. ba.TopArticles, _ = xstr.SplitInts(arts)
  247. res = append(res, ba)
  248. }
  249. err = rows.Err()
  250. promErrorCheck(err)
  251. return
  252. }
  253. // SearchArts get articles publish time after ptime
  254. func (d *Dao) SearchArts(c context.Context, ptime int64) (res []*model.SearchArt, err error) {
  255. var rows *sql.Rows
  256. now := time.Now().Unix()
  257. for ; ptime < now; ptime += _searchInterval {
  258. if rows, err = d.searchArtsStmt.Query(c, ptime, ptime+_searchInterval); err != nil {
  259. PromError("db:searchArts")
  260. log.Error("mysql: search arts Query() error(%+v)", err)
  261. return
  262. }
  263. defer rows.Close()
  264. for rows.Next() {
  265. ba := &model.SearchArt{}
  266. var t string
  267. if err = rows.Scan(&ba.ID, &ba.PublishTime, &t, &ba.StatsView, &ba.StatsReply); err != nil {
  268. PromError("db:searchArts")
  269. log.Error("mysql: search arts Scan() error(%+v)", err)
  270. return
  271. }
  272. if t != "" {
  273. ba.Tags = strings.Split(t, ",")
  274. }
  275. res = append(res, ba)
  276. }
  277. if err = rows.Err(); err != nil {
  278. PromError("db:searchArts")
  279. log.Error("mysql: search arts Query() error(%+v)", err)
  280. return
  281. }
  282. }
  283. return
  284. }
  285. // AddCheatFilter .
  286. func (d *Dao) AddCheatFilter(c context.Context, aid int64, lv int) (err error) {
  287. if _, err = d.addCheatStmt.Exec(c, aid, lv, lv); err != nil {
  288. PromError("db:新增防刷过滤")
  289. log.Error("mysql: addCheatFilter state(aid: %v, lv: %v) error(%+v)", aid, lv, err)
  290. return
  291. }
  292. log.Info("mysql: addCheatFilter state(aid: %v, lv: %v)", aid, lv)
  293. return
  294. }
  295. // DelCheatFilter .
  296. func (d *Dao) DelCheatFilter(c context.Context, aid int64) (err error) {
  297. if _, err = d.delCheatStmt.Exec(c, time.Now().Unix(), aid); err != nil {
  298. PromError("db:删除防刷过滤")
  299. log.Error("mysql: delCheatFilter state(aid: %v) error(%+v)", aid, err)
  300. return
  301. }
  302. log.Info("mysql: delCheatFilter state(aid: %v)", aid)
  303. return
  304. }