- 벌크 자막: 브라우저 우선 + API fallback, 광고 즉시 skip, 대기 시간 단축 - 벌크 자막/추출: 선택한 영상만 처리 가능 (체크박스 선택 후 실행) - 자막 실패 시 no_transcript 상태 마킹하여 재시도 방지 - 검색 시 필터 조건 무시 (채널/장르/가격/지역/영역 초기화) - 리셋 버튼 클릭 시 검색어 입력란 초기화 - RestaurantMapper updateFields에 google_place_id, rating 등 geocoding 필드 추가 - SearchMapper에 tabling_url, catchtable_url, phone, website 필드 추가 - 식당 상세에 네이버 지도 링크 추가 - YouTubeService.getTranscriptApi public 전환 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
273 lines
11 KiB
XML
273 lines
11 KiB
XML
<?xml version="1.0" encoding="UTF-8" ?>
|
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
|
<mapper namespace="com.tasteby.mapper.RestaurantMapper">
|
|
|
|
<!-- ===== Result Maps ===== -->
|
|
|
|
<resultMap id="restaurantMap" type="Restaurant">
|
|
<id property="id" column="id"/>
|
|
<result property="name" column="name"/>
|
|
<result property="address" column="address"/>
|
|
<result property="region" column="region"/>
|
|
<result property="latitude" column="latitude"/>
|
|
<result property="longitude" column="longitude"/>
|
|
<result property="cuisineType" column="cuisine_type"/>
|
|
<result property="priceRange" column="price_range"/>
|
|
<result property="phone" column="phone"/>
|
|
<result property="website" column="website"/>
|
|
<result property="googlePlaceId" column="google_place_id"/>
|
|
<result property="tablingUrl" column="tabling_url"/>
|
|
<result property="catchtableUrl" column="catchtable_url"/>
|
|
<result property="businessStatus" column="business_status"/>
|
|
<result property="rating" column="rating"/>
|
|
<result property="ratingCount" column="rating_count"/>
|
|
<result property="updatedAt" column="updated_at"/>
|
|
</resultMap>
|
|
|
|
<!-- ===== Queries ===== -->
|
|
|
|
<select id="findAll" resultMap="restaurantMap">
|
|
SELECT DISTINCT r.id, r.name, r.address, r.region, r.latitude, r.longitude,
|
|
r.cuisine_type, r.price_range, r.google_place_id, r.tabling_url, r.catchtable_url,
|
|
r.business_status, r.rating, r.rating_count, r.updated_at
|
|
FROM restaurants r
|
|
<if test="channel != null and channel != ''">
|
|
JOIN video_restaurants vr_f ON vr_f.restaurant_id = r.id
|
|
JOIN videos v_f ON v_f.id = vr_f.video_id
|
|
JOIN channels c_f ON c_f.id = v_f.channel_id
|
|
</if>
|
|
<where>
|
|
r.latitude IS NOT NULL
|
|
AND EXISTS (SELECT 1 FROM video_restaurants vr0 WHERE vr0.restaurant_id = r.id)
|
|
<if test="cuisine != null and cuisine != ''">
|
|
AND r.cuisine_type = #{cuisine}
|
|
</if>
|
|
<if test="region != null and region != ''">
|
|
AND r.region LIKE '%' || #{region} || '%'
|
|
</if>
|
|
<if test="channel != null and channel != ''">
|
|
AND c_f.channel_name = #{channel}
|
|
</if>
|
|
</where>
|
|
ORDER BY r.updated_at DESC
|
|
OFFSET #{offset} ROWS FETCH NEXT #{limit} ROWS ONLY
|
|
</select>
|
|
|
|
<select id="findById" resultMap="restaurantMap">
|
|
SELECT r.id, r.name, r.address, r.region, r.latitude, r.longitude,
|
|
r.cuisine_type, r.price_range, r.phone, r.website, r.google_place_id,
|
|
r.tabling_url, r.catchtable_url, r.business_status, r.rating, r.rating_count
|
|
FROM restaurants r
|
|
WHERE r.id = #{id}
|
|
</select>
|
|
|
|
<select id="findVideoLinks" resultType="map">
|
|
SELECT v.video_id, v.title, v.url,
|
|
TO_CHAR(v.published_at, 'YYYY-MM-DD"T"HH24:MI:SS') AS published_at,
|
|
vr.foods_mentioned, vr.evaluation, vr.guests,
|
|
c.channel_name, c.channel_id
|
|
FROM video_restaurants vr
|
|
JOIN videos v ON v.id = vr.video_id
|
|
JOIN channels c ON c.id = v.channel_id
|
|
WHERE vr.restaurant_id = #{restaurantId}
|
|
ORDER BY v.published_at DESC
|
|
</select>
|
|
|
|
<!-- ===== Insert ===== -->
|
|
|
|
<insert id="insertRestaurant">
|
|
INSERT INTO restaurants (id, name, address, region, latitude, longitude,
|
|
cuisine_type, price_range, google_place_id,
|
|
phone, website, business_status, rating, rating_count)
|
|
VALUES (#{id}, #{name}, #{address}, #{region}, #{latitude}, #{longitude},
|
|
#{cuisineType}, #{priceRange}, #{googlePlaceId},
|
|
#{phone}, #{website}, #{businessStatus}, #{rating}, #{ratingCount})
|
|
</insert>
|
|
|
|
<!-- ===== Update with COALESCE ===== -->
|
|
|
|
<update id="updateRestaurant">
|
|
UPDATE restaurants SET
|
|
name = #{name},
|
|
address = COALESCE(#{address}, address),
|
|
region = COALESCE(#{region}, region),
|
|
latitude = COALESCE(#{latitude}, latitude),
|
|
longitude = COALESCE(#{longitude}, longitude),
|
|
cuisine_type = COALESCE(#{cuisineType}, cuisine_type),
|
|
price_range = COALESCE(#{priceRange}, price_range),
|
|
google_place_id = COALESCE(#{googlePlaceId}, google_place_id),
|
|
phone = COALESCE(#{phone}, phone),
|
|
website = COALESCE(#{website}, website),
|
|
business_status = COALESCE(#{businessStatus}, business_status),
|
|
rating = COALESCE(#{rating}, rating),
|
|
rating_count = COALESCE(#{ratingCount}, rating_count),
|
|
updated_at = SYSTIMESTAMP
|
|
WHERE id = #{id}
|
|
</update>
|
|
|
|
<!-- ===== Dynamic field update ===== -->
|
|
|
|
<update id="updateFields">
|
|
UPDATE restaurants SET
|
|
<trim suffixOverrides=",">
|
|
<if test="fields.containsKey('name')">
|
|
name = #{fields.name},
|
|
</if>
|
|
<if test="fields.containsKey('address')">
|
|
address = #{fields.address},
|
|
</if>
|
|
<if test="fields.containsKey('region')">
|
|
region = #{fields.region},
|
|
</if>
|
|
<if test="fields.containsKey('cuisine_type')">
|
|
cuisine_type = #{fields.cuisine_type},
|
|
</if>
|
|
<if test="fields.containsKey('price_range')">
|
|
price_range = #{fields.price_range},
|
|
</if>
|
|
<if test="fields.containsKey('phone')">
|
|
phone = #{fields.phone},
|
|
</if>
|
|
<if test="fields.containsKey('website')">
|
|
website = #{fields.website},
|
|
</if>
|
|
<if test="fields.containsKey('tabling_url')">
|
|
tabling_url = #{fields.tabling_url},
|
|
</if>
|
|
<if test="fields.containsKey('catchtable_url')">
|
|
catchtable_url = #{fields.catchtable_url},
|
|
</if>
|
|
<if test="fields.containsKey('latitude')">
|
|
latitude = #{fields.latitude},
|
|
</if>
|
|
<if test="fields.containsKey('longitude')">
|
|
longitude = #{fields.longitude},
|
|
</if>
|
|
<if test="fields.containsKey('google_place_id')">
|
|
google_place_id = #{fields.google_place_id},
|
|
</if>
|
|
<if test="fields.containsKey('business_status')">
|
|
business_status = #{fields.business_status},
|
|
</if>
|
|
<if test="fields.containsKey('rating')">
|
|
rating = #{fields.rating},
|
|
</if>
|
|
<if test="fields.containsKey('rating_count')">
|
|
rating_count = #{fields.rating_count},
|
|
</if>
|
|
updated_at = SYSTIMESTAMP,
|
|
</trim>
|
|
WHERE id = #{id}
|
|
</update>
|
|
|
|
<!-- ===== Cascade deletes ===== -->
|
|
|
|
<delete id="deleteVectors">
|
|
DELETE FROM restaurant_vectors WHERE restaurant_id = #{id}
|
|
</delete>
|
|
|
|
<delete id="deleteReviews">
|
|
DELETE FROM user_reviews WHERE restaurant_id = #{id}
|
|
</delete>
|
|
|
|
<delete id="deleteFavorites">
|
|
DELETE FROM user_favorites WHERE restaurant_id = #{id}
|
|
</delete>
|
|
|
|
<delete id="deleteVideoRestaurants">
|
|
DELETE FROM video_restaurants WHERE restaurant_id = #{id}
|
|
</delete>
|
|
|
|
<delete id="deleteRestaurant">
|
|
DELETE FROM restaurants WHERE id = #{id}
|
|
</delete>
|
|
|
|
<!-- ===== Link video-restaurant ===== -->
|
|
|
|
<insert id="linkVideoRestaurant">
|
|
INSERT INTO video_restaurants (id, video_id, restaurant_id, foods_mentioned, evaluation, guests)
|
|
VALUES (#{id}, #{videoId}, #{restaurantId}, #{foods,jdbcType=CLOB}, #{evaluation,jdbcType=CLOB}, #{guests,jdbcType=CLOB})
|
|
</insert>
|
|
|
|
<!-- ===== Lookups ===== -->
|
|
|
|
<select id="findIdByPlaceId" resultType="string">
|
|
SELECT id FROM restaurants WHERE google_place_id = #{placeId} FETCH FIRST 1 ROWS ONLY
|
|
</select>
|
|
|
|
<select id="findIdByName" resultType="string">
|
|
SELECT id FROM restaurants WHERE name = #{name} FETCH FIRST 1 ROWS ONLY
|
|
</select>
|
|
|
|
<!-- ===== Batch enrichment queries ===== -->
|
|
|
|
<select id="findChannelsByRestaurantIds" resultType="map">
|
|
SELECT DISTINCT vr.restaurant_id, c.channel_name
|
|
FROM video_restaurants vr
|
|
JOIN videos v ON v.id = vr.video_id
|
|
JOIN channels c ON c.id = v.channel_id
|
|
WHERE vr.restaurant_id IN
|
|
<foreach item="id" collection="ids" open="(" separator="," close=")">
|
|
#{id}
|
|
</foreach>
|
|
</select>
|
|
|
|
<select id="findFoodsByRestaurantIds" resultType="map">
|
|
SELECT vr.restaurant_id, vr.foods_mentioned
|
|
FROM video_restaurants vr
|
|
WHERE vr.restaurant_id IN
|
|
<foreach item="id" collection="ids" open="(" separator="," close=")">
|
|
#{id}
|
|
</foreach>
|
|
</select>
|
|
|
|
<select id="findWithoutTabling" resultMap="restaurantMap">
|
|
SELECT r.id, r.name, r.address, r.region
|
|
FROM restaurants r
|
|
WHERE r.tabling_url IS NULL
|
|
AND r.latitude IS NOT NULL
|
|
AND EXISTS (SELECT 1 FROM video_restaurants vr WHERE vr.restaurant_id = r.id)
|
|
ORDER BY r.name
|
|
</select>
|
|
|
|
<select id="findWithoutCatchtable" resultMap="restaurantMap">
|
|
SELECT r.id, r.name, r.address, r.region
|
|
FROM restaurants r
|
|
WHERE r.catchtable_url IS NULL
|
|
AND r.latitude IS NOT NULL
|
|
AND EXISTS (SELECT 1 FROM video_restaurants vr WHERE vr.restaurant_id = r.id)
|
|
ORDER BY r.name
|
|
</select>
|
|
|
|
<!-- ===== Remap operations ===== -->
|
|
|
|
<update id="updateCuisineType">
|
|
UPDATE restaurants SET cuisine_type = #{cuisineType} WHERE id = #{id}
|
|
</update>
|
|
|
|
<update id="updateFoodsMentioned">
|
|
UPDATE video_restaurants SET foods_mentioned = #{foods,jdbcType=CLOB} WHERE id = #{id}
|
|
</update>
|
|
|
|
<select id="findForRemapCuisine" resultType="map">
|
|
SELECT r.id, r.name, r.cuisine_type,
|
|
(SELECT LISTAGG(TO_CHAR(DBMS_LOB.SUBSTR(vr.foods_mentioned, 500, 1)), '|')
|
|
WITHIN GROUP (ORDER BY vr.id)
|
|
FROM video_restaurants vr WHERE vr.restaurant_id = r.id) AS foods
|
|
FROM restaurants r
|
|
WHERE EXISTS (SELECT 1 FROM video_restaurants vr2 WHERE vr2.restaurant_id = r.id)
|
|
ORDER BY r.name
|
|
</select>
|
|
|
|
<select id="findForRemapFoods" resultType="map">
|
|
SELECT vr.id, r.name, r.cuisine_type,
|
|
TO_CHAR(vr.foods_mentioned) AS foods_mentioned,
|
|
v.title
|
|
FROM video_restaurants vr
|
|
JOIN restaurants r ON r.id = vr.restaurant_id
|
|
JOIN videos v ON v.id = vr.video_id
|
|
ORDER BY r.name
|
|
</select>
|
|
|
|
</mapper>
|