main.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. package main
  2. import (
  3. "C"
  4. "encoding/json"
  5. "fmt"
  6. "newsffi/model"
  7. // "time" // This is not used directly in main.go
  8. "gorm.io/driver/mysql"
  9. "gorm.io/gorm"
  10. "gorm.io/gorm/logger"
  11. )
  12. // --- Helper Structs for JSON RPC ---
  13. type NewsListParams struct {
  14. Where map[string]interface{} `json:"where"`
  15. Order string `json:"order"`
  16. Page int `json:"page"`
  17. Limit int `json:"limit"`
  18. }
  19. type NewsListResult struct {
  20. Total int64 `json:"total"`
  21. Data []model.News `json:"data"`
  22. }
  23. // Config struct to map the yaml structure
  24. type Config struct {
  25. Database DatabaseConfig `yaml:"database"`
  26. }
  27. type DatabaseConfig struct {
  28. Host string `yaml:"host"`
  29. Port int `yaml:"port"`
  30. User string `yaml:"user"`
  31. Password string `yaml:"password"`
  32. DbName string `yaml:"dbname"`
  33. Charset string `yaml:"charset"`
  34. }
  35. // Global database connection pool
  36. var db *gorm.DB
  37. // --- Initialization Function ---
  38. //export Initialize
  39. func Initialize(host, user, password, dbname, charset *C.char, port C.int) *C.char {
  40. // Prevent re-initialization
  41. if db != nil {
  42. return C.CString(`{"status": "ok", "message": "already initialized"}`)
  43. }
  44. goHost := C.GoString(host)
  45. goUser := C.GoString(user)
  46. goPassword := C.GoString(password)
  47. goDbname := C.GoString(dbname)
  48. goCharset := C.GoString(charset)
  49. goPort := int(port)
  50. var err error
  51. dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=True&loc=Local",
  52. goUser,
  53. goPassword,
  54. goHost,
  55. goPort,
  56. goDbname,
  57. goCharset,
  58. )
  59. gormDB, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
  60. Logger: logger.Default.LogMode(logger.Silent), // Use Silent to avoid excessive logging
  61. })
  62. if err != nil {
  63. db = nil // Ensure db is nil on failure
  64. return C.CString(fmt.Sprintf(`{"error": "failed to connect database: %v"}`, err))
  65. }
  66. db = gormDB
  67. // Auto-migrate schema on first successful initialization
  68. err = db.AutoMigrate(&model.NewsCategory{}, &model.News{})
  69. if err != nil {
  70. db = nil // Rollback on failure
  71. return C.CString(fmt.Sprintf(`{"error": "database migration failed: %v"}`, err))
  72. }
  73. return C.CString(`{"status": "ok"}`)
  74. }
  75. // --- Category Service Functions ---
  76. //export GetCategoryList
  77. func GetCategoryList(whereJson *C.char) *C.char {
  78. if db == nil {
  79. return C.CString(`{"error": "database connection is not initialized"}`)
  80. }
  81. var where map[string]interface{}
  82. goWhereJson := C.GoString(whereJson)
  83. if goWhereJson != "" && goWhereJson != "{}" {
  84. if err := json.Unmarshal([]byte(goWhereJson), &where); err != nil {
  85. return C.CString(fmt.Sprintf(`{"error": "invalid where json: %s"}`, err.Error()))
  86. }
  87. }
  88. var categories []model.NewsCategory
  89. query := db.Model(&model.NewsCategory{})
  90. if len(where) > 0 {
  91. query = query.Where(where)
  92. }
  93. if err := query.Order("sort asc, id desc").Find(&categories).Error; err != nil {
  94. return C.CString(fmt.Sprintf(`{"error": "query failed: %s"}`, err.Error()))
  95. }
  96. resultJson, err := json.Marshal(categories)
  97. if err != nil {
  98. return C.CString(fmt.Sprintf(`{"error": "json marshal failed: %s"}`, err.Error()))
  99. }
  100. return C.CString(string(resultJson))
  101. }
  102. //export GetCategoryByID
  103. func GetCategoryByID(id C.int) *C.char {
  104. if db == nil {
  105. return C.CString(`{"error": "database connection is not initialized"}`)
  106. }
  107. var category model.NewsCategory
  108. if err := db.First(&category, int(id)).Error; err != nil {
  109. if err == gorm.ErrRecordNotFound {
  110. return C.CString(fmt.Sprintf(`{"error": "category with id %d not found"}`, id))
  111. }
  112. return C.CString(fmt.Sprintf(`{"error": "query failed: %s"}`, err.Error()))
  113. }
  114. resultJson, err := json.Marshal(category)
  115. if err != nil {
  116. return C.CString(fmt.Sprintf(`{"error": "json marshal failed: %s"}`, err.Error()))
  117. }
  118. return C.CString(string(resultJson))
  119. }
  120. //export CreateCategory
  121. func CreateCategory(dataJson *C.char) *C.char {
  122. if db == nil {
  123. return C.CString(`{"error": "database connection is not initialized"}`)
  124. }
  125. var category model.NewsCategory
  126. goDataJson := C.GoString(dataJson)
  127. if err := json.Unmarshal([]byte(goDataJson), &category); err != nil {
  128. return C.CString(fmt.Sprintf(`{"error": "invalid data json: %s"}`, err.Error()))
  129. }
  130. category.ID = 0
  131. if err := db.Create(&category).Error; err != nil {
  132. return C.CString(fmt.Sprintf(`{"error": "create failed: %s"}`, err.Error()))
  133. }
  134. resultJson, err := json.Marshal(category)
  135. if err != nil {
  136. return C.CString(fmt.Sprintf(`{"error": "json marshal failed: %s"}`, err.Error()))
  137. }
  138. return C.CString(string(resultJson))
  139. }
  140. //export UpdateCategory
  141. func UpdateCategory(id C.int, dataJson *C.char) *C.char {
  142. if db == nil {
  143. return C.CString(`{"error": "database connection is not initialized"}`)
  144. }
  145. var category model.NewsCategory
  146. if err := db.First(&category, int(id)).Error; err != nil {
  147. if err == gorm.ErrRecordNotFound {
  148. return C.CString(fmt.Sprintf(`{"error": "category with id %d not found"}`, id))
  149. }
  150. return C.CString(fmt.Sprintf(`{"error": "query failed: %s"}`, err.Error()))
  151. }
  152. var updateData map[string]interface{}
  153. goDataJson := C.GoString(dataJson)
  154. if err := json.Unmarshal([]byte(goDataJson), &updateData); err != nil {
  155. return C.CString(fmt.Sprintf(`{"error": "invalid data json: %s"}`, err.Error()))
  156. }
  157. delete(updateData, "id")
  158. delete(updateData, "ID")
  159. if err := db.Model(&category).Updates(updateData).Error; err != nil {
  160. return C.CString(fmt.Sprintf(`{"error": "update failed: %s"}`, err.Error()))
  161. }
  162. resultJson, err := json.Marshal(category)
  163. if err != nil {
  164. return C.CString(fmt.Sprintf(`{"error": "json marshal failed: %s"}`, err.Error()))
  165. }
  166. return C.CString(string(resultJson))
  167. }
  168. //export DeleteCategory
  169. func DeleteCategory(id C.int) *C.char {
  170. if db == nil {
  171. return C.CString(`{"error": "database connection is not initialized"}`)
  172. }
  173. result := db.Delete(&model.NewsCategory{}, int(id))
  174. if result.Error != nil {
  175. return C.CString(fmt.Sprintf(`{"error": "delete failed: %s"}`, result.Error.Error()))
  176. }
  177. if result.RowsAffected == 0 {
  178. return C.CString(fmt.Sprintf(`{"error": "category with id %d not found or already deleted"}`, id))
  179. }
  180. return C.CString(`{"status": "ok"}`)
  181. }
  182. // --- News Service Functions ---
  183. //export GetNewsList
  184. func GetNewsList(paramsJson *C.char) *C.char {
  185. if db == nil {
  186. return C.CString(`{"error": "database connection is not initialized"}`)
  187. }
  188. var params NewsListParams
  189. goParamsJson := C.GoString(paramsJson)
  190. if goParamsJson != "" && goParamsJson != "{}" {
  191. if err := json.Unmarshal([]byte(goParamsJson), &params); err != nil {
  192. return C.CString(fmt.Sprintf(`{"error": "invalid params json: %s"}`, err.Error()))
  193. }
  194. }
  195. if params.Page <= 0 {
  196. params.Page = 1
  197. }
  198. if params.Limit <= 0 {
  199. params.Limit = 20
  200. }
  201. if params.Order == "" {
  202. params.Order = "is_top desc, id desc"
  203. }
  204. var news []model.News
  205. var total int64
  206. query := db.Model(&model.News{})
  207. if len(params.Where) > 0 {
  208. query = query.Where(params.Where)
  209. }
  210. if err := query.Count(&total).Error; err != nil {
  211. return C.CString(fmt.Sprintf(`{"error": "count query failed: %s"}`, err.Error()))
  212. }
  213. offset := (params.Page - 1) * params.Limit
  214. if err := query.Order(params.Order).Offset(offset).Limit(params.Limit).Find(&news).Error; err != nil {
  215. return C.CString(fmt.Sprintf(`{"error": "query failed: %s"}`, err.Error()))
  216. }
  217. result := NewsListResult{
  218. Total: total,
  219. Data: news,
  220. }
  221. resultJson, err := json.Marshal(result)
  222. if err != nil {
  223. return C.CString(fmt.Sprintf(`{"error": "json marshal failed: %s"}`, err.Error()))
  224. }
  225. return C.CString(string(resultJson))
  226. }
  227. //export GetNewsByID
  228. func GetNewsByID(id C.int) *C.char {
  229. if db == nil {
  230. return C.CString(`{"error": "database connection is not initialized"}`)
  231. }
  232. var news model.News
  233. if err := db.First(&news, int(id)).Error; err != nil {
  234. if err == gorm.ErrRecordNotFound {
  235. return C.CString(fmt.Sprintf(`{"error": "news with id %d not found"}`, id))
  236. }
  237. return C.CString(fmt.Sprintf(`{"error": "query failed: %s"}`, err.Error()))
  238. }
  239. resultJson, err := json.Marshal(news)
  240. if err != nil {
  241. return C.CString(fmt.Sprintf(`{"error": "json marshal failed: %s"}`, err.Error()))
  242. }
  243. return C.CString(string(resultJson))
  244. }
  245. //export CreateNews
  246. func CreateNews(dataJson *C.char) *C.char {
  247. if db == nil {
  248. return C.CString(`{"error": "database connection is not initialized"}`)
  249. }
  250. var news model.News
  251. goDataJson := C.GoString(dataJson)
  252. if err := json.Unmarshal([]byte(goDataJson), &news); err != nil {
  253. return C.CString(fmt.Sprintf(`{"error": "invalid data json: %s"}`, err.Error()))
  254. }
  255. news.ID = 0
  256. if err := db.Create(&news).Error; err != nil {
  257. return C.CString(fmt.Sprintf(`{"error": "create failed: %s"}`, err.Error()))
  258. }
  259. resultJson, err := json.Marshal(news)
  260. if err != nil {
  261. return C.CString(fmt.Sprintf(`{"error": "json marshal failed: %s"}`, err.Error()))
  262. }
  263. return C.CString(string(resultJson))
  264. }
  265. //export UpdateNews
  266. func UpdateNews(id C.int, dataJson *C.char) *C.char {
  267. if db == nil {
  268. return C.CString(`{"error": "database connection is not initialized"}`)
  269. }
  270. var news model.News
  271. if err := db.First(&news, int(id)).Error; err != nil {
  272. if err == gorm.ErrRecordNotFound {
  273. return C.CString(fmt.Sprintf(`{"error": "news with id %d not found"}`, id))
  274. }
  275. return C.CString(fmt.Sprintf(`{"error": "query failed: %s"}`, err.Error()))
  276. }
  277. var updateData map[string]interface{}
  278. goDataJson := C.GoString(dataJson)
  279. if err := json.Unmarshal([]byte(goDataJson), &updateData); err != nil {
  280. return C.CString(fmt.Sprintf(`{"error": "invalid data json: %s"}`, err.Error()))
  281. }
  282. delete(updateData, "id")
  283. delete(updateData, "ID")
  284. if err := db.Model(&news).Updates(updateData).Error; err != nil {
  285. return C.CString(fmt.Sprintf(`{"error": "update failed: %s"}`, err.Error()))
  286. }
  287. resultJson, err := json.Marshal(news)
  288. if err != nil {
  289. return C.CString(fmt.Sprintf(`{"error": "json marshal failed: %s"}`, err.Error()))
  290. }
  291. return C.CString(string(resultJson))
  292. }
  293. //export DeleteNews
  294. func DeleteNews(id C.int) *C.char {
  295. if db == nil {
  296. return C.CString(`{"error": "database connection is not initialized"}`)
  297. }
  298. result := db.Delete(&model.News{}, int(id))
  299. if result.Error != nil {
  300. return C.CString(fmt.Sprintf(`{"error": "delete failed: %s"}`, result.Error.Error()))
  301. }
  302. if result.RowsAffected == 0 {
  303. return C.CString(fmt.Sprintf(`{"error": "news with id %d not found or already deleted"}`, id))
  304. }
  305. return C.CString(`{"status": "ok"}`)
  306. }
  307. // main is required for building a C shared library.
  308. func main() {}