xml.go 9.9 KB

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