movie.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. package controller
  2. import (
  3. "crawler/config"
  4. "crawler/model"
  5. "crawler/service"
  6. "crawler/utility"
  7. "github.com/gin-gonic/gin"
  8. "github.com/gocolly/colly"
  9. "log"
  10. "net"
  11. "net/http"
  12. "time"
  13. )
  14. type MovieController interface {
  15. SearchDailyBoxOfficeList(c *gin.Context)
  16. SearchWeeklylyBoxOfficeList(c *gin.Context)
  17. SearchMovieList(c *gin.Context)
  18. }
  19. type Movie struct {
  20. MovieListModel model.MovieListModel
  21. MovieDailyModel model.MovieDailyModel
  22. MovieWeeklyModel model.MovieWeeklyModel
  23. MovieInfoModel model.MovieInfoModel
  24. MovieSearchModel model.MovieSearchModel
  25. MovieDetailModel model.MovieDetailModel
  26. Kobis model.Kobis
  27. Rest service.Rest
  28. }
  29. // GET SearchDailyBoxOfficeList
  30. func (this *Movie) SearchDailyBoxOfficeList(c *gin.Context) {
  31. var req = this.MovieDailyModel.SearchDailyBoxOfficeParams
  32. if err := c.ShouldBind(&req); err != nil {
  33. c.JSON(http.StatusBadRequest, err.Error())
  34. return
  35. }
  36. var (
  37. total, rows = 0, 0
  38. list = make([]model.MovieDailyTable, 0)
  39. )
  40. // 검색 변수 생성
  41. params, err := this.MovieDailyModel.MakeParams(req)
  42. if err != nil {
  43. c.JSON(http.StatusBadRequest, err.Error())
  44. return
  45. }
  46. // 검색값이 있는지 확인
  47. ids, err := this.MovieSearchModel.SelectIDs(params)
  48. if err != nil {
  49. c.JSON(http.StatusBadRequest, err.Error())
  50. return
  51. }
  52. // 검색 내역이 있음
  53. if ids != "" {
  54. // DB에서 먼저 조회해본다.
  55. list, err = this.MovieDailyModel.List(ids)
  56. if err != nil {
  57. c.JSON(http.StatusBadRequest, err.Error())
  58. return
  59. }
  60. }
  61. // 목록이 없으면 API 호출
  62. if len(list) <= 0 {
  63. data, err := this.Kobis.MovieDailyBoxOfficeListAPI(req)
  64. if err != nil {
  65. c.JSON(http.StatusBadRequest, err.Error())
  66. }
  67. // 데이터 새로 입력
  68. if len(data.BoxOfficeResult.DailyBoxOfficeList) > 0 {
  69. if err = this.MovieDailyModel.Insert(data); err != nil {
  70. c.JSON(http.StatusBadRequest, err.Error())
  71. return
  72. }
  73. var movieCd []string
  74. for _, row := range data.BoxOfficeResult.DailyBoxOfficeList {
  75. movieCd = append(movieCd, row.MovieCd)
  76. }
  77. lastInsertIds, _ := this.MovieDailyModel.LastInsertIDs(movieCd, req.TargetDt)
  78. if err != nil {
  79. c.JSON(http.StatusBadRequest, err.Error())
  80. return
  81. }
  82. // 검색 결과 저장
  83. movieSearch := this.MovieSearchModel.MovieSearch
  84. movieSearch.Params = params
  85. movieSearch.IDs = lastInsertIds
  86. if err = this.MovieSearchModel.Insert(movieSearch); err != nil {
  87. c.JSON(http.StatusBadRequest, err.Error())
  88. return
  89. }
  90. if lastInsertIds != "" {
  91. list, err = this.MovieDailyModel.List(lastInsertIds)
  92. if err != nil {
  93. c.JSON(http.StatusBadRequest, err.Error())
  94. return
  95. }
  96. }
  97. }
  98. }
  99. // 상세 정보를 조회해서 저장한다.
  100. for i, row := range list {
  101. row.Detail, _ = this.FindMovieDetail(row.MovieCd)
  102. list[i] = row
  103. rows++
  104. }
  105. total = this.MovieDailyModel.Total()
  106. c.JSON(http.StatusOK, gin.H{
  107. "total": total,
  108. "rows": rows,
  109. "list": list,
  110. })
  111. }
  112. // GET SearchWeeklylyBoxOfficeList
  113. func (this *Movie) SearchWeeklyBoxOfficeList(c *gin.Context) {
  114. var req = this.MovieWeeklyModel.SearchWeeklyBoxOfficeParams
  115. if err := c.ShouldBind(&req); err != nil {
  116. c.JSON(http.StatusBadRequest, err.Error())
  117. return
  118. }
  119. var (
  120. total, rows = 0, 0
  121. list = make([]model.MovieWeeklyTable, 0)
  122. )
  123. // 검색 변수 생성
  124. params, err := this.MovieWeeklyModel.MakeParams(req)
  125. if err != nil {
  126. c.JSON(http.StatusBadRequest, err.Error())
  127. return
  128. }
  129. // 검색값이 있는지 확인
  130. ids, err := this.MovieSearchModel.SelectIDs(params)
  131. if err != nil {
  132. c.JSON(http.StatusBadRequest, err.Error())
  133. return
  134. }
  135. // 검색 내역이 있음
  136. if ids != "" {
  137. // DB에서 먼저 조회해본다.
  138. list, err = this.MovieWeeklyModel.List(ids)
  139. if err != nil {
  140. c.JSON(http.StatusBadRequest, err.Error())
  141. return
  142. }
  143. }
  144. // 목록이 없으면 API 호출
  145. if len(list) <= 0 {
  146. data, err := this.Kobis.MovieWeeklyBoxOfficeListAPI(req)
  147. if err != nil {
  148. c.JSON(http.StatusBadRequest, err.Error())
  149. }
  150. // 데이터 새로 입력
  151. if len(data.BoxOfficeResult.WeeklyBoxOfficeList) > 0 {
  152. if err = this.MovieWeeklyModel.Insert(data); err != nil {
  153. c.JSON(http.StatusBadRequest, err.Error())
  154. return
  155. }
  156. var movieCd []string
  157. for _, row := range data.BoxOfficeResult.WeeklyBoxOfficeList {
  158. movieCd = append(movieCd, row.MovieCd)
  159. }
  160. lastInsertIds, _ := this.MovieWeeklyModel.LastInsertIDs(movieCd, req.TargetDt)
  161. if err != nil {
  162. c.JSON(http.StatusBadRequest, err.Error())
  163. return
  164. }
  165. // 검색 결과 저장
  166. movieSearch := this.MovieSearchModel.MovieSearch
  167. movieSearch.Params = params
  168. movieSearch.IDs = lastInsertIds
  169. if err = this.MovieSearchModel.Insert(movieSearch); err != nil {
  170. c.JSON(http.StatusBadRequest, err.Error())
  171. return
  172. }
  173. if lastInsertIds != "" {
  174. list, err = this.MovieWeeklyModel.List(lastInsertIds)
  175. if err != nil {
  176. c.JSON(http.StatusBadRequest, err.Error())
  177. return
  178. }
  179. }
  180. }
  181. }
  182. // 상세 정보를 조회해서 저장한다.
  183. for i, row := range list {
  184. row.Detail, _ = this.FindMovieDetail(row.MovieCd)
  185. list[i] = row
  186. rows++
  187. }
  188. total = this.MovieWeeklyModel.Total()
  189. c.JSON(http.StatusOK, gin.H{
  190. "total": total,
  191. "rows": rows,
  192. "list": list,
  193. })
  194. }
  195. // GET SearchMovieList
  196. func (this *Movie) SearchMovieList(c *gin.Context) {
  197. var req = this.MovieListModel.SearchMovieListParams
  198. if err := c.ShouldBind(&req); err != nil {
  199. c.JSON(http.StatusBadRequest, err.Error())
  200. return
  201. }
  202. type result = struct {
  203. MovieListInfo model.MovieListInfo `json:"summary"`
  204. Info *model.MovieInfoTable `json:"info"`
  205. Detail *model.MovieDetailTable `json:"detail"`
  206. }
  207. var (
  208. total, rows = 0, 0
  209. list = make([]result, 0)
  210. )
  211. data, err := this.Kobis.MovieListAPI(req)
  212. if err != nil {
  213. c.JSON(http.StatusBadRequest, err.Error())
  214. }
  215. // 데이터 새로 입력
  216. if data.MovieListResult.TotCnt > 0 {
  217. if err = this.MovieListModel.Insert(data.MovieListResult.MovieList); err != nil {
  218. c.JSON(http.StatusBadRequest, err.Error())
  219. return
  220. }
  221. }
  222. // 기본, 상세 정보를 조회해서 저장한다.
  223. for _, row := range data.MovieListResult.MovieList {
  224. info, _ := this.FindMovieInfo(req.Key, row.MovieCd)
  225. detail, _ := this.FindMovieDetail(row.MovieCd)
  226. list = append(list, result{
  227. MovieListInfo: row,
  228. Info: info,
  229. Detail: detail,
  230. })
  231. rows++
  232. }
  233. total = this.MovieListModel.Total()
  234. c.JSON(http.StatusOK, gin.H{
  235. "total": total,
  236. "rows": rows,
  237. "list": list,
  238. })
  239. }
  240. /**
  241. * 영화 일별 박스오피스 정보 조회
  242. * /movie/searchDailyInfo
  243. */
  244. func (this *Movie) SearchDailyInfo(c *gin.Context) {
  245. var req = struct {
  246. Key string `form:"key" binding:"required"`
  247. DailyID int `form:"dailyID" binding:"required"`
  248. }{}
  249. if err := c.ShouldBind(&req); err != nil {
  250. c.JSON(http.StatusBadRequest, err.Error())
  251. return
  252. }
  253. stats, err := this.MovieDailyModel.Info(req.DailyID)
  254. if err != nil {
  255. c.JSON(http.StatusBadRequest, err.Error())
  256. return
  257. }
  258. if stats.DailyID == 0 {
  259. c.JSON(http.StatusBadRequest, "잘못된 요청입니다.")
  260. return
  261. }
  262. info, err := this.FindMovieInfo(req.Key, stats.MovieCd)
  263. if err != nil {
  264. c.JSON(http.StatusBadRequest, err.Error())
  265. return
  266. }
  267. detail, err := this.FindMovieDetail(stats.MovieCd)
  268. if err != nil {
  269. c.JSON(http.StatusBadRequest, err.Error())
  270. return
  271. }
  272. c.JSON(http.StatusOK, gin.H{
  273. "stats": stats,
  274. "info": info,
  275. "detail": detail,
  276. })
  277. }
  278. /**
  279. * 영화 주간/주말 박스오피스 정보 조회
  280. * /movie/searchWeeklyInfo
  281. */
  282. func (this *Movie) SearchWeeklyInfo(c *gin.Context) {
  283. var req = struct {
  284. Key string `form:"key" binding:"required"`
  285. WeeklyID int `form:"weeklyID" binding:"required"`
  286. }{}
  287. if err := c.ShouldBind(&req); err != nil {
  288. c.JSON(http.StatusBadRequest, err.Error())
  289. return
  290. }
  291. stats, err := this.MovieWeeklyModel.Info(req.WeeklyID)
  292. if err != nil {
  293. c.JSON(http.StatusBadRequest, err.Error())
  294. return
  295. }
  296. if stats.WeeklyID == 0 {
  297. c.JSON(http.StatusBadRequest, "잘못된 요청입니다.")
  298. return
  299. }
  300. info, err := this.FindMovieInfo(req.Key, stats.MovieCd)
  301. if err != nil {
  302. c.JSON(http.StatusBadRequest, err.Error())
  303. return
  304. }
  305. detail, err := this.FindMovieDetail(stats.MovieCd)
  306. if err != nil {
  307. c.JSON(http.StatusBadRequest, err.Error())
  308. return
  309. }
  310. c.JSON(http.StatusOK, gin.H{
  311. "stats": stats,
  312. "info": info,
  313. "detail": detail,
  314. })
  315. }
  316. /**
  317. * 영화 정보 조회
  318. * /movie/searchMovieInfo
  319. */
  320. func (this *Movie) SearchMovieInfo(c *gin.Context) {
  321. var req = this.MovieInfoModel.SearchMovieInfoParams
  322. if err := c.ShouldBind(&req); err != nil {
  323. c.JSON(http.StatusBadRequest, err.Error())
  324. return
  325. }
  326. info, err := this.FindMovieInfo(req.Key, req.MovieCd)
  327. if err != nil {
  328. c.JSON(http.StatusBadRequest, err.Error())
  329. return
  330. }
  331. detail, err := this.FindMovieDetail(req.MovieCd)
  332. if err != nil {
  333. c.JSON(http.StatusBadRequest, err.Error())
  334. return
  335. }
  336. c.JSON(http.StatusOK, gin.H{
  337. "info": info,
  338. "detail": detail,
  339. })
  340. }
  341. // 영화 기본 정보 조회
  342. func (this *Movie) FindMovieInfo(key, movieCd string) (*model.MovieInfoTable, error) {
  343. if this.MovieListModel.IsExists(movieCd) == false || this.MovieInfoModel.IsExists(movieCd) == false {
  344. req := this.MovieInfoModel.SearchMovieInfoParams
  345. req.Key = key
  346. req.MovieCd = movieCd
  347. data, err := this.Kobis.MovieInfoAPI(req)
  348. if err != nil {
  349. return nil, err
  350. }
  351. err = this.MovieListModel.Replace(model.MovieListInfo{
  352. MovieCd: data.MovieInfoResult.MovieInfo.MovieCd,
  353. MovieNm: data.MovieInfoResult.MovieInfo.MovieNm,
  354. MovieNmEn: data.MovieInfoResult.MovieInfo.MovieNmEn,
  355. PrdtYear: data.MovieInfoResult.MovieInfo.PrdtYear,
  356. OpenDt: data.MovieInfoResult.MovieInfo.OpenDt,
  357. TypeNm: data.MovieInfoResult.MovieInfo.TypeNm,
  358. PrdtStatNm: data.MovieInfoResult.MovieInfo.PrdtStatNm,
  359. NationAlt: "",
  360. GenreAlt: "",
  361. RepNationNm: "",
  362. RepGenreNm: "",
  363. Directors: nil,
  364. Companys: nil,
  365. })
  366. if err != nil {
  367. return nil, err
  368. }
  369. // 영화 기본 정보 입력
  370. this.MovieInfoModel.Insert(data.MovieInfoResult.MovieInfo)
  371. if err != nil {
  372. return nil, err
  373. }
  374. }
  375. movieInfo, err := this.MovieInfoModel.Info(movieCd)
  376. return &movieInfo, err
  377. }
  378. // 영화 주요 정보 조회
  379. func (this *Movie) FindMovieDetail(movieCd string) (*model.MovieDetailTable, error) {
  380. if this.MovieDetailModel.IsExists(movieCd) == false {
  381. movieDetail := this.MovieDetailModel.MovieDetail
  382. movieDetail.MovieCd = movieCd
  383. // c2 := c1.Clone()
  384. c1 := colly.NewCollector(
  385. colly.AllowedDomains(config.KOBIS_DOMAIN),
  386. colly.IgnoreRobotsTxt(),
  387. colly.Async(false),
  388. )
  389. c1.WithTransport(&http.Transport{
  390. DialContext: (&net.Dialer{
  391. Timeout: 30 * time.Second,
  392. KeepAlive: 30 * time.Second,
  393. }).DialContext,
  394. MaxIdleConns: 0,
  395. IdleConnTimeout: 10 * time.Second,
  396. TLSHandshakeTimeout: 10 * time.Second,
  397. ExpectContinueTimeout: 10 * time.Second,
  398. })
  399. c1.OnRequest(func(r *colly.Request) {
  400. r.Headers.Set("User-Agent", utility.RandomString())
  401. r.Headers.Set("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8")
  402. })
  403. c1.OnError(func(_ *colly.Response, err error) {
  404. log.Printf("Error(c1) : %s\n", err.Error())
  405. })
  406. /*
  407. 관객 수, 누적 매출액 조회
  408. */
  409. /*
  410. c2.OnRequest(func(r *colly.Request) {
  411. r.Headers.Set("User-Agent", utility.RandomString())
  412. r.Headers.Set("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8")
  413. })
  414. c2.OnError(func(_ *colly.Response, err error) {
  415. log.Printf("Error(c2) : %s\n", err.Error())
  416. })
  417. */
  418. c1.OnHTML(".item_tab.basic", func(e *colly.HTMLElement) {
  419. var host = config.KOBIS_HOST
  420. movieDetail.MainImg = e.ChildAttr("a.fl.thumb", "href")
  421. if movieDetail.MainImg != "" && movieDetail.MainImg != "#" {
  422. movieDetail.MainImg = host + movieDetail.MainImg
  423. }
  424. movieDetail.ThumbImg = e.ChildAttr("a.fl.thumb > img", "src")
  425. if movieDetail.ThumbImg != "" && movieDetail.ThumbImg != "#" {
  426. movieDetail.ThumbImg = host + movieDetail.ThumbImg
  427. }
  428. movieDetail.Synopsis = e.ChildText("div.info.info2 p.desc_info")
  429. e.ForEach("div#post > input", func(_ int, ee *colly.HTMLElement) {
  430. movieDetail.Poster = append(movieDetail.Poster, model.Poster{
  431. Thumb: host + ee.Attr("thn_img"),
  432. Origin: host + ee.Attr("img"),
  433. })
  434. })
  435. e.ForEach("div#stl > input", func(_ int, ee *colly.HTMLElement) {
  436. movieDetail.StillCut = append(movieDetail.StillCut, model.StillCut{
  437. Thumb: host + ee.Attr("thn_img"),
  438. Origin: host + ee.Attr("img"),
  439. })
  440. })
  441. })
  442. /*
  443. c2.OnHTML("body", func(e *colly.HTMLElement) {
  444. var (
  445. tr = e.DOM.Find(".info").Eq(0).Find("table tbody tr").Eq(1)
  446. saleAcc = utility.RemoveSpecialChar(strings.Replace(tr.Find("td").Eq(2).Text(), "(100%)", "", 1))
  447. audiAcc = utility.RemoveSpecialChar(strings.Replace(tr.Find("td").Eq(3).Text(), "(100%)", "", 1))
  448. )
  449. SaleAcc, _ := strconv.Atoi(saleAcc)
  450. AudiAcc, _ := strconv.Atoi(audiAcc)
  451. movieDetail.SaleAcc = SaleAcc
  452. movieDetail.AudiAcc = AudiAcc
  453. })
  454. */
  455. err := c1.Post(config.MOVIE_DETAIL, map[string]string{
  456. "code": movieCd,
  457. "sType": "",
  458. "titleYN": "Y",
  459. "etcParam": "",
  460. "isOuterReq": "false",
  461. })
  462. if this.Rest.Check(err) {
  463. return nil, err
  464. }
  465. /*
  466. err = c2.Post(config.MOVIE_DETAIL, map[string]string{
  467. "code": movieCd,
  468. "sType": "stat",
  469. })
  470. if this.Rest.Check(err) {
  471. return nil, err
  472. }
  473. */
  474. err = this.MovieDetailModel.Insert(movieDetail)
  475. if this.Rest.Check(err) {
  476. return nil, err
  477. }
  478. }
  479. movieDetail, err := this.MovieDetailModel.Info(movieCd)
  480. return &movieDetail, err
  481. }