| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426 |
- package main
- import (
- "C"
- "encoding/json"
- "fmt"
- "newsffi/model"
- "time"
- "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
- // 自动设置创建时间和更新时间
- now := time.Now()
- news.CreateTime = now
- news.UpdateTime = now
- 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")
-
- // 自动设置更新时间
- updateData["update_time"] = time.Now()
- 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"}`)
- }
- //export IncrementNewsViews
- func IncrementNewsViews(id C.int) *C.char {
- if db == nil {
- return C.CString(`{"error": "database connection is not initialized"}`)
- }
- // 使用原生SQL更新views字段,避免并发问题
- result := db.Exec("UPDATE cy_news SET views = views + 1 WHERE id = ? AND delete_time IS NULL", int(id))
- if result.Error != nil {
- return C.CString(fmt.Sprintf(`{"error": "increment views failed: %s"}`, result.Error.Error()))
- }
- if result.RowsAffected == 0 {
- return C.CString(fmt.Sprintf(`{"error": "news with id %d not found"}`, id))
- }
- // 返回更新后的文章信息
- var news model.News
- if err := db.First(&news, int(id)).Error; err != nil {
- // 即使查询失败,views已经更新成功,返回成功状态
- return C.CString(`{"status": "ok", "message": "views incremented successfully"}`)
- }
- resultJson, err := json.Marshal(news)
- if err != nil {
- return C.CString(`{"status": "ok", "message": "views incremented successfully"}`)
- }
- return C.CString(string(resultJson))
- }
- // main is required for building a C shared library.
- func main() {}
|