xml.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. package main
  2. import (
  3. "fmt"
  4. "html"
  5. "io/ioutil"
  6. "os"
  7. "strings"
  8. "time"
  9. "golang-fave/engine/mysqlpool"
  10. "golang-fave/engine/sqlw"
  11. "golang-fave/engine/wrapper/config"
  12. "golang-fave/utils"
  13. )
  14. func xml_gen_currencies(db *sqlw.DB, conf *config.Config) string {
  15. result := ``
  16. rows, err := db.Query(
  17. `SELECT
  18. code,
  19. coefficient
  20. FROM
  21. shop_currencies
  22. ORDER BY
  23. id ASC
  24. ;`,
  25. )
  26. if err == nil {
  27. defer rows.Close()
  28. values := make([]string, 2)
  29. scan := make([]interface{}, len(values))
  30. for i := range values {
  31. scan[i] = &values[i]
  32. }
  33. for rows.Next() {
  34. err = rows.Scan(scan...)
  35. if err == nil {
  36. result += `<currency id="` + html.EscapeString(string(values[0])) + `" rate="` + html.EscapeString(string(values[1])) + `"/>`
  37. }
  38. }
  39. }
  40. return result
  41. }
  42. func xml_gen_categories(db *sqlw.DB, conf *config.Config) string {
  43. result := ``
  44. rows, err := db.Query(
  45. `SELECT
  46. data.id,
  47. data.user,
  48. data.name,
  49. data.alias,
  50. data.lft,
  51. data.rgt,
  52. MAX(data.parent_id) AS parent_id
  53. FROM
  54. (
  55. SELECT
  56. node.id,
  57. node.user,
  58. node.name,
  59. node.alias,
  60. node.lft,
  61. node.rgt,
  62. parent.id AS parent_id
  63. FROM
  64. shop_cats AS node,
  65. shop_cats AS parent
  66. WHERE
  67. node.lft BETWEEN parent.lft AND parent.rgt AND
  68. node.id > 1
  69. ORDER BY
  70. node.lft ASC
  71. ) AS data
  72. WHERE
  73. data.id <> data.parent_id
  74. GROUP BY
  75. data.id
  76. ORDER BY
  77. data.lft ASC
  78. ;`,
  79. )
  80. if err == nil {
  81. defer rows.Close()
  82. values := make([]string, 7)
  83. scan := make([]interface{}, len(values))
  84. for i := range values {
  85. scan[i] = &values[i]
  86. }
  87. for rows.Next() {
  88. err = rows.Scan(scan...)
  89. if err == nil {
  90. if utils.StrToInt(string(values[6])) > 1 {
  91. result += `<category id="` + html.EscapeString(string(values[0])) + `" parentId="` + html.EscapeString(string(values[6])) + `">` + html.EscapeString(string(values[2])) + `</category>`
  92. } else {
  93. result += `<category id="` + html.EscapeString(string(values[0])) + `">` + html.EscapeString(string(values[2])) + `</category>`
  94. }
  95. }
  96. }
  97. }
  98. return result
  99. }
  100. func xml_gen_offer_pictures(db *sqlw.DB, conf *config.Config, product_id int) string {
  101. result := ``
  102. rows, err := db.Query(
  103. `SELECT
  104. shop_product_images.product_id,
  105. shop_product_images.filename
  106. FROM
  107. shop_product_images
  108. WHERE
  109. shop_product_images.product_id = ?
  110. ORDER BY
  111. shop_product_images.ord ASC
  112. ;`,
  113. product_id,
  114. )
  115. if err == nil {
  116. defer rows.Close()
  117. values := make([]string, 2)
  118. scan := make([]interface{}, len(values))
  119. for i := range values {
  120. scan[i] = &values[i]
  121. }
  122. for rows.Next() {
  123. err = rows.Scan(scan...)
  124. if err == nil {
  125. result += `<picture>` + html.EscapeString((*conf).API.XML.Url) + `products/images/` + html.EscapeString(string(values[0])) + `/` + html.EscapeString(string(values[1])) + `</picture>`
  126. }
  127. }
  128. }
  129. return result
  130. }
  131. func xml_gen_offer_attributes(db *sqlw.DB, conf *config.Config, product_id int) string {
  132. result := ``
  133. filter_ids := []int{}
  134. filter_names := map[int]string{}
  135. filter_values := map[int][]string{}
  136. rows, err := db.Query(
  137. `SELECT
  138. shop_filters.id,
  139. shop_filters.filter,
  140. shop_filters_values.name
  141. FROM
  142. shop_filter_product_values
  143. LEFT JOIN shop_filters_values ON shop_filters_values.id = shop_filter_product_values.filter_value_id
  144. LEFT JOIN shop_filters ON shop_filters.id = shop_filters_values.filter_id
  145. WHERE
  146. shop_filter_product_values.product_id = ?
  147. ORDER BY
  148. shop_filters.filter ASC,
  149. shop_filters_values.name ASC
  150. ;`,
  151. product_id,
  152. )
  153. if err == nil {
  154. defer rows.Close()
  155. values := make([]string, 3)
  156. scan := make([]interface{}, len(values))
  157. for i := range values {
  158. scan[i] = &values[i]
  159. }
  160. for rows.Next() {
  161. err = rows.Scan(scan...)
  162. if err == nil {
  163. if !utils.InArrayInt(filter_ids, utils.StrToInt(string(values[0]))) {
  164. filter_ids = append(filter_ids, utils.StrToInt(string(values[0])))
  165. }
  166. filter_names[utils.StrToInt(string(values[0]))] = html.EscapeString(string(values[1]))
  167. filter_values[utils.StrToInt(string(values[0]))] = append(filter_values[utils.StrToInt(string(values[0]))], string(values[2]))
  168. }
  169. }
  170. }
  171. for _, filter_id := range filter_ids {
  172. result += `<param name="` + html.EscapeString(filter_names[filter_id]) + `">` + html.EscapeString(strings.Join(filter_values[filter_id], ", ")) + `</param>`
  173. }
  174. return result
  175. }
  176. func xml_gen_offers(db *sqlw.DB, conf *config.Config) string {
  177. result := ``
  178. rows, err := db.Query(
  179. `SELECT
  180. shop_products.id,
  181. shop_currencies.code,
  182. shop_products.price,
  183. shop_products.name,
  184. shop_products.alias,
  185. shop_products.vendor,
  186. shop_products.quantity,
  187. shop_products.category,
  188. shop_products.content
  189. FROM
  190. shop_products
  191. LEFT JOIN shop_currencies ON shop_currencies.id = shop_products.currency
  192. WHERE
  193. shop_products.active = 1 AND
  194. shop_products.category > 1
  195. ORDER BY
  196. shop_products.id
  197. ;`,
  198. )
  199. if err == nil {
  200. defer rows.Close()
  201. values := make([]string, 9)
  202. scan := make([]interface{}, len(values))
  203. for i := range values {
  204. scan[i] = &values[i]
  205. }
  206. for rows.Next() {
  207. err = rows.Scan(scan...)
  208. if err == nil {
  209. result += `<offer id="` + html.EscapeString(string(values[0])) + `" available="true">`
  210. result += `<url>` + html.EscapeString((*conf).API.XML.Url) + `shop/` + html.EscapeString(string(values[4])) + `/</url>`
  211. result += `<price>` + utils.Float64ToStrF(utils.StrToFloat64(string(values[2])), "%.2f") + `</price>`
  212. result += `<currencyId>` + html.EscapeString(string(values[1])) + `</currencyId>`
  213. result += `<categoryId>` + html.EscapeString(string(values[7])) + `</categoryId>`
  214. result += xml_gen_offer_pictures(db, conf, utils.StrToInt(string(values[0])))
  215. result += `<vendor>` + html.EscapeString(string(values[5])) + `</vendor>`
  216. result += `<stock_quantity>` + html.EscapeString(string(values[6])) + `</stock_quantity>`
  217. result += `<name>` + html.EscapeString(string(values[3])) + `</name>`
  218. result += `<description><![CDATA[` + string(values[8]) + `]]></description>`
  219. result += xml_gen_offer_attributes(db, conf, utils.StrToInt(string(values[0])))
  220. result += `</offer>`
  221. }
  222. }
  223. }
  224. return result
  225. }
  226. func xml_generate(db *sqlw.DB, conf *config.Config) string {
  227. return `<?xml version="1.0" encoding="UTF-8"?>` +
  228. `<!DOCTYPE yml_catalog SYSTEM "shops.dtd">` +
  229. `<yml_catalog date="` + time.Unix(int64(time.Now().Unix()), 0).Format("2006-01-02 15:04") + `">` +
  230. `<shop>` +
  231. `<name>` + html.EscapeString((*conf).API.XML.Name) + `</name>` +
  232. `<company>` + html.EscapeString((*conf).API.XML.Company) + `</company>` +
  233. `<url>` + html.EscapeString((*conf).API.XML.Url) + `</url>` +
  234. `<currencies>` + xml_gen_currencies(db, conf) + `</currencies>` +
  235. `<categories>` + xml_gen_categories(db, conf) + `</categories>` +
  236. `<offers>` + xml_gen_offers(db, conf) + `</offers>` +
  237. `</shop>` +
  238. `</yml_catalog>`
  239. }
  240. func xml_create(dir, host string, db *sqlw.DB) {
  241. conf := config.ConfigNew()
  242. if err := conf.ConfigRead(strings.Join([]string{dir, "config", "config.json"}, string(os.PathSeparator))); err == nil {
  243. if (*conf).API.XML.Enabled == 1 {
  244. if file, err := os.Create(strings.Join([]string{dir, "htdocs", "products.xml"}, string(os.PathSeparator))); err == nil {
  245. file.Write([]byte(xml_generate(db, conf)))
  246. file.Close()
  247. } else {
  248. fmt.Printf("Xml generation error (file): %v\n", err)
  249. }
  250. }
  251. } else {
  252. fmt.Printf("Xml generation error (config): %v\n", err)
  253. }
  254. }
  255. func xml_detect(dir, host string, mp *mysqlpool.MySqlPool) {
  256. db := mp.Get(host)
  257. if db != nil {
  258. trigger := strings.Join([]string{dir, "tmp", "trigger.xml.run"}, string(os.PathSeparator))
  259. if utils.IsFileExists(trigger) {
  260. if err := db.Ping(); err == nil {
  261. xml_create(dir, host, db)
  262. os.Remove(trigger)
  263. }
  264. }
  265. }
  266. }
  267. func xml_loop(www_dir string, stop chan bool, mp *mysqlpool.MySqlPool) {
  268. dirs, err := ioutil.ReadDir(www_dir)
  269. if err == nil {
  270. for _, dir := range dirs {
  271. select {
  272. case <-stop:
  273. break
  274. default:
  275. if mp != nil {
  276. target_dir := strings.Join([]string{www_dir, dir.Name()}, string(os.PathSeparator))
  277. if utils.IsDirExists(target_dir) {
  278. xml_detect(target_dir, dir.Name(), mp)
  279. }
  280. }
  281. }
  282. }
  283. }
  284. }
  285. func xml_start(www_dir string, mp *mysqlpool.MySqlPool) (chan bool, chan bool) {
  286. ch := make(chan bool)
  287. stop := make(chan bool)
  288. // Run at start
  289. xml_loop(www_dir, stop, mp)
  290. go func() {
  291. for {
  292. select {
  293. case <-time.After(5 * time.Second):
  294. // Run every 5 seconds
  295. xml_loop(www_dir, stop, mp)
  296. case <-ch:
  297. ch <- true
  298. return
  299. }
  300. }
  301. }()
  302. return ch, stop
  303. }
  304. func xml_stop(ch, stop chan bool) {
  305. for {
  306. select {
  307. case stop <- true:
  308. case ch <- true:
  309. <-ch
  310. return
  311. case <-time.After(3 * time.Second):
  312. fmt.Println("Xml error: force exit by timeout after 3 seconds")
  313. return
  314. }
  315. }
  316. }