|
|
@@ -0,0 +1,389 @@
|
|
|
+package main
|
|
|
+
|
|
|
+import (
|
|
|
+ "C"
|
|
|
+ "encoding/json"
|
|
|
+ "fmt"
|
|
|
+ "newsffi/model"
|
|
|
+
|
|
|
+ // "time" // This is not used directly in main.go
|
|
|
+
|
|
|
+ "gorm.io/driver/mysql"
|
|
|
+ "gorm.io/gorm"
|
|
|
+ "gorm.io/gorm/logger"
|
|
|
+)
|
|
|
+
|
|
|
+// --- Helper Structs for JSON RPC ---
|
|
|
+type NewsListParams struct {
|
|
|
+ Where map[string]interface{} `json:"where"`
|
|
|
+ Order string `json:"order"`
|
|
|
+ Page int `json:"page"`
|
|
|
+ Limit int `json:"limit"`
|
|
|
+}
|
|
|
+
|
|
|
+type NewsListResult struct {
|
|
|
+ Total int64 `json:"total"`
|
|
|
+ Data []model.News `json:"data"`
|
|
|
+}
|
|
|
+
|
|
|
+// Config struct to map the yaml structure
|
|
|
+type Config struct {
|
|
|
+ Database DatabaseConfig `yaml:"database"`
|
|
|
+}
|
|
|
+
|
|
|
+type DatabaseConfig struct {
|
|
|
+ Host string `yaml:"host"`
|
|
|
+ Port int `yaml:"port"`
|
|
|
+ User string `yaml:"user"`
|
|
|
+ Password string `yaml:"password"`
|
|
|
+ DbName string `yaml:"dbname"`
|
|
|
+ Charset string `yaml:"charset"`
|
|
|
+}
|
|
|
+
|
|
|
+// Global database connection pool
|
|
|
+var db *gorm.DB
|
|
|
+
|
|
|
+// --- Initialization Function ---
|
|
|
+
|
|
|
+//export Initialize
|
|
|
+func Initialize(host, user, password, dbname, charset *C.char, port C.int) *C.char {
|
|
|
+ // Prevent re-initialization
|
|
|
+ if db != nil {
|
|
|
+ return C.CString(`{"status": "ok", "message": "already initialized"}`)
|
|
|
+ }
|
|
|
+
|
|
|
+ goHost := C.GoString(host)
|
|
|
+ goUser := C.GoString(user)
|
|
|
+ goPassword := C.GoString(password)
|
|
|
+ goDbname := C.GoString(dbname)
|
|
|
+ goCharset := C.GoString(charset)
|
|
|
+ goPort := int(port)
|
|
|
+
|
|
|
+ var err error
|
|
|
+ dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=True&loc=Local",
|
|
|
+ goUser,
|
|
|
+ goPassword,
|
|
|
+ goHost,
|
|
|
+ goPort,
|
|
|
+ goDbname,
|
|
|
+ goCharset,
|
|
|
+ )
|
|
|
+
|
|
|
+ gormDB, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
|
|
|
+ Logger: logger.Default.LogMode(logger.Silent), // Use Silent to avoid excessive logging
|
|
|
+ })
|
|
|
+
|
|
|
+ if err != nil {
|
|
|
+ db = nil // Ensure db is nil on failure
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "failed to connect database: %v"}`, err))
|
|
|
+ }
|
|
|
+
|
|
|
+ db = gormDB
|
|
|
+
|
|
|
+ // Auto-migrate schema on first successful initialization
|
|
|
+ err = db.AutoMigrate(&model.NewsCategory{}, &model.News{})
|
|
|
+ if err != nil {
|
|
|
+ db = nil // Rollback on failure
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "database migration failed: %v"}`, err))
|
|
|
+ }
|
|
|
+
|
|
|
+ return C.CString(`{"status": "ok"}`)
|
|
|
+}
|
|
|
+
|
|
|
+// --- Category Service Functions ---
|
|
|
+
|
|
|
+//export GetCategoryList
|
|
|
+func GetCategoryList(whereJson *C.char) *C.char {
|
|
|
+ if db == nil {
|
|
|
+ return C.CString(`{"error": "database connection is not initialized"}`)
|
|
|
+ }
|
|
|
+
|
|
|
+ var where map[string]interface{}
|
|
|
+ goWhereJson := C.GoString(whereJson)
|
|
|
+ if goWhereJson != "" && goWhereJson != "{}" {
|
|
|
+ if err := json.Unmarshal([]byte(goWhereJson), &where); err != nil {
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "invalid where json: %s"}`, err.Error()))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ var categories []model.NewsCategory
|
|
|
+ query := db.Model(&model.NewsCategory{})
|
|
|
+
|
|
|
+ if len(where) > 0 {
|
|
|
+ query = query.Where(where)
|
|
|
+ }
|
|
|
+
|
|
|
+ if err := query.Order("sort asc, id desc").Find(&categories).Error; err != nil {
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "query failed: %s"}`, err.Error()))
|
|
|
+ }
|
|
|
+
|
|
|
+ resultJson, err := json.Marshal(categories)
|
|
|
+ if err != nil {
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "json marshal failed: %s"}`, err.Error()))
|
|
|
+ }
|
|
|
+
|
|
|
+ return C.CString(string(resultJson))
|
|
|
+}
|
|
|
+
|
|
|
+//export GetCategoryByID
|
|
|
+func GetCategoryByID(id C.int) *C.char {
|
|
|
+ if db == nil {
|
|
|
+ return C.CString(`{"error": "database connection is not initialized"}`)
|
|
|
+ }
|
|
|
+
|
|
|
+ var category model.NewsCategory
|
|
|
+ if err := db.First(&category, int(id)).Error; err != nil {
|
|
|
+ if err == gorm.ErrRecordNotFound {
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "category with id %d not found"}`, id))
|
|
|
+ }
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "query failed: %s"}`, err.Error()))
|
|
|
+ }
|
|
|
+
|
|
|
+ resultJson, err := json.Marshal(category)
|
|
|
+ if err != nil {
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "json marshal failed: %s"}`, err.Error()))
|
|
|
+ }
|
|
|
+
|
|
|
+ return C.CString(string(resultJson))
|
|
|
+}
|
|
|
+
|
|
|
+//export CreateCategory
|
|
|
+func CreateCategory(dataJson *C.char) *C.char {
|
|
|
+ if db == nil {
|
|
|
+ return C.CString(`{"error": "database connection is not initialized"}`)
|
|
|
+ }
|
|
|
+
|
|
|
+ var category model.NewsCategory
|
|
|
+ goDataJson := C.GoString(dataJson)
|
|
|
+ if err := json.Unmarshal([]byte(goDataJson), &category); err != nil {
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "invalid data json: %s"}`, err.Error()))
|
|
|
+ }
|
|
|
+
|
|
|
+ category.ID = 0
|
|
|
+
|
|
|
+ if err := db.Create(&category).Error; err != nil {
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "create failed: %s"}`, err.Error()))
|
|
|
+ }
|
|
|
+
|
|
|
+ resultJson, err := json.Marshal(category)
|
|
|
+ if err != nil {
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "json marshal failed: %s"}`, err.Error()))
|
|
|
+ }
|
|
|
+
|
|
|
+ return C.CString(string(resultJson))
|
|
|
+}
|
|
|
+
|
|
|
+//export UpdateCategory
|
|
|
+func UpdateCategory(id C.int, dataJson *C.char) *C.char {
|
|
|
+ if db == nil {
|
|
|
+ return C.CString(`{"error": "database connection is not initialized"}`)
|
|
|
+ }
|
|
|
+
|
|
|
+ var category model.NewsCategory
|
|
|
+ if err := db.First(&category, int(id)).Error; err != nil {
|
|
|
+ if err == gorm.ErrRecordNotFound {
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "category with id %d not found"}`, id))
|
|
|
+ }
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "query failed: %s"}`, err.Error()))
|
|
|
+ }
|
|
|
+
|
|
|
+ var updateData map[string]interface{}
|
|
|
+ goDataJson := C.GoString(dataJson)
|
|
|
+ if err := json.Unmarshal([]byte(goDataJson), &updateData); err != nil {
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "invalid data json: %s"}`, err.Error()))
|
|
|
+ }
|
|
|
+
|
|
|
+ delete(updateData, "id")
|
|
|
+ delete(updateData, "ID")
|
|
|
+
|
|
|
+ if err := db.Model(&category).Updates(updateData).Error; err != nil {
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "update failed: %s"}`, err.Error()))
|
|
|
+ }
|
|
|
+
|
|
|
+ resultJson, err := json.Marshal(category)
|
|
|
+ if err != nil {
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "json marshal failed: %s"}`, err.Error()))
|
|
|
+ }
|
|
|
+
|
|
|
+ return C.CString(string(resultJson))
|
|
|
+}
|
|
|
+
|
|
|
+//export DeleteCategory
|
|
|
+func DeleteCategory(id C.int) *C.char {
|
|
|
+ if db == nil {
|
|
|
+ return C.CString(`{"error": "database connection is not initialized"}`)
|
|
|
+ }
|
|
|
+
|
|
|
+ result := db.Delete(&model.NewsCategory{}, int(id))
|
|
|
+ if result.Error != nil {
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "delete failed: %s"}`, result.Error.Error()))
|
|
|
+ }
|
|
|
+
|
|
|
+ if result.RowsAffected == 0 {
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "category with id %d not found or already deleted"}`, id))
|
|
|
+ }
|
|
|
+
|
|
|
+ return C.CString(`{"status": "ok"}`)
|
|
|
+}
|
|
|
+
|
|
|
+// --- News Service Functions ---
|
|
|
+
|
|
|
+//export GetNewsList
|
|
|
+func GetNewsList(paramsJson *C.char) *C.char {
|
|
|
+ if db == nil {
|
|
|
+ return C.CString(`{"error": "database connection is not initialized"}`)
|
|
|
+ }
|
|
|
+
|
|
|
+ var params NewsListParams
|
|
|
+ goParamsJson := C.GoString(paramsJson)
|
|
|
+ if goParamsJson != "" && goParamsJson != "{}" {
|
|
|
+ if err := json.Unmarshal([]byte(goParamsJson), ¶ms); err != nil {
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "invalid params json: %s"}`, err.Error()))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if params.Page <= 0 {
|
|
|
+ params.Page = 1
|
|
|
+ }
|
|
|
+ if params.Limit <= 0 {
|
|
|
+ params.Limit = 20
|
|
|
+ }
|
|
|
+ if params.Order == "" {
|
|
|
+ params.Order = "is_top desc, id desc"
|
|
|
+ }
|
|
|
+
|
|
|
+ var news []model.News
|
|
|
+ var total int64
|
|
|
+
|
|
|
+ query := db.Model(&model.News{})
|
|
|
+
|
|
|
+ if len(params.Where) > 0 {
|
|
|
+ query = query.Where(params.Where)
|
|
|
+ }
|
|
|
+
|
|
|
+ if err := query.Count(&total).Error; err != nil {
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "count query failed: %s"}`, err.Error()))
|
|
|
+ }
|
|
|
+
|
|
|
+ offset := (params.Page - 1) * params.Limit
|
|
|
+
|
|
|
+ if err := query.Order(params.Order).Offset(offset).Limit(params.Limit).Find(&news).Error; err != nil {
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "query failed: %s"}`, err.Error()))
|
|
|
+ }
|
|
|
+
|
|
|
+ result := NewsListResult{
|
|
|
+ Total: total,
|
|
|
+ Data: news,
|
|
|
+ }
|
|
|
+
|
|
|
+ resultJson, err := json.Marshal(result)
|
|
|
+ if err != nil {
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "json marshal failed: %s"}`, err.Error()))
|
|
|
+ }
|
|
|
+
|
|
|
+ return C.CString(string(resultJson))
|
|
|
+}
|
|
|
+
|
|
|
+//export GetNewsByID
|
|
|
+func GetNewsByID(id C.int) *C.char {
|
|
|
+ if db == nil {
|
|
|
+ return C.CString(`{"error": "database connection is not initialized"}`)
|
|
|
+ }
|
|
|
+
|
|
|
+ var news model.News
|
|
|
+ if err := db.First(&news, int(id)).Error; err != nil {
|
|
|
+ if err == gorm.ErrRecordNotFound {
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "news with id %d not found"}`, id))
|
|
|
+ }
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "query failed: %s"}`, err.Error()))
|
|
|
+ }
|
|
|
+
|
|
|
+ resultJson, err := json.Marshal(news)
|
|
|
+ if err != nil {
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "json marshal failed: %s"}`, err.Error()))
|
|
|
+ }
|
|
|
+
|
|
|
+ return C.CString(string(resultJson))
|
|
|
+}
|
|
|
+
|
|
|
+//export CreateNews
|
|
|
+func CreateNews(dataJson *C.char) *C.char {
|
|
|
+ if db == nil {
|
|
|
+ return C.CString(`{"error": "database connection is not initialized"}`)
|
|
|
+ }
|
|
|
+
|
|
|
+ var news model.News
|
|
|
+ goDataJson := C.GoString(dataJson)
|
|
|
+ if err := json.Unmarshal([]byte(goDataJson), &news); err != nil {
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "invalid data json: %s"}`, err.Error()))
|
|
|
+ }
|
|
|
+
|
|
|
+ news.ID = 0
|
|
|
+
|
|
|
+ if err := db.Create(&news).Error; err != nil {
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "create failed: %s"}`, err.Error()))
|
|
|
+ }
|
|
|
+
|
|
|
+ resultJson, err := json.Marshal(news)
|
|
|
+ if err != nil {
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "json marshal failed: %s"}`, err.Error()))
|
|
|
+ }
|
|
|
+
|
|
|
+ return C.CString(string(resultJson))
|
|
|
+}
|
|
|
+
|
|
|
+//export UpdateNews
|
|
|
+func UpdateNews(id C.int, dataJson *C.char) *C.char {
|
|
|
+ if db == nil {
|
|
|
+ return C.CString(`{"error": "database connection is not initialized"}`)
|
|
|
+ }
|
|
|
+
|
|
|
+ var news model.News
|
|
|
+ if err := db.First(&news, int(id)).Error; err != nil {
|
|
|
+ if err == gorm.ErrRecordNotFound {
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "news with id %d not found"}`, id))
|
|
|
+ }
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "query failed: %s"}`, err.Error()))
|
|
|
+ }
|
|
|
+
|
|
|
+ var updateData map[string]interface{}
|
|
|
+ goDataJson := C.GoString(dataJson)
|
|
|
+ if err := json.Unmarshal([]byte(goDataJson), &updateData); err != nil {
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "invalid data json: %s"}`, err.Error()))
|
|
|
+ }
|
|
|
+
|
|
|
+ delete(updateData, "id")
|
|
|
+ delete(updateData, "ID")
|
|
|
+
|
|
|
+ if err := db.Model(&news).Updates(updateData).Error; err != nil {
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "update failed: %s"}`, err.Error()))
|
|
|
+ }
|
|
|
+
|
|
|
+ resultJson, err := json.Marshal(news)
|
|
|
+ if err != nil {
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "json marshal failed: %s"}`, err.Error()))
|
|
|
+ }
|
|
|
+
|
|
|
+ return C.CString(string(resultJson))
|
|
|
+}
|
|
|
+
|
|
|
+//export DeleteNews
|
|
|
+func DeleteNews(id C.int) *C.char {
|
|
|
+ if db == nil {
|
|
|
+ return C.CString(`{"error": "database connection is not initialized"}`)
|
|
|
+ }
|
|
|
+
|
|
|
+ result := db.Delete(&model.News{}, int(id))
|
|
|
+ if result.Error != nil {
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "delete failed: %s"}`, result.Error.Error()))
|
|
|
+ }
|
|
|
+
|
|
|
+ if result.RowsAffected == 0 {
|
|
|
+ return C.CString(fmt.Sprintf(`{"error": "news with id %d not found or already deleted"}`, id))
|
|
|
+ }
|
|
|
+
|
|
|
+ return C.CString(`{"status": "ok"}`)
|
|
|
+}
|
|
|
+
|
|
|
+// main is required for building a C shared library.
|
|
|
+func main() {}
|