movie.go 13 KB

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