package controller import ( "crawler/config" "crawler/model" "crawler/service" "crawler/utility" "github.com/gin-gonic/gin" "github.com/gocolly/colly" "log" "net" "net/http" "time" ) type MovieController interface { SearchDailyBoxOfficeList(c *gin.Context) SearchWeeklylyBoxOfficeList(c *gin.Context) SearchMovieList(c *gin.Context) } type Movie struct { MovieListModel model.MovieListModel MovieDailyModel model.MovieDailyModel MovieWeeklyModel model.MovieWeeklyModel MovieInfoModel model.MovieInfoModel MovieSearchModel model.MovieSearchModel MovieDetailModel model.MovieDetailModel Kobis model.Kobis Rest service.Rest } // GET SearchDailyBoxOfficeList func (this *Movie) SearchDailyBoxOfficeList(c *gin.Context) { var req = this.MovieDailyModel.SearchDailyBoxOfficeParams if err := c.ShouldBind(&req); err != nil { c.JSON(http.StatusBadRequest, err.Error()) return } var ( total, rows = 0, 0 list = make([]model.MovieDailyTable, 0) ) // 검색 변수 생성 params, err := this.MovieDailyModel.MakeParams(req) if err != nil { c.JSON(http.StatusBadRequest, err.Error()) return } // 검색값이 있는지 확인 ids, err := this.MovieSearchModel.SelectIDs(params) if err != nil { c.JSON(http.StatusBadRequest, err.Error()) return } // 검색 내역이 있음 if ids != "" { // DB에서 먼저 조회해본다. list, err = this.MovieDailyModel.List(ids) if err != nil { c.JSON(http.StatusBadRequest, err.Error()) return } } // 목록이 없으면 API 호출 if len(list) <= 0 { data, err := this.Kobis.MovieDailyBoxOfficeListAPI(req) if err != nil { c.JSON(http.StatusBadRequest, err.Error()) } // 데이터 새로 입력 if len(data.BoxOfficeResult.DailyBoxOfficeList) > 0 { if err = this.MovieDailyModel.Insert(data); err != nil { c.JSON(http.StatusBadRequest, err.Error()) return } var movieCd []string for _, row := range data.BoxOfficeResult.DailyBoxOfficeList { movieCd = append(movieCd, row.MovieCd) } lastInsertIds, _ := this.MovieDailyModel.LastInsertIDs(movieCd, req.TargetDt) if err != nil { c.JSON(http.StatusBadRequest, err.Error()) return } // 검색 결과 저장 movieSearch := this.MovieSearchModel.MovieSearch movieSearch.Params = params movieSearch.IDs = lastInsertIds if err = this.MovieSearchModel.Insert(movieSearch); err != nil { c.JSON(http.StatusBadRequest, err.Error()) return } if lastInsertIds != "" { list, err = this.MovieDailyModel.List(lastInsertIds) if err != nil { c.JSON(http.StatusBadRequest, err.Error()) return } } } } // 상세 정보를 조회해서 저장한다. for i, row := range list { row.Detail, _ = this.FindMovieDetail(row.MovieCd) list[i] = row rows++ } total = this.MovieDailyModel.Total() c.JSON(http.StatusOK, gin.H{ "total": total, "rows": rows, "list": list, }) } // GET SearchWeeklylyBoxOfficeList func (this *Movie) SearchWeeklyBoxOfficeList(c *gin.Context) { var req = this.MovieWeeklyModel.SearchWeeklyBoxOfficeParams if err := c.ShouldBind(&req); err != nil { c.JSON(http.StatusBadRequest, err.Error()) return } var ( total, rows = 0, 0 list = make([]model.MovieWeeklyTable, 0) ) // 검색 변수 생성 params, err := this.MovieWeeklyModel.MakeParams(req) if err != nil { c.JSON(http.StatusBadRequest, err.Error()) return } // 검색값이 있는지 확인 ids, err := this.MovieSearchModel.SelectIDs(params) if err != nil { c.JSON(http.StatusBadRequest, err.Error()) return } // 검색 내역이 있음 if ids != "" { // DB에서 먼저 조회해본다. list, err = this.MovieWeeklyModel.List(ids) if err != nil { c.JSON(http.StatusBadRequest, err.Error()) return } } // 목록이 없으면 API 호출 if len(list) <= 0 { data, err := this.Kobis.MovieWeeklyBoxOfficeListAPI(req) if err != nil { c.JSON(http.StatusBadRequest, err.Error()) } // 데이터 새로 입력 if len(data.BoxOfficeResult.WeeklyBoxOfficeList) > 0 { if err = this.MovieWeeklyModel.Insert(data); err != nil { c.JSON(http.StatusBadRequest, err.Error()) return } var movieCd []string for _, row := range data.BoxOfficeResult.WeeklyBoxOfficeList { movieCd = append(movieCd, row.MovieCd) } lastInsertIds, _ := this.MovieWeeklyModel.LastInsertIDs(movieCd, req.TargetDt) if err != nil { c.JSON(http.StatusBadRequest, err.Error()) return } // 검색 결과 저장 movieSearch := this.MovieSearchModel.MovieSearch movieSearch.Params = params movieSearch.IDs = lastInsertIds if err = this.MovieSearchModel.Insert(movieSearch); err != nil { c.JSON(http.StatusBadRequest, err.Error()) return } if lastInsertIds != "" { list, err = this.MovieWeeklyModel.List(lastInsertIds) if err != nil { c.JSON(http.StatusBadRequest, err.Error()) return } } } } // 상세 정보를 조회해서 저장한다. for i, row := range list { row.Detail, _ = this.FindMovieDetail(row.MovieCd) list[i] = row rows++ } total = this.MovieWeeklyModel.Total() c.JSON(http.StatusOK, gin.H{ "total": total, "rows": rows, "list": list, }) } // GET SearchMovieList func (this *Movie) SearchMovieList(c *gin.Context) { var req = this.MovieListModel.SearchMovieListParams if err := c.ShouldBind(&req); err != nil { c.JSON(http.StatusBadRequest, err.Error()) return } type result = struct { MovieListInfo model.MovieListInfo `json:"summary"` Info *model.MovieInfoTable `json:"info"` Detail *model.MovieDetailTable `json:"detail"` } var ( total, rows = 0, 0 list = make([]result, 0) ) data, err := this.Kobis.MovieListAPI(req) if err != nil { c.JSON(http.StatusBadRequest, err.Error()) } // 데이터 새로 입력 if data.MovieListResult.TotCnt > 0 { if err = this.MovieListModel.Insert(data.MovieListResult.MovieList); err != nil { c.JSON(http.StatusBadRequest, err.Error()) return } } // 기본, 상세 정보를 조회해서 저장한다. for _, row := range data.MovieListResult.MovieList { info, _ := this.FindMovieInfo(req.Key, row.MovieCd) detail, _ := this.FindMovieDetail(row.MovieCd) list = append(list, result{ MovieListInfo: row, Info: info, Detail: detail, }) rows++ } total = this.MovieListModel.Total() c.JSON(http.StatusOK, gin.H{ "total": total, "rows": rows, "list": list, }) } /** * 영화 일별 박스오피스 정보 조회 * /movie/searchDailyInfo */ func (this *Movie) SearchDailyInfo(c *gin.Context) { var req = struct { Key string `form:"key" binding:"required"` DailyID int `form:"dailyID" binding:"required"` }{} if err := c.ShouldBind(&req); err != nil { c.JSON(http.StatusBadRequest, err.Error()) return } stats, err := this.MovieDailyModel.Info(req.DailyID) if err != nil { c.JSON(http.StatusBadRequest, err.Error()) return } if stats.DailyID == 0 { c.JSON(http.StatusBadRequest, "잘못된 요청입니다.") return } info, err := this.FindMovieInfo(req.Key, stats.MovieCd) if err != nil { c.JSON(http.StatusBadRequest, err.Error()) return } detail, err := this.FindMovieDetail(stats.MovieCd) if err != nil { c.JSON(http.StatusBadRequest, err.Error()) return } c.JSON(http.StatusOK, gin.H{ "stats": stats, "info": info, "detail": detail, }) } /** * 영화 주간/주말 박스오피스 정보 조회 * /movie/searchWeeklyInfo */ func (this *Movie) SearchWeeklyInfo(c *gin.Context) { var req = struct { Key string `form:"key" binding:"required"` WeeklyID int `form:"weeklyID" binding:"required"` }{} if err := c.ShouldBind(&req); err != nil { c.JSON(http.StatusBadRequest, err.Error()) return } stats, err := this.MovieWeeklyModel.Info(req.WeeklyID) if err != nil { c.JSON(http.StatusBadRequest, err.Error()) return } if stats.WeeklyID == 0 { c.JSON(http.StatusBadRequest, "잘못된 요청입니다.") return } info, err := this.FindMovieInfo(req.Key, stats.MovieCd) if err != nil { c.JSON(http.StatusBadRequest, err.Error()) return } detail, err := this.FindMovieDetail(stats.MovieCd) if err != nil { c.JSON(http.StatusBadRequest, err.Error()) return } c.JSON(http.StatusOK, gin.H{ "stats": stats, "info": info, "detail": detail, }) } /** * 영화 정보 조회 * /movie/searchMovieInfo */ func (this *Movie) SearchMovieInfo(c *gin.Context) { var req = this.MovieInfoModel.SearchMovieInfoParams if err := c.ShouldBind(&req); err != nil { c.JSON(http.StatusBadRequest, err.Error()) return } info, err := this.FindMovieInfo(req.Key, req.MovieCd) if err != nil { c.JSON(http.StatusBadRequest, err.Error()) return } detail, err := this.FindMovieDetail(req.MovieCd) if err != nil { c.JSON(http.StatusBadRequest, err.Error()) return } c.JSON(http.StatusOK, gin.H{ "info": info, "detail": detail, }) } // 영화 기본 정보 조회 func (this *Movie) FindMovieInfo(key, movieCd string) (*model.MovieInfoTable, error) { if this.MovieListModel.IsExists(movieCd) == false || this.MovieInfoModel.IsExists(movieCd) == false { req := this.MovieInfoModel.SearchMovieInfoParams req.Key = key req.MovieCd = movieCd data, err := this.Kobis.MovieInfoAPI(req) if err != nil { return nil, err } err = this.MovieListModel.Replace(model.MovieListInfo{ MovieCd: data.MovieInfoResult.MovieInfo.MovieCd, MovieNm: data.MovieInfoResult.MovieInfo.MovieNm, MovieNmEn: data.MovieInfoResult.MovieInfo.MovieNmEn, PrdtYear: data.MovieInfoResult.MovieInfo.PrdtYear, OpenDt: data.MovieInfoResult.MovieInfo.OpenDt, TypeNm: data.MovieInfoResult.MovieInfo.TypeNm, PrdtStatNm: data.MovieInfoResult.MovieInfo.PrdtStatNm, NationAlt: "", GenreAlt: "", RepNationNm: "", RepGenreNm: "", Directors: nil, Companys: nil, }) if err != nil { return nil, err } // 영화 기본 정보 입력 this.MovieInfoModel.Insert(data.MovieInfoResult.MovieInfo) if err != nil { return nil, err } } movieInfo, err := this.MovieInfoModel.Info(movieCd) return &movieInfo, err } // 영화 주요 정보 조회 func (this *Movie) FindMovieDetail(movieCd string) (*model.MovieDetailTable, error) { if this.MovieDetailModel.IsExists(movieCd) == false { movieDetail := this.MovieDetailModel.MovieDetail movieDetail.MovieCd = movieCd // c2 := c1.Clone() c1 := colly.NewCollector( colly.AllowedDomains(config.KOBIS_DOMAIN), colly.IgnoreRobotsTxt(), colly.Async(false), ) c1.WithTransport(&http.Transport{ DialContext: (&net.Dialer{ Timeout: 30 * time.Second, KeepAlive: 30 * time.Second, }).DialContext, MaxIdleConns: 0, IdleConnTimeout: 10 * time.Second, TLSHandshakeTimeout: 10 * time.Second, ExpectContinueTimeout: 10 * time.Second, }) c1.OnRequest(func(r *colly.Request) { r.Headers.Set("User-Agent", utility.RandomString()) r.Headers.Set("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8") }) c1.OnError(func(_ *colly.Response, err error) { log.Printf("Error(c1) : %s\n", err.Error()) }) /* 관객 수, 누적 매출액 조회 */ /* c2.OnRequest(func(r *colly.Request) { r.Headers.Set("User-Agent", utility.RandomString()) r.Headers.Set("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8") }) c2.OnError(func(_ *colly.Response, err error) { log.Printf("Error(c2) : %s\n", err.Error()) }) */ c1.OnHTML(".item_tab.basic", func(e *colly.HTMLElement) { var host = config.KOBIS_HOST movieDetail.MainImg = e.ChildAttr("a.fl.thumb", "href") if movieDetail.MainImg != "" && movieDetail.MainImg != "#" { movieDetail.MainImg = host + movieDetail.MainImg } movieDetail.ThumbImg = e.ChildAttr("a.fl.thumb > img", "src") if movieDetail.ThumbImg != "" && movieDetail.ThumbImg != "#" { movieDetail.ThumbImg = host + movieDetail.ThumbImg } movieDetail.Synopsis = e.ChildText("div.info.info2 p.desc_info") e.ForEach("div#post > input", func(_ int, ee *colly.HTMLElement) { movieDetail.Poster = append(movieDetail.Poster, model.Poster{ Thumb: host + ee.Attr("thn_img"), Origin: host + ee.Attr("img"), }) }) e.ForEach("div#stl > input", func(_ int, ee *colly.HTMLElement) { movieDetail.StillCut = append(movieDetail.StillCut, model.StillCut{ Thumb: host + ee.Attr("thn_img"), Origin: host + ee.Attr("img"), }) }) }) /* c2.OnHTML("body", func(e *colly.HTMLElement) { var ( tr = e.DOM.Find(".info").Eq(0).Find("table tbody tr").Eq(1) saleAcc = utility.RemoveSpecialChar(strings.Replace(tr.Find("td").Eq(2).Text(), "(100%)", "", 1)) audiAcc = utility.RemoveSpecialChar(strings.Replace(tr.Find("td").Eq(3).Text(), "(100%)", "", 1)) ) SaleAcc, _ := strconv.Atoi(saleAcc) AudiAcc, _ := strconv.Atoi(audiAcc) movieDetail.SaleAcc = SaleAcc movieDetail.AudiAcc = AudiAcc }) */ err := c1.Post(config.MOVIE_DETAIL, map[string]string{ "code": movieCd, "sType": "", "titleYN": "Y", "etcParam": "", "isOuterReq": "false", }) if this.Rest.Check(err) { return nil, err } /* err = c2.Post(config.MOVIE_DETAIL, map[string]string{ "code": movieCd, "sType": "stat", }) if this.Rest.Check(err) { return nil, err } */ err = this.MovieDetailModel.Insert(movieDetail) if this.Rest.Check(err) { return nil, err } } movieDetail, err := this.MovieDetailModel.Info(movieCd) return &movieDetail, err }