public_offering.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. package maycur
  2. import (
  3. "crypto/hmac"
  4. "crypto/md5"
  5. "encoding/hex"
  6. "encoding/json"
  7. "fmt"
  8. "github.com/rdlucklib/rdluck_tools/common"
  9. "hongze/hz_crm_api/models"
  10. "hongze/hz_crm_api/services/alarm_msg"
  11. "hongze/hz_crm_api/utils"
  12. "io/ioutil"
  13. "net/http"
  14. "net/url"
  15. "sort"
  16. "strconv"
  17. "strings"
  18. "time"
  19. )
  20. const (
  21. ShanghaiCRMCompanyListUrl = `/v2/Customer/basicSaleCustomerRelation`
  22. ShanghaiCRMSellerListUrl = `/v2/AdminUser/EquitySaleDeptMemberList`
  23. ShanghaiCRMCompanyListUrlRelease = `https://crm.hzinsights.com/openapi/v2/Customer/basicSaleCustomerRelation`
  24. ShanghaiCRMSellerListUrlRelease = `https://crm.hzinsights.com/openapi/v2/AdminUser/EquitySaleDeptMemberList`
  25. )
  26. // openApiParamsBuild 生成Api参数(含签名)
  27. func openApiParamsBuild(paramMap map[string]string) (params url.Values) {
  28. // 公共参数
  29. strRand := utils.GetRandStringNoSpecialChar(32)
  30. timestamp := fmt.Sprint(time.Now().Unix())
  31. values := map[string]string{
  32. "app_key": utils.CRM_OPEN_API_APP_KEY,
  33. "nonce_str": strRand,
  34. "timestamp": timestamp,
  35. }
  36. // ASCII排序
  37. keys := make([]string, 0)
  38. keys = append(keys, "app_key", "nonce_str", "timestamp")
  39. for k, v := range paramMap {
  40. keys = append(keys, k)
  41. values[k] = v
  42. }
  43. sort.Strings(keys)
  44. params = make(map[string][]string)
  45. signArr := make([]string, 0) // 参与签名生成的参数
  46. for _, k := range keys {
  47. params.Add(k, values[k])
  48. signArr = append(signArr, fmt.Sprint(k, "=", values[k]))
  49. }
  50. // 签名
  51. strA := strings.Join(signArr, "&")
  52. h := hmac.New(md5.New, []byte(utils.CRM_OPEN_API_APP_SECRET))
  53. h.Write([]byte(strA))
  54. strB := hex.EncodeToString(h.Sum([]byte("")))
  55. strD := common.UrlEncode(strB)
  56. params.Add("signature", strD)
  57. return
  58. }
  59. // PublicOfferingCompanyApiResp 客户列表响应体
  60. type PublicOfferingCompanyApiResp struct {
  61. Code int `description:"状态码: 1-成功; 0-失败; >1-指定错误"`
  62. Msg string `description:"提示信息"`
  63. Time int64 `description:"时间戳"`
  64. Data struct {
  65. Paginator struct {
  66. Total int `description:"数据总量"`
  67. CurrentPage int `description:"当前页码" json:"current_page"`
  68. ListRow int `description:"每页数据量" json:"list_row"`
  69. LastPage int `description:"最后一页页码" json:"last_page"`
  70. } `description:"分页信息"`
  71. Data []struct {
  72. Name string `description:"公司名称"`
  73. ShortName string `description:"公司简称" json:"short_name"`
  74. Social string `description:"公司社会信用码"`
  75. UserId string `description:"销售ID" json:"user_id"`
  76. Username string `description:"销售名称"`
  77. } `description:"公司列表"`
  78. } `description:"响应数据"`
  79. }
  80. // CurlPublicOfferingCompanyApi 请求客户列表接口
  81. func CurlPublicOfferingCompanyApi(strParams string) (body []byte, err error) {
  82. //curlUrl := fmt.Sprintf("%s%s?%s", utils.CRM_OPEN_API_URL, ShanghaiCRMCompanyListUrl, strParams)
  83. curlUrl := fmt.Sprintf("%s?%s", ShanghaiCRMCompanyListUrlRelease, strParams)
  84. res, e := http.Get(curlUrl)
  85. if e != nil {
  86. err = fmt.Errorf("http get err: %s", e.Error())
  87. return
  88. }
  89. defer func() {
  90. _ = res.Body.Close()
  91. }()
  92. body, e = ioutil.ReadAll(res.Body)
  93. if e != nil {
  94. err = fmt.Errorf("http body err: %s", e.Error())
  95. return
  96. }
  97. //utils.FileLog.Info("open api http resp: %s", string(body))
  98. return
  99. }
  100. // PublicOfferingSaleApiResp 销售列表响应体
  101. type PublicOfferingSaleApiResp struct {
  102. Code int `description:"状态码: 1-成功; 0-失败; >1-指定错误"`
  103. Msg string `description:"提示信息"`
  104. Time int64 `description:"时间戳"`
  105. Data []struct {
  106. Mobile string `description:"手机号"`
  107. Nickname string `description:"销售姓名"`
  108. DeptText string `description:"部门描述" json:"dept_text"`
  109. IsLeader int `description:"是否为负责人: 0-否; 1-是" json:"is_leader"`
  110. } `description:"响应数据"`
  111. }
  112. // CurlEquitySaleListApi 请求公募销售列表接口
  113. func CurlEquitySaleListApi(strParams string) (body []byte, err error) {
  114. //curlUrl := fmt.Sprintf("%s%s?%s", utils.CRM_OPEN_API_URL, ShanghaiCRMSellerListUrl, strParams)
  115. curlUrl := fmt.Sprintf("%s?%s", ShanghaiCRMSellerListUrlRelease, strParams)
  116. res, e := http.Get(curlUrl)
  117. if e != nil {
  118. err = fmt.Errorf("http get err: %s", e.Error())
  119. return
  120. }
  121. defer func() {
  122. _ = res.Body.Close()
  123. }()
  124. body, e = ioutil.ReadAll(res.Body)
  125. if e != nil {
  126. err = fmt.Errorf("http body err: %s", e.Error())
  127. return
  128. }
  129. utils.FileLog.Info("open api http resp: %s", string(body))
  130. return
  131. }
  132. // SyncPublicOfferingSale 同步公募销售
  133. func SyncPublicOfferingSale() (err error) {
  134. defer func() {
  135. if err != nil {
  136. msg := fmt.Sprintf("每刻-同步公募销售失败, ErrMsg: %s", err.Error())
  137. utils.FileLog.Error(msg)
  138. go alarm_msg.SendAlarmMsg(msg, 3)
  139. }
  140. }()
  141. paramMap := make(map[string]string)
  142. params := openApiParamsBuild(paramMap)
  143. strParams := params.Encode()
  144. res, e := CurlEquitySaleListApi(strParams)
  145. if e != nil {
  146. err = fmt.Errorf("获取公募销售列表失败, Err: %s", e.Error())
  147. return
  148. }
  149. if len(res) == 0 {
  150. err = fmt.Errorf("公募销售数据为空")
  151. return
  152. }
  153. var resp PublicOfferingSaleApiResp
  154. if e := json.Unmarshal(res, &resp); e != nil {
  155. err = fmt.Errorf("解析公募销售数据失败, Err: %s; Body: %s", e.Error(), string(res))
  156. return
  157. }
  158. if resp.Code != 1 {
  159. err = fmt.Errorf("获取公募销售数据失败, Body: %s", string(res))
  160. return
  161. }
  162. sales := resp.Data
  163. // 去重
  164. saleOB := new(models.MaycurPublicOfferingSale)
  165. saleCond := ``
  166. salePars := make([]interface{}, 0)
  167. origins, e := saleOB.GetItemsByCondition(saleCond, salePars, []string{}, "")
  168. if e != nil {
  169. err = fmt.Errorf("获取公募销售留档失败, Err: %s", e.Error())
  170. return
  171. }
  172. salesNameVal := make(map[string]*models.MaycurPublicOfferingSale)
  173. for _, s := range origins {
  174. salesNameVal[s.Nickname] = s
  175. }
  176. // 新增
  177. inserts := make([]*models.MaycurPublicOfferingSale, 0)
  178. updateCols := []string{"Mobile", "DeptText", "IsLeader", "ModifyTime"}
  179. for _, v := range sales {
  180. s := salesNameVal[v.Nickname]
  181. if s != nil {
  182. s.Mobile = v.Mobile
  183. s.DeptText = v.DeptText
  184. s.IsLeader = v.IsLeader
  185. s.ModifyTime = time.Now().Local()
  186. if e = s.Update(updateCols); e != nil {
  187. err = fmt.Errorf("更新公募销售信息失败, Err: %s", e.Error())
  188. return
  189. }
  190. continue
  191. }
  192. inserts = append(inserts, &models.MaycurPublicOfferingSale{
  193. Mobile: v.Mobile,
  194. Nickname: v.Nickname,
  195. CreateTime: time.Now().Local(),
  196. ModifyTime: time.Now().Local(),
  197. })
  198. }
  199. if e = saleOB.CreateMulti(inserts); e != nil {
  200. err = fmt.Errorf("新增公募销售失败, Err: %s", e.Error())
  201. return
  202. }
  203. return
  204. }
  205. // SyncPublicOfferingCompany 同步客户列表, 过滤出公募客户入库留档
  206. func SyncPublicOfferingCompany() (err error) {
  207. defer func() {
  208. if err != nil {
  209. msg := fmt.Sprintf("每刻-同步公募客户失败, ErrMsg: %s", err.Error())
  210. utils.FileLog.Error(msg)
  211. go alarm_msg.SendAlarmMsg(msg, 3)
  212. }
  213. }()
  214. // 去重
  215. companyOB := new(models.MaycurPublicOfferingCompany)
  216. companyCond := ``
  217. companyPars := make([]interface{}, 0)
  218. origins, e := companyOB.GetItemsByCondition(companyCond, companyPars, []string{}, "")
  219. if e != nil {
  220. err = fmt.Errorf("获取公募客户留档失败, Err: %s", e.Error())
  221. return
  222. }
  223. companyCodeVal := make(map[string]*models.MaycurPublicOfferingCompany)
  224. for _, s := range origins {
  225. companyCodeVal[s.Social] = s
  226. }
  227. // 获取公募销售
  228. saleOB := new(models.MaycurPublicOfferingSale)
  229. sales, e := saleOB.GetItemsByCondition(``, make([]interface{}, 0), []string{}, "")
  230. if e != nil {
  231. err = fmt.Errorf("获取公募销售列表失败, Err: %s", e.Error())
  232. return
  233. }
  234. saleArr := make([]string, 0)
  235. for _, s := range sales {
  236. saleArr = append(saleArr, s.Nickname)
  237. }
  238. currentPage := 1
  239. pageSize := 100
  240. updateCols := []string{"SaleNames", "ModifyTime"}
  241. codeSales := make(map[string][]string)
  242. for {
  243. paramMap := make(map[string]string)
  244. paramMap["limit"] = strconv.Itoa(pageSize)
  245. paramMap["f_e"] = "Equity"
  246. paramMap["page"] = strconv.Itoa(currentPage)
  247. params := openApiParamsBuild(paramMap)
  248. strParams := params.Encode()
  249. res, e := CurlPublicOfferingCompanyApi(strParams)
  250. if e != nil {
  251. err = fmt.Errorf("获取公募销售列表失败, Err: %s", e.Error())
  252. return
  253. }
  254. if len(res) == 0 {
  255. err = fmt.Errorf("公募销售数据为空")
  256. return
  257. }
  258. var resp PublicOfferingCompanyApiResp
  259. if e := json.Unmarshal(res, &resp); e != nil {
  260. err = fmt.Errorf("解析公募客户数据失败, Err: %s; Body: %s", e.Error(), string(res))
  261. return
  262. }
  263. if resp.Code != 1 {
  264. err = fmt.Errorf("获取公募客户数据失败, Body: %s", string(res))
  265. return
  266. }
  267. data := resp.Data
  268. companies := data.Data
  269. codeCompany := make(map[string]*models.MaycurPublicOfferingCompany)
  270. for _, c := range companies {
  271. // 过滤测试客户
  272. if strings.Contains(c.Name, "测试") || strings.Contains(strings.ToLower(c.Name), "test") {
  273. continue
  274. }
  275. // 如果该客户的销售中无公募销售, 则忽略
  276. if !utils.InArrayByStr(saleArr, c.Username) {
  277. continue
  278. }
  279. if codeCompany[c.Social] == nil {
  280. v := &models.MaycurPublicOfferingCompany{
  281. Name: c.Name,
  282. ShortName: c.ShortName,
  283. Social: c.Social,
  284. CreateTime: time.Now().Local(),
  285. ModifyTime: time.Now().Local(),
  286. }
  287. codeCompany[c.Social] = v
  288. }
  289. if c.Username != "" && !utils.InArrayByStr(codeSales[c.Social], c.Username) {
  290. codeSales[c.Social] = append(codeSales[c.Social], c.Username)
  291. }
  292. }
  293. inserts := make([]*models.MaycurPublicOfferingCompany, 0)
  294. for _, c := range codeCompany {
  295. saleNames := strings.Join(codeSales[c.Social], ",")
  296. ex := companyCodeVal[c.Social]
  297. if ex != nil {
  298. // 更新销售
  299. ex.SaleNames = saleNames
  300. ex.ModifyTime = time.Now().Local()
  301. if e = ex.Update(updateCols); e != nil {
  302. err = fmt.Errorf("更新客户信息失败, Err: %s", e.Error())
  303. return
  304. }
  305. continue
  306. }
  307. c.SaleNames = saleNames
  308. inserts = append(inserts, c)
  309. // 本次新增的客户也加入map, 因为可能出现下一页有上一页的客户, 下一次循环时更新
  310. companyCodeVal[c.Social] = c
  311. }
  312. ob := new(models.MaycurPublicOfferingCompany)
  313. if e = ob.CreateMulti(inserts); e != nil {
  314. err = fmt.Errorf("批量新增客户信息失败, Err: %s", e.Error())
  315. return
  316. }
  317. // 最后一页
  318. if data.Paginator.LastPage == currentPage {
  319. break
  320. }
  321. currentPage += 1
  322. }
  323. return
  324. }
  325. // GetPublicOfferingSaleLeader 获取公募销售对应的组长
  326. func GetPublicOfferingSaleLeader() (leaderMap map[string][]string, err error) {
  327. saleOB := new(models.MaycurPublicOfferingSale)
  328. sales, e := saleOB.GetItemsByCondition(``, make([]interface{}, 0), []string{}, "")
  329. if e != nil {
  330. err = fmt.Errorf("获取公募销售列表失败, Err: %s", e.Error())
  331. return
  332. }
  333. if sales == nil {
  334. return
  335. }
  336. if len(sales) == 0 {
  337. return
  338. }
  339. // 销售组组长
  340. groupsLeader := make(map[string][]string)
  341. for _, s := range sales {
  342. // 存在这种数据->北京组、上海组
  343. gs := strings.Split(s.DeptText, "、")
  344. for _, g := range gs {
  345. if s.IsLeader == 1 {
  346. groupsLeader[g] = append(groupsLeader[g], s.Nickname)
  347. }
  348. }
  349. }
  350. // 销售对应组长
  351. leaderMap = make(map[string][]string)
  352. for _, s := range sales {
  353. // 组长置空
  354. ls := make([]string, 0)
  355. if s.IsLeader == 1 {
  356. leaderMap[s.Nickname] = ls
  357. continue
  358. }
  359. // 组员
  360. gs := strings.Split(s.DeptText, "、")
  361. for _, g := range gs {
  362. if len(groupsLeader[g]) > 0 {
  363. for _, l := range groupsLeader[g] {
  364. if !utils.InArrayByStr(ls, l) {
  365. ls = append(ls, l)
  366. }
  367. }
  368. }
  369. }
  370. leaderMap[s.Nickname] = ls
  371. }
  372. return
  373. }