goalu 寫入權矩陣(階段 1 硬門檻)
一句話:198 張活表中 47 張有兩個以上應用程式在寫,其中 hcust/hcust_trd 各有 7 個 writer; david@% 與 tom@% 目前是 GRANT ALL PRIVILEGES ON *.*(全域超權,不限 goalu), 只有 salary_app 已收斂成最小權限。這份矩陣是階段 1 帳號拆分與階段 2 CI 攔檢的共同依據。
產生日期:2026-08-18 · 資料來源:真實 DB(information_schema)+ 全站靜態掃描 · 重生指令見文末〈可重跑〉一節(矩陣必須可重生,不可手改後漂移)
1. 摘要
| 指標 | 數量 |
|---|
| goalu 表總數 | 579 張(+25 views) |
| 活表(近 90 天有寫入,本矩陣範圍) | 198 張 |
| 冷表(90 天無寫入,另案處理) | 381 張 |
| 🔴 兩個以上 app writer | 47 張 |
| 🟢 單一 app writer | 103 張 |
| ⚪ 無 app writer(DB 內部寫或待確認) | 48 張(其中 14 張由 SP/EVENT/TRIGGER 寫) |
| DB 內部寫入層 | 68 個 PROCEDURE 有寫入、20 個 EVENT(全 ENABLED)、8 個 TRIGGER |
2. 站台 × DB 帳號現況
| 站台 | 目錄 | DB 帳號 | 目前權限 | 狀態 |
|---|
| ERP(kngoatmilk) | /var/www/kngoatmilk/prod/html | david | ALL ON *.* | 🔴 待拆 |
| html/salary | /var/www/html/salary | david(明碼寫在 include/cndb.php) | ALL ON *.* | 🔴 待拆 |
| html/report | /var/www/html/report | david(明碼 include/cndb.php) | ALL ON *.* | 🔴 待拆 |
| html/sales | /var/www/html/sales | david(明碼 include/cndb.php) | ALL ON *.* | 🔴 待拆 |
| html/sony | /var/www/html/sony | david(明碼 admin/_inc/config.php) | ALL ON *.* | 🔴 待拆 |
| kng.tw | /var/www/kng.tw/public_html | david(明碼 api/db.php) | ALL ON *.* | 🔴 待拆 |
| www.kngoatmilk.com | /var/www/www.kngoatmilk.com | david(env DB_ERP_USER) | ALL ON *.* | 🔴 待拆 |
| forms | /var/www/forms.kngoatmilk.com | david | ALL ON *.* | 🔴 待拆 |
| line | /var/www/line.kngoatmilk.com | david | ALL ON *.* | 🔴 待拆 |
| invoice | /var/www/invoice | david | ALL ON *.* | 🔴 待拆 |
| delivery | /var/www/delivery.kngoatmilk.com | david | ALL ON *.* | 🔴 待拆 |
| pbx | /var/www/pbx.kngoatmilk.com | david | ALL ON *.* | 🔴 待拆 |
| pbx-laravel12 | /var/www/pbx-laravel12 | david | ALL ON *.* | 🔴 待拆 |
| inventory | /var/www/inventory.kng.tw | tom | ALL ON *.* | 🔴 待拆 |
| story | /var/www/story.kng.tw | tom | ALL ON *.* | 🔴 待拆 |
| salary(new) | /var/www/salary | salary_app | 9 條表級最小權限 | ✅ 已完成(範本) |
| salary 測試 | /var/www/salary (phpunit) | salary_test_ro | SELECT ON goalu.* | ✅ 已完成 |
| (唯讀工具) | dbview / MCP | dbview_ro、mcp_ro | SELECT ON goalu.* | ✅ 唯讀 |
🔴 david@%/tom@% 是 ALL ON *.*——不只 goalu,整台 MariaDB 全庫可寫(含 callhub、muxue、lk、shop…)。 拆帳號時第一步是把 *.* 收成 goalu.*,再逐站收到表級。
3. 核心表矩陣
欄位說明:W=寫入者(Insert/Update/Delete/Replace/Truncate);R=讀取者; DB 內部=SP/EVENT/TRIGGER 寫入(這些繞過應用層,拆帳號時 DEFINER 要一併確認)。
站台縮寫:sal=html/salary ERP=kngoatmilk rpt=html/report sls=html/sales sony=html/sony
家戶客戶/訂單/帳務 (42 張,其中 19 張多 writer)
| 表 | W(寫入者) | R(讀取者) | DB 內部寫入 | 判定 |
|---|
hcust | sal:DIU ERP:DIU sls:IU www:DIU forms:IU dlv:IU invt:U | rpt kng line inv pbx pbx12 | SP×6 / ⚡本表有 trigger×6 | 🔴 多 writer |
hcust_trd | sal:IU ERP:DIU sls:DIU www:DIU forms:IU inv:IU invt:DI | rpt pbx pbx12 | TG:update_addr_log@hcust,update_area_log@hcust,update_comp_idno_log@hcust,update_lv_tp_log@hcust,update_rv_tp_log@hcust / SP×13 | 🔴 多 writer |
hcust_dorder | ERP:DIU sls:IU www:DIU forms:IU | sal rpt inv | SP×3 / ⚡本表有 trigger×1 | 🔴 多 writer |
payment | sal:DIU sls:IU www:IU line:IU | forms | SP×1 | 🔴 多 writer |
address_book_indexes | sal:DU ERP:DU line:I | rpt www inv dlv | — | 🔴 多 writer |
customers | sal:DU ERP:DIU line:IU | rpt www inv dlv pbx pbx12 | — | 🔴 多 writer |
hcust_token | sal:D www:DIU line:DIU | ERP rpt | — | 🔴 多 writer |
trades | sal:DIU ERP:DIU www:IU | rpt line inv | SP×4 | 🔴 多 writer |
address_books | sal:DU ERP:DI | rpt www line | — | 🔴 多 writer |
apart_sales | sal:IU inv:IU | rpt sls forms | — | 🔴 多 writer |
change_resources | sal:I ERP:I | rpt | — | 🔴 多 writer |
contacts | sal:D ERP:DIU | — | — | 🔴 多 writer |
cub_rcv | sal:IU ERP:I | www line | — | 🔴 多 writer |
cub_record | sal:I ERP:I | — | — | 🔴 多 writer |
customer_evens | sal:D ERP:DIU | — | — | 🔴 多 writer |
customer_price_details | sal:I ERP:IU | inv | — | 🔴 多 writer |
customer_prices | sal:I ERP:IU | inv | — | 🔴 多 writer |
hcust_ant | ERP:I inv:U | sal rpt sls www line | EV:dvm_bill_auto_post / SP×7 | 🔴 多 writer |
order_records | sal:D ERP:I | rpt | — | 🔴 多 writer |
apart_comp | inv:IU | sal rpt sls forms | — | 🟢 單一 |
cub_estimate | sal:I | — | — | 🟢 單一 |
customer_phone_index | sal:DIT | — | — | 🟢 單一 |
customer_pms | ERP:DI | sal inv | SP×2 | 🟢 單一 |
customer_pre_jobs | ERP:I | — | — | 🟢 單一 |
customer_price_templates | ERP:IU | — | — | 🟢 單一 |
customers_gps | sal:IU | — | — | 🟢 單一 |
hcust_amt_pre | ERP:IU | sal www inv | SP×8 | 🟢 單一 |
hcust_amt_rcv | ERP:DIU | sal line inv | EV:dvm_bill_auto_post / SP×7 | 🟢 單一 |
hcust_ant_mk | ERP:IU | — | — | 🟢 單一 |
hcust_dv | ERP:I | sal rpt sls www line | SP×2 | 🟢 單一 |
hcust_even | ERP:DIU | — | — | 🟢 單一 |
hcust_qtyf | ERP:I | sal sls www line inv | EV:qtyf_credit / SP×10 | 🟢 單一 |
hcust_token_archive | sal:I | — | — | 🟢 單一 |
hcust_trd_del | ERP:I | — | — | 🟢 單一 |
hcust_trd_rt | ERP:I | — | — | 🟢 單一 |
payment_attempts | sal:I | — | — | 🟢 單一 |
payment_callback_failures | sal:I | — | SP×1 | 🟢 單一 |
customer_gs | — | sal ERP rpt inv | — | ❓ 待確認 |
customer_hs | — | sal ERP | — | ❓ 待確認 |
hcust_close | — | ERP | — | ❓ 待確認 |
hcust_dorder_rt | — | rpt sls | TG:rt_order@hcust_dorder,rt_order_copy1_copy1@hcust_dorder_oat_origin | ⚙️ DB 內部 |
hcust_sales_per | — | — | — | ❓ 待確認 |
團體客戶 (17 張,其中 4 張多 writer)
| 表 | W(寫入者) | R(讀取者) | DB 內部寫入 | 判定 |
|---|
gcust_ant | sal:U ERP:I inv:U | — | — | 🔴 多 writer |
gcust | sal:U ERP:DIU | rpt www inv | SP×1 | 🔴 多 writer |
gcust_cls | sal:DIU ERP:DIU | www | SP×1 | 🔴 多 writer |
gcust_ml | ERP:DIU invt:U | sal | — | 🔴 多 writer |
gcust_ant_single | sal:IU | — | — | 🟢 單一 |
gcust_atm_posted | sal:I | — | — | 🟢 單一 |
gcust_dorder | ERP:DIU | sal www | — | 🟢 單一 |
gcust_dv | ERP:I | sal rpt inv | — | 🟢 單一 |
gcust_even | ERP:DIU | — | SP×1 | 🟢 單一 |
gcust_mu | ERP:DIU | sal | — | 🟢 單一 |
gcust_narea_ant | ERP:I | — | — | 🟢 單一 |
gcust_narea_idno | sal:I | — | — | 🟢 單一 |
gcust_price | ERP:DIU | inv | — | 🟢 單一 |
gcust_price_classes | ERP:DIU | — | — | 🟢 單一 |
gcust_trd | ERP:DIU | sal | SP×1 | 🟢 單一 |
gcust_close | — | ERP | — | ❓ 待確認 |
gcust_ml_items | — | sal ERP invt | — | ❓ 待確認 |
配送/估奶 (22 張,其中 5 張多 writer)
| 表 | W(寫入者) | R(讀取者) | DB 內部寫入 | 判定 |
|---|
daily_orders | sal:D ERP:DIU | rpt www line inv invt | — | 🔴 多 writer |
dv_orders | sal:D ERP:IU | inv | — | 🔴 多 writer |
marea | sal:U ERP:DIU | rpt www forms line inv dlv invt | SP×1 | 🔴 多 writer |
rt10_list | sal:U forms:U | rpt | SP×1 | 🔴 多 writer |
weekly_orders | sal:DU ERP:I | rpt line | — | 🔴 多 writer |
daily_dvs | ERP:I | rpt invt | EV:inventory_update_dv,inventory_update_dv2 / SP×2 | 🟢 單一 |
daily_gprod_jobs | ERP:I | — | — | 🟢 單一 |
daily_gprods | ERP:DI | — | — | 🟢 單一 |
delivery_list | dlv:IU | sal | — | 🟢 單一 |
dv_covers | ERP:I | sal dlv | — | 🟢 單一 |
dv_dvms | ERP:I | sal | — | 🟢 單一 |
marea_covers | ERP:I | — | — | 🟢 單一 |
sales_hcust_qty | ERP:I | — | — | 🟢 單一 |
weekly_order_items | sal:D | ERP rpt line | — | 🟢 單一 |
daily_orders_shadow | — | ERP | — | ❓ 待確認 |
daily_sales | — | rpt | EV:daily_sales / SP×1 | ⚙️ DB 內部 |
dv_orders_shadow | — | ERP | — | ❓ 待確認 |
dv_unit_price | — | sal ERP | EV:new_dv_unit_price / SP×1 | ⚙️ DB 內部 |
i_dv_record_copy3 | — | sal rpt | EV:insert_dv_record_copy3,insert_dv_record_copy3_daily | ⚙️ DB 內部 |
marea_cover_details | — | ERP | — | ❓ 待確認 |
sales_dvm_products | — | ERP | — | ❓ 待確認 |
sales_dvms | — | ERP rpt | — | ❓ 待確認 |
庫存 (4 張,其中 0 張多 writer)
| 表 | W(寫入者) | R(讀取者) | DB 內部寫入 | 判定 |
|---|
inventory_details | invt:DIU | sal | EV:inventory_update_dv,inventory_update_dv2 / SP×4 | 🟢 單一 |
inventory_items | invt:DIU | sal | EV:inventory_update_dv,inventory_update_dv2 / SP×4 | 🟢 單一 |
inventory_main | invt:IU | sal | SP×2 | 🟢 單一 |
inventory_side_products | invt:IU | — | — | 🟢 單一 |
發票/請款 (14 張,其中 7 張多 writer)
| 表 | W(寫入者) | R(讀取者) | DB 內部寫入 | 判定 |
|---|
i_form | sal:DIU www:DIU kng:U forms:IU line:DIU | — | SP×1 | 🔴 多 writer |
invoices | sal:I ERP:DI inv:DIU | line | SP×4 | 🔴 多 writer |
i_deadline | ERP:DIU inv:IU | sal rpt sls www forms line | — | 🔴 多 writer |
i_form_track | www:I forms:IU | sal kng | — | 🔴 多 writer |
i_visit_data | sal:DIU sls:DIU | rpt | — | 🔴 多 writer |
i_visit_group | sal:IU sls:IU | rpt | — | 🔴 多 writer |
invoice_details | sal:I inv:DI | ERP line | SP×5 | 🔴 多 writer |
i_prod_no_dvs | ERP:DI | — | — | 🟢 單一 |
i_day_qty | — | ERP rpt | EV:i_day_qty | ⚙️ DB 內部 |
i_lbt | — | — | — | ❓ 待確認 |
i_prod | — | sal ERP rpt www forms line inv invt | — | ❓ 待確認 |
i_rv_tp_area | — | sal ERP www | — | ❓ 待確認 |
i_sales_car | — | inv | — | ❓ 待確認 |
i_sales_daily | — | inv | — | ❓ 待確認 |
電訪/通聯 (9 張,其中 2 張多 writer)
| 表 | W(寫入者) | R(讀取者) | DB 內部寫入 | 判定 |
|---|
pbx_call_record | sal:I pbx:IU pbx12:IU | rpt inv story | — | 🔴 多 writer |
call_empty | sal:DI inv:I | — | — | 🔴 多 writer |
call_data | sal:U | rpt inv | EV:retain_data_05T1_cust,retain_data_event,topup_call_data_daily,update_daily_stats / TG:delete_calldata_after_star@hcust / SP×7 | 🟢 單一 |
call_empty_auto | sal:IU | — | — | 🟢 單一 |
pbx_call_transcript | sal:U | rpt | — | 🟢 單一 |
call_05T1_cust | — | — | EV:retain_data_05T1_cust,retain_data_event / SP×2 | ⚙️ DB 內部 |
call_list | — | sal rpt inv | EV:refill_call_list_daily,retain_data_event,topup_call_data_daily / SP×3 | ⚙️ DB 內部 |
pbx_call_transcript_breeze | — | — | — | ❓ 待確認 |
pbx_token | — | pbx pbx12 | — | ❓ 待確認 |
訊息/LINE (15 張,其中 6 張多 writer)
| 表 | W(寫入者) | R(讀取者) | DB 內部寫入 | 判定 |
|---|
SMS_log | sal:I www:IU forms:I inv:IU | ERP | — | 🔴 多 writer |
line_chat | sal:IU www:I line:IU | ERP rpt | — | 🔴 多 writer |
line_group_link | sal:DIU line:IU | ERP | — | 🔴 多 writer |
message_logs | sal:DIU www:U | ERP | — | 🔴 多 writer |
sony_line_chat | sal:IU sony:IU | — | — | 🔴 多 writer |
sony_line_user | sal:IU sony:IU | — | — | 🔴 多 writer |
line_chat_auto_reply_log | sal:I | — | — | 🟢 單一 |
line_chat_summary | sal:IU | — | — | 🟢 單一 |
line_cs_quality_cases | sal:DI | — | — | 🟢 單一 |
line_cs_quality_jobs | sal:IU | — | — | 🟢 單一 |
line_menu_clicks | line:I | sal | — | 🟢 單一 |
line_notice | sal:DI | — | — | 🟢 單一 |
line_unfollow | line:IU | sal | — | 🟢 單一 |
line_unlogin_assign | line:DI | sal | — | 🟢 單一 |
line_unlogin_list | sal:IT | line | — | 🟢 單一 |
人資/薪資 (14 張,其中 1 張多 writer)
| 表 | W(寫入者) | R(讀取者) | DB 內部寫入 | 判定 |
|---|
hum | sal:U inv:IU | ERP rpt invt salN | — | 🔴 多 writer |
hum_con | inv:IU | sal | — | 🟢 單一 |
hum_nrdd | inv:IU | sal | — | 🟢 單一 |
hum_rel | inv:DIU | — | — | 🟢 單一 |
hum_related_cust | sal:DI | — | — | 🟢 單一 |
hum_sar | salN:D | — | — | 🟢 單一 |
hum_sard | salN:DU | — | — | 🟢 單一 |
hum_wktime | sal:I | inv | — | 🟢 單一 |
salary_export_audits | salN:I | — | — | 🟢 單一 |
sys_uemp | inv:IU | sal ERP rpt sls line dlv invt salN | — | 🟢 單一 |
hum_dvm | — | — | — | ❓ 待確認 |
hum_msar | — | sal salN | SP×3 | ⚙️ DB 內部 |
hum_msard | — | salN | SP×3 | ⚙️ DB 內部 |
hum_special_off | — | inv | EV:insert_special_off_day / SP×2 | ⚙️ DB 內部 |
系統/排程/稽核 (17 張,其中 3 張多 writer)
| 表 | W(寫入者) | R(讀取者) | DB 內部寫入 | 判定 |
|---|
bug_reports | sal:IU line:U | — | — | 🔴 多 writer |
cron_alerts | sal:IU ERP:I | — | — | 🔴 多 writer |
sys_sales | rpt:U inv:IU | sal ERP sls forms dlv | — | 🔴 多 writer |
cron_job_logs | sal:IU | — | — | 🟢 單一 |
cron_jobs | sal:IU | — | — | 🟢 單一 |
cron_permission_snapshots | sal:IU | — | — | 🟢 單一 |
sys_pm | ERP:IU | sal inv | — | 🟢 單一 |
sys_sidebar_menu | sal:DITU | — | — | 🟢 單一 |
audits | — | ERP | — | ❓ 待確認 |
jobs | — | ERP | — | ❓ 待確認 |
personal_access_tokens | — | — | — | ❓ 待確認 |
posting_log | — | ERP line | — | ❓ 待確認 |
procedure_performance_logs | — | invt | EV:inventory_update_dv,inventory_update_dv2,refill_call_list_daily,topup_call_data_daily / SP×4 | ⚙️ DB 內部 |
sys_cpu | — | — | — | ❓ 待確認 |
sys_log | — | — | — | ❓ 待確認 |
sys_rpt_list | — | — | — | ❓ 待確認 |
task_results | — | ERP | — | ❓ 待確認 |
AI/其他 (4 張,其中 0 張多 writer)
| 表 | W(寫入者) | R(讀取者) | DB 內部寫入 | 判定 |
|---|
ai_assets | — | sal | — | ❓ 待確認 |
ai_jobs | — | sal | — | ❓ 待確認 |
ai_module_health | — | — | — | ❓ 待確認 |
gpu_jobs | — | sal | EV:gpu_jobs_retention | ⚙️ DB 內部 |
未分類 (40 張,其中 0 張多 writer)
| 表 | W(寫入者) | R(讀取者) | DB 內部寫入 | 判定 |
|---|
birthday_card_done | sal:DI | — | — | 🟢 單一 |
date_tags | invt:DI | — | — | 🟢 單一 |
element_events | ERP:I | — | — | 🟢 單一 |
estimate_runs | ERP:I | — | — | 🟢 單一 |
fax_print_log | sal:I | — | — | 🟢 單一 |
fax_rotation | sal:I | — | — | 🟢 單一 |
gprod | ERP:IU | sal sls www forms line inv | — | 🟢 單一 |
i_credit_qtyf | ERP:I | — | EV:qtyf_credit / SP×1 | 🟢 單一 |
i_dvm_yn | ERP:DIU | sal | — | 🟢 單一 |
i_send_msg | sal:DR | — | — | 🟢 單一 |
i_steambox_pos | sal:R | ERP | — | 🟢 單一 |
ibadtrdcount | rpt:DIU | — | — | 🟢 單一 |
iexpense | inv:IU | rpt | — | 🟢 單一 |
net_perf_ad_spend | sal:I | — | — | 🟢 單一 |
net_perf_config | sal:U | — | — | 🟢 單一 |
net_perf_settlement | sal:I | — | — | 🟢 單一 |
notes | sal:DIU | — | — | 🟢 單一 |
oldprice40_review_notes | sal:DI | — | — | 🟢 單一 |
orders_form_other | sal:I | — | — | 🟢 單一 |
page_daily_access | sal:I | — | — | 🟢 單一 |
page_info | sal:IU | — | — | 🟢 單一 |
price_templates | ERP:DIU | — | — | 🟢 單一 |
punch_hum_anyip | sal:DI | — | — | 🟢 單一 |
renewal_sms_log | sal:DIU | — | — | 🟢 單一 |
salary2txt_log | sal:I | — | — | 🟢 單一 |
sales_dept_performance_targets | ERP:IU | — | — | 🟢 單一 |
sony_admin_user | sony:U | — | — | 🟢 單一 |
sor_config | sal:I | — | — | 🟢 單一 |
sor_extra | sal:DI | — | — | 🟢 單一 |
sor_maintain_school | sal:DI | — | — | 🟢 單一 |
sor_price | sal:U | — | — | 🟢 單一 |
tasks | ERP:IU | — | — | 🟢 單一 |
toy_stocks | invt:DI | — | — | 🟢 單一 |
youtube_summaries | sal:DIU | — | — | 🟢 單一 |
julaug40_review_cache | — | — | — | ❓ 待確認 |
newold_review_cache | — | — | — | ❓ 待確認 |
oldprice40_charged_cache | — | sal | — | ❓ 待確認 |
oldprice40_review_cache | — | — | — | ❓ 待確認 |
rpt_delivery_achievement | — | sal rpt | EV:evt_daily_delivery_update / SP×1 | ⚙️ DB 內部 |
sales_report | — | rpt sls | EV:sales_report / SP×1 | ⚙️ DB 內部 |
2.1 🔴 非 web 直連者:Windows 桌面 client(原盤點漏掉的第 17 個 writer)
這是 david@% 那個 %(允許任意 host)的存在理由,也是「停用 david」的最大阻擋。
| 指標 | 近 90 天 |
|---|
| 操作筆數 | 214 |
| 來源電腦 | 8 台(DESKTOP-URBL6KG、MAY-PC、DESKTOP-F0ROLQ6…) |
| 使用者 | 16 人 |
| 最近一次 | 2026-08-17 |
用到的功能(sys_log.prg_id,即桌面程式的表單名):
| 表單 | 對應業務 | 筆數 | 人數 | 影響的表 |
|---|
frmhcust | 客戶維護 | 172 | 13 | hcust(+6 個 trigger 連鎖) |
frmdept_work0 / frmdept_work | 部門工作 | 10 | 2 | hum_* |
frmhum / frmhum_msar / frmhum_rpt10/13/20 | 人資與薪資 | 13 | 3 | hum、hum_msar |
frmhcust_ant_err / frmgcust_ant_edit | 應收帳款調整 | 9 | 2 | hcust_ant、gcust_ant |
frmsales_hcust_qty | 業務業績 | 3 | 1 | sales_hcust_qty |
frmhcust_mulist2 / frmhcust_mdqty / frmhcust_close_chk / frmvend | 客戶名單/數量/結清/廠商 | 7 | — | 家戶相關 |
🔴 兩個直接後果: 1. hum_msar(薪資)不是只有新薪資站在碰——桌面 frmhum_msar 也在改。 C2 薪資站的 9 月驗收要把這件事納入:新站不是唯一入口。 2. david@% 不能在桌面 client 退場前停用。階段 1 的「david 連續 7 天零新連線」 出口條件,必須先確認這 8 台電腦已停用或改走新帳號,否則會直接打斷 16 個人的日常作業。
追蹤方式:sys_log 就是它的存取日誌(muser/comp_name/prg_id/log_time), 停用前先用它確認歸零,比看 processlist 可靠。
4. DB 內部寫入層(繞過應用層)
這一層是矩陣最容易被忽略的部分:拆完應用帳號,這些照樣會寫。 SP/EVENT/TRIGGER 都以 DEFINER 身分執行——目前多為 david@%, 所以「把 david 停用」之前必須先把這些 DEFINER 換掉,否則排程與觸發器會全數失效。
4.1 EVENT(20 個,全部 ENABLED)
| EVENT | 呼叫的 SP | 寫入表 |
|---|
daily_sales | daily_sales | daily_sales |
dvm_bill_auto_post | dvm_bill_auto_post | hcust_amt_rcv, hcust_ant |
evt_daily_delivery_update | sp_daily_delivery_update | rpt_delivery_achievement |
gpu_jobs_retention | — | gpu_jobs |
i_day_qty | — | i_day_qty |
insert_dv_point | insert_dv_point | ⚠️ 查無寫入表(多為寫冷表或僅讀,待確認) |
insert_dv_record_copy3 | — | i_dv_record_copy3 |
insert_dv_record_copy3_daily | — | i_dv_record_copy3 |
insert_special_off_day | insert_special_off_day | hum_special_off |
inventory_update_dv | inventory_update_dv | daily_dvs, inventory_details, inventory_items, procedure_performance_logs |
inventory_update_dv2 | inventory_update_dv | daily_dvs, inventory_details, inventory_items, procedure_performance_logs |
new_dv_unit_price | new_dv_unit_price | dv_unit_price |
new_dv_unit_price_point | new_dv_unit_price_point | ⚠️ 查無寫入表(多為寫冷表或僅讀,待確認) |
qtyf_credit | qtyf_credit | hcust_qtyf, i_credit_qtyf |
refill_call_list_daily | refill_call_list_recent | call_list, procedure_performance_logs |
retain_data_05T1_cust | retain_data_05T1_cust | call_05T1_cust, call_data |
retain_data_event | retain_data_event | call_05T1_cust, call_data, call_list |
sales_report | sales_report | sales_report |
topup_call_data_daily | topup_call_data_05T1 | call_data, call_list, procedure_performance_logs |
update_daily_stats | update_daily_stats | call_data |
4.2 TRIGGER(8 個)
| TRIGGER | 掛在 | 時機 | 連帶寫入 |
|---|
delete_calldata_after_star | hcust | AFTER UPDATE | call_data |
rt_order | hcust_dorder | AFTER UPDATE | hcust_dorder_rt |
rt_order_copy1_copy1 | hcust_dorder_oat_origin | AFTER UPDATE | hcust_dorder_rt |
update_addr_log | hcust | AFTER UPDATE | hcust_trd |
update_area_log | hcust | AFTER UPDATE | hcust_trd |
update_comp_idno_log | hcust | AFTER UPDATE | hcust_trd |
update_lv_tp_log | hcust | AFTER UPDATE | hcust_trd |
update_rv_tp_log | hcust | AFTER UPDATE | hcust_trd |
🔴 hcust 上掛了 6 個 AFTER UPDATE trigger——任何一個站 UPDATE hcust, 都會連帶寫 hcust_trd(異動記錄)與 call_data。這是「7 個 writer」真正的殺傷力所在: 不是 7 個地方改客戶資料,是 7 個地方各自觸發一整串連鎖寫入。
4.3 PROCEDURE
178 個 PROCEDURE 中 68 個有寫入。寫入表數最多的前 15 個:
| PROCEDURE | 寫入表數 | 寫入表(節選) |
|---|
payment_post | 8 | hcust_amt_pre, hcust_amt_rcv, hcust_ant, hcust_qtyf, hcust_trd, payment, payment_callback_failures, trades |
double_amt_post | 6 | hcust_amt_pre, hcust_amt_rcv, hcust_ant, hcust_qtyf, hcust_trd, trades |
hcust_pre_post | 6 | hcust_amt_pre, hcust_amt_rcv, hcust_ant, hcust_qtyf, hcust_trd, trades |
inventory_update_dv | 4 | daily_dvs, inventory_details, inventory_items, procedure_performance_logs |
pre_over7_post | 4 | hcust_amt_pre, hcust_amt_rcv, hcust_ant, hcust_trd |
inventory_create | 3 | inventory_details, inventory_items, inventory_main |
write_hcust_addr | 3 | hcust, hcust_dorder, hcust_trd |
inventory_transfer | 3 | inventory_details, inventory_items, inventory_main |
rcv_ant_undo | 3 | hcust_amt_pre, hcust_ant, hcust_qtyf |
retain_data_event | 3 | call_05T1_cust, call_data, call_list |
topup_call_data_05T1 | 3 | call_data, call_list, procedure_performance_logs |
create_invoices_hcust_v2 | 2 | invoice_details, invoices |
delete_amt_pre | 2 | customer_pms, hcust_amt_pre |
inventory_update | 2 | inventory_details, inventory_items |
create_invoices_gcust | 2 | invoice_details, invoices |
5. 表對表的同步/衍生關係(同步方向)
| 來源 | → | 目的 | 機制 | 現況 |
|---|
hcust_dorder | → | weekly_orders | ERP SyncOld job 雙寫 | 🔄 並行中——salary 等舊站仍讀舊表,故雙寫不能停;ERP 重心是收斂雙寫,不是刪死碼 |
hcust_dorder | → | hcust_dorder_rt | TRIGGER rt_order (AFTER UPDATE) | 🔄 生效中 |
hcust | → | hcust_trd | TRIGGER update_addr/area/rv_tp/lv_tp/comp_idno_log | 🔄 生效中(5 個 trigger 都寫異動記錄) |
hcust | → | call_data | TRIGGER delete_calldata_after_star | 🔄 客戶改名為 * 開頭時停掉電訪名單 |
daily_orders | → | daily_orders_shadow | shadow 估奶驗證 | ⏸️ 已停用(goalu.tasks id 45/46 status→0,2026-08-17);影子表凍結觀察一個月結後清 |
dv_orders | → | dv_orders_shadow | 同上 | ⏸️ 已停用 |
| (多表) | → | i_dv_record_copy3 | EVENT insert_dv_record_copy3 / _daily | 🔄 每日寫入,526 萬列 |
call_data | ↔ | call_list / call_05T1_cust | EVENT retain_data_event、refill_call_list_daily、topup_call_data_daily | 🔄 每日輪替電訪名單 |
dorder / dorderd / hcust_trdh | — | — | (無) | 🧊 真凍結——2025-03-14 同日停寫,可列入冷表退場 |
6. 三方一致抽查(checklist 硬門檻驗證)
checklist 要求「抽查 3 張表,矩陣記載與呼叫關係、實際 GRANT 三方一致」。 ⚠️ 說明:codebase-memory 不索引 SQL 字面值(表名寫在字串裡), 故第二方改用掃描器輸出的實際寫入點(檔:行),比符號圖更硬。
hum_msar
- 矩陣記載 writer:—
- 矩陣記載 reader:html/salary, salary(new)
- DB 內部:SP×3、EVENT —、本表 trigger —
程式碼實證(掃描器記錄的寫入檔,取樣附行號):
hcust
- 矩陣記載 writer:html/salary(DIU), html/sales(IU), www.kngoatmilk.com(DIU), inventory(U), forms(IU), delivery(IU), ERP(kngoatmilk)(DIU)
- 矩陣記載 reader:html/report, invoice, kng.tw, line, pbx, pbx-laravel12
- DB 內部:SP×6、EVENT —、本表 trigger ['update_area_log(AFTER UPDATE)', 'update_rv_tp_log(AFTER UPDATE)', 'update_addr_log(AFTER UPDATE)', 'update_lv_tp_log(AFTER UPDATE)', 'delete_calldata_after_star(AFTER UPDATE)', 'update_comp_idno_log(AFTER UPDATE)']
程式碼實證(掃描器記錄的寫入檔,取樣附行號):
html/salary UPDATE → api/dv_qty_group.php:15html/salary UPDATE → call_list/api/empty.php:28html/salary INSERT → sms_management/scripts/test_hcust_convert.php:7html/salary DELETE → sms_management/scripts/test_dual_temp.php:228html/sales UPDATE → api/drink.php:64html/sales UPDATE → api/drink_order.php:105html/sales INSERT → api/sales.php:435www.kngoatmilk.com INSERT → api/create_hcust.php:6www.kngoatmilk.com INSERT → api/form.php:11www.kngoatmilk.com UPDATE → api/credit/credit_callback.php:18
trades
- 矩陣記載 writer:html/salary(DIU), www.kngoatmilk.com(IU), ERP(kngoatmilk)(DIU)
- 矩陣記載 reader:html/report, invoice, line
- DB 內部:SP×4、EVENT —、本表 trigger —
程式碼實證(掃描器記錄的寫入檔,取樣附行號):
html/salary UPDATE → api/cron/cub_b2b.php:12html/salary UPDATE → api/cron/trial_cancel_reschedule.php:21html/salary DELETE → sms_management/scripts/test_dual_temp.php:220html/salary DELETE → sms_management/scripts/test_hcust_convert.php:304html/salary INSERT → api/messaging.php:1660html/salary INSERT → api/repair_trades_20260501.php:191www.kngoatmilk.com INSERT → api/credit/credit_callback.php:298ERP(kngoatmilk) repo:create → app/Jobs/HcustAmtPreNotify.phpERP(kngoatmilk) repo:create → app/Models/Services/AddressBookService.phpERP(kngoatmilk) repo:update → app/Models/Services/TradeService.php
三方比對結論
| 表 | 矩陣 | 程式碼實證 | 實際 GRANT | 一致? |
|---|
hum_msar | 無 web app writer、SP×3 | 新薪資站零寫入呼叫(僅 CALL);但桌面 frmhum_msar 在寫(見 2.1) | salary_app 有 SELECT,INSERT,UPDATE,DELETE | ⚠️ 矩陣需補「桌面 client」欄;salary_app GRANT 過寬——見第 7 節 |
hcust | 7 個 app writer+6 trigger | 7 站皆有實際寫入點 | 全部走 david=ALL ON *.* | ✅ 一致(但現況本身就是問題) |
trades | 3 個 app writer+SP×4 | api/messaging.php:1660、credit_callback.php:298、ERP TradeService | 同上 | ✅ 一致 |
7. 立即可收窄的權限(矩陣的第一個實際成果)
| 表 | 目前 GRANT | 程式實際用到 | 建議 |
|---|
hum_sar | SELECT,INSERT,UPDATE,DELETE | SELECT+DELETE(Livewire/Sar/Sars.php) | 撤 INSERT,UPDATE(新增/修改走 SP) |
hum_sard | SELECT,INSERT,UPDATE,DELETE | SELECT+UPDATE+DELETE(Livewire/Sard/*) | 撤 INSERT |
hum_msar | SELECT,INSERT,UPDATE,DELETE | 只有 SELECT(寫入由 SP salary/salary_dv 代勞) | 撤 INSERT,UPDATE,DELETE |
hum_msard | SELECT,INSERT,UPDATE,DELETE | 只有 SELECT(同上) | 撤 INSERT,UPDATE,DELETE |
salary_export_audits | SELECT,INSERT | SELECT+INSERT(ExportController) | ✅ 剛好 |
⚠️ 先別直接 REVOKE:SP 以 DEFINER=david@% 代跑,撤掉表級權限不影響 CALL, 但 9 月算薪驗收(docs/decision-c2-mount-20260817.md)還沒跑完, 建議驗收通過後再收,並在收完後跑 composer test 與 database/gm/verify.sh 複驗。
8. 收斂路線(對應戰線 B「寫入權集中」)
8.1 優先序:先動 writer 最多的
| 順位 | 表 | writer 數 | 為什麼先動 |
|---|
| 1 | hcust | 7 | 客戶主檔+6 個 trigger 連鎖寫入,事故半徑最大 |
| 2 | hcust_trd | 7 | 業務異動核(trades 三方寫入之一),帳務與配送都靠它 |
| 3 | i_form | 5 | 表單收單入口,對外站與 LINE 都在寫 |
| 4 | hcust_dorder | 4 | 訂單主檔,且正與 weekly_orders 雙寫並行 |
| 5 | payment | 4 | 金流紀錄,4 個 writer 分散在對外站與 LINE |
| 6 | SMS_log | 4 | 訊息稽核,收斂進 kng/notify 後應只剩一個 writer |
| 7 | address_book_indexes | 3 | PK 是保留字 index,多方寫入易出錯 |
| 8 | customers | 3 | 新世代客戶表,ERP 應為唯一 writer |
| 9 | gcust_ant | 3 | 團戶應收,與 invoice 站重疊 |
| 10 | hcust_token | 3 | 對外票券,www 與 line 各寫各的 |
8.2 階段 1 待辦(本矩陣直接衍生)
- 把
david@%/tom@% 從 ALL ON *.* 收成 goalu.*——這一步不改任何程式碼, 卻立刻消除「一個帳號打穿全機 MariaDB(callhub/muxue/lk/shop…)」的風險。先做這個。 - 逐站建專屬帳號,照本矩陣的 W/R 欄授權;順序建議由讀多寫少的站開始:
html/report(2 張表寫入)→ html/sony(3)→ kng.tw(1)→ forms(3)→ inventory(7)→ line(4)→ invoice(3)→ html/sales(6)→ www(11)→ ERP(最後,它應該是唯一 writer) html/salary 是最大宗(87 張表寫入)——它同時是「總務室」問題本體, 不建議一次拆,應隨階段 5 對內站歸併逐模組搬遷。- 🔴 桌面 client 先處理:
david@% 不能在 8 台 Windows 電腦(16 人)退場前停用。 先用 sys_log 確認實際使用者與功能,決定「搬到 web」或「配一個專屬帳號 legacy_desktop」。 這是階段 1 出口條件(david 零連線)的真正阻擋點。 - DEFINER 換人:停用 david 之前,先把 68 個 SP/20 個 EVENT/8 個 TRIGGER 的 DEFINER 換成專用的
goalu_scheduler 帳號,否則排程全掛。 - 明碼密碼:
html/salary/html/report/html/sales/html/sony/kng.tw 五站 把 david/密碼寫死在 PHP 裡,拆帳號時一併改成讀 env。
8.3 刻意不做
- 不動 381 張冷表:90 天無寫入,退場另案處理,不佔階段 1 的驗證預算。
- 不追求「一張表只有一個 writer」:矩陣的門檻是成文——每張表要能指出誰是唯一 writer、
誰只能讀。實際收斂是階段 2~5 的事,階段 1 只要矩陣正確且帳號照它授權。
9. 可重跑(矩陣不可手改後漂移)
掃描器在 tools/db-matrix/,重生指令:
cd /var/www/html/salary/tools/db-matrix
./rebuild.sh # 重跑全部掃描並覆寫 ../../erp_docs/write-permission-matrix.md
內部流程:
active_tables.txt ← information_schema.TABLES(90 天內 UPDATE_TIME)scan_writers.py ← raw SQL(INSERT/UPDATE/DELETE/REPLACE/TRUNCATE/CALL)+ DB::table()->writescan_eloquent.py ← Eloquent Model → table,再找靜態寫入呼叫scan_erp_repo.py ← ERP 專用:Repository → Entity → table(ERP 走 Repository 統一寫入層)scan_readers.py ← FROM/JOIN/DB::table()/Model 存在即算 readerdb_chain.py ← EVENT →(遞迴)CALL SP → 寫入表;TRIGGER 同理gen_matrix.py + finalize.py ← 合併輸出本文件
掃描器的已知限制(讀矩陣前先知道)
| 限制 | 影響 | 緩解 |
|---|
靜態掃描抓不到動態表名($table 變數拼接) | 可能漏 writer | 48 張「無 app writer」已逐張人工複查,見第 10 節 |
Eloquent 關聯寫入($parent->items()->create())未涵蓋 | 可能漏 writer | ERP 走 Repository,已另掃;其餘站關聯寫入少 |
.sql 檔(SP 定義/dump)已排除 | 避免把 SP 備份誤算成應用寫入 | SP 寫入改由 DB 端 information_schema 取得,更準 |
UPDATE_TIME 對 InnoDB 非精確值 | 活表/冷表邊界可能±數張 | 邊界表以業務判斷為準,不以 90 天硬切 |
10. 無 app writer 的 48 張表
10.1 由 DB 內部寫入(14 張)——正常
| 表 | 寫入者 |
|---|
call_05T1_cust | EVENT retain_data_05T1_cust, retain_data_event / SP×2(retain_data_05T1_cust, retain_data_event) |
call_list | EVENT refill_call_list_daily, retain_data_event, topup_call_data_daily / SP×3(refill_call_list_recent, retain_data_event, topup_call_data_05T1) |
daily_sales | EVENT daily_sales / SP×1(daily_sales) |
dv_unit_price | EVENT new_dv_unit_price / SP×1(new_dv_unit_price) |
gpu_jobs | EVENT gpu_jobs_retention |
hcust_dorder_rt | TRIGGER rt_order@hcust_dorder, rt_order_copy1_copy1@hcust_dorder_oat_origin |
hum_msar | SP×3(salary, salary_add_up, salary_dv) |
hum_msard | SP×3(salary, salary_add_up, salary_dv) |
hum_special_off | EVENT insert_special_off_day / SP×2(insert_special_off, insert_special_off_day) |
i_day_qty | EVENT i_day_qty |
i_dv_record_copy3 | EVENT insert_dv_record_copy3, insert_dv_record_copy3_daily |
procedure_performance_logs | EVENT inventory_update_dv, inventory_update_dv2, refill_call_list_daily, topup_call_data_daily / SP×4(inventory_update_dv, refill_call_list_recent, retain_data_05T1_list, topup_call_data_05T1) |
rpt_delivery_achievement | EVENT evt_daily_delivery_update / SP×1(sp_daily_delivery_update) |
sales_report | EVENT sales_report / SP×1(sales_report) |
10.2 查無任何 writer(34 張)——待人工確認
這些表近 90 天有寫入時間戳,卻掃不到 writer。可能原因:外部腳本(bash/python cron)、 動態表名、其他主機寫入、或 UPDATE_TIME 因 optimize 而變動(實際已停寫)。
| 表 | 有引用的站(非寫入) | 研判 |
|---|
ai_assets | html/salary | 待查 |
ai_jobs | html/salary | 待查 |
ai_module_health | — | aigen 能力控制台,疑走 bash/python |
audits | ERP(kngoatmilk) | ERP auditing 套件寫(vendor) |
customer_gs | ERP(kngoatmilk), html/report, html/salary, invoice | 待查 |
customer_hs | ERP(kngoatmilk), html/salary | 待查 |
daily_orders_shadow | ERP(kngoatmilk) | 待查 |
dv_orders_shadow | ERP(kngoatmilk) | 待查 |
gcust_close | ERP(kngoatmilk) | 待查 |
gcust_ml_items | ERP(kngoatmilk), html/salary, inventory | 待查 |
hcust_close | ERP(kngoatmilk) | 待查 |
hcust_sales_per | — | 疑由 SP 動態寫 |
hum_dvm | — | 疑由 SP 動態寫 |
i_lbt | — | 疑已停寫(UPDATE_TIME 誤差) |
i_prod | ERP(kngoatmilk), forms, html/report, html/salary, inventory, invoice, line, www.kngoatmilk.com | 待查 |
i_rv_tp_area | ERP(kngoatmilk), html/salary, www.kngoatmilk.com | 待查 |
i_sales_car | invoice | 待查 |
i_sales_daily | invoice | 待查 |
jobs | ERP(kngoatmilk) | Laravel queue 內建表,框架寫(vendor 已排除掃描) |
julaug40_review_cache | — | 待查 |
marea_cover_details | ERP(kngoatmilk) | 疑已停寫 |
newold_review_cache | — | 待查 |
oldprice40_charged_cache | html/salary | 待查 |
oldprice40_review_cache | — | 待查 |
pbx_call_transcript_breeze | — | 夜批通話轉錄(daily_run.sh)寫,非 PHP |
pbx_token | pbx, pbx-laravel12 | 待查 |
personal_access_tokens | — | Sanctum 發 token 時寫(vendor) |
posting_log | ERP(kngoatmilk), line | 待查 |
sales_dvms | ERP(kngoatmilk), html/report | 待查 |
sales_dvm_products | ERP(kngoatmilk) | 待查 |
sys_cpu | — | 疑為主機監控腳本寫(非 PHP) |
sys_log | — | ✅ 已查明:Windows 桌面 client 的存取日誌(見 2.1),不是 web 站寫的 |
sys_rpt_list | — | ✅ 已查明:桌面 client 的報表列印記錄(SALES_HCUST_QTY 等) |
task_results | ERP(kngoatmilk) | 疑為排程結果表,ERP TaskRepository 動態寫 |
相關文件: 整合規劃 v5 · 執行路線圖 · 七階段檢核清單 · 網域規劃 · 薪資站帳號範本 /var/www/salary/docs/db-account-matrix.md