xml.go 10 KB

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