บันทึกการติดต่อลูกค้าและรูปใน MinIO (sys-crm-activity-images)
ในโมดูลโทรติดตามของ /crm ผู้ใช้บันทึกการคุยกับลูกค้าพร้อมแนบรูปได้ ข้อมูลเก็บในตาราง crm_customer_activity ส่วนรูปเก็บใน MinIO/S3 โดยตารางเก็บไว้เพียง key ของรูป
ตาราง crm_customer_activity
สร้างที่ handler.go:878-892 คอลัมน์ ได้แก่ cust_code, cust_name, channel, subject, note_text, contact_person, contact_result, next_followup_date (varchar(40)), created_by, image_key, image_thumb_key และ create_date_time_now
GET /api/v1/crm/activitiesอ่าน 100 แถวล่าสุด (:797-820)- ก่อนอ่าน ระบบเรียก
ensureSeedActivitiesซึ่งใส่ข้อมูลตัวอย่างให้เฉพาะฐานbcdemo(:426-436,:1007-1017)
บันทึกกิจกรรม (POST /api/v1/crm/activities)
- รับได้ทั้ง multipart และ JSON โดย multipart จำกัด body ที่ 12 MB และ form ที่ 10 MB รูปส่งในฟิลด์
fileหรือimage(handler.go:442-483) - ฟิลด์ที่รับ:
cust_code,cust_name,channel,subject,note,contact_person,contact_result,next_followup_date,by(:495-505) - ค่าตั้งต้น: รหัสลูกค้าตัวอย่าง ช่องทาง "โทรติดตาม" หัวข้อ "ติดตามการสั่งซื้อและดูแลลูกค้า" ผลการติดต่อ "สนใจ / ติดตามผลต่อ" และผู้บันทึกเป็น "พนักงานฝ่ายขาย (<username>)" (
:520-538) noteต้องมีค่า ถ้าว่างจะตอบKindValidation(:529-531)- ถ้ามีรูปจะอัปโหลดเข้า MinIO แล้ว insert แถวผ่าน
insertUseCaseถ้า insert ไม่สำเร็จจะคืน error (:533-600)
ฝั่งหน้าจอ handleAddCustomerNote ส่ง FormData ที่มีฟิลด์ตามข้อ 2 และแนบรูปในฟิลด์ file (CrmDashboard.tsx:704-730)
กติกาไฟล์รูป (uploadActivityImageToMinIO, :738-790)
- ไฟล์ว่างหรือเกิน 10 MB จะถูกปฏิเสธ
- ตรวจชนิดจากเนื้อไฟล์ด้วย
http.DetectContentTypeรับ JPEG และ PNG ส่วนไฟล์อื่นต้องขึ้นต้นด้วยRIFFจึงจะถือเป็น WEBP - key =
<ฐานข้อมูล>/crm/<วันเวลา>_<sha256 ย่อ><นามสกุล>และภาพย่อ JPEG ที่<key>.thumb.jpgถ้าสร้างภาพย่อไม่สำเร็จจะใช้ key ของรูปหลักแทน POST /api/v1/crm/upload(UploadImage) ใช้กติกาเดียวกันสำหรับอัปโหลดรูปอย่างเดียว (:673-690)
ส่งรูป (GET /api/v1/crm/image, :605-668)
- key ต้องขึ้นต้นด้วย
<ฐานข้อมูลใน session>/และห้ามมี..ไม่เช่นนั้นจะตอบ not found variant=thumbnailจะอ่านภาพย่อ ซึ่งGetThumbnailสร้างใหม่ให้เมื่อยังไม่มี (minio.go:163)- ถ้าอ่านจาก MinIO ไม่ได้จะส่งรูป placeholder กลับไป แต่ไม่เขียนทับรูปจริงใน MinIO
- Next API proxy กรองชนิดไฟล์ด้วย
safeMediaContentTypeและใส่X-Content-Type-Options: nosniff(api/crm/image/route.ts:62-68)
บทเรียนที่บันทึกไว้: เดิม proxy ส่ง Content-Type ของไฟล์อัปโหลดต่อตรง ๆ (S5) และเดิม ServeImage เขียน placeholder ทับรูปหลักฐานจริงเมื่อ MinIO ล่ม (B7) (KMS_SECURITY_DATA_INTEGRITY_LESSONS.md:18, :29)
การตั้งค่า MinIO
NewMinIOClientFromEnv อ่านค่าจาก env โดยลองชื่อแบบ S3 ก่อนแล้วจึงแบบ R2 (minio.go:41-58)
| ค่า | env |
|---|---|
| endpoint | S3_ENDPOINT หรือ R2_ENDPOINT |
| bucket | S3_BUCKET_NAME หรือ R2_BUCKET_NAME |
| access key | S3_ACCESS_KEY_ID หรือ R2_ACCESS_KEY_ID |
| secret key | S3_SECRET_ACCESS_KEY หรือ R2_SECRET_ACCESS_KEY |
| region | S3_REGION |
HTTP client ตั้ง timeout 25 วินาที handler ของ e-Tax, Procure และ Market ก็ใช้ชนิด *crmhttp.MinIOClient นี้เก็บรูปหลักฐาน (etax/handler.go:41, procure/handler.go:44, market/handler.go:44) ดู สร้างและประทับเวลาเอกสาร e-Tax (sys-etax-document-stamp)
ที่มาในโค้ด
backend/internal/adapter/inbound/http/crm/handler.go:426-436ListActivities,:797-820SQL อ่านกิจกรรมbackend/internal/adapter/inbound/http/crm/handler.go:442-531รับคำขอ ค่าตั้งต้น และตรวจnotebackend/internal/adapter/inbound/http/crm/handler.go:533-600อัปโหลดรูปและ insertbackend/internal/adapter/inbound/http/crm/handler.go:605-668ServeImagebackend/internal/adapter/inbound/http/crm/handler.go:673-690UploadImagebackend/internal/adapter/inbound/http/crm/handler.go:738-790กติกาไฟล์และ keybackend/internal/adapter/inbound/http/crm/handler.go:878-892DDL,:1007-1017seed เฉพาะbcdemobackend/internal/adapter/inbound/http/crm/minio.go:41-58env ของ MinIO,:163GetThumbnailfrontend/crm/CrmDashboard.tsx:704-730ฟอร์มบันทึกกิจกรรมfrontend/src/app/api/crm/image/route.ts:62-68proxy รูป- ดูเพิ่ม: ระบบบริหารความสัมพันธ์ลูกค้า (sys-crm) · API อัปโหลดรูปพร้อมแทรกแถว /blob/insert (Blob insert endpoint) · แชท AI เอกสารขายในแชท และปุ่มเปิดบิลจาก LINE OA (sys-lineoa-chat-ai)