xml.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. package workers
  2. import (
  3. "context"
  4. "fmt"
  5. "html"
  6. "io/ioutil"
  7. "os"
  8. "strings"
  9. "time"
  10. "golang-fave/engine/config"
  11. "golang-fave/engine/mysqlpool"
  12. "golang-fave/engine/sqlw"
  13. "golang-fave/engine/utils"
  14. "github.com/vladimirok5959/golang-worker/worker"
  15. )
  16. func XmlGenerator(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. ctx,
  114. `SELECT
  115. code,
  116. coefficient
  117. FROM
  118. fave_shop_currencies
  119. ORDER BY
  120. id ASC
  121. ;`,
  122. )
  123. if err == nil {
  124. defer rows.Close()
  125. values := make([]string, 2)
  126. scan := make([]interface{}, len(values))
  127. for i := range values {
  128. scan[i] = &values[i]
  129. }
  130. for rows.Next() {
  131. err = rows.Scan(scan...)
  132. if err == nil {
  133. result += `<currency id="` + html.EscapeString(string(values[0])) + `" rate="` + html.EscapeString(string(values[1])) + `"/>`
  134. }
  135. }
  136. }
  137. return result, nil
  138. }
  139. func xml_gen_categories(ctx context.Context, db *sqlw.DB, conf *config.Config) (string, error) {
  140. result := ``
  141. rows, err := db.Query(
  142. ctx,
  143. `SELECT
  144. data.id,
  145. data.user,
  146. data.name,
  147. data.alias,
  148. data.lft,
  149. data.rgt,
  150. MAX(data.parent_id) AS parent_id
  151. FROM
  152. (
  153. SELECT
  154. node.id,
  155. node.user,
  156. node.name,
  157. node.alias,
  158. node.lft,
  159. node.rgt,
  160. parent.id AS parent_id
  161. FROM
  162. fave_shop_cats AS node,
  163. fave_shop_cats AS parent
  164. WHERE
  165. node.lft BETWEEN parent.lft AND parent.rgt AND
  166. node.id > 1
  167. ORDER BY
  168. node.lft ASC
  169. ) AS data
  170. WHERE
  171. data.id <> data.parent_id
  172. GROUP BY
  173. data.id
  174. ORDER BY
  175. data.lft ASC
  176. ;`,
  177. )
  178. if err == nil {
  179. defer rows.Close()
  180. values := make([]string, 7)
  181. scan := make([]interface{}, len(values))
  182. for i := range values {
  183. scan[i] = &values[i]
  184. }
  185. for rows.Next() {
  186. err = rows.Scan(scan...)
  187. if err == nil {
  188. if utils.StrToInt(string(values[6])) > 1 {
  189. result += `<category id="` + html.EscapeString(string(values[0])) + `" parentId="` + html.EscapeString(string(values[6])) + `">` + html.EscapeString(string(values[2])) + `</category>`
  190. } else {
  191. result += `<category id="` + html.EscapeString(string(values[0])) + `">` + html.EscapeString(string(values[2])) + `</category>`
  192. }
  193. }
  194. }
  195. }
  196. return result, nil
  197. }
  198. func xml_gen_offers(ctx context.Context, db *sqlw.DB, conf *config.Config) (string, error) {
  199. result := ``
  200. rows, err := db.Query(
  201. ctx,
  202. `SELECT
  203. fave_shop_products.id,
  204. fave_shop_currencies.code,
  205. fave_shop_products.price,
  206. fave_shop_products.name,
  207. fave_shop_products.alias,
  208. fave_shop_products.vendor,
  209. fave_shop_products.quantity,
  210. fave_shop_products.category,
  211. fave_shop_products.content,
  212. IFNULL(fave_shop_products.parent_id, 0),
  213. fave_shop_products.price_old,
  214. fave_shop_products.price_promo
  215. FROM
  216. fave_shop_products
  217. LEFT JOIN fave_shop_currencies ON fave_shop_currencies.id = fave_shop_products.currency
  218. WHERE
  219. fave_shop_products.active = 1 AND
  220. fave_shop_products.category > 1
  221. ORDER BY
  222. fave_shop_products.id
  223. ;`,
  224. )
  225. if err == nil {
  226. defer rows.Close()
  227. values := make([]string, 12)
  228. scan := make([]interface{}, len(values))
  229. for i := range values {
  230. scan[i] = &values[i]
  231. }
  232. for rows.Next() {
  233. err = rows.Scan(scan...)
  234. if err == nil {
  235. result += `<offer id="` + html.EscapeString(string(values[0])) + `" available="true">`
  236. result += `<url>` + html.EscapeString((*conf).API.XML.Url) + `shop/` + html.EscapeString(string(values[4])) + `/</url>`
  237. result += `<price>` + utils.Float64ToStrF(utils.StrToFloat64(string(values[2])), "%.2f") + `</price>`
  238. if utils.StrToFloat64(string(values[10])) > 0 {
  239. result += `<price_old>` + utils.Float64ToStrF(utils.StrToFloat64(string(values[10])), "%.2f") + `</price_old>`
  240. }
  241. if utils.StrToFloat64(string(values[11])) > 0 {
  242. result += `<price_promo>` + utils.Float64ToStrF(utils.StrToFloat64(string(values[11])), "%.2f") + `</price_promo>`
  243. }
  244. result += `<currencyId>` + html.EscapeString(string(values[1])) + `</currencyId>`
  245. result += `<categoryId>` + html.EscapeString(string(values[7])) + `</categoryId>`
  246. result += xml_gen_offer_pictures(ctx, db, conf, utils.StrToInt(string(values[0])), utils.StrToInt(string(values[9])))
  247. result += `<vendor>` + html.EscapeString(string(values[5])) + `</vendor>`
  248. result += `<stock_quantity>` + html.EscapeString(string(values[6])) + `</stock_quantity>`
  249. result += `<name>` + html.EscapeString(string(values[3])) + ` ` + html.EscapeString(string(values[0])) + `</name>`
  250. result += `<description><![CDATA[` + string(values[8]) + `]]></description>`
  251. result += xml_gen_offer_attributes(ctx, db, conf, utils.StrToInt(string(values[0])))
  252. result += `</offer>`
  253. }
  254. }
  255. }
  256. return result, nil
  257. }
  258. func xml_gen_offer_pictures(ctx context.Context, db *sqlw.DB, conf *config.Config, product_id, parent_id int) string {
  259. result := ``
  260. if rows, err := db.Query(
  261. ctx,
  262. `SELECT
  263. fave_shop_product_images.product_id,
  264. fave_shop_product_images.filename
  265. FROM
  266. fave_shop_product_images
  267. WHERE
  268. fave_shop_product_images.product_id = ?
  269. ORDER BY
  270. fave_shop_product_images.ord ASC
  271. ;`,
  272. product_id,
  273. ); err == nil {
  274. defer rows.Close()
  275. values := make([]string, 2)
  276. scan := make([]interface{}, len(values))
  277. for i := range values {
  278. scan[i] = &values[i]
  279. }
  280. for rows.Next() {
  281. err = rows.Scan(scan...)
  282. if err == nil {
  283. result += `<picture>` + html.EscapeString((*conf).API.XML.Url) + `products/images/` + html.EscapeString(string(values[0])) + `/` + html.EscapeString(string(values[1])) + `</picture>`
  284. }
  285. }
  286. }
  287. // Get images from parent
  288. if result == "" && parent_id > 0 {
  289. if rows, err := db.Query(
  290. ctx,
  291. `SELECT
  292. fave_shop_product_images.product_id,
  293. fave_shop_product_images.filename
  294. FROM
  295. fave_shop_product_images
  296. WHERE
  297. fave_shop_product_images.product_id = ?
  298. ORDER BY
  299. fave_shop_product_images.ord ASC
  300. ;`,
  301. parent_id,
  302. ); err == nil {
  303. defer rows.Close()
  304. values := make([]string, 2)
  305. scan := make([]interface{}, len(values))
  306. for i := range values {
  307. scan[i] = &values[i]
  308. }
  309. for rows.Next() {
  310. err = rows.Scan(scan...)
  311. if err == nil {
  312. result += `<picture>` + html.EscapeString((*conf).API.XML.Url) + `products/images/` + html.EscapeString(string(values[0])) + `/` + html.EscapeString(string(values[1])) + `</picture>`
  313. }
  314. }
  315. }
  316. }
  317. return result
  318. }
  319. func xml_gen_offer_attributes(ctx context.Context, db *sqlw.DB, conf *config.Config, product_id int) string {
  320. result := ``
  321. filter_ids := []int{}
  322. filter_names := map[int]string{}
  323. filter_values := map[int][]string{}
  324. rows, err := db.Query(
  325. ctx,
  326. `SELECT
  327. fave_shop_filters.id,
  328. fave_shop_filters.filter,
  329. fave_shop_filters_values.name
  330. FROM
  331. fave_shop_filter_product_values
  332. LEFT JOIN fave_shop_filters_values ON fave_shop_filters_values.id = fave_shop_filter_product_values.filter_value_id
  333. LEFT JOIN fave_shop_filters ON fave_shop_filters.id = fave_shop_filters_values.filter_id
  334. WHERE
  335. fave_shop_filter_product_values.product_id = ?
  336. ORDER BY
  337. fave_shop_filters.filter ASC,
  338. fave_shop_filters_values.name ASC
  339. ;`,
  340. product_id,
  341. )
  342. if err == nil {
  343. defer rows.Close()
  344. values := make([]string, 3)
  345. scan := make([]interface{}, len(values))
  346. for i := range values {
  347. scan[i] = &values[i]
  348. }
  349. for rows.Next() {
  350. err = rows.Scan(scan...)
  351. if err == nil {
  352. if !utils.InArrayInt(filter_ids, utils.StrToInt(string(values[0]))) {
  353. filter_ids = append(filter_ids, utils.StrToInt(string(values[0])))
  354. }
  355. filter_names[utils.StrToInt(string(values[0]))] = html.EscapeString(string(values[1]))
  356. filter_values[utils.StrToInt(string(values[0]))] = append(filter_values[utils.StrToInt(string(values[0]))], string(values[2]))
  357. }
  358. }
  359. }
  360. for _, filter_id := range filter_ids {
  361. result += `<param name="` + html.EscapeString(filter_names[filter_id]) + `">` + html.EscapeString(strings.Join(filter_values[filter_id], ", ")) + `</param>`
  362. }
  363. return result
  364. }