---
# System prepended metadata

title: 紹銘交接
tags: [jambo, work]

---

## 商家分析 (前端)
[前端操作說明](https://hackmd.io/@shaomin/jba)

## 商家分析 (後端)
### 權限
> user 的 group 若包含 
> - 'DS'則可以看到全部使用的資料。 (內部使用)
> - 'CUS_DS' 則可以看到自己及shop_user的資料。 (客戶使用)
### 統計資料表和執行及排程

| model name | Desc. | Update time| file name|
| -------- | -------- | -------- | -------- |
| CommodityStatistic| 商品銷售狀況統計 | 每日清晨4:50 | update_statistic_commodity.py|
| OrderStatistic |訂單成立數量、訂單成立金額、訂單付款金額統計| 每日清晨4:53 |update_statistic_order.py|
| LiveStatistic |直播留言觀看人次統計| 每日清晨4:56 |update_statistic_comment.py|
| CustomerStatistic | 紀錄每日每位消費者是否為新客、距離上次購買天數是幾天，以及當日消費金額、數量。| 每日清晨4:58 |update_statistic_customer.py|
| CustomerStatisticExtra | 因為一開始沒有記錄到棄單狀況，故使用這個資料表補上。| 每日清晨4:58 |update_statistic_customer.py|
| CustomerStatisticSession| 紀錄每場次每位消費者是否為新客、距離上次購買天數是幾天，以及該場次消費金額、數量。| 每日清晨4:40 | update_statistic_customer_session.py|
| StreamStatistic | 使用FB Graph API `read_insights` 於直播結束後讀取影片分析資訊。 model有註解說明各欄位用意。| 每日清晨4:45 |update_statistic_stream.py|
| CommodityPredictCategory | 依照模型儲存預測結果，若有人工修改也會存入。| 每日清晨6:51 | predict_commodity_category.py|

### 開發注意事項
#### 前端
其他地方沒使用過的JS套件`tablesorter`, `echarts`
> [Apache ECharts](https://echarts.apache.org/zh/index.html) 
> 圓餅圖、柱狀圖，後端算好資料，依照格式套入這個套件呈現出來。
```javascript=
  var CloudChart = echarts.init(document.getElementById('plot_word_cloud')); // init 一個echarts可以使用的畫板
  var data = [];
  {% if word_cloud %}
  data = {{ word_cloud|safe }}; //接收後端資料
  {% endif %}
  var maskImage = new Image();

  var option = {
    tooltip: {},
    series: [ {
      type: 'wordCloud', // 使用文字雲
      sizeRange: [12, 150],
      // ... 省略
      data: data // 將資料放入套件
    }]
  };
  CloudChart.setOption(option); // 將欲呈現的圖形參數呈現在畫板上。
  
```
> 
> [tablesorter](https://mottie.github.io/tablesorter/docs/#Demo)
> 該處為了減少查詢壓力，並不會在表格點選排序時重新查詢，僅會透過前端套件，針對現有資料排序。
> 

`filterChange` 這個函示會用來控制顯示哪些東西。原則上是一個功能會用一個 css name 去包住全部。 所以filter改變的時候，會全部隱藏，再調整顯示對應內容。
> EX. `search_user`: 這個是用來顯示搜尋使用者的功能。
```javascript=
function filterChange() {
    let option_dates = document.getElementById('search_dates');
    let option_keyword = document.getElementById('search_commodity_keyword');
    let option_sessions = document.getElementById('search_sessions');
    option_dates.setAttribute('hidden', 'hidden');
    option_keyword.setAttribute('hidden', 'hidden');
    option_sessions.setAttribute('hidden', 'hidden');
    $('.commodity-display').hide();
    $('#commodity-table').hide();
    $('#plot_word_cloud').hide();
    $('.live-display').hide();
    $('.category-display').hide();
    $('.customer-display').hide();
    $('.store-recommendation-display').hide();
    $('.fb-stream-display').hide();
    $('.order-amount-display').hide();
    $('.business-report-display').hide();
    $('.search_user').hide();
    $('#search_commodity_keyword').removeAttr('disabled');
    $('#search_sessions').removeAttr('disabled');
    on_time_period_changed();
    if ($('#filter').val() == 'commodity') {
      $('.commodity-display').show();
      $('.search_user').show();
      option_dates.removeAttribute('hidden');
      option_keyword.removeAttribute('hidden');
      if ($("select[name='search']").val() == 'sessions'){
        document.querySelector('#search-select').value='dates';
      }
      if($('#commodity_display').val() == 'word_cloud') {
        $('#plot_word_cloud').show();
      } else {
        $('#commodity-table').show();
      }
    } else if ($('#filter').val() == 'live') {
      $('.live-display').show();
      document.querySelector('#search-select').value='dates';
      option_dates.removeAttribute('hidden');
    } else if ($('#filter').val() == 'category') {
      $('.category-display').show();
      $('.search_user').show();
      document.querySelector('#search-select').value='dates';
      option_dates.removeAttribute('hidden');
    } else if ($('#filter').val() == 'customer') {
      $('.customer-display').show();
      $('.search_user').show();
      if ($("select[name='search']").val() == 'commodity_keyword'){
        document.querySelector('#search-select').value='dates';
      }
      option_dates.removeAttribute('hidden');
      option_sessions.removeAttribute('hidden');
    } else if ($('#filter').val() == 'live_store_recommendation') {
      $('.store-recommendation-display').show();
      document.querySelector('#search-select').value='commodity_keyword';
      option_keyword.removeAttribute('hidden');
    } else if ($('#filter').val() == 'fb_stream') {
      $('.fb-stream-display').show();
      $('.search_user').show();
      document.querySelector('#search-select').value='dates';
      option_dates.removeAttribute('hidden');
    } else if ($('#filter').val() == 'order_amount') {
      $('.order-amount-display').show();
      $('.search_user').show();
      document.querySelector('#search-select').value='dates';
      option_dates.removeAttribute('hidden');
    } else if ($('#filter').val() == 'business_report') {
      $('.business-report-display').show();
      $('.search_user').show();
      document.querySelector('#search-select').value='dates';
      option_dates.removeAttribute('hidden');
    }
    change_datepicker();
    selectSearch();
  }
```
#### 後端
> 針對`search_table`去排查。

![](https://hackmd.io/_uploads/H1ougeWYn.png)


## ES 相關設定及使用
* 安裝套件 [[安裝篇] Elasticsearch Logstash Kibana](https://hackmd.io/@shaomin/ELK_install)
* 同步資料及管理設定 [[使用/管理篇] Elasticsearch Logstash Kibana](https://hackmd.io/@shaomin/ELK_usage)
* ES 語法 [Elasticsearch 語法](https://hackmd.io/@shaomin/es_query)

##### STEP
1. 透過安裝套件教學，逐步安裝 ES 及 Logstash 的套件。
2. 確認服務預作正常後，填寫設定檔，並開始透過Logstash將資料倒入ES。
3. 透過 ES 語法查看確認 ES 內的資料及服務狀況。


## 粉絲小幫手 
### 粉絲訂單資料統計排程
>每秒執行`update_user_transaction_summary_aggregate.py` 去從 `Api_UserTransactionChangeLog` 撈取訂單變化量加總存入`Api_UserTransactionSummaryAggregate`
>> 執行的時候會透過 `Api_config` 去紀錄執行到Log的哪一筆資料，以及最後更新時間，其中 utsa update status 若取消勾選則停止同步。
>> ![](https://hackmd.io/_uploads/Sy4BGlWKn.png)

`Api_UserTransactionChangeLog` 會在 api_order 執行 save 的時候做一個變化量的計算。下圖函示寫於`api/utils.py`中。
![](https://hackmd.io/_uploads/BkZqXgZY3.png)

`Api_UserTransactionSummaryAggregate`會在粉絲小幫手，如下圖方式顯示。
![](https://hackmd.io/_uploads/SJWtMgbYh.png)
![](https://hackmd.io/_uploads/HkWpGxbKh.png)

## 東南亞
### 購物車一頁式結單
:::warning
> 設定方式
> -
> - PC 開啟 <font color="#f00">`enable_one_page_checkout`</font>
>
> 設計架構
> -
>  - 一個連結(hash_key)代表一次付款流程。結帳之後該連結轉為追蹤訂單狀態(訂單處理中 or 歷史訂單)。
>  
> ```python
> class OnePageCheckout(models.Model):
>     user = models.ForeignKey(User)
>     from_id = models.CharField(max_length=64, db_index=True)
>     from_name = models.CharField(max_length=128, null=True, blank=True)
>     comment_user = models.ForeignKey(CommentUser, on_delete=models.SET_NULL, null=True, blank=True)
>     hash_key = models.CharField(max_length=16, null=True, blank=True, unique=True, db_index=True)
>     orders = models.ManyToManyField('Order', related_name='order_one_pages', blank=True)
>     bill = models.ForeignKey('pay.Bill', on_delete=models.SET_NULL, null=True, blank=True)
>     session = models.ForeignKey(Session, on_delete=models.SET_NULL, null=True, blank=True)
>     created_time = models.DateTimeField(auto_now_add=True)
> ```
>
> - 連結中的 hash_key 主要是用來查找 from_id 以及是否有 bill，若沒有bill，這個hash_key可以一直加入新訂單。
> - 訂單建立時，若有找到可用的 hash_key，便不會再新增，直到該hash_key 被賦予 bill。
> 
> 得標/結單... 通知連結
> -
> 1. 開啟一頁式結單後，基本上發送的結單連結都會是一頁式。
> 邏輯為 `https://{DOMAIN_NAME}/op/{USER_ID}/{HASH_KEY}`
> 其中 op 代表 `one page`
> ex. https://sea.jambolive.tv/op/2853/86763d49f6/
> 2. 一頁式結單連結的通知內容是加總的，並不是發送單次order資訊，而是所有未結單 order 的資訊。
>
> 3. 取得或建立連結方式
> - 以下函示可透過order或from_id取得連結。若資料查不到既有可用連結，則會建立新的。
> File: <font color="Blue"> **`comment/utils.py`** </font>
> ```python
> def get_one_page_checkout_url(pc, order=None, from_id=None):
> ```
> - bill 可以透過models中如下的函示取得。
> `checked` => 為真時，不會重抓PC。
> `origin_url` => 若設定沒開，或是bill沒有對應的link，則回傳購物車或訂單處理中連結。
> ```python
> def get_opc_url(self, checked=False, origin_url=None):
> ```
>
> 登入邏輯
> -
> - 若為一頁式結單，會將消費者的session中`is_one_page`標示為真，並轉往一頁式結單，不進行後續登入比對。
> File: <font color="Blue"> **`pay/decorators.py`** </font>
>
> ```python
> def facebook_login(view):
>     #...省略
>     # One Page Checkout
>     is_one_page = request.session.get('is_one_page', False)
>     one_page_do_pay = request.session.get('one_page_do_pay', False)
>     if is_one_page and ('/op/' in path_info):
>         return view(request, *args, **kwargs)
>     elif is_one_page and one_page_do_pay:
>         qd = request.GET.copy()
>         if user_id:
>             return redirect(reverse('one_page_checkout', kwargs={'user_id': user_id, 'hash_key': request.session.get('one_page_key')}) + '?' + qd.urlencode())
>         else:
>             return redirect(reverse('opc_redirect', kwargs={'hash_key': > request.session.get('one_page_key')}) + '?' + qd.urlencode())
>     elif is_one_page and not from_name and not from_id and '/checkout/' not in path_info and ('/shop/' in path_info or '/store/' in path_info):
>         return view(request, *args, **kwargs)
>     else:
>         clear_OPC_session(request)
> ```
> 需注意事項
> -
> - <font color="green">```一頁式購物車的跳轉```</font> 
目前一頁式連結進入後，會在session紀錄，若有跳轉的連結，將session清掉了，便會無法回到需要登入購物車。
Ex. 例如購物車前往商城，商城再點選購物車。
> - <font color="green">```psid的功能使用問題```</font> 
因為一頁式結單主要是psid免登入，所以有些asid在用的功能可能會有衝突。
Ex. 專屬折扣，原先只能在asid上設置。
:::
### 購物車合併結單
:::warning
> 設定方式
> -
> - PC 開啟 <font color="#f00">`enable_checkout_merge_bill`</font>
>
> 設計架構
> -
>  - 進入購物車後，會抓取上次已成立未確認付款的付款單。並在前後端做金額及折扣的計算。
>  - 其中扣除前次已付款金額，並告知當前應付款金額，為該功能主要賣點。
> ![](https://hackmd.io/_uploads/Hk2PlSGY3.png =250x120)
> 
>  - 前端會同時列出，已成立跟未成立的訂單。上方為未成立，下方為已成立。
>  ![](https://hackmd.io/_uploads/rJx1AyBzY2.png =500x380)
>
> 開發邏輯
> - 
> - 若開啟該功能，會去抓取符合條件的付款單。
> ```python
> could_merge_bills = models.Bill.objects.filter(payment_method__in=payment_method_supported_for_checkout_bill_merge,user_id=user_id,from_id=from_id).filter(status=models.Bill.Status.ESTABLISHED, is_paid=False)
> ```
> - 若有可以合併的付款單，會用變數```could_merge_bill```, ```could_merge_bill_orders```, ```could_merge_bill_orders_total```作為參數去計算。
> 1. could_merge_bill: 紀錄最新一筆可以合併的付款單。
> 2. could_merge_bill_orders: 紀錄可合併付款單的訂單資料。
> 3. could_merge_bill_orders_total: 訂單金額總和，不包含運費、手續費...等
> 
> 需注意事項
> -
> - <font color="green">```僅支援轉帳付款方式使用```</font> 
因為泰國僅能透過轉帳方式去驗證實際付款金額，驗證後於前端做計算，告知建議的應付款金額。
> - <font color="green">```紅利功能未開放```</font> 
因為合併後計算折扣即實際付款金額尚未穩定，故紅利部分先暫緩。
> - <font color="green">``````</font> 
因為合併後計算折扣即實際付款金額尚未穩定，故紅利部分先暫緩。
:::

## LINE
### 購物
:::info
> <font color="Green">**商品倒入LINE購物可被搜尋**</font>[color=#f24f9e]
> 
> 操作流程
> -
> 1. 賣家在商品小幫手點選```LINE上架商品```，便會將商品的```line_on_shelf_time``` 更新。
> 2. LINE 定期呼叫 API 取得資料後，便可以出現在LINE購物APP上。
> 
> - 下方為我們提供給LINE的API網址。
> File: <font color="Blue"> **`api/lineshopping.py`** </font>
> https://jambolive.tv/api/lineshopping/product_full/{USER_ID}/
https://jambolive.tv/api/lineshopping/product_partial/{USER_ID}/
https://jambolive.tv/api/lineshopping/cate_full/{USER_ID}/
>
> - 文件我找不到了QQ 檔名如下
![](https://hackmd.io/_uploads/BkJqErztn.png)
>
> 需注意事項
> -
> - 若未來使用量大，應當先處理好資料，存成檔案，直接回傳給LINE。
:::

### 直播 - GLC
:::info
> 設定方式
> - 
> table: <font color="Blue"> **`api_lineaccount`** </font>
> ![](https://hackmd.io/_uploads/HkbLb9Gt2.png =300x250)
> 1. 基本LINE帳號資料。
> 2. Live api version: 選擇 GLC。
(JP為日本團隊開發，GLC為韓國團隊開發)
> 3. Live product display status: 卡牌顯示方式。
> 4. Post back mode： 訂單及付款單建立後回拋給LINE的邏輯。
>    訂單建立時，會回傳資料給LINE，顯式於LINE購物車。
>    付款單建立時，會回傳資料給LINE，用於回饋點數計算。
> 

| 回拋方式 | 付款單 | 訂單 |
| -------- | -------- | -------- |
| All Bill, LINE Order | 全回傳 |  僅LINE帳號建立訂單回傳  |
| All Bill, All Order  | 全回傳  |  全回傳  |
| LINE Bill, LINE Order| 僅LINE帳號建立付款單回傳 |  僅LINE帳號建立訂單回傳  |
| LINE Bill, All Order | 僅LINE帳號建立付款單回傳 | 全回傳  |

> 
> 操作流程
> -
> ![](https://hackmd.io/_uploads/SyjRxUQt3.png)
>
> 0. 請先確保OA帳號有申請 GLC 直播功能。
> 1. 直播小幫手點選LINE直播。其中`直播首圖`、`直播標題`、`開播訊息` 為必填。
> 2. 點選建立後，進入[LINE GLC 後台](https://global-live-commerce.line.biz/)取得串流金鑰、串流網址。
> ![](https://hackmd.io/_uploads/HkfcW8Qt3.png)
> 3. 透過直播串流工具，傳送直播畫面到GLC後台。
> 4. 確定GLC後台有影像，可以點選開始直播，或點進入JamboLive直播小幫手點選`LINE直播設定`中的開始直播。
> ![](https://hackmd.io/_uploads/SJPUGLmK2.png)
> 
> 開發事項
> -
> #### 啟用直播的API
> Path: <font color="Blue"> **`pay/linebuy.py`** </font> => `def live_settings(live, line_account, action=False):`
>> 
>> ![](https://hackmd.io/_uploads/Hk98SUQF2.png)
>>
>> - 基本上沿用先前開發JP版本直播的功能，僅需注意若`line_account.live_api_version == GLC` 會有不同的API server，並且參數也會些微不同。
>
> #### 啟用直播的參數
> Path: <font color="Blue"> **`api/views.py`** </font> => `def line_live(request):`
>> 
>> ![](https://hackmd.io/_uploads/rkjs4ImYh.png)
>> - BC＝order by chat(關鍵字收單)、PL=Product list(商品列表收單)
>> - PL mode 不用送關鍵字。
>
> #### LINE ecid 資料交流
>> LINE ecid 有點類似 psid。是在直播喊單的時候，LINE傳訂單給我們的識別碼。
>> - 在LINE點選進入購物車的時候，連結會帶上ecid，進入購物車LINE帳號登入後，可以用 LINE uid 綁定 ecid，將訂單歸戶。
>> - ecid 有時效且同用戶每場直播都不同，所以我們並沒有儲存。
>> ```
>> https://jambolive.tv/pay/checkout/12305/line/?ecid=800ffeae8895c32f0aa62ab21fe5fc5c8440bc714b34b26ec3e678acac48bc89
>> ```
> #### 手動回拋訂單給LINE
> Path: <font color="Blue"> **`pay/tasks.py`** </font> => `def linePostBackOrder(bill_id, ecid, kind):`
>> ```=python
>> bs = pay_models.Bill.objects.filter(user_id=13665).exclude(Q(status=pay_models.Bill.Status.NOT_ESTABLISHED) | Q(status=pay_models.Bill.Status.TRASHED)).order_by('-id')
>> for b in bs:
>>    linePostBackOrder(b.id, 'jambolive0line0order0fb0checkout', 'custom')
>
> 文件
> -
> [[LINE ECAC][LINE TW Shopping] GLC LIVE Commerce Product data feed API Specification (zh-tw) ver. 1.5 .pdf](https://drive.google.com/file/d/1aUKTza9nxsrwajrBb5hxiPT_WSWtW8ea/view?usp=sharing)
:::

### LINE 通知
* 未結單通知包含按鈕(內文160字元)。
* 一般通知(內文5000字元)。
#### Refference
1. [LINE OA 後台](https://manager.line.biz/)
2. [LINE GLC 後台](https://global-live-commerce.line.biz/)
3. [LINE Developers 後台](https://developers.line.biz/console/)

## Fasney 後支付

> 設定方式
> - 
 1. 於 admin後台api/personalconfig 中搜尋 fasney pay account，並選取或建立帳號 。
2. 於 admin後台api/personalconfig 中搜尋 After pay percentage，用於建立後支付手續費。
3. 若需開啟後支付分期
    1. 於admin後台 api/personalconfig 中搜尋Enable after pay installment，並將其勾選。
    2. 於 admin後台api/personalconfig 中搜尋 After pay installment list，輸入與合作廠商簽約之分期期數 (EX. 簽約3,6期，則輸入 3,6)。
    3. 於 admin後台api/personalconfig 中搜尋 After pay installment percentage 3/6/12/18/24，輸入後支付分期之手續費。
4. 代後台設定完成後，商家便可於商家設定中，將付款設定勾選「後支付」啟用。

> 開發注意事項
> -
- 交易流程跟其他第三方支付一樣，唯一差異是消費者第一次使用Fasney後支付會需要驗證個人資料，驗證個人資料這段期間，付款單會屬於未成立狀態，等待付款完成的API送過來，該筆付款單的訂單也沒有被其他付款方式付款，才會轉成成立。
![](https://hackmd.io/_uploads/rk-phYQF2.png)
- 有設定`ip_white_list`去限制可以回傳交易狀況給我們的IP.
- 可提供測試環境，給他們窗口信箱，會寄測試APP的連結。
![](https://hackmd.io/_uploads/HkAuaY7Kh.png)

#### refference

1. [官網](https://www.fasney.com/)
2. [開發文件](https://docs.google.com/document/d/1mkA50JUpwIMbtz2m6fTRTpsVu2LpteDf/edit?usp=sharing&ouid=101567832984958542928&rtpof=true&sd=true)

## FB 權限申請
:::success
![](https://hackmd.io/_uploads/H1oICYXK2.png)
1. 上線中的APP必須提供測試帳號，或使用測試環境送審。
2. 登入過程中必須跳出可以讓使用者勾選權限的畫面。(每次要送審時，都要確保測試帳號的token清空權限，讓審核人員登入平台時可以出現取得權限畫面。
3. FB開發人員中雖然有提供的測試帳號，但那個測試環境是封閉的，而且我這次一直沒辦法在那個封閉環境中建立粉絲專頁。雖然我有在送審文案中提出這點，也請審核人員用我們提供的測試用戶檢查登入機制，再用可以獲取粉專分析資料的審核帳號登入。

綜合以上原因，最後是我自己在FB正式環境中新建一個全新的帳號，並且將粉專應該要有的分析資料也都準備好，再將這個帳好設定為開發人員，讓審核人員用這個帳號登入平台，待審核通過後再更改密碼及移除測試人員。

送審範例申請了很多權限，後續可以參考這個範例取得權限。
:::
![](https://hackmd.io/_uploads/BkyP7grtn.png)

#### refference
1. [送審範例](https://xeroneit.net/blog/xerochat-how-to-submit-app-for-review)
2. [應用程式審查說明文件](https://developers.facebook.com/docs/app-review/introduction)

## 政大資料匯出

### 連線方式
[政大NAS 連結](https://uoclabnas720.synology.me:5001/#/signin)
- 帳號: uoclab_jambo
- 密碼: Uoclab85024jambo??

### 腳本
```
53 21   * * *   /bin/sh /home/web/projects/scpbackend/django/scripts/nccu/cronjob_export.sh
```
- cronjob_export.sh => 因為留言cache存兩天，所以兩天匯出一次前兩天得留言即可，用判斷`/tmp/altday` 這個存不存在去判斷要不要匯出留言。
- nccu_export_comments.py => 匯出留言資料，存入/home/web/nccu_comment
- nccu_export_comments_email.py => 匯出留言資料，寄送mail
- nccu_export_commodity.py => 匯出商品資料
- nccu_export_order_info.py => 匯出訂單資料

## Kbank

### 驗證流程
#### i. 從圖片中解析QRCode.
:::warning
Here is our read slip qrcode solution now.

We use this one for frontend scan.
1. https://github.com/nimiq/qr-scanner
And if not found, we will do it by opencv package on backend.
2. https://note.nkmk.me/en/python-opencv-qrcode/
If above function can't get any result, then we call following link.
3. https://zxing.org/w/decode.jspx
:::

#### ii. Kbank 驗證

Path: <font color="Blue"> **`pay/kbank.py`** </font> 
::: warning
1. function ```do_verify``` => 
    - 取得欲查詢的付款單及其對應的所有匯款紀錄。
    - 驗證當前帶入的匯款編碼(QR Code 解碼內容)。
    - 取得匯款單資料後，檢驗`時間`, `銀行代碼`, `重複上傳`, `金額`
    - 若有啟用發送通知，執行發送。
2. function ```readQR``` => 
    - 執行上述解析QRCode的第二、第三步驟。
3. function ``re_verify``` => 
    - 購物車前端抓不到匯款單編碼時執行。
    - 出貨小幫手，賣家手動執行。
:::