feat: Tích hợp Bản quyền License AES-256 (OTH.Adapter), Biên bản giao ban A4, Biểu đồ tròn và Tài liệu triển khai demo

This commit is contained in:
ongtrieuhau
2026-10-27 02:04:23 -07:00
parent 6d54ca20f6
commit 358cfbfde1
13 changed files with 2149 additions and 32 deletions
+70
View File
@@ -2,12 +2,14 @@ package handlers
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"
"web-giao-ban-go/config"
"web-giao-ban-go/db"
"web-giao-ban-go/license"
"web-giao-ban-go/logger"
"web-giao-ban-go/updater"
)
@@ -28,6 +30,9 @@ func HandleGetAdminConfig(w http.ResponseWriter, r *http.Request) {
if respCfg.Updater.Token != "" {
respCfg.Updater.Token = "••••••••"
}
if respCfg.License.CryptoKey != "" {
respCfg.License.CryptoKey = "••••••••"
}
_ = json.NewEncoder(w).Encode(map[string]interface{}{
"success": true,
@@ -78,6 +83,19 @@ func HandleSaveAdminConfig(w http.ResponseWriter, r *http.Request) {
input.Updater.Token = enc
}
// Handle License Crypto Key
if input.License.CryptoKey == "" || input.License.CryptoKey == "••••••••" {
input.License.CryptoKey = current.License.CryptoKey
input.DecryptedCryptoKey = current.DecryptedCryptoKey
} else {
input.DecryptedCryptoKey = input.License.CryptoKey
enc, _ := config.Encrypt(input.License.CryptoKey)
input.License.CryptoKey = enc
}
if input.License.Key == "" {
input.License.Key = current.License.Key
}
if err := config.SaveConfig(&input); err != nil {
w.WriteHeader(http.StatusInternalServerError)
_ = json.NewEncoder(w).Encode(map[string]interface{}{"success": false, "message": "Lưu file thất bại: " + err.Error()})
@@ -216,3 +234,55 @@ func HandleApplyUpdate(w http.ResponseWriter, r *http.Request) {
"message": "Đã cập nhật phiên bản mới thành công! Vui lòng khởi động lại phần mềm để áp dụng.",
})
}
// GET /api/license/status
func HandleGetLicenseStatus(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
status := license.GetStatus()
_ = json.NewEncoder(w).Encode(map[string]interface{}{
"success": true,
"data": status,
})
}
// POST /api/license/activate
func HandleActivateLicense(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
var body struct {
Key string `json:"key"`
}
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
w.WriteHeader(http.StatusBadRequest)
_ = json.NewEncoder(w).Encode(map[string]interface{}{
"success": false,
"message": "Dữ liệu JSON không hợp lệ: " + err.Error(),
})
return
}
key := strings.TrimSpace(body.Key)
if key == "" {
w.WriteHeader(http.StatusBadRequest)
_ = json.NewEncoder(w).Encode(map[string]interface{}{
"success": false,
"message": "Vui lòng nhập chuỗi key bản quyền (chỉ nhập chuỗi mã hóa DECODE)",
})
return
}
info, err := license.Activate(key)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
_ = json.NewEncoder(w).Encode(map[string]interface{}{
"success": false,
"message": "Kích hoạt thất bại: " + err.Error(),
})
return
}
_ = json.NewEncoder(w).Encode(map[string]interface{}{
"success": true,
"message": fmt.Sprintf("Kích hoạt bản quyền thành công cho đơn vị '%s' (Mã BV: %s)!", info.TenBV, info.Mabvbh),
"data": info,
})
}
+37 -2
View File
@@ -10,6 +10,7 @@ import (
"web-giao-ban-go/config"
"web-giao-ban-go/db"
"web-giao-ban-go/license"
)
// Helper to generate partition filters: namkt IN (...) AND thangkt IN (...)
@@ -61,13 +62,32 @@ func GetThangNamFilter(fromStr, toStr, prefix string) string {
)
}
func checkLicense(w http.ResponseWriter) bool {
valid, msg := license.CheckValid()
if !valid {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusForbidden)
_ = json.NewEncoder(w).Encode(map[string]interface{}{
"success": false,
"license_valid": false,
"message": msg,
"error": msg,
})
return false
}
return true
}
// GET /api/databases
func HandleGetDatabases(w http.ResponseWriter, r *http.Request) {
cfg := config.Get()
w.Header().Set("Content-Type", "application/json; charset=utf-8")
licStatus := license.GetStatus()
_ = json.NewEncoder(w).Encode(map[string]interface{}{
"success": true,
"data": cfg.Database.Databases,
"success": true,
"data": cfg.Database.Databases,
"license_valid": licStatus.IsValid,
"license": licStatus,
})
}
@@ -297,6 +317,9 @@ func queryOutpatient(database, fromDate, toDate string) (*OutpatientData, error)
// GET /api/giao-ban-ngoai-tru
func HandleGiaoBanNgoaiTru(w http.ResponseWriter, r *http.Request) {
if !checkLicense(w) {
return
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
q := r.URL.Query()
database := q.Get("database")
@@ -399,6 +422,9 @@ func queryInpatient(database, fromDate, toDate string) (*InpatientData, error) {
// GET /api/giao-ban-noi-tru
func HandleGiaoBanNoiTru(w http.ResponseWriter, r *http.Request) {
if !checkLicense(w) {
return
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
q := r.URL.Query()
database := q.Get("database")
@@ -448,6 +474,9 @@ func HandleGiaoBanNoiTru(w http.ResponseWriter, r *http.Request) {
// GET /api/danh-sach-chuyen-vien
func HandleDanhSachChuyenVien(w http.ResponseWriter, r *http.Request) {
if !checkLicense(w) {
return
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
q := r.URL.Query()
database := q.Get("database")
@@ -538,6 +567,9 @@ func HandleDanhSachChuyenVien(w http.ResponseWriter, r *http.Request) {
// GET /api/danh-sach-theo-doi
func HandleDanhSachTheoDoi(w http.ResponseWriter, r *http.Request) {
if !checkLicense(w) {
return
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
q := r.URL.Query()
database := q.Get("database")
@@ -636,6 +668,9 @@ func HandleDanhSachTheoDoi(w http.ResponseWriter, r *http.Request) {
// POST /api/cap-nhat-ghi-chu
func HandleCapNhatGhiChu(w http.ResponseWriter, r *http.Request) {
if !checkLicense(w) {
return
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
var body struct {
Maba string `json:"maba"`