xml.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  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. ;`,
  111. product_id,
  112. )
  113. if err == nil {
  114. defer rows.Close()
  115. values := make([]string, 2)
  116. scan := make([]interface{}, len(values))
  117. for i := range values {
  118. scan[i] = &values[i]
  119. }
  120. for rows.Next() {
  121. err = rows.Scan(scan...)
  122. if err == nil {
  123. result += `<picture>` + html.EscapeString((*conf).API.XML.Url) + `products/images/` + html.EscapeString(string(values[0])) + `/` + html.EscapeString(string(values[1])) + `</picture>`
  124. }
  125. }
  126. }
  127. return result
  128. }
  129. func xml_gen_offer_attributes(db *sqlw.DB, conf *config.Config, product_id int) string {
  130. result := ``
  131. filter_ids := []int{}
  132. filter_names := map[int]string{}
  133. filter_values := map[int][]string{}
  134. rows, err := db.Query(
  135. `SELECT
  136. shop_filters.id,
  137. shop_filters.filter,
  138. shop_filters_values.name
  139. FROM
  140. shop_filter_product_values
  141. LEFT JOIN shop_filters_values ON shop_filters_values.id = shop_filter_product_values.filter_value_id
  142. LEFT JOIN shop_filters ON shop_filters.id = shop_filters_values.filter_id
  143. WHERE
  144. shop_filter_product_values.product_id = ?
  145. ORDER BY
  146. shop_filters.filter ASC,
  147. shop_filters_values.name ASC
  148. ;`,
  149. product_id,
  150. )
  151. if err == nil {
  152. defer rows.Close()
  153. values := make([]string, 3)
  154. scan := make([]interface{}, len(values))
  155. for i := range values {
  156. scan[i] = &values[i]
  157. }
  158. for rows.Next() {
  159. err = rows.Scan(scan...)
  160. if err == nil {
  161. if !utils.InArrayInt(filter_ids, utils.StrToInt(string(values[0]))) {
  162. filter_ids = append(filter_ids, utils.StrToInt(string(values[0])))
  163. }
  164. filter_names[utils.StrToInt(string(values[0]))] = html.EscapeString(string(values[1]))
  165. filter_values[utils.StrToInt(string(values[0]))] = append(filter_values[utils.StrToInt(string(values[0]))], string(values[2]))
  166. }
  167. }
  168. }
  169. for _, filter_id := range filter_ids {
  170. result += `<param name="` + html.EscapeString(filter_names[filter_id]) + `">` + html.EscapeString(strings.Join(filter_values[filter_id], ", ")) + `</param>`
  171. }
  172. return result
  173. }
  174. func xml_gen_offers(db *sqlw.DB, conf *config.Config) string {
  175. result := ``
  176. rows, err := db.Query(
  177. `SELECT
  178. shop_products.id,
  179. shop_currencies.code,
  180. shop_products.price,
  181. shop_products.name,
  182. shop_products.alias,
  183. shop_products.vendor,
  184. shop_products.quantity,
  185. shop_products.category,
  186. shop_products.content
  187. FROM
  188. shop_products
  189. LEFT JOIN shop_currencies ON shop_currencies.id = shop_products.currency
  190. WHERE
  191. shop_products.active = 1 AND
  192. shop_products.category > 1
  193. ORDER BY
  194. shop_products.id
  195. ;`,
  196. )
  197. if err == nil {
  198. defer rows.Close()
  199. values := make([]string, 9)
  200. scan := make([]interface{}, len(values))
  201. for i := range values {
  202. scan[i] = &values[i]
  203. }
  204. for rows.Next() {
  205. err = rows.Scan(scan...)
  206. if err == nil {
  207. result += `<offer id="` + html.EscapeString(string(values[0])) + `" available="true">`
  208. result += `<url>` + html.EscapeString((*conf).API.XML.Url) + `shop/` + html.EscapeString(string(values[4])) + `/</url>`
  209. result += `<price>` + utils.Float64ToStrF(utils.StrToFloat64(string(values[2])), "%.2f") + `</price>`
  210. result += `<currencyId>` + html.EscapeString(string(values[1])) + `</currencyId>`
  211. result += `<categoryId>` + html.EscapeString(string(values[7])) + `</categoryId>`
  212. result += xml_gen_offer_pictures(db, conf, utils.StrToInt(string(values[0])))
  213. result += `<vendor>` + html.EscapeString(string(values[5])) + `</vendor>`
  214. result += `<stock_quantity>` + html.EscapeString(string(values[6])) + `</stock_quantity>`
  215. result += `<name>` + html.EscapeString(string(values[3])) + `</name>`
  216. result += `<description><![CDATA[` + string(values[8]) + `]]></description>`
  217. result += xml_gen_offer_attributes(db, conf, utils.StrToInt(string(values[0])))
  218. result += `</offer>`
  219. }
  220. }
  221. }
  222. return result
  223. }
  224. func xml_generate(db *sqlw.DB, conf *config.Config) string {
  225. return `<?xml version="1.0" encoding="UTF-8"?>` +
  226. `<!DOCTYPE yml_catalog SYSTEM "shops.dtd">` +
  227. `<yml_catalog date="` + time.Unix(int64(time.Now().Unix()), 0).Format("2006-01-02 15:04") + `">` +
  228. `<shop>` +
  229. `<name>` + html.EscapeString((*conf).API.XML.Name) + `</name>` +
  230. `<company>` + html.EscapeString((*conf).API.XML.Company) + `</company>` +
  231. `<url>` + html.EscapeString((*conf).API.XML.Url) + `</url>` +
  232. `<currencies>` + xml_gen_currencies(db, conf) + `</currencies>` +
  233. `<categories>` + xml_gen_categories(db, conf) + `</categories>` +
  234. `<offers>` + xml_gen_offers(db, conf) + `</offers>` +
  235. `</shop>` +
  236. `</yml_catalog>`
  237. }
  238. func xml_create(dir, host string, db *sqlw.DB) {
  239. conf := config.ConfigNew()
  240. if err := conf.ConfigRead(strings.Join([]string{dir, "config", "config.json"}, string(os.PathSeparator))); err == nil {
  241. if (*conf).API.XML.Enabled == 1 {
  242. if file, err := os.Create(strings.Join([]string{dir, "htdocs", "products.xml"}, string(os.PathSeparator))); err == nil {
  243. file.Write([]byte(xml_generate(db, conf)))
  244. file.Close()
  245. } else {
  246. fmt.Printf("Xml generation error (file): %v\n", err)
  247. }
  248. }
  249. } else {
  250. fmt.Printf("Xml generation error (config): %v\n", err)
  251. }
  252. }
  253. func xml_detect(dir, host string, mp *mysqlpool.MySqlPool) {
  254. db := mp.Get(host)
  255. if db != nil {
  256. trigger := strings.Join([]string{dir, "tmp", "trigger.xml.run"}, string(os.PathSeparator))
  257. if utils.IsFileExists(trigger) {
  258. if err := db.Ping(); err == nil {
  259. xml_create(dir, host, db)
  260. os.Remove(trigger)
  261. }
  262. }
  263. }
  264. }
  265. func xml_loop(www_dir string, stop chan bool, mp *mysqlpool.MySqlPool) {
  266. dirs, err := ioutil.ReadDir(www_dir)
  267. if err == nil {
  268. for _, dir := range dirs {
  269. select {
  270. case <-stop:
  271. break
  272. default:
  273. if mp != nil {
  274. target_dir := strings.Join([]string{www_dir, dir.Name()}, string(os.PathSeparator))
  275. if utils.IsDirExists(target_dir) {
  276. xml_detect(target_dir, dir.Name(), mp)
  277. }
  278. }
  279. }
  280. }
  281. }
  282. }
  283. func xml_start(www_dir string, mp *mysqlpool.MySqlPool) (chan bool, chan bool) {
  284. ch := make(chan bool)
  285. stop := make(chan bool)
  286. // Run at start
  287. xml_loop(www_dir, stop, mp)
  288. go func() {
  289. for {
  290. select {
  291. case <-time.After(5 * time.Second):
  292. // Run every 5 seconds
  293. xml_loop(www_dir, stop, mp)
  294. case <-ch:
  295. ch <- true
  296. return
  297. }
  298. }
  299. }()
  300. return ch, stop
  301. }
  302. func xml_stop(ch, stop chan bool) {
  303. for {
  304. select {
  305. case stop <- true:
  306. case ch <- true:
  307. <-ch
  308. return
  309. case <-time.After(3 * time.Second):
  310. fmt.Println("Xml error: force exit by timeout after 3 seconds")
  311. return
  312. }
  313. }
  314. }