xml.go 10 KB

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