#process#picture#attachment#sml_doc_images#images#advisory-lock
แนบรูปเอกสารและรูปสินค้า (Document & item pictures)
ระบบเก็บรูปแนบ 2 ชุดในฐานข้อมูลบริษัท: รูปประกอบ เอกสาร (ตาราง sml_doc_images) และรูปของ สินค้า/บาร์โค้ด/พนักงาน/ทรัพย์สิน (ตาราง images) ทั้งสองใช้ตัวบันทึกร่วมกันคือ replacePictureSet ซึ่งบันทึกแบบ "แทนที่ทั้งชุด" ภายใน transaction เดียว
ทำงานอย่างไร
รูปเอกสาร (docpicture/*)
- Action ของ route:
formats(รายการประเภทเอกสารของโมดูล),docs(ค้นรายการเอกสาร, หน้าละ 50 =docPicturePageSize),get(ดูรูปของเอกสารหนึ่งใบ),save(บันทึก), และimage(GET ดึงไฟล์รูปตามid) DocPictureFormatsผูกโมดูลกับerp_doc_format.screen_codeผ่าน mapdocPictureFormatsโมดูลic,po,so,ap,ar,cb; โมดูลglไม่มีรายการประเภทเอกสาร- คีย์ของรูป (
docPictureKey) ก่อนอื่นตรวจว่าเอกสารมีจริง:- ไม่ใช่ GL: ค้น
doc_no+doc_format_codeในic_trans,ap_ar_transหรือcb_trans(อย่างใดอย่างหนึ่ง) ถ้าไม่พบ ->KindNotFound"ไม่พบเอกสาร"; คีย์คือdoc_noตามที่พิมพ์ - GL: ค้น
gl_journalด้วยdoc_date+doc_no+book_code; คีย์ = วันที่d/MM/yyyy+doc_no+book_codeแล้วลบ/ออก (glPictureKey, ตามคอมเมนต์คือ_codepicของ C#) ปีเป็น พ.ศ. เมื่อerp_option.year_type = 1หรือเมื่อไม่มีแถวerp_optionเลย (ค่าเริ่มต้นแบบ th-TH ของเดสก์ท็อป) - คีย์ยาวเกิน 50 ตัวอักษร (
docPictureKeyLen, เพราะimage_idเป็น varchar(50)) -> error "เก็บรูปไม่ได้"
- ไม่ใช่ GL: ค้น
รูปสินค้า (itempicture/*)
- Action:
list(ค้นพร้อมคำค้นหลายคำ, หน้าละ 50),get,save,image Kindมี 4 แบบ ตัวเลือกitem | barcode | staff | assetซึ่งitemPictureTargetค้นต้นทางต่างกัน:item:ic_inventory(รหัส,name_1,unit_standard); แก้ไขได้เมื่อupdate_detail = 1barcode:ic_inventory_barcodeเข้าic_inventory(ชื่อจากdescriptionหรือname_1); แก้ไขได้ตามic_inventory.update_detail = 1staff:erp_user;asset:as_asset(สองแบบนี้แก้ไขได้เสมอ)
- ถ้ารายการแก้ไขไม่ได้ ->
KindConflict"สินค้านี้ยังไม่พร้อมแก้ไข จึงแนบรูปไม่ได้" - เมื่อบันทึกสำเร็จเขียน
master_logs1 แถว (function_code 2,function_type 2,computer_name 'WEB',menu_name=ItemPictureหรือItemBarcodePicture)
ข้อจำกัดและการตรวจไฟล์
| รายการ | รูปเอกสาร | รูปสินค้า |
|---|---|---|
| จำนวนรูปสูงสุด | 8 (docPictureMaxImages) |
6 (itemPictureMaxImages) |
| ขนาดต่อรูป | 5 MB (docPictureMaxBytes) |
ใช้ตัวถอดรหัสเดียวกัน (5 MB) |
| ขนาด body ของคำขอ save | 16 MB (maxJSONBody) |
48 MB (itemPictureBodyLimit) |
decodePictureItemsMax: รูปใหม่มีid = 0และdataเป็น base64 (ตัดคำนำหน้าdata:...,ออกให้); รูปที่เก็บไว้มีid > 0และdataว่าง; รูปแบบอื่น -> error "รายการรูปที่ N ไม่ถูกต้อง"; เกินจำนวน -> "แนบรูปได้ไม่เกิน N รูป"; เกินขนาด -> "ใหญ่เกิน 5 MB"- ชนิดไฟล์ตรวจจาก magic bytes (
sniffImage) ยอมรับ PNG, JPG, GIF, BMP เท่านั้น ไม่เชื่อนามสกุลหรือ content-type ที่ผู้ใช้ส่งมา DocPictureMimeคืน mime ที่ตรวจได้ หรือapplication/octet-stream; handler ส่งCache-Control: private, max-age=300และX-Content-Type-Options: nosniff
การบันทึกแบบแทนที่ทั้งชุด (replacePictureSet)
- ล็อก
pg_advisory_xact_lock(hashtext('docpic:' || ตาราง || ':' || คีย์))กันสองคนบันทึกชุดเดียวกันพร้อมกัน - ตรวจว่า
roworderของรูปที่ขอเก็บ (keep) ยังอยู่ครบ ถ้าไม่ ->KindConflict"รูปนี้ถูกเปลี่ยนโดยผู้ใช้อื่น" - ลบรูปที่ไม่อยู่ใน
keep, เรียงimage_orderใหม่, แล้วแทรกรูปใหม่(image_id, image_file, guid_code, image_order)
- คอมเมนต์ในโค้ดระบุว่าพอร์ตจาก Java
_SaveImageList
สิทธิ์
docpicture/formats|docs|get|imageและitempicture/list|get|image=read;docpicture/saveและitempicture/save=write(การตรวจสิทธิ์เมนูของ route process (Process route rights))
จุดที่ต้องระวัง
- ส่ง
dataของรูปเดิมมาซ้ำพร้อมid > 0ไม่ได้: โค้ดมองเป็นรายการไม่ถูกต้อง (รูปเดิมให้ส่งแค่id) - รูป GL ผูกกับคีย์ที่ประกอบจากวันที่ จึงขึ้นกับ
year_typeของบริษัท ถ้าเปลี่ยนค่านี้คีย์ที่คำนวณจะเปลี่ยน (ข้อสรุปจากสูตรในglPictureKey, ยังไม่ได้ทดสอบกับข้อมูลจริง) listPicturesใช้ownStorageURLกรอง URL ภายนอก: ยอมเฉพาะ path/api/crm/imageที่keyขึ้นต้นด้วยชื่อฐานข้อมูลของบริษัทนั้น
ที่มาในโค้ด
backend/internal/application/process/doc_picture.go:27-39- ชื่อตารางและข้อจำกัดbackend/internal/application/process/doc_picture.go:74-DocPictureFormats;:130DocPictureDocsbackend/internal/application/process/doc_picture.go:200-glPictureKey;:209docPictureKey;:254checkPictureKeyLenbackend/internal/application/process/doc_picture.go:299-ownStorageURL;:311DocPictureGet;:333DocPictureFilebackend/internal/application/process/doc_picture.go:361-sniffImage;:376DocPictureMime;:387decodePictureItemsMaxbackend/internal/application/process/doc_picture.go:426-DocPictureSave;:455replacePictureSetbackend/internal/application/process/item_picture.go:22- ค่าคงที่;:87ItemPictureList;:172itemPictureTarget;:210ItemPictureGet;:231ItemPictureFile;:252ItemPictureSavebackend/internal/adapter/inbound/http/process/docpicture_handler.go:36- routedocpicture;:89ดึงรูปbackend/internal/adapter/inbound/http/process/stocktools_handler.go:44-64- routeitempicturebackend/internal/adapter/inbound/http/process/permission.go:30-42- สิทธิ์ของ route รูป
ดูเพิ่ม
- ตัวกระจายคำขอของ process (Process dispatcher)
- การตรวจสิทธิ์เมนูของ route process (Process route rights)
- ข้อมูลสินค้าและข้อมูลสินค้าเพิ่มเติม (Item master & item detail)
- สถานะต่อเนื่องของเอกสาร กราฟโยงเอกสาร และเลขที่เอกสารถัดไป (Doc flow & numbering)
- ภาพรวมกลุ่ม process ของ Backend (Process overview)