rocket-a


git -C '/home/opc/rocketa.git' show --

commit f70385251317348253f5ebffeffc00e3a1d6d563
Author: Satoshi Ujihara <satoshi_ujihara@fivegate.jp>
Date:   Thu Jan 29 17:46:01 2026 +0900

    gitignore 更新
    compser.json 更新
    本番環境oci_func 作成

diff --git a/.gitignore b/.gitignore
index 40e3443..aa558a7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,6 +10,5 @@ Homestead.json
 Homestead.yaml
 npm-debug.log
 yarn-error.log
-.vscode/sftp.json
-.vscode/settings.json
+.vscode
 ~$n18i.xlsx
diff --git a/composer.json b/composer.json
index e106b89..3727dc5 100644
--- a/composer.json
+++ b/composer.json
@@ -23,6 +23,7 @@
         "league/flysystem-aws-s3-v3": "^1.0",
         "maatwebsite/excel": "^3.1",
         "mpdf/mpdf": "^8.0",
+        "oracle/oci-php-sdk": "^0.1.0",
         "php-parallel-lint/php-console-color": "*",
         "phpoffice/phpspreadsheet": "^1.11"
     },
@@ -71,4 +72,4 @@
             "@php artisan key:generate --ansi"
         ]
     }
-}
+}
\ No newline at end of file
diff --git a/oci_func_prod/click/func.py b/oci_func_prod/click/func.py
new file mode 100644
index 0000000..dab5f63
--- /dev/null
+++ b/oci_func_prod/click/func.py
@@ -0,0 +1,640 @@
+
+import io
+import json
+import logging
+import json
+from sqlalchemy import create_engine, text
+import datetime
+import string
+import random
+import urllib.parse
+import hashlib
+import os, time
+import re
+
+from fdk import response
+from urllib.parse import quote
+
+os.environ['TZ'] = 'Asia/Tokyo'
+time.tzset()
+logging.basicConfig(level=logging.INFO)
+
+
+def handler(ctx, data: io.BytesIO = None):
+
+    logging.info("handler started")
+    headers = ctx.Headers()
+    full_url = ctx.RequestURL()
+    query_string = urllib.parse.urlparse(full_url).query
+    query_params = urllib.parse.parse_qs(query_string)
+
+    DB_HOST = "api.rocket-a.com"
+    DB_USER = "root"
+    DB_PASSWORD = "buSDonry4%h6rm-0fy"
+    DB_NAME = "rocketa-api"
+
+    # 接続URLの作成: mysql+pymysql://user:password@host/dbname
+    DB_URL = f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}/{DB_NAME}"
+    engine = create_engine(DB_URL)
+    connection = engine.connect()
+
+    # 変数初期値
+    dt_now                = datetime.datetime.now() + datetime.timedelta(hours = 9)
+    date                  = dt_now.strftime('%Y%m%d%H%M%S')
+    format_date           = dt_now.strftime('%Y-%m-%d %H:%M:%S')
+    created_ym            = dt_now.strftime('%Y%m')
+    url                   = ''
+    result_method         = 1
+    adjust_token          = ''
+    dv_pc                 = 0
+    dv_and_dc             = 0
+    dv_and_au             = 0
+    dv_and_sb             = 0
+    dv_and_other          = 0
+    dv_ios_dc             = 0
+    dv_ios_au             = 0
+    dv_ios_sb             = 0
+    dv_ios_other          = 0
+    error                 = 0
+    ad_id                 = ''
+    client_id             = ''
+    media_id              = ''
+    uid                   = ''
+    banner_id             = ''
+    ip                    = headers.get('x-real-ip')
+    # user_agent            = headers.get('User-Agent')
+    user_agent            = headers.get('user-agent')[1]
+    referer               = ''
+    net_price             = ''
+    net_price_unit        = ''
+    gross_price           = ''
+    gross_price_unit      = ''
+    group_id              = ''
+    other_parameters      = ''
+    hash_value            = ''
+    hash_text             = ''
+
+    other_parameter_list  =[]
+
+    defined_parameters = {
+        'ad_id',
+        'client_id',
+        'media_id',
+        'media_uid',
+        'banner_id',
+        'sid',
+        'amount',
+        'reward',
+        'count',
+        'status',
+        'date',
+        'stage'
+    }
+    if headers.get('referer') is not None:
+        referer = headers.get('referer')
+
+    # GETパラメータチェックと変数化
+    if query_params.get('ad_id') is not None:
+        ad_id = query_params.get('ad_id', [None])[0]
+    else:
+        error = 101
+
+    if query_params.get('client_id') is not None:
+        client_id = query_params.get('client_id', [None])[0]
+
+    else:
+        error = 102
+
+    if query_params.get('media_id') is not None:
+        media_id = query_params.get('media_id', [None])[0]
+
+    else:
+        error = 103
+
+    if query_params.get('media_uid') is not None:
+        uid = query_params.get('media_uid', [None])[0]
+
+    if query_params.get('banner_id') is not None:
+        banner_id = query_params.get('banner_id', [None])[0]
+
+    # パートナー独自パラメータをother_parametersに保存
+    for key, value in query_params.items():
+        logging.info(f"■■■125■■■■{key}={value[0]}")
+        if key not in defined_parameters:
+#            other_parameters = other_parameters + key + '=' + value + '&'
+            other_parameter_list.append(f"{key}={value[0]}")
+
+    other_parameters = '&'.join(other_parameter_list)
+    if other_parameters is not None:
+        logging.info(f"■■■131■■■■{other_parameters}")
+#        other_parameters = other_parameters.rstrip('&')
+
+    logging.info(f"■■■163■■■■{error}")
+    # 広告詳細取得
+    if error == 0:
+        sql_query  = 'SELECT '
+        sql_query += '    a.url, '
+        sql_query += '    a.result_method, '
+        sql_query += '    a.dv_pc, '
+        sql_query += '    a.dv_and_dc, '
+        sql_query += '    a.dv_and_au, '
+        sql_query += '    a.dv_and_sb, '
+        sql_query += '    a.dv_and_other, '
+        sql_query += '    a.dv_ios_dc, '
+        sql_query += '    a.dv_ios_au, '
+        sql_query += '    a.dv_ios_sb, '
+        sql_query += '    a.dv_ios_other, '
+        sql_query += '    a.adjust_token '
+        sql_query += 'FROM '
+        sql_query += '    ad_datas AS a '
+        sql_query += 'INNER JOIN '
+        sql_query += '    ad_join_media_datas AS j '
+        sql_query += 'ON '
+        sql_query += '    a.master_ad_id = j.master_ad_id '
+        sql_query += 'WHERE '
+        sql_query += '    a.master_ad_id = :master_ad_id '
+        sql_query += 'AND '
+        sql_query += '    a.client_id = :client_id '
+        sql_query += 'AND '
+        sql_query += '    j.media_id = :media_id '
+        sql_query += 'AND '
+        sql_query += '    a.status = 1 '
+        sql_query += 'AND '
+        sql_query += '    j.status = 1 '
+        sql_query += 'AND '
+        sql_query += '    a.start_date <= CAST(:start_date AS DATETIME) '
+        sql_query += 'AND '
+        sql_query += '    a.end_date > CAST(:end_date AS DATETIME) '
+        sql_query += 'AND '
+        sql_query += '    a.url != "" '
+        sql_query += 'LIMIT 1 '
+
+        params = {
+            "master_ad_id": int(ad_id),
+            "client_id": int(client_id),
+            "media_id": int(media_id),
+            "start_date": format_date,
+            "end_date": format_date,
+        }
+        # SQLAlchemyが安全にクエリを組み立て、SQLインジェクションを防ぐ
+        result = connection.execute(text(sql_query), params)
+        
+        # 結果を取得
+        ad_data = result.fetchone()
+
+        if ad_data:
+            ad_data_dict = ad_data._asdict() 
+            url = ad_data_dict['url']
+            result_method = ad_data_dict['result_method']
+            dv_pc = ad_data_dict['dv_pc']
+            dv_and_dc = ad_data_dict['dv_and_dc']
+            dv_and_au = ad_data_dict['dv_and_au']
+            dv_and_sb = ad_data_dict['dv_and_sb']
+            dv_and_other = ad_data_dict['dv_and_other']
+            dv_ios_dc = ad_data_dict['dv_ios_dc']
+            dv_ios_au = ad_data_dict['dv_ios_au']
+            dv_ios_sb = ad_data_dict['dv_ios_sb']
+            dv_ios_other = ad_data_dict['dv_ios_other']
+
+            try:
+                adjust_token = ad_data_dict['adjust_token']
+            except KeyError:
+                pass
+            
+            logging.info(dict(headers))
+            logging.info(f"■■■236■■■■{user_agent}")
+            
+            #デバイスチェック
+            # if any("iPhone OS" in item for item in user_agent) or any("iPad" in item for item in user_agent):
+            if 'iPhone OS' in user_agent or 'iPad' in user_agent:
+                if dv_ios_dc == 0 and dv_ios_au == 0 and dv_ios_sb == 0 and dv_ios_other == 0:
+                    error = 2
+
+            # elif any("Android" in item for item in user_agent):
+            elif 'Android' in user_agent:
+                if dv_and_dc == 0 and dv_and_au == 0 and dv_and_sb == 0 and dv_and_other == 0:
+                    error = 2
+
+            else:
+                if dv_pc == 0:
+                    error = 2
+        else:
+            error = 301
+
+
+        
+        logging.info("■■■255■■■■")
+        # 特別遷移先設定取得
+        if banner_id != '':
+            sql_query  = 'SELECT '
+            sql_query += '    url, '
+            sql_query += '    ios_url, '
+            sql_query += '    and_url '
+            sql_query += 'FROM '
+            sql_query += '    ad_material_datas '
+            sql_query += 'WHERE '
+            sql_query += '    status = 1 '
+            sql_query += 'AND '
+            sql_query += '    master_ad_id = :master_ad_id '
+            sql_query += 'AND '
+            sql_query += '    master_material_id = :master_material_id '
+            sql_query += 'LIMIT 1 '
+
+
+            params = {
+                "master_ad_id": int(ad_id),
+                "master_material_id": int(banner_id),
+            }
+            # SQLAlchemyが安全にクエリを組み立て、SQLインジェクションを防ぐ
+            result = connection.execute(text(sql_query), params)
+            
+            # 結果を取得
+            ad_material_data = result.fetchone()
+
+
+            if ad_material_data:
+                ad_material_data_dict = ad_material_data._asdict() 
+                # 特別遷移先に書き換え
+                if ad_material_data_dict['url']: 
+                    url = ad_material_data_dict['url']
+                # iOS
+                if ad_material_data_dict['ios_url']: 
+                    url = ad_material_data_dict['ios_url']
+                # Android
+                if ad_material_data_dict['and_url']: 
+                    url = ad_material_data_dict['and_url']
+        # sid生成
+        if error == 0:
+            while True:
+                sid  = ''
+
+                dat           = string.digits + string.ascii_lowercase
+                random_string = ''.join([random.choice(dat) for i in range(16)])
+                sid           = random_string + date
+
+                sql_query  = 'SELECT '
+                sql_query += '    id '
+                sql_query += 'FROM '
+                sql_query += '    click_records '
+                sql_query += 'WHERE '
+                sql_query += '    sid = :sid '
+                sql_query += 'AND '
+                sql_query += '    created_ym = :created_ym '
+                sql_query += 'LIMIT 1'
+
+
+                params = {
+                    "sid": sid,
+                    "created_ym": int(created_ym)
+                }
+                # SQLAlchemyが安全にクエリを組み立て、SQLインジェクションを防ぐ
+                result = connection.execute(text(sql_query), params)
+                
+                # 結果を取得
+                click_data = result.fetchone()
+
+                if not click_data:
+                    break
+
+    # クリック時の報酬を取得
+    logging.info(f"■■■329■■■■{error}")
+    if error == 0:
+        sql_query  = 'SELECT '
+        sql_query += '    net_price, '
+        sql_query += '    net_price_unit, '
+        sql_query += '    gross_price, '
+        sql_query += '    gross_price_unit, '
+        sql_query += '    group_id '
+        sql_query += 'FROM '
+        sql_query += '    ad_reward_datas '
+        sql_query += 'WHERE '
+        sql_query += '    ad_id = :ad_id '
+        sql_query += 'AND '
+        sql_query += '    type = 1 '
+        sql_query += 'AND '
+        sql_query += '    stage_id IS NULL '
+        sql_query += 'AND '
+        sql_query += '    product_id IS NULL '
+        sql_query += 'AND '
+        sql_query += '    ( '
+        sql_query += '        media_id = :media_id '
+        sql_query += '    OR '
+        sql_query += '        media_id IS NULL '
+        sql_query += '    ) '
+        sql_query += 'ORDER BY media_id DESC '
+
+        params = {
+            "ad_id": int(ad_id),
+            "media_id": int(media_id),
+        }
+        # SQLAlchemyが安全にクエリを組み立て、SQLインジェクションを防ぐ
+        result = connection.execute(text(sql_query), params)
+        
+        # 結果を取得
+        reward_list = result.fetchall()
+
+        if reward_list:
+            for reward_data in reward_list:
+                try:
+                    reward_data_dict = reward_data._asdict() 
+                    group_id = reward_data_dict['group_id']
+
+                    sql_query  = 'SELECT '
+                    sql_query += '    id '
+                    sql_query += 'FROM '
+                    sql_query += '    ad_reward_group_datas '
+                    sql_query += 'WHERE '
+                    sql_query += '    status = 1 '
+                    sql_query += 'AND '
+                    sql_query += '    master_id = :master_id '
+                    sql_query += 'AND '
+                    sql_query += '    ad_id = :ad_id '
+                    sql_query += 'AND '
+                    sql_query += '    valid_start_date <= :valid_start_date '
+                    sql_query += 'AND '
+                    sql_query += '    valid_end_date > :valid_end_date '
+
+                    logging.info(f"■■■386■■■■{reward_data_dict}")
+                    params = {
+                        "master_id": int(group_id or 0),
+                        "ad_id": int(ad_id or 0),
+                        "valid_start_date": format_date,
+                        "valid_end_date": format_date,
+                    }
+                    # SQLAlchemyが安全にクエリを組み立て、SQLインジェクションを防ぐ
+                    result = connection.execute(text(sql_query), params)
+                    
+                    # 結果を取得
+                    group_data = result.fetchone()
+
+                    if group_data:
+                        net_price        = reward_data_dict['net_price']
+                        net_price_unit   = reward_data_dict['net_price_unit']
+                        gross_price      = reward_data_dict['gross_price']
+                        gross_price_unit = reward_data_dict['gross_price_unit']
+                        break
+
+                    else:
+                        net_price        = reward_data_dict['net_price']
+                        net_price_unit   = reward_data_dict['net_price_unit']
+                        gross_price      = reward_data_dict['gross_price']
+                        gross_price_unit = reward_data_dict['gross_price_unit']
+                        continue
+                except KeyError:
+                    logging.info(f"■■■415■■■■KeyError")
+                    pass
+
+        else:
+            #報酬額データがない
+            error=302
+
+    #報酬額データがない
+    if net_price is None or net_price_unit is None or gross_price is None or gross_price_unit is None:
+        error=302
+        logging.info(f"■■■424■■■■報酬額ない")
+    # sidと遷移情報を保存
+    if error == 0:
+        sql_query  = 'INSERT INTO '
+        sql_query += '    click_records '
+        sql_query += '( '
+        sql_query += '    master_ad_id, '
+        sql_query += '    client_id, '
+        sql_query += '    media_id, '
+        sql_query += '    banner_id, '
+        sql_query += '    uid, '
+        sql_query += '    sid, '
+        sql_query += '    ip, '
+        sql_query += '    user_agent, '
+        sql_query += '    referer, '
+        sql_query += '    net_price, '
+        sql_query += '    net_price_unit, '
+        sql_query += '    gross_price, '
+        sql_query += '    gross_price_unit, '
+        sql_query += '    other_parameters, '
+        sql_query += '    created_ym '
+        sql_query += ') VALUES ( '
+        sql_query += '    :master_ad_id, '
+        sql_query += '    :client_id, '
+        sql_query += '    :media_id, '
+        sql_query += '    :banner_id, '
+        sql_query += '    :uid, '
+        sql_query += '    :sid, '
+        sql_query += '    :ip, '
+        sql_query += '    :user_agent, '
+        sql_query += '    :referer, '
+        sql_query += '    :net_price, '
+        sql_query += '    :net_price_unit, '
+        sql_query += '    :gross_price, '
+        sql_query += '    :gross_price_unit, '
+        sql_query += '    :other_parameters, '
+        sql_query += '    :created_ym '
+        sql_query += ') '
+
+        params = {
+            "master_ad_id": int(ad_id),
+            "client_id": int(client_id),
+            "media_id": int(media_id),
+            "banner_id": int(banner_id) if banner_id else None,
+            "uid": uid  if uid else None,
+            "sid": sid,
+            "ip": ip,
+            "user_agent": user_agent,
+            "referer": referer if referer else None,
+            "net_price": net_price if net_price else None,
+            "net_price_unit": net_price_unit if net_price_unit else None,
+            "gross_price": gross_price if gross_price else None,
+            "gross_price_unit": gross_price_unit if gross_price_unit else None,
+            "other_parameters": other_parameters if other_parameters else None,
+            "created_ym": int(created_ym),
+        }
+        result = connection.execute(text(sql_query), params)
+        logging.info(f"■■■445■■■{text(sql_query)}")
+        # トランザクションをコミットして変更を永続化
+        connection.commit()
+        """
+        # 広告ページへ遷移
+        if '?' in url:
+            # ソケット通信の時はパラメータはデフォ
+            if int(result_method) == 2:
+                url += '&sid=' + sid + '&ad_id=' + ad_id + '&client_id=' + client_id + '&media_id=' + media_id
+
+            # AppsFlyer特別処理
+            elif int(result_method) == 3:
+                url += '&af_siteid=' + media_id + '&pid=adleap_int&af_click_lookback=7d&af_sub4=' + sid + '&af_sub1=' + ad_id + '&af_sub2=' + client_id + '&af_sub3=' + media_id
+
+            # Adjust特別処理
+            elif int(result_method) == 4:
+                callback_url = 'https://api.rocket-a.com/service/result?sid=' + sid + '&ad_id=' + ad_id + '&client_id=' + client_id + '&media_id=' + media_id + '&device_id={idfa||gps_adid}&adjust_id={adid}'
+
+                if uid != '':
+                    callback_url += '&media_uid=' + uid
+
+                encode_callback_url = urllib.parse.quote(callback_url, safe='')
+
+                url += '&install_callback=' + encode_callback_url
+
+                # CPE
+                if adjust_token != '':
+                    encode_callback_url_add_token = urllib.parse.quote(callback_url + '&adjust_token=' + adjust_token, safe='')
+
+                    url += '&event_callback_' + adjust_token + '=' + encode_callback_url_add_token
+
+            # Airbridge特別処理
+            elif int(result_method) == 5:
+                url += '&sid=' + sid + '&custom_ad_id=' + ad_id + '&custom_client_id=' + client_id + '&sub_id=' + media_id
+
+            # Tyrads特別処理
+            elif int(result_method) == 6:
+                url += '&sub1=' + sid + '&sub2=' + ad_id + '&sub3=' + client_id + '&media_id=' + media_id
+
+            # AyeT Studios特別処理
+            elif int(result_method) == 7:
+                url += '&custom_1=' + sid + '&custom_2=' + ad_id + '&custom_3=' + client_id + '&media_id=' + media_id
+
+            # Torox特別処理
+            elif int(result_method) == 8:
+                url += '&user_id=' + sid + '&subid1=' + ad_id + '&subid2=' + client_id + '&subid3=' + media_id
+
+            # Appricot Ads特別処理
+            elif int(result_method) == 9:
+                url += '&sub1=' + sid + '&sub6=' + ad_id + '&sub7=' + client_id + '&media_id=' + media_id
+
+            # Singular特別処理
+            elif int(result_method) == 10:
+                url += '&cl=' + sid + '&ad_id=' + ad_id + '&client_id=' + client_id + '&media_id=' + media_id
+            else:                
+                pass
+                #client_id独自処理削除
+        else:
+            # ソケット通信の時はパラメータはデフォ
+            if int(result_method) == 2:
+                url += '?sid=' + sid + '&ad_id=' + ad_id + '&client_id=' + client_id + '&media_id=' + media_id
+
+            # AppsFlyer特別処理
+            elif int(result_method) == 3:
+                url += '?af_siteid=' + media_id + '&pid=adleap_int&af_click_lookback=7d&af_sub4=' + sid + '&af_sub1=' + ad_id + '&af_sub2=' + client_id + '&af_sub3=' + media_id
+
+            #Adjust特別処理
+            elif int(result_method) == 4:
+                callback_url = 'https://api.rocket-a.com/service/result?sid=' + sid + '&ad_id=' + ad_id + '&client_id=' + client_id + '&media_id=' + media_id + '&device_id={idfa||gps_adid}&adjust_id={adid}'
+
+                if uid != '':
+                    callback_url += '&media_uid=' + uid
+
+                encode_callback_url = urllib.parse.quote(callback_url, safe='')
+
+                url += '?install_callback=' + encode_callback_url
+
+                # CPE
+                if adjust_token != '':
+                    encode_callback_url_add_token = urllib.parse.quote(callback_url + '&adjust_token=' + adjust_token, safe='')
+
+                    url += '&event_callback_' + adjust_token + '=' + encode_callback_url_add_token
+
+            # Airbridge特別処理
+            elif int(result_method) == 5:
+                url += '?sid=' + sid + '&custom_ad_id=' + ad_id + '&custom_client_id=' + client_id + '&sub_id=' + media_id
+
+            # Tyrads特別処理
+            elif int(result_method) == 6:
+                url += '?sub1=' + sid + '&sub2=' + ad_id + '&sub3=' + client_id + '&media_id=' + media_id
+
+            # AyeT Studios特別処理
+            elif int(result_method) == 7:
+                url += '?custom_1=' + sid + '&custom_2=' + ad_id + '&custom_3=' + client_id + '&media_id=' + media_id
+
+            # Torox特別処理
+            elif int(result_method) == 8:
+                url += '?user_id=' + sid + '&subid1=' + ad_id + '&subid2=' + client_id + '&subid3=' + media_id
+
+            # Appricot Ads特別処理
+            elif int(result_method) == 9:
+                url += '?sub1=' + sid + '&sub6=' + ad_id + '&sub7=' + client_id + '&media_id=' + media_id
+
+            # Singular特別処理
+            elif int(result_method) == 10:
+                url += '?cl=' + sid + '&ad_id=' + ad_id + '&client_id=' + client_id + '&media_id=' + media_id
+
+            else:
+            #client_id独自処理削除
+                url += '?sid=' + sid + '&ad_id=' + ad_id + '&client_id=' + client_id + '&media_id=' + media_id
+
+        if uid != '' and int(result_method) != 4:
+            url += '&media_uid=' + uid
+        """
+        """
+        params = {
+            "sid": sid,
+            "ad_id": ad_id,
+            "client_id": client_id,
+            "media_id": media_id,
+        }
+
+
+        formatter = string.Formatter()
+        fields = [fname for _, fname, _, _ in formatter.parse(url) if fname]
+        missing = [f for f in fields if f not in params]
+        if missing:
+            params = {
+                "sid": sid,
+                "ad_id": ad_id,
+                "client_id": client_id,
+                "media_id": media_id,
+                "media_uid": uid,
+            }
+            formatter = string.Formatter()
+            fields = [fname for _, fname, _, _ in formatter.parse(url) if fname]
+            missing = [f for f in fields if f not in params]
+
+            if missing or uid == '':
+                error = 401
+                logging.info(f"■■■589■■■パラメータエラー")
+            else:
+                url = url.format(sid=sid, ad_id=ad_id, client_id=client_id, media_id=media_id, media_uid=uid)
+        else:
+            url = url.format(sid=sid, ad_id=ad_id, client_id=client_id, media_id=media_id)
+        """
+        #master_ad_idという名前だと扱いずらい
+        params["ad_id"] = params.pop("master_ad_id")
+        params["media_uid"] = params.pop("uid")
+        # 置換処理(URLエンコードあり)
+        result = re.sub(
+            r"\{(\w+)\}",
+            lambda m: quote(str(params.get(m.group(1)))) if params.get(m.group(1)) is not None else "",
+            url
+        )
+
+        logging.info(f"■■■595■■■{result}")
+
+        if error == 0:
+            return response.Response(
+                ctx,
+                status_code=302, 
+                headers={"Location": result}
+            )
+
+    
+    # エラーページへ遷移
+    error_page = 'https://api.rocket-a.com/service/error?error=' + str(error)
+    if error == 2:
+        error_page = 'https://api.rocket-a.com/service/device_error'
+
+
+    return response.Response(
+        ctx,
+        status_code=302, 
+        headers={"Location": error_page}
+    )
+
+'''
+        # testdic = click_data._asdict()
+        # logging.info(testdic)
+        return response.Response(
+            ctx,
+            response_data=f"Helloa{dv_ios_other}",
+            headers={"Content-Type": "application/json"}
+        ) 
+'''
+
+
+
diff --git a/oci_func_prod/click/func.yaml b/oci_func_prod/click/func.yaml
new file mode 100644
index 0000000..9eba686
--- /dev/null
+++ b/oci_func_prod/click/func.yaml
@@ -0,0 +1,8 @@
+schema_version: 20180708
+name: click
+version: 0.0.1
+runtime: python
+build_image: fnproject/python:3.12-dev
+run_image: fnproject/python:3.12
+entrypoint: /python/bin/fdk /function/func.py handler
+memory: 256
\ No newline at end of file
diff --git a/oci_func_prod/click/requirements.txt b/oci_func_prod/click/requirements.txt
new file mode 100644
index 0000000..a12b610
--- /dev/null
+++ b/oci_func_prod/click/requirements.txt
@@ -0,0 +1,3 @@
+fdk>=0.1.103
+sqlalchemy
+pymysql
diff --git a/oci_func_prod/click_test/func.py b/oci_func_prod/click_test/func.py
new file mode 100644
index 0000000..b5109f5
--- /dev/null
+++ b/oci_func_prod/click_test/func.py
@@ -0,0 +1,638 @@
+
+import io
+import json
+import logging
+import json
+from sqlalchemy import create_engine, text
+import datetime
+import string
+import random
+import urllib.parse
+import hashlib
+import os, time
+import re
+
+from fdk import response
+from urllib.parse import quote
+
+os.environ['TZ'] = 'Asia/Tokyo'
+time.tzset()
+logging.basicConfig(level=logging.INFO)
+
+
+def handler(ctx, data: io.BytesIO = None):
+
+    logging.info("handler started")
+    headers = ctx.Headers()
+    full_url = ctx.RequestURL()
+    query_string = urllib.parse.urlparse(full_url).query
+    query_params = urllib.parse.parse_qs(query_string)
+
+    DB_HOST = "api.rocket-a.com"
+    DB_USER = "root"
+    DB_PASSWORD = "buSDonry4%h6rm-0fy"
+    DB_NAME = "rocketa-api"
+
+    # 接続URLの作成: mysql+pymysql://user:password@host/dbname
+    DB_URL = f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}/{DB_NAME}"
+    engine = create_engine(DB_URL)
+    connection = engine.connect()
+
+    # 変数初期値
+    dt_now                = datetime.datetime.now() + datetime.timedelta(hours = 9)
+    date                  = dt_now.strftime('%Y%m%d%H%M%S')
+    format_date           = dt_now.strftime('%Y-%m-%d %H:%M:%S')
+    created_ym            = dt_now.strftime('%Y%m')
+    url                   = ''
+    result_method         = 1
+    adjust_token          = ''
+    dv_pc                 = 0
+    dv_and_dc             = 0
+    dv_and_au             = 0
+    dv_and_sb             = 0
+    dv_and_other          = 0
+    dv_ios_dc             = 0
+    dv_ios_au             = 0
+    dv_ios_sb             = 0
+    dv_ios_other          = 0
+    error                 = 0
+    ad_id                 = ''
+    client_id             = ''
+    media_id              = ''
+    uid                   = ''
+    banner_id             = ''
+    ip                    = headers.get('x-real-ip')
+    # user_agent            = headers.get('User-Agent')
+    user_agent            = headers.get('user-agent')[1]
+    referer               = ''
+    net_price             = ''
+    net_price_unit        = ''
+    gross_price           = ''
+    gross_price_unit      = ''
+    group_id              = ''
+    other_parameters      = ''
+    hash_value            = ''
+    hash_text             = ''
+
+    other_parameter_list  =[]
+
+    defined_parameters = {
+        'ad_id',
+        'client_id',
+        'media_id',
+        'media_uid',
+        'banner_id',
+        'sid',
+        'amount',
+        'reward',
+        'count',
+        'status',
+        'date',
+        'stage'
+    }
+    if headers.get('referer') is not None:
+        referer = headers.get('referer')
+
+    # GETパラメータチェックと変数化
+    if query_params.get('ad_id') is not None:
+        ad_id = query_params.get('ad_id', [None])[0]
+    else:
+        error = 101
+
+    if query_params.get('client_id') is not None:
+        client_id = query_params.get('client_id', [None])[0]
+
+    else:
+        error = 102
+
+    if query_params.get('media_id') is not None:
+        media_id = query_params.get('media_id', [None])[0]
+
+    else:
+        error = 103
+
+    if query_params.get('media_uid') is not None:
+        uid = query_params.get('media_uid', [None])[0]
+
+    if query_params.get('banner_id') is not None:
+        banner_id = query_params.get('banner_id', [None])[0]
+
+    # パートナー独自パラメータをother_parametersに保存
+    for key, value in query_params.items():
+        logging.info(f"■■■125■■■■{key}={value[0]}")
+        if key not in defined_parameters:
+#            other_parameters = other_parameters + key + '=' + value + '&'
+            other_parameter_list.append(f"{key}={value[0]}")
+
+    other_parameters = '&'.join(other_parameter_list)
+    if other_parameters is not None:
+        logging.info(f"■■■131■■■■{other_parameters}")
+#        other_parameters = other_parameters.rstrip('&')
+
+    logging.info(f"■■■163■■■■{error}")
+    # 広告詳細取得
+    if error == 0:
+        sql_query  = 'SELECT '
+        sql_query += '    a.url, '
+        sql_query += '    a.result_method, '
+        sql_query += '    a.dv_pc, '
+        sql_query += '    a.dv_and_dc, '
+        sql_query += '    a.dv_and_au, '
+        sql_query += '    a.dv_and_sb, '
+        sql_query += '    a.dv_and_other, '
+        sql_query += '    a.dv_ios_dc, '
+        sql_query += '    a.dv_ios_au, '
+        sql_query += '    a.dv_ios_sb, '
+        sql_query += '    a.dv_ios_other, '
+        sql_query += '    a.adjust_token '
+        sql_query += 'FROM '
+        sql_query += '    ad_datas AS a '
+        sql_query += 'INNER JOIN '
+        sql_query += '    ad_join_media_datas AS j '
+        sql_query += 'ON '
+        sql_query += '    a.master_ad_id = j.master_ad_id '
+        sql_query += 'WHERE '
+        sql_query += '    a.master_ad_id = :master_ad_id '
+        sql_query += 'AND '
+        sql_query += '    a.client_id = :client_id '
+        sql_query += 'AND '
+        sql_query += '    j.media_id = :media_id '
+        sql_query += 'AND '
+        sql_query += '    a.status = 1 '
+        sql_query += 'AND '
+        sql_query += '    j.status = 1 '
+        sql_query += 'AND '
+        sql_query += '    a.start_date <= CAST(:start_date AS DATETIME) '
+        sql_query += 'AND '
+        sql_query += '    a.end_date > CAST(:end_date AS DATETIME) '
+        sql_query += 'AND '
+        sql_query += '    a.url != "" '
+        sql_query += 'LIMIT 1 '
+
+        params = {
+            "master_ad_id": int(ad_id),
+            "client_id": int(client_id),
+            "media_id": int(media_id),
+            "start_date": format_date,
+            "end_date": format_date,
+        }
+        # SQLAlchemyが安全にクエリを組み立て、SQLインジェクションを防ぐ
+        result = connection.execute(text(sql_query), params)
+        
+        # 結果を取得
+        ad_data = result.fetchone()
+
+        if ad_data:
+            ad_data_dict = ad_data._asdict() 
+            url = ad_data_dict['url']
+            result_method = ad_data_dict['result_method']
+            dv_pc = ad_data_dict['dv_pc']
+            dv_and_dc = ad_data_dict['dv_and_dc']
+            dv_and_au = ad_data_dict['dv_and_au']
+            dv_and_sb = ad_data_dict['dv_and_sb']
+            dv_and_other = ad_data_dict['dv_and_other']
+            dv_ios_dc = ad_data_dict['dv_ios_dc']
+            dv_ios_au = ad_data_dict['dv_ios_au']
+            dv_ios_sb = ad_data_dict['dv_ios_sb']
+            dv_ios_other = ad_data_dict['dv_ios_other']
+
+            try:
+                adjust_token = ad_data_dict['adjust_token']
+            except KeyError:
+                pass
+            
+            logging.info(dict(headers))
+            logging.info(f"■■■236■■■■{user_agent}")
+            
+            #デバイスチェック
+            # if any("iPhone OS" in item for item in user_agent) or any("iPad" in item for item in user_agent):
+            if 'iPhone OS' in user_agent or 'iPad' in user_agent:
+                if dv_ios_dc == 0 and dv_ios_au == 0 and dv_ios_sb == 0 and dv_ios_other == 0:
+                    error = 2
+
+            # elif any("Android" in item for item in user_agent):
+            elif 'Android' in user_agent:
+                if dv_and_dc == 0 and dv_and_au == 0 and dv_and_sb == 0 and dv_and_other == 0:
+                    error = 2
+
+            else:
+                if dv_pc == 0:
+                    error = 2
+        else:
+            error = 301
+
+
+        
+        logging.info("■■■255■■■■")
+        # 特別遷移先設定取得
+        if banner_id != '':
+            sql_query  = 'SELECT '
+            sql_query += '    url, '
+            sql_query += '    ios_url, '
+            sql_query += '    and_url '
+            sql_query += 'FROM '
+            sql_query += '    ad_material_datas '
+            sql_query += 'WHERE '
+            sql_query += '    status = 1 '
+            sql_query += 'AND '
+            sql_query += '    master_ad_id = :master_ad_id '
+            sql_query += 'AND '
+            sql_query += '    master_material_id = :master_material_id '
+            sql_query += 'LIMIT 1 '
+
+
+            params = {
+                "master_ad_id": int(ad_id),
+                "master_material_id": int(banner_id),
+            }
+            # SQLAlchemyが安全にクエリを組み立て、SQLインジェクションを防ぐ
+            result = connection.execute(text(sql_query), params)
+            
+            # 結果を取得
+            ad_material_data = result.fetchone()
+
+
+            if ad_material_data:
+                ad_material_data_dict = ad_material_data._asdict() 
+                # 特別遷移先に書き換え
+                if ad_material_data_dict['url']: 
+                    url = ad_material_data_dict['url']
+                # iOS
+                if ad_material_data_dict['ios_url']: 
+                    url = ad_material_data_dict['ios_url']
+                # Android
+                if ad_material_data_dict['and_url']: 
+                    url = ad_material_data_dict['and_url']
+        # sid生成
+        if error == 0:
+            while True:
+                sid  = ''
+
+                dat           = string.digits + string.ascii_lowercase
+                random_string = ''.join([random.choice(dat) for i in range(16)])
+                sid           = random_string + date
+
+                sql_query  = 'SELECT '
+                sql_query += '    id '
+                sql_query += 'FROM '
+                sql_query += '    click_records_test '
+                sql_query += 'WHERE '
+                sql_query += '    sid = :sid '
+                sql_query += 'AND '
+                sql_query += '    created_ym = :created_ym '
+                sql_query += 'LIMIT 1'
+
+
+                params = {
+                    "sid": sid,
+                    "created_ym": int(created_ym)
+                }
+                # SQLAlchemyが安全にクエリを組み立て、SQLインジェクションを防ぐ
+                result = connection.execute(text(sql_query), params)
+                
+                # 結果を取得
+                click_data = result.fetchone()
+
+                if not click_data:
+                    break
+
+    # クリック時の報酬を取得
+    logging.info(f"■■■329■■■■{error}")
+    if error == 0:
+        sql_query  = 'SELECT '
+        sql_query += '    net_price, '
+        sql_query += '    net_price_unit, '
+        sql_query += '    gross_price, '
+        sql_query += '    gross_price_unit, '
+        sql_query += '    group_id '
+        sql_query += 'FROM '
+        sql_query += '    ad_reward_datas '
+        sql_query += 'WHERE '
+        sql_query += '    ad_id = :ad_id '
+        sql_query += 'AND '
+        sql_query += '    type = 1 '
+        sql_query += 'AND '
+        sql_query += '    stage_id IS NULL '
+        sql_query += 'AND '
+        sql_query += '    product_id IS NULL '
+        sql_query += 'AND '
+        sql_query += '    ( '
+        sql_query += '        media_id = :media_id '
+        sql_query += '    OR '
+        sql_query += '        media_id IS NULL '
+        sql_query += '    ) '
+        sql_query += 'ORDER BY media_id DESC '
+
+        params = {
+            "ad_id": int(ad_id),
+            "media_id": int(media_id),
+        }
+        # SQLAlchemyが安全にクエリを組み立て、SQLインジェクションを防ぐ
+        result = connection.execute(text(sql_query), params)
+        
+        # 結果を取得
+        reward_list = result.fetchall()
+
+        if reward_list:
+            for reward_data in reward_list:
+                try:
+                    reward_data_dict = reward_data._asdict() 
+                    group_id = reward_data_dict['group_id']
+
+                    sql_query  = 'SELECT '
+                    sql_query += '    id '
+                    sql_query += 'FROM '
+                    sql_query += '    ad_reward_group_datas '
+                    sql_query += 'WHERE '
+                    sql_query += '    status = 1 '
+                    sql_query += 'AND '
+                    sql_query += '    master_id = :master_id '
+                    sql_query += 'AND '
+                    sql_query += '    ad_id = :ad_id '
+                    sql_query += 'AND '
+                    sql_query += '    valid_start_date <= :valid_start_date '
+                    sql_query += 'AND '
+                    sql_query += '    valid_end_date > :valid_end_date '
+
+                    logging.info(f"■■■386■■■■{reward_data_dict}")
+                    params = {
+                        "master_id": int(group_id or 0),
+                        "ad_id": int(ad_id or 0),
+                        "valid_start_date": format_date,
+                        "valid_end_date": format_date,
+                    }
+                    # SQLAlchemyが安全にクエリを組み立て、SQLインジェクションを防ぐ
+                    result = connection.execute(text(sql_query), params)
+                    
+                    # 結果を取得
+                    group_data = result.fetchone()
+
+                    if group_data:
+                        net_price        = reward_data_dict['net_price']
+                        net_price_unit   = reward_data_dict['net_price_unit']
+                        gross_price      = reward_data_dict['gross_price']
+                        gross_price_unit = reward_data_dict['gross_price_unit']
+                        break
+
+                    else:
+                        net_price        = reward_data_dict['net_price']
+                        net_price_unit   = reward_data_dict['net_price_unit']
+                        gross_price      = reward_data_dict['gross_price']
+                        gross_price_unit = reward_data_dict['gross_price_unit']
+                        continue
+                except KeyError:
+                    logging.info(f"■■■415■■■■KeyError")
+                    pass
+
+        else:
+            #報酬額データがない
+            error=302
+
+    #報酬額データがない
+    if net_price is None or net_price_unit is None or gross_price is None or gross_price_unit is None:
+        error=302
+        logging.info(f"■■■424■■■■報酬額ない")
+    # sidと遷移情報を保存
+    if error == 0:
+        sql_query  = 'INSERT INTO '
+        sql_query += '    click_records_test '
+        sql_query += '( '
+        sql_query += '    master_ad_id, '
+        sql_query += '    client_id, '
+        sql_query += '    media_id, '
+        sql_query += '    banner_id, '
+        sql_query += '    uid, '
+        sql_query += '    sid, '
+        sql_query += '    ip, '
+        sql_query += '    user_agent, '
+        sql_query += '    referer, '
+        sql_query += '    net_price, '
+        sql_query += '    net_price_unit, '
+        sql_query += '    gross_price, '
+        sql_query += '    gross_price_unit, '
+        sql_query += '    other_parameters, '
+        sql_query += '    created_ym '
+        sql_query += ') VALUES ( '
+        sql_query += '    :master_ad_id, '
+        sql_query += '    :client_id, '
+        sql_query += '    :media_id, '
+        sql_query += '    :banner_id, '
+        sql_query += '    :uid, '
+        sql_query += '    :sid, '
+        sql_query += '    :ip, '
+        sql_query += '    :user_agent, '
+        sql_query += '    :referer, '
+        sql_query += '    :net_price, '
+        sql_query += '    :net_price_unit, '
+        sql_query += '    :gross_price, '
+        sql_query += '    :gross_price_unit, '
+        sql_query += '    :other_parameters, '
+        sql_query += '    :created_ym '
+        sql_query += ') '
+
+        params = {
+            "master_ad_id": int(ad_id),
+            "client_id": int(client_id),
+            "media_id": int(media_id),
+            "banner_id": int(banner_id) if banner_id else None,
+            "uid": uid  if uid else None,
+            "sid": sid,
+            "ip": ip,
+            "user_agent": user_agent,
+            "referer": referer if referer else None,
+            "net_price": net_price if net_price else None,
+            "net_price_unit": net_price_unit if net_price_unit else None,
+            "gross_price": gross_price if gross_price else None,
+            "gross_price_unit": gross_price_unit if gross_price_unit else None,
+            "other_parameters": other_parameters if other_parameters else None,
+            "created_ym": int(created_ym),
+        }
+        result = connection.execute(text(sql_query), params)
+        logging.info(f"■■■445■■■{text(sql_query)}")
+        # トランザクションをコミットして変更を永続化
+        connection.commit()
+        """
+        # 広告ページへ遷移
+        if '?' in url:
+            # ソケット通信の時はパラメータはデフォ
+            if int(result_method) == 2:
+                url += '&sid=' + sid + '&ad_id=' + ad_id + '&client_id=' + client_id + '&media_id=' + media_id
+
+            # AppsFlyer特別処理
+            elif int(result_method) == 3:
+                url += '&af_siteid=' + media_id + '&pid=adleap_int&af_click_lookback=7d&af_sub4=' + sid + '&af_sub1=' + ad_id + '&af_sub2=' + client_id + '&af_sub3=' + media_id
+
+            # Adjust特別処理
+            elif int(result_method) == 4:
+                callback_url = 'https://api.rocket-a.com/service/result?sid=' + sid + '&ad_id=' + ad_id + '&client_id=' + client_id + '&media_id=' + media_id + '&device_id={idfa||gps_adid}&adjust_id={adid}'
+
+                if uid != '':
+                    callback_url += '&media_uid=' + uid
+
+                encode_callback_url = urllib.parse.quote(callback_url, safe='')
+
+                url += '&install_callback=' + encode_callback_url
+
+                # CPE
+                if adjust_token != '':
+                    encode_callback_url_add_token = urllib.parse.quote(callback_url + '&adjust_token=' + adjust_token, safe='')
+
+                    url += '&event_callback_' + adjust_token + '=' + encode_callback_url_add_token
+
+            # Airbridge特別処理
+            elif int(result_method) == 5:
+                url += '&sid=' + sid + '&custom_ad_id=' + ad_id + '&custom_client_id=' + client_id + '&sub_id=' + media_id
+
+            # Tyrads特別処理
+            elif int(result_method) == 6:
+                url += '&sub1=' + sid + '&sub2=' + ad_id + '&sub3=' + client_id + '&media_id=' + media_id
+
+            # AyeT Studios特別処理
+            elif int(result_method) == 7:
+                url += '&custom_1=' + sid + '&custom_2=' + ad_id + '&custom_3=' + client_id + '&media_id=' + media_id
+
+            # Torox特別処理
+            elif int(result_method) == 8:
+                url += '&user_id=' + sid + '&subid1=' + ad_id + '&subid2=' + client_id + '&subid3=' + media_id
+
+            # Appricot Ads特別処理
+            elif int(result_method) == 9:
+                url += '&sub1=' + sid + '&sub6=' + ad_id + '&sub7=' + client_id + '&media_id=' + media_id
+
+            # Singular特別処理
+            elif int(result_method) == 10:
+                url += '&cl=' + sid + '&ad_id=' + ad_id + '&client_id=' + client_id + '&media_id=' + media_id
+            else:                
+                pass
+                #client_id独自処理削除
+        else:
+            # ソケット通信の時はパラメータはデフォ
+            if int(result_method) == 2:
+                url += '?sid=' + sid + '&ad_id=' + ad_id + '&client_id=' + client_id + '&media_id=' + media_id
+
+            # AppsFlyer特別処理
+            elif int(result_method) == 3:
+                url += '?af_siteid=' + media_id + '&pid=adleap_int&af_click_lookback=7d&af_sub4=' + sid + '&af_sub1=' + ad_id + '&af_sub2=' + client_id + '&af_sub3=' + media_id
+
+            #Adjust特別処理
+            elif int(result_method) == 4:
+                callback_url = 'https://api.rocket-a.com/service/result?sid=' + sid + '&ad_id=' + ad_id + '&client_id=' + client_id + '&media_id=' + media_id + '&device_id={idfa||gps_adid}&adjust_id={adid}'
+
+                if uid != '':
+                    callback_url += '&media_uid=' + uid
+
+                encode_callback_url = urllib.parse.quote(callback_url, safe='')
+
+                url += '?install_callback=' + encode_callback_url
+
+                # CPE
+                if adjust_token != '':
+                    encode_callback_url_add_token = urllib.parse.quote(callback_url + '&adjust_token=' + adjust_token, safe='')
+
+                    url += '&event_callback_' + adjust_token + '=' + encode_callback_url_add_token
+
+            # Airbridge特別処理
+            elif int(result_method) == 5:
+                url += '?sid=' + sid + '&custom_ad_id=' + ad_id + '&custom_client_id=' + client_id + '&sub_id=' + media_id
+
+            # Tyrads特別処理
+            elif int(result_method) == 6:
+                url += '?sub1=' + sid + '&sub2=' + ad_id + '&sub3=' + client_id + '&media_id=' + media_id
+
+            # AyeT Studios特別処理
+            elif int(result_method) == 7:
+                url += '?custom_1=' + sid + '&custom_2=' + ad_id + '&custom_3=' + client_id + '&media_id=' + media_id
+
+            # Torox特別処理
+            elif int(result_method) == 8:
+                url += '?user_id=' + sid + '&subid1=' + ad_id + '&subid2=' + client_id + '&subid3=' + media_id
+
+            # Appricot Ads特別処理
+            elif int(result_method) == 9:
+                url += '?sub1=' + sid + '&sub6=' + ad_id + '&sub7=' + client_id + '&media_id=' + media_id
+
+            # Singular特別処理
+            elif int(result_method) == 10:
+                url += '?cl=' + sid + '&ad_id=' + ad_id + '&client_id=' + client_id + '&media_id=' + media_id
+
+            else:
+            #client_id独自処理削除
+                url += '?sid=' + sid + '&ad_id=' + ad_id + '&client_id=' + client_id + '&media_id=' + media_id
+
+        if uid != '' and int(result_method) != 4:
+            url += '&media_uid=' + uid
+        """
+        """
+        params = {
+            "sid": sid,
+            "ad_id": ad_id,
+            "client_id": client_id,
+            "media_id": media_id,
+        }
+
+
+        formatter = string.Formatter()
+        fields = [fname for _, fname, _, _ in formatter.parse(url) if fname]
+        missing = [f for f in fields if f not in params]
+        if missing:
+            params = {
+                "sid": sid,
+                "ad_id": ad_id,
+                "client_id": client_id,
+                "media_id": media_id,
+                "media_uid": uid,
+            }
+            formatter = string.Formatter()
+            fields = [fname for _, fname, _, _ in formatter.parse(url) if fname]
+            missing = [f for f in fields if f not in params]
+
+            if missing or uid == '':
+                error = 401
+                logging.info(f"■■■589■■■パラメータエラー")
+            else:
+                url = url.format(sid=sid, ad_id=ad_id, client_id=client_id, media_id=media_id, media_uid=uid)
+        else:
+            url = url.format(sid=sid, ad_id=ad_id, client_id=client_id, media_id=media_id)
+        """
+        #master_ad_idという名前だと扱いずらい
+        params["ad_id"] = params.pop("master_ad_id")
+        # 置換処理(URLエンコードあり)
+        result = re.sub(
+            r"\{(\w+)\}",
+            lambda m: quote(str(params.get(m.group(1)))) if params.get(m.group(1)) is not None else "",
+            url
+        )
+        logging.info(f"■■■595■■■{result}")
+
+        if error == 0:
+            return response.Response(
+                ctx,
+                status_code=302, 
+                headers={"Location": result}
+            )
+
+    
+    # エラーページへ遷移
+    error_page = 'https://api.rocket-a.com/service/error?error=' + str(error)
+    if error == 2:
+        error_page = 'https://api.rocket-a.com/service/device_error'
+
+
+    return response.Response(
+        ctx,
+        status_code=302, 
+        headers={"Location": error_page}
+    )
+
+'''
+        # testdic = click_data._asdict()
+        # logging.info(testdic)
+        return response.Response(
+            ctx,
+            response_data=f"Helloa{dv_ios_other}",
+            headers={"Content-Type": "application/json"}
+        ) 
+'''
+
+
+
diff --git a/oci_func_prod/click_test/func.yaml b/oci_func_prod/click_test/func.yaml
new file mode 100644
index 0000000..e0790f6
--- /dev/null
+++ b/oci_func_prod/click_test/func.yaml
@@ -0,0 +1,8 @@
+schema_version: 20180708
+name: click_test
+version: 0.0.1
+runtime: python
+build_image: fnproject/python:3.12-dev
+run_image: fnproject/python:3.12
+entrypoint: /python/bin/fdk /function/func.py handler
+memory: 256
\ No newline at end of file
diff --git a/oci_func_prod/click_test/requirements.txt b/oci_func_prod/click_test/requirements.txt
new file mode 100644
index 0000000..a12b610
--- /dev/null
+++ b/oci_func_prod/click_test/requirements.txt
@@ -0,0 +1,3 @@
+fdk>=0.1.103
+sqlalchemy
+pymysql
diff --git a/oci_func_prod/device_error/func.py b/oci_func_prod/device_error/func.py
new file mode 100644
index 0000000..19f7d4c
--- /dev/null
+++ b/oci_func_prod/device_error/func.py
@@ -0,0 +1,39 @@
+import io
+import json
+import logging
+import datetime
+
+from fdk import response
+
+dt_now = datetime.datetime.now() + datetime.timedelta(hours = 9)
+
+html  = '<!doctype html>'
+html += '<html lang="ja">'
+html += '<head>'
+html += '<meta charset="utf-8">'
+html += '<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">'
+html += '<title>Rocket A|無効のリンク</title>'
+html += '<style>*,*::before,*::after{box-sizing: border-box;}body{display:-webkit-box;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;flex-direction:column;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";margin:0;min-height:100vh;font-size:0.88rem;font-weight:400;line-height:1.5;color:#555;text-align:left;background-color:#ffffff;}.container .logo{margin:8rem auto 1rem;padding:3rem .5rem;width:120px;height:120px;background-color:#343a40;border-radius:50%;}.container .logo img{display:inline-block;margin:0 auto;line-height:120px;}.container h5,.container p{margin:0;padding:0;text-align:center;color:#555;}.container h5{font-size:1.5rem;font-weight:bold;line-height:1.2;margin-bottom:.5rem;}.container p,.copyright{font-size:0.88rem;}.copyright{margin:auto auto 0;padding:.5rem 0;width:100%;text-align:center;color:#fff;background-color:#343a40;}</style>'
+html += '</head>'
+html += '<body>'
+html += '<div class="container">'
+html += '<div class="logo"><img src="https://client.rocket-a.com/front/rocketa_logo_mono.png" width="100%" title="" alt=""></div>'
+html += '<h5>無効のリンク</h5>'
+html += '<p>誠に申し訳ございませんが、<br>お使いのデバイスこの広告に対応しておりません。</p>'
+html += '</div>'
+html += '<div class="copyright">Copyright (C) 2025-'
+html += dt_now.strftime('%Y')
+html += ' aix inc. All Rights Reserved.</div>'
+html += '</body>'
+html += '</html>'
+
+
+def handler(ctx, data: io.BytesIO = None):
+
+    return response.Response (
+        ctx,
+        status_code=404,
+        headers={"Content-Type": "text/html"},
+        response_data= html
+    )
+
diff --git a/oci_func_prod/device_error/func.yaml b/oci_func_prod/device_error/func.yaml
new file mode 100644
index 0000000..defc596
--- /dev/null
+++ b/oci_func_prod/device_error/func.yaml
@@ -0,0 +1,8 @@
+schema_version: 20180708
+name: device_error
+version: 0.0.1
+runtime: python
+build_image: fnproject/python:3.12-dev
+run_image: fnproject/python:3.12
+entrypoint: /python/bin/fdk /function/func.py handler
+memory: 256
\ No newline at end of file
diff --git a/oci_func_prod/device_error/requirements.txt b/oci_func_prod/device_error/requirements.txt
new file mode 100644
index 0000000..58a638e
--- /dev/null
+++ b/oci_func_prod/device_error/requirements.txt
@@ -0,0 +1 @@
+fdk>=0.1.103
\ No newline at end of file
diff --git a/oci_func_prod/error/func.py b/oci_func_prod/error/func.py
new file mode 100644
index 0000000..df0e7d1
--- /dev/null
+++ b/oci_func_prod/error/func.py
@@ -0,0 +1,60 @@
+import io
+import json
+import logging
+import datetime
+import urllib.parse
+
+from fdk import response
+
+dt_now = datetime.datetime.now() + datetime.timedelta(hours = 9)
+
+#error
+#101 ad_id param error
+#102 client_id param error
+#103 media_id param error
+#301 not found ad data
+#302 not found rewward data
+#401 url error (遷移先パラメータと取得パラメータの不一致)
+
+
+logging.basicConfig(level=logging.INFO)
+
+def handler(ctx, data: io.BytesIO = None):
+
+    full_url = ctx.RequestURL()
+    query_string = urllib.parse.urlparse(full_url).query
+    query_params = urllib.parse.parse_qs(query_string)
+    error = ''
+    if query_params.get('error') is not None:
+        error = query_params.get('error', [None])[0]
+    return output(ctx, error)
+
+
+
+def output(ctx, error):
+    html  = '<!doctype html>'
+    html += '<html lang="ja">'
+    html += '<head>'
+    html += '<meta charset="utf-8">'
+    html += '<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">'
+    html += '<title>Rocket A|無効のリンク</title>'
+    html += '<style>*,*::before,*::after{box-sizing: border-box;}body{display:-webkit-box;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;flex-direction:column;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";margin:0;min-height:100vh;font-size:0.88rem;font-weight:400;line-height:1.5;color:#555;text-align:left;background-color:#ffffff;}.container .logo{margin:8rem auto 1rem;padding:3rem .5rem;width:120px;height:120px;background-color:#343a40;border-radius:50%;}.container .logo img{display:inline-block;margin:0 auto;line-height:120px;}.container h5,.container p{margin:0;padding:0;text-align:center;color:#555;}.container h5{font-size:1.5rem;font-weight:bold;line-height:1.2;margin-bottom:.5rem;}.container p,.copyright{font-size:0.88rem;}.copyright{margin:auto auto 0;padding:.5rem 0;width:100%;text-align:center;color:#fff;background-color:#343a40;}</style>'
+    html += '</head>'
+    html += '<body>'
+    html += '<div class="container">'
+    html += '<div class="logo"><img src="https://client.rocket-a.com/front/rocketa_logo_mono.png" width="100%" title="" alt=""></div>'
+    html += '<h5>無効のリンク<br />ERROR CODE:'+error+'</h5>'
+    html += '<p>誠に申し訳ございませんが、<br>この広告のリンクは現在無効となっております。</p>'
+    html += '</div>'
+    html += '<div class="copyright">Copyright (C) 2025-'
+    html += dt_now.strftime('%Y')
+    html += ' aix inc. All Rights Reserved.</div>'
+    html += '</body>'
+    html += '</html>'
+
+    return response.Response (
+        ctx,
+        status_code=404,
+        headers={"Content-Type": "text/html"},
+        response_data= html
+    )
\ No newline at end of file
diff --git a/oci_func_prod/error/func.yaml b/oci_func_prod/error/func.yaml
new file mode 100644
index 0000000..748cbdf
--- /dev/null
+++ b/oci_func_prod/error/func.yaml
@@ -0,0 +1,8 @@
+schema_version: 20180708
+name: error
+version: 0.0.1
+runtime: python
+build_image: fnproject/python:3.12-dev
+run_image: fnproject/python:3.12
+entrypoint: /python/bin/fdk /function/func.py handler
+memory: 256
\ No newline at end of file
diff --git a/oci_func_prod/error/requirements.txt b/oci_func_prod/error/requirements.txt
new file mode 100644
index 0000000..58a638e
--- /dev/null
+++ b/oci_func_prod/error/requirements.txt
@@ -0,0 +1 @@
+fdk>=0.1.103
\ No newline at end of file
diff --git a/oci_func_prod/log/func.py b/oci_func_prod/log/func.py
new file mode 100644
index 0000000..7cce0c1
--- /dev/null
+++ b/oci_func_prod/log/func.py
@@ -0,0 +1,63 @@
+import io
+import json
+import logging
+import json
+import datetime
+import urllib.parse
+from sqlalchemy import create_engine, text
+
+from fdk import response
+
+logging.basicConfig(level=logging.INFO)
+
+int_param_list = {
+    'ad_id',
+    'client_id',
+    'amount',
+    'sales_count',
+    'stage',
+}
+
+
+def handler(ctx, data: io.BytesIO = None):
+    logging.info("handler started")
+    headers = ctx.Headers()
+
+    full_url = ctx.RequestURL()
+
+    DB_HOST = "api.rocket-a.com"
+    DB_USER = "root"
+    DB_PASSWORD = "buSDonry4%h6rm-0fy"
+    DB_NAME = "rocketa-api"
+
+    # 接続URLの作成: mysql+pymysql://user:password@host/dbname
+    DB_URL = f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}/{DB_NAME}"
+    engine = create_engine(DB_URL)
+    connection = engine.connect()
+
+
+    # 成果データ保存
+    sql_query  = 'INSERT INTO '
+    sql_query += ' logs '
+    sql_query += '( '
+    sql_query += '    log '
+    sql_query += ') VALUES ( '
+    sql_query += '   :log '
+    sql_query += ') '
+    params = {
+        "log": full_url if full_url else None,
+    }
+    result = connection.execute(text(sql_query), params)
+
+    # トランザクションをコミットして変更を永続化
+    connection.commit()
+
+    # 終了ステータス表示
+
+    return response.Response(
+        ctx,
+        status_code=200,
+        response_data=json.dumps('OK'),
+        headers={"Access-Control-Allow-Origin": "*","Access-Control-Allow-Headers": "Content-Type"}
+    )
+
diff --git a/oci_func_prod/log/func.yaml b/oci_func_prod/log/func.yaml
new file mode 100644
index 0000000..e9184c9
--- /dev/null
+++ b/oci_func_prod/log/func.yaml
@@ -0,0 +1,8 @@
+schema_version: 20180708
+name: log
+version: 0.0.1
+runtime: python
+build_image: fnproject/python:3.12-dev
+run_image: fnproject/python:3.12
+entrypoint: /python/bin/fdk /function/func.py handler
+memory: 256
\ No newline at end of file
diff --git a/oci_func_prod/log/requirements.txt b/oci_func_prod/log/requirements.txt
new file mode 100644
index 0000000..e3b54a1
--- /dev/null
+++ b/oci_func_prod/log/requirements.txt
@@ -0,0 +1,3 @@
+fdk>=0.1.103
+sqlalchemy
+pymysql
\ No newline at end of file
diff --git a/oci_func_prod/result/func.py b/oci_func_prod/result/func.py
new file mode 100644
index 0000000..6b19337
--- /dev/null
+++ b/oci_func_prod/result/func.py
@@ -0,0 +1,213 @@
+import io
+import json
+import logging
+import json
+import datetime
+import urllib.parse
+from sqlalchemy import create_engine, text
+
+from fdk import response
+
+logging.basicConfig(level=logging.INFO)
+
+int_param_list = {
+    'ad_id',
+    'client_id',
+    'amount',
+    'sales_count',
+    'stage',
+}
+
+
+def handler(ctx, data: io.BytesIO = None):
+    logging.info("handler started")
+    headers = ctx.Headers()
+
+    full_url = ctx.RequestURL()
+    query_string = urllib.parse.urlparse(full_url).query
+    logging.info(f"■■■28■■■{query_string}")
+    query_params = urllib.parse.parse_qs(query_string)
+
+    DB_HOST = "api.rocket-a.com"
+    DB_USER = "root"
+    DB_PASSWORD = "buSDonry4%h6rm-0fy"
+    DB_NAME = "rocketa-api"
+
+    # 接続URLの作成: mysql+pymysql://user:password@host/dbname
+    DB_URL = f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}/{DB_NAME}"
+    engine = create_engine(DB_URL)
+    connection = engine.connect()
+
+    send_param_list = {
+        'sid'          : '',
+        'ad_id'        : '',
+        'client_id'    : '',
+        'uid'          : '',
+        'uid2'         : '',
+        'product_code' : '',
+        'amount'       : '',
+        'sales_count'  : '',
+        'stage'        : '',
+        'domain'       : '',
+        'sender'       : '',
+    }
+    dt_now      = datetime.datetime.now() + datetime.timedelta(hours = 9)
+    format_date = dt_now.strftime('%Y-%m-%d %H:%M:%S')
+    created_ym  = dt_now.strftime('%Y%m')
+    error       = 0
+    parameters  = query_string
+    ip          = ''
+    param_event = ''
+
+    i = 0
+    for key, value_list in query_params.items():
+        logging.info(f"■■■{key}■■■")
+        # 1. キーが格納先のリストに存在するかチェック
+        if key in send_param_list:
+            
+            # 2. 値のリストが空ではないかチェック (あれば処理を続行)
+            if value_list:
+                
+                # 3. リストの最初の要素 [0] を取り出し、格納先の辞書に代入
+                #    (通常、クエリパラメータで同じキーが複数回渡されることは稀なため)
+                send_param_list[key] = value_list[0]
+                i += 1
+            else:
+                pass
+    
+    if i == 0:
+
+        return response.Response(
+            ctx,
+            response_data=json.dumps('Bad Request'),
+            headers={"Content-Type": "application/json"}
+        ) 
+
+
+
+    # ソケット判定 謎の値を送られてきても対応できるように
+    if send_param_list['sender'] != '2' and send_param_list['sender'] != '3':
+        send_param_list['sender'] = 1
+
+        # クライアントのip取得
+        ip = headers.get('x-real-ip')
+
+    # 必須パラメータチェック
+    if send_param_list['sid'] == '':
+        error = 1
+
+    #if send_param_list['ad_id'] == '':
+    #    error = 1
+
+    #if send_param_list['client_id'] == '':
+    #    error = 1
+    
+    # 廃棄イベントのリストに有る場合は処理を止める 
+    if query_params.get('event') is not None:
+        param_event = query_params.get('event')
+        
+        sql_query  = 'SELECT '
+        sql_query += '    id '
+        sql_query += 'FROM '
+        sql_query += '    dispose_events '
+        sql_query += 'WHERE '
+        sql_query += '    event = :event '
+        sql_query += 'LIMIT 1 '
+
+        params = {
+            "event": param_event,
+        }
+        # SQLAlchemyが安全にクエリを組み立て、SQLインジェクションを防ぐ
+        result = connection.execute(text(sql_query), params)
+
+        dispose_event = result.fetchone()
+    
+        if dispose_event:
+            return response.Response(
+                ctx,
+                response_data=json.dumps('Dispose Event'),
+                headers={"Content-Type": "application/json"}
+            ) 
+        
+    # 成果データ保存
+    sql_query  = 'INSERT INTO '
+    sql_query += '    result_records '
+    sql_query += '( '
+    sql_query += '    ip, '
+    sql_query += '    sid, '
+    sql_query += '    ad_id, '
+    sql_query += '    client_id, '
+    sql_query += '    uid, '
+    sql_query += '    uid2, '
+    sql_query += '    product_code, '
+    sql_query += '    amount, '
+    sql_query += '    sales_count, '
+    sql_query += '    stage, '
+    sql_query += '    domain, '
+    sql_query += '    sender, '
+    sql_query += '    parameters, '
+    sql_query += '    created_ym '
+    sql_query += ') VALUES ( '
+    sql_query += '    :ip, '
+    sql_query += '    :sid, '
+    sql_query += '    :ad_id, '
+    sql_query += '    :client_id, '
+    sql_query += '    :uid, '
+    sql_query += '    :uid2, '
+    sql_query += '    :product_code, '
+    sql_query += '    :amount, '
+    sql_query += '    :sales_count, '
+    sql_query += '    :stage, '
+    sql_query += '    :domain, '
+    sql_query += '    :sender, '
+    sql_query += '    :parameters, '
+    sql_query += '    :created_ym '
+    sql_query += ') '
+
+    params = {
+        "ip": ip if ip else None,
+        "sid": send_param_list['sid'] if send_param_list['sid'] else None,
+        "ad_id": int(send_param_list['ad_id']) if send_param_list['ad_id'] else None,
+        "client_id": int(send_param_list['client_id']) if send_param_list['client_id'] else None,
+        "uid": send_param_list['uid'] if send_param_list['uid'] else None,
+        "uid2": send_param_list['uid2'] if send_param_list['uid2'] else None,
+        "product_code": send_param_list['product_code'] if send_param_list['product_code'] else None,
+        "amount": int(send_param_list['amount']) if send_param_list['amount'] else None,
+        "sales_count": int(send_param_list['sales_count']) if send_param_list['sales_count'] else None,
+        "stage": int(send_param_list['stage']) if send_param_list['stage'] else None,
+        "domain": send_param_list['domain'] if send_param_list['domain'] else None,
+        "sender": int(send_param_list['sender']),
+        "parameters": parameters,
+        "created_ym": int(created_ym), 
+    }
+    result = connection.execute(text(sql_query), params)
+
+    # トランザクションをコミットして変更を永続化
+    connection.commit()
+
+
+
+    # 終了ステータス表示
+    if error == 0:
+        return response.Response(
+            ctx,
+            status_code=200,
+            response_data=json.dumps('OK'),
+            headers={"Access-Control-Allow-Origin": "*","Access-Control-Allow-Headers": "Content-Type"}
+        )
+    else:
+        return response.Response(
+            ctx,
+            status_code=200,
+            response_data=json.dumps('Bad Request'),
+            headers={"Access-Control-Allow-Origin": "*","Access-Control-Allow-Headers": "Content-Type"}
+        )
+'''
+    # testdic = send_param_list._asdict()
+    logging.info(send_param_list)
+    return response.Response(
+        ctx,
+        response_data=f"Hello",
+        headers={"Content-Type": "application/json"}
+    ) 
+'''
\ No newline at end of file
diff --git a/oci_func_prod/result/func.yaml b/oci_func_prod/result/func.yaml
new file mode 100644
index 0000000..a9d4366
--- /dev/null
+++ b/oci_func_prod/result/func.yaml
@@ -0,0 +1,8 @@
+schema_version: 20180708
+name: result
+version: 0.0.1
+runtime: python
+build_image: fnproject/python:3.12-dev
+run_image: fnproject/python:3.12
+entrypoint: /python/bin/fdk /function/func.py handler
+memory: 256
\ No newline at end of file
diff --git a/oci_func_prod/result/requirements.txt b/oci_func_prod/result/requirements.txt
new file mode 100644
index 0000000..a12b610
--- /dev/null
+++ b/oci_func_prod/result/requirements.txt
@@ -0,0 +1,3 @@
+fdk>=0.1.103
+sqlalchemy
+pymysql
diff --git a/oci_func_prod/result_test/func.py b/oci_func_prod/result_test/func.py
new file mode 100644
index 0000000..410920d
--- /dev/null
+++ b/oci_func_prod/result_test/func.py
@@ -0,0 +1,214 @@
+import io
+import json
+import logging
+import json
+import datetime
+import urllib.parse
+from sqlalchemy import create_engine, text
+
+from fdk import response
+
+logging.basicConfig(level=logging.INFO)
+
+int_param_list = {
+    'ad_id',
+    'client_id',
+    'amount',
+    'sales_count',
+    'stage',
+}
+
+
+def handler(ctx, data: io.BytesIO = None):
+    logging.info("handler started")
+    headers = ctx.Headers()
+
+    full_url = ctx.RequestURL()
+    query_string = urllib.parse.urlparse(full_url).query
+    logging.info(f"■■■28■■■{query_string}")
+    query_params = urllib.parse.parse_qs(query_string)
+
+    DB_HOST = "api.rocket-a.com"
+    DB_USER = "root"
+    DB_PASSWORD = "buSDonry4%h6rm-0fy"
+    DB_NAME = "rocketa-api"
+
+    # 接続URLの作成: mysql+pymysql://user:password@host/dbname
+    DB_URL = f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}/{DB_NAME}"
+    engine = create_engine(DB_URL)
+    connection = engine.connect()
+
+    send_param_list = {
+        'sid'          : '',
+        'ad_id'        : '',
+        'client_id'    : '',
+        'uid'          : '',
+        'uid2'         : '',
+        'product_code' : '',
+        'amount'       : '',
+        'sales_count'  : '',
+        'stage'        : '',
+        'domain'       : '',
+        'sender'       : '',
+    }
+    dt_now      = datetime.datetime.now() + datetime.timedelta(hours = 9)
+    format_date = dt_now.strftime('%Y-%m-%d %H:%M:%S')
+    created_ym  = dt_now.strftime('%Y%m')
+    error       = 0
+    parameters  = query_string
+    ip          = ''
+    param_event = ''
+
+    i = 0
+    for key, value_list in query_params.items():
+        logging.info(f"■■■{key}■■■")
+        # 1. キーが格納先のリストに存在するかチェック
+        if key in send_param_list:
+            
+            # 2. 値のリストが空ではないかチェック (あれば処理を続行)
+            if value_list:
+                
+                # 3. リストの最初の要素 [0] を取り出し、格納先の辞書に代入
+                #    (通常、クエリパラメータで同じキーが複数回渡されることは稀なため)
+                send_param_list[key] = value_list[0]
+                i += 1
+            else:
+                pass
+    
+    if i == 0:
+
+        return response.Response(
+            ctx,
+            response_data=json.dumps('Bad Request'),
+            headers={"Content-Type": "application/json"}
+        ) 
+
+
+
+    # ソケット判定 謎の値を送られてきても対応できるように
+    if send_param_list['sender'] != '2' and send_param_list['sender'] != '3':
+        send_param_list['sender'] = 1
+
+        # クライアントのip取得
+        ip = headers.get('x-real-ip')
+
+    # 必須パラメータチェック
+    if send_param_list['sid'] == '':
+        error = 1
+
+    #if send_param_list['ad_id'] == '':
+    #    error = 1
+
+    #if send_param_list['client_id'] == '':
+    #    error = 1
+    
+    # 廃棄イベントのリストに有る場合は処理を止める 
+    if query_params.get('event') is not None:
+        param_event = query_params.get('event')
+        
+        sql_query  = 'SELECT '
+        sql_query += '    id '
+        sql_query += 'FROM '
+        sql_query += '    dispose_events '
+        sql_query += 'WHERE '
+        sql_query += '    event = :event '
+        sql_query += 'LIMIT 1 '
+
+        params = {
+            "event": param_event,
+        }
+        # SQLAlchemyが安全にクエリを組み立て、SQLインジェクションを防ぐ
+        result = connection.execute(text(sql_query), params)
+
+        dispose_event = result.fetchone()
+    
+        if dispose_event:
+            return response.Response(
+                ctx,
+                response_data=json.dumps('Dispose Event'),
+                headers={"Content-Type": "application/json"}
+            ) 
+        
+    # 成果データ保存
+    sql_query  = 'INSERT INTO '
+    sql_query += '    result_records_test '
+    sql_query += '( '
+    sql_query += '    ip, '
+    sql_query += '    sid, '
+    sql_query += '    ad_id, '
+    sql_query += '    client_id, '
+    sql_query += '    uid, '
+    sql_query += '    uid2, '
+    sql_query += '    product_code, '
+    sql_query += '    amount, '
+    sql_query += '    sales_count, '
+    sql_query += '    stage, '
+    sql_query += '    domain, '
+    sql_query += '    sender, '
+    sql_query += '    parameters, '
+    sql_query += '    created_ym '
+    sql_query += ') VALUES ( '
+    sql_query += '    :ip, '
+    sql_query += '    :sid, '
+    sql_query += '    :ad_id, '
+    sql_query += '    :client_id, '
+    sql_query += '    :uid, '
+    sql_query += '    :uid2, '
+    sql_query += '    :product_code, '
+    sql_query += '    :amount, '
+    sql_query += '    :sales_count, '
+    sql_query += '    :stage, '
+    sql_query += '    :domain, '
+    sql_query += '    :sender, '
+    sql_query += '    :parameters, '
+    sql_query += '    :created_ym '
+    sql_query += ') '
+
+    params = {
+        "ip": ip if ip else None,
+        "sid": send_param_list['sid'] if send_param_list['sid'] else None,
+        "ad_id": int(send_param_list['ad_id']) if send_param_list['ad_id'] else None,
+        "client_id": int(send_param_list['client_id']) if send_param_list['client_id'] else None,
+        "uid": send_param_list['uid'] if send_param_list['uid'] else None,
+        "uid2": send_param_list['uid2'] if send_param_list['uid2'] else None,
+        "product_code": send_param_list['product_code'] if send_param_list['product_code'] else None,
+        "amount": int(send_param_list['amount']) if send_param_list['amount'] else None,
+        "sales_count": int(send_param_list['sales_count']) if send_param_list['sales_count'] else None,
+        "stage": int(send_param_list['stage']) if send_param_list['stage'] else None,
+        "domain": send_param_list['domain'] if send_param_list['domain'] else None,
+        "sender": int(send_param_list['sender']),
+        "parameters": parameters,
+        "created_ym": int(created_ym), 
+    }
+    result = connection.execute(text(sql_query), params)
+
+    # トランザクションをコミットして変更を永続化
+    connection.commit()
+
+
+    inserted_id = result.lastrowid
+    logging.info(f"■■■{inserted_id}■■■")
+    # 終了ステータス表示
+    if error == 0:
+        return response.Response(
+            ctx,
+            status_code=200,
+            response_data=json.dumps({"result": "OK", "insert_id":inserted_id}),
+            headers={"Access-Control-Allow-Origin": "*","Access-Control-Allow-Headers": "Content-Type"}
+        )
+    else:
+        return response.Response(
+            ctx,
+            status_code=200,
+            response_data=json.dumps({"result": "Bad Request"}),
+            headers={"Access-Control-Allow-Origin": "*","Access-Control-Allow-Headers": "Content-Type"}
+        )
+'''
+    # testdic = send_param_list._asdict()
+    logging.info(send_param_list)
+    return response.Response(
+        ctx,
+        response_data=f"Hello",
+        headers={"Content-Type": "application/json"}
+    ) 
+'''
\ No newline at end of file
diff --git a/oci_func_prod/result_test/func.yaml b/oci_func_prod/result_test/func.yaml
new file mode 100644
index 0000000..ad633b7
--- /dev/null
+++ b/oci_func_prod/result_test/func.yaml
@@ -0,0 +1,8 @@
+schema_version: 20180708
+name: result_test
+version: 0.0.1
+runtime: python
+build_image: fnproject/python:3.12-dev
+run_image: fnproject/python:3.12
+entrypoint: /python/bin/fdk /function/func.py handler
+memory: 256
\ No newline at end of file
diff --git a/oci_func_prod/result_test/requirements.txt b/oci_func_prod/result_test/requirements.txt
new file mode 100644
index 0000000..a12b610
--- /dev/null
+++ b/oci_func_prod/result_test/requirements.txt
@@ -0,0 +1,3 @@
+fdk>=0.1.103
+sqlalchemy
+pymysql
diff --git a/oci_func_prod/test/func.py b/oci_func_prod/test/func.py
new file mode 100644
index 0000000..e1053b7
--- /dev/null
+++ b/oci_func_prod/test/func.py
@@ -0,0 +1,79 @@
+import io
+import urllib.parse
+import datetime
+import logging
+from fdk import response
+
+
+def handler(ctx, data: io.BytesIO = None):
+
+    # ===== 時刻(JST)=====
+    now_jst = datetime.datetime.utcnow() + datetime.timedelta(hours=9)
+
+    # ===== URL / Query =====
+    full_url = ctx.RequestURL()
+    parsed = urllib.parse.urlparse(full_url)
+    query_params = urllib.parse.parse_qs(parsed.query)
+
+    error = query_params.get('error', [''])[0]
+
+    # ===== Headers =====
+    headers = ctx.Headers()
+
+    x_real_ip = headers.get('x-real-ip', '')
+    user_agent = headers.get('user-agent', '')
+    referer = headers.get('referer', '')
+
+    return output(
+        ctx,
+        now_jst,
+        full_url,
+        error,
+        x_real_ip,
+        user_agent,
+        referer
+    )
+
+
+def output(ctx, now_jst, full_url, error, x_real_ip, user_agent, referer):
+
+    html = f"""<!doctype html>
+<html lang="ja">
+<head>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+<title>TEST</title>
+<style>
+*,*::before,*::after{{box-sizing:border-box;}}
+body{{display:flex;flex-direction:column;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif;margin:0;min-height:100vh;font-size:.88rem;line-height:1.5;color:#555;background:#fff;}}
+.container{{max-width:800px;margin:3rem auto;padding:1rem;}}
+h1{{font-size:1.4rem;margin-bottom:1rem;}}
+table{{width:100%;border-collapse:collapse;}}
+th,td{{padding:.5rem;border:1px solid #ddd;text-align:left;word-break:break-all;}}
+th{{background:#f5f5f5;width:30%;}}
+footer{{margin-top:auto;padding:.5rem;text-align:center;color:#fff;background:#343a40;}}
+</style>
+</head>
+<body>
+<div class="container">
+<h1>Request Info</h1>
+<table>
+<tr><th>日時 (JST)</th><td>{now_jst.strftime('%Y-%m-%d %H:%M:%S')}</td></tr>
+<tr><th>Request URL</th><td>{full_url}</td></tr>
+<tr><th>Error Param</th><td>{error}</td></tr>
+<tr><th>X-Real-IP</th><td>{x_real_ip}</td></tr>
+<tr><th>User-Agent</th><td>{user_agent}</td></tr>
+<tr><th>Referer</th><td>{referer}</td></tr>
+</table>
+</div>
+<footer>TEST</footer>
+</body>
+</html>
+"""
+
+    return response.Response(
+        ctx,
+        status_code=404,
+        headers={"Content-Type": "text/html; charset=utf-8"},
+        response_data=html
+    )
diff --git a/oci_func_prod/test/func.yaml b/oci_func_prod/test/func.yaml
new file mode 100644
index 0000000..76e09d5
--- /dev/null
+++ b/oci_func_prod/test/func.yaml
@@ -0,0 +1,8 @@
+schema_version: 20180708
+name: test
+version: 0.0.1
+runtime: python
+build_image: fnproject/python:3.12-dev
+run_image: fnproject/python:3.12
+entrypoint: /python/bin/fdk /function/func.py handler
+memory: 256
\ No newline at end of file
diff --git a/oci_func_prod/test/requirements.txt b/oci_func_prod/test/requirements.txt
new file mode 100644
index 0000000..58a638e
--- /dev/null
+++ b/oci_func_prod/test/requirements.txt
@@ -0,0 +1 @@
+fdk>=0.1.103
\ No newline at end of file

diff.txt · 最終更新: by root