main.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. package main
  2. import (
  3. . "app.yhyue.com/moapp/jybase/common"
  4. . "app.yhyue.com/moapp/jybase/mongodb"
  5. "context"
  6. "flag"
  7. "fmt"
  8. "github.com/gogf/gf/v2/container/garray"
  9. "github.com/gogf/gf/v2/container/gmap"
  10. "github.com/gogf/gf/v2/container/gset"
  11. "github.com/gogf/gf/v2/frame/g"
  12. "github.com/gogf/gf/v2/os/gctx"
  13. "github.com/gogf/gf/v2/os/gtime"
  14. "github.com/gogf/gf/v2/text/gstr"
  15. "github.com/gogf/gf/v2/util/gconv"
  16. "golang.org/x/net/html"
  17. "net/rpc"
  18. "regexp"
  19. "strings"
  20. "sync"
  21. "time"
  22. )
  23. var (
  24. TaskConfig *taskConfig
  25. mgo *MongodbSim
  26. mgoBid *MongodbSim
  27. allSpaceReg = regexp.MustCompile(`([\s   ])+`)
  28. spaceReg = regexp.MustCompile(`\s+`)
  29. brReg = regexp.MustCompile(`(<br/>(\s+)?)+`)
  30. )
  31. type taskConfig struct {
  32. LastTime int64
  33. }
  34. type BidDataItem struct {
  35. Zbgs *BidDataObj //中标公示
  36. Zcgs *BidDataObj //招采公示
  37. Yxsjgs *BidDataObj //意向商机公示
  38. }
  39. type BidDataObj struct {
  40. Winners *gset.Set
  41. Purchasinglist *gset.Set
  42. BidDataDetails *garray.SortedArray
  43. }
  44. type BidDataDetail struct {
  45. ProjectName string //项目名称
  46. ProjectCode string //项目编号
  47. Area string //项目地区
  48. Buyer string //招采单位
  49. BudgetOriginal float64 //
  50. Budget string //预算金额
  51. Amount float64 //金额
  52. Winner string //中标单位
  53. BidamountOriginal float64 //
  54. Bidamount string //中标金额
  55. Detail string //建设内容
  56. Bidendtime string //投标截止时间
  57. PurchaseTime string //预计采购时间
  58. Projectscope string //需求概况 projectscope
  59. }
  60. type Article struct {
  61. Title string
  62. Content string
  63. }
  64. func newBidDataObj() *BidDataObj {
  65. return &BidDataObj{Winners: gset.New(true), Purchasinglist: gset.New(true), BidDataDetails: garray.NewSortedArray(func(a, b interface{}) int {
  66. if a.(*BidDataDetail).Amount > b.(*BidDataDetail).Amount {
  67. return -1
  68. } else if a.(*BidDataDetail).Amount < b.(*BidDataDetail).Amount {
  69. return 1
  70. }
  71. return 0
  72. }, true)}
  73. }
  74. func init() {
  75. ctx := gctx.New()
  76. mgo = &MongodbSim{
  77. MongodbAddr: g.Config().MustGet(ctx, "mongodb.main.address").String(),
  78. Size: g.Config().MustGet(ctx, "mongodb.main.size").Int(),
  79. DbName: g.Config().MustGet(ctx, "mongodb.main.dbName").String(),
  80. UserName: g.Config().MustGet(ctx, "mongodb.main.userName").String(),
  81. Password: g.Config().MustGet(ctx, "mongodb.main.password").String(),
  82. }
  83. mgo.InitPool()
  84. mgoBid = &MongodbSim{
  85. MongodbAddr: g.Config().MustGet(ctx, "mongodb.bidding.address").String(),
  86. Size: g.Config().MustGet(ctx, "mongodb.bidding.size").Int(),
  87. DbName: g.Config().MustGet(ctx, "mongodb.bidding.dbName").String(),
  88. UserName: g.Config().MustGet(ctx, "mongodb.bidding.userName").String(),
  89. Password: g.Config().MustGet(ctx, "mongodb.bidding.password").String(),
  90. }
  91. mgoBid.InitPool()
  92. ReadConfig("./task.json", &TaskConfig)
  93. }
  94. func main() {
  95. model := flag.Int("m", 0, "1:上传素材 2:非定时任务")
  96. startTime := flag.Int64("s", 0, "开始时间")
  97. endTime := flag.Int64("e", 0, "结束时间")
  98. flag.Parse()
  99. //log.Println(detailCapture(filterHtml(`一、项目编号:ZL24011540000(招标文件编号:ZL24011540000)<br/>二、项目名称:西藏低温冷害、干旱、病虫害气象预警信息化平台<br/>三、中标(成交)信息<br/>供应商名称:重庆阿里九九科技有限公司<br/>供应商地址:拉萨市北京西路回民巷2号<br/>中标(成交)金额:61.8000000(万元)<br/>四、主要标的信息<br/><table border=\"1\">\t<tbody>\t\t<tr>\t\t\t<td>序号</td>\t\t\t<td>   供应商名称  </td>\t\t\t<td>   服务名称  </td>\t\t\t<td>   服务范围  </td>\t\t\t<td>   服务要求  </td>\t\t\t<td>   服务时间  </td>\t\t\t<td>   服务标准  </td>\t\t</tr>\t\t<tr>\t\t\t<td>1</td>\t\t\t<td>   重庆阿里九九科技有限公司  </td>\t\t\t<td>   详见响应文件  </td>\t\t\t<td>   详见响应文件  </td>\t\t\t<td>   详见响应文件  </td>\t\t\t<td>   详见响应文件  </td>\t\t\t<td>   详见响应文件  </td>\t\t</tr>\t\t<tr>\t\t\t<td> </td>\t\t\t<td> </td>\t\t\t<td> </td>\t\t\t<td> </td>\t\t\t<td> </td>\t\t\t<td> </td>\t\t\t<td> </td>\t\t</tr>\t</tbody></table><br/>五、评审专家(单一来源采购人员)名单:<br/>程标、程伟伟、姚曌臻<br/>六、代理服务收费标准及金额:<br/>本项目代理费收费标准:按发改价格【2015】299号文相关规定向中标人收取<br/>本项目代理费总金额:0.927000 万元(人民币)<br/>七、公告期限<br/>自本公告发布之日起1个工作日。<br/>八、其它补充事宜<br/>九、凡对本次公告内容提出询问,请按以下方式联系。<br/>1.采购人信息<br/>名 称:西藏自治区气候中心     <br/>地址:西藏自治区拉萨市城关区林廓北路1号        <br/>联系方式:张先生 0891-6330101      <br/>2.采购代理机构信息<br/>名 称:西藏则立项目管理有限公司            <br/>地 址:拉萨市柳梧新区海亮天城商铺11-7号            <br/>联系方式:陈先生 17708911306            <br/>3.项目联系方式<br/>项目联系人:陈先生<br/>电 话:  17708911306`)))
  100. //addDraft(0, 1736289976) //f_sourceinfo_2025Jslt_msx 江苏 西藏
  101. //addDraft(1740731400, 1840731400) //f_sourceinfo_2025Sjcs 广东 上海
  102. //return
  103. if *model == 1 {
  104. addMaterial()
  105. } else if *model == 2 {
  106. addDraft(*startTime, *endTime)
  107. } else {
  108. SimpleCrontab(false, g.Config().MustGet(gctx.New(), "timeTask").String(), func() {
  109. et := time.Now().Unix()
  110. addDraft(TaskConfig.LastTime, et)
  111. TaskConfig.LastTime = et
  112. WriteSysConfig("./task.json", *TaskConfig)
  113. })
  114. <-chan bool(nil)
  115. }
  116. }
  117. func addMaterial() {
  118. ctx := gctx.New()
  119. var path = "./thumb.png"
  120. result, err := g.Client().Post(ctx, "https://api.weixin.qq.com/cgi-bin/material/add_material?access_token="+getAccessToken(ctx)+"&type=image", "media=@file:"+path)
  121. if err != nil {
  122. g.Log().Error(ctx, err)
  123. return
  124. }
  125. defer result.Close()
  126. g.Log().Info(ctx, "新增永久素材请求返回结果", result.ReadAllString())
  127. }
  128. // 新增草稿箱
  129. func addDraft(startTime, endTime int64) {
  130. ctx := gctx.New()
  131. g.Log().Info(ctx, "开始新增草稿箱任务。。。")
  132. sess := mgo.GetMgoConn()
  133. defer mgo.DestoryMongoConn(sess)
  134. it := sess.DB(g.Config().MustGet(ctx, "mongodb.main.dbName").String()).C(g.Config().MustGet(ctx, "mongodb.main.collection").String()).Find(map[string]interface{}{
  135. "i_updatetime": map[string]interface{}{
  136. "$gte": startTime,
  137. "$lt": endTime,
  138. },
  139. "i_ckdata": map[string]interface{}{
  140. "$gt": 0,
  141. },
  142. "v_baseinfo.informatized": "是",
  143. }).Select(map[string]interface{}{"id": 1, "v_baseinfo": 1}).Iter()
  144. all := gmap.New(true)
  145. pool := make(chan bool, 5)
  146. wait := &sync.WaitGroup{}
  147. index := 0
  148. areas := map[string]bool{}
  149. for _, v := range g.Config().MustGet(ctx, "areas").Strings() {
  150. areas[v] = true
  151. }
  152. for mt := make(map[string]interface{}); it.Next(&mt); {
  153. pool <- true
  154. wait.Add(1)
  155. go func(mn map[string]interface{}) {
  156. defer func() {
  157. <-pool
  158. wait.Done()
  159. }()
  160. v_baseinfo, _ := mn["v_baseinfo"].(map[string]interface{})
  161. area, _ := v_baseinfo["area"].(string)
  162. toptype, _ := v_baseinfo["toptype"].(string)
  163. subtype, _ := v_baseinfo["subtype"].(string)
  164. if !areas[area] || (toptype != "招标" && toptype != "采购意向" && subtype != "中标" && subtype != "成交") {
  165. return
  166. }
  167. var bdi *BidDataItem
  168. if all.Get(area) == nil {
  169. bdi = &BidDataItem{Zbgs: newBidDataObj(), Zcgs: newBidDataObj(), Yxsjgs: newBidDataObj()}
  170. all.Set(area, bdi)
  171. } else {
  172. bdi = all.Get(area).(*BidDataItem)
  173. }
  174. var bdo *BidDataObj
  175. if toptype == "招标" {
  176. bdo = bdi.Zcgs
  177. } else if toptype == "结果" {
  178. bdo = bdi.Zbgs
  179. } else {
  180. bdo = bdi.Yxsjgs
  181. }
  182. purchasinglist, _ := v_baseinfo["purchasinglist"].([]interface{})
  183. bidendtime := gconv.Int64(v_baseinfo["bidendtime"])
  184. procurementlist, _ := v_baseinfo["procurementlist"].([]interface{})
  185. if id := gconv.String(mn["id"]); id != "" && (purchasinglist == nil || len(purchasinglist) == 0 || v_baseinfo["bidendtime"] == nil || procurementlist == nil || len(procurementlist) == 0) {
  186. bidding, ok := mgoBid.FindById("bidding", id, `{"purchasinglist":1,"bidendtime":1,"procurementlist":1}`)
  187. if ok && bidding != nil && len(*bidding) > 0 {
  188. if purchasinglist == nil {
  189. purchasinglist, _ = (*bidding)["purchasinglist"].([]interface{})
  190. }
  191. if bidendtime == 0 {
  192. bidendtime = gconv.Int64((*bidding)["bidendtime"])
  193. }
  194. if procurementlist == nil {
  195. procurementlist, _ = (*bidding)["procurementlist"].([]interface{})
  196. }
  197. }
  198. }
  199. //
  200. if len(purchasinglist) > 0 {
  201. firstOne, _ := purchasinglist[0].(map[string]interface{})
  202. itemname := gconv.String(firstOne["itemname"])
  203. if itemname != "" {
  204. bdo.Purchasinglist.Add(itemname)
  205. }
  206. }
  207. //
  208. purchaseTime := ""
  209. if len(procurementlist) > 0 {
  210. firstOne, _ := procurementlist[0].(map[string]interface{})
  211. purchaseTime = gconv.String(firstOne["expurasingtime"])
  212. }
  213. //
  214. winner, _ := v_baseinfo["winner"].(string)
  215. bdo.Winners.Add(winner)
  216. //
  217. detail := filterHtml(gconv.String(v_baseinfo["projectscope"]))
  218. newDetail := detailCapture(detail)
  219. if detail != newDetail {
  220. newDetail += g.Config().MustGet(ctx, "template.captureAppend").String()
  221. }
  222. bdd := &BidDataDetail{
  223. ProjectName: gconv.String(v_baseinfo["projectname"]),
  224. ProjectCode: gconv.String(v_baseinfo["projectcode"]),
  225. Area: area,
  226. Buyer: gconv.String(v_baseinfo["buyer"]),
  227. BudgetOriginal: gconv.Float64(v_baseinfo["budget"]),
  228. Winner: winner,
  229. BidamountOriginal: gconv.Float64(v_baseinfo["bidamount"]),
  230. Detail: newDetail,
  231. PurchaseTime: purchaseTime,
  232. Projectscope: gconv.String(v_baseinfo["projectscope"]),
  233. }
  234. if bdd.BudgetOriginal > 0 {
  235. bdd.Budget = gconv.String(RetainDecimal(gconv.Float64(v_baseinfo["budget"])/10000, 2)) + "万元"
  236. }
  237. if bdd.BidamountOriginal > 0 {
  238. bdd.Bidamount = gconv.String(RetainDecimal(gconv.Float64(v_baseinfo["bidamount"])/10000, 2)) + "万元"
  239. }
  240. if v_baseinfo["bidamount"] != nil {
  241. bdd.Amount = bdd.BidamountOriginal
  242. } else {
  243. bdd.Amount = bdd.BudgetOriginal
  244. }
  245. if bdd.ProjectName == "" {
  246. bdd.ProjectName = gconv.String(v_baseinfo["title"])
  247. }
  248. if bidendtime > 0 {
  249. bdd.Bidendtime = gtime.New(bidendtime).Layout(time.DateTime)
  250. }
  251. bdo.BidDataDetails.Add(bdd)
  252. }(mt)
  253. index++
  254. if index%500 == 0 {
  255. g.Log().Info(ctx, "加载清洗数据。。。", index)
  256. }
  257. mt = make(map[string]interface{})
  258. }
  259. wait.Wait()
  260. g.Log().Info(ctx, "清洗数据加载结束。。。", index)
  261. articles := []*Article{}
  262. ymd := gtime.Now().Format("Ymd")
  263. purchasingSize := g.Config().MustGet(ctx, "template.purchasingSize").Int()
  264. winnerSize := g.Config().MustGet(ctx, "template.winnerSize").Int()
  265. htmlBody := g.Config().MustGet(ctx, "template.htmlBody").String()
  266. htmlTopImg := g.Config().MustGet(ctx, "template.htmlTopImg").String()
  267. htmlProjectSpit := g.Config().MustGet(ctx, "template.htmlProjectSpit").String()
  268. htmlProjectName := g.Config().MustGet(ctx, "template.htmlProjectName").String()
  269. htmlProjectOther := g.Config().MustGet(ctx, "template.htmlProjectOther").String()
  270. htmlBottom := g.Config().MustGet(ctx, "template.htmlBottom").String()
  271. all.Iterator(func(k, v interface{}) bool {
  272. kk := k.(string)
  273. vv := v.(*BidDataItem)
  274. //
  275. if !vv.Zbgs.BidDataDetails.IsEmpty() {
  276. vv.Zbgs.BidDataDetails.Sort()
  277. content := fmt.Sprintf(g.Config().MustGet(ctx, "template.zbgs.content").String(), kk, garray.NewArrayFrom(vv.Zbgs.Purchasinglist.Pops(purchasingSize)).Join("、"), garray.NewArrayFrom(vv.Zbgs.Winners.Pops(winnerSize)).Join("、"))
  278. content += htmlTopImg
  279. for k, v := range vv.Zbgs.BidDataDetails.PopLefts(g.Config().MustGet(ctx, "template.zhongProjectSize").Int()) {
  280. if k > 0 {
  281. content += htmlProjectSpit
  282. }
  283. vv := v.(*BidDataDetail)
  284. content += fmt.Sprintf(htmlProjectName, NumToChinese(k+1), vv.ProjectName)
  285. if vv.Buyer != "" {
  286. content += fmt.Sprintf(htmlProjectOther, "招采单位", vv.Buyer)
  287. }
  288. if vv.Winner != "" {
  289. content += fmt.Sprintf(htmlProjectOther, "中标单位", vv.Winner)
  290. }
  291. if vv.Bidamount != "" {
  292. content += fmt.Sprintf(htmlProjectOther, "中标金额", vv.Bidamount)
  293. }
  294. if vv.Detail != "" {
  295. content += fmt.Sprintf(htmlProjectOther, "建设内容", vv.Detail)
  296. }
  297. }
  298. articles = append(articles, &Article{
  299. Title: fmt.Sprintf("%s_%s_中标标讯", ymd, kk),
  300. Content: fmt.Sprintf(htmlBody, content+htmlBottom),
  301. })
  302. }
  303. //
  304. if !vv.Zcgs.BidDataDetails.IsEmpty() {
  305. vv.Zcgs.BidDataDetails.Sort()
  306. content := ""
  307. var allBudget float64
  308. for k, v := range vv.Zcgs.BidDataDetails.PopLefts(g.Config().MustGet(ctx, "template.zhaoProjectSize").Int()) {
  309. if k > 0 {
  310. content += htmlProjectSpit
  311. }
  312. vv := v.(*BidDataDetail)
  313. allBudget += vv.BudgetOriginal
  314. content += fmt.Sprintf(htmlProjectName, NumToChinese(k+1), vv.ProjectName)
  315. if vv.ProjectCode != "" {
  316. content += fmt.Sprintf(htmlProjectOther, "项目编号", vv.ProjectCode)
  317. }
  318. if vv.Buyer != "" {
  319. content += fmt.Sprintf(htmlProjectOther, "采购单位", vv.Buyer)
  320. }
  321. if vv.Area != "" {
  322. content += fmt.Sprintf(htmlProjectOther, "项目地区", vv.Area)
  323. }
  324. if vv.Budget != "" {
  325. content += fmt.Sprintf(htmlProjectOther, "预算金额", vv.Budget)
  326. }
  327. if vv.Bidendtime != "" {
  328. content += fmt.Sprintf(htmlProjectOther, "投标截止时间", vv.Bidendtime)
  329. }
  330. if vv.Detail != "" {
  331. content += fmt.Sprintf(htmlProjectOther, "项目建设内容", vv.Detail)
  332. }
  333. }
  334. content = fmt.Sprintf(g.Config().MustGet(ctx, "template.zcgs.content").String(), kk, garray.NewArrayFrom(vv.Zcgs.Purchasinglist.Pops(purchasingSize)).Join("、"), gconv.String(RetainDecimal(allBudget/10000, 2))+"万元") + htmlTopImg + content
  335. articles = append(articles, &Article{
  336. Title: fmt.Sprintf("%s_%s_招标标讯", ymd, kk),
  337. Content: fmt.Sprintf(htmlBody, content+htmlBottom),
  338. })
  339. }
  340. //
  341. if !vv.Yxsjgs.BidDataDetails.IsEmpty() {
  342. vv.Yxsjgs.BidDataDetails.Sort()
  343. content := fmt.Sprintf(g.Config().MustGet(ctx, "template.yxsjgs.content").String(), kk)
  344. content += htmlTopImg
  345. for k, v := range vv.Yxsjgs.BidDataDetails.PopLefts(g.Config().MustGet(ctx, "template.caiProjectSize").Int()) {
  346. if k > 0 {
  347. content += htmlProjectSpit
  348. }
  349. vv := v.(*BidDataDetail)
  350. content += fmt.Sprintf(htmlProjectName, NumToChinese(k+1), vv.ProjectName)
  351. content += fmt.Sprintf(htmlProjectOther, "项目地区", vv.Area)
  352. if vv.Buyer != "" {
  353. content += fmt.Sprintf(htmlProjectOther, "采购单位", vv.Buyer)
  354. }
  355. if vv.Budget != "" {
  356. content += fmt.Sprintf(htmlProjectOther, "预算金额", vv.Budget)
  357. }
  358. if vv.PurchaseTime != "" {
  359. content += fmt.Sprintf(htmlProjectOther, "预计采购时间", vv.PurchaseTime)
  360. }
  361. if vv.Detail != "" {
  362. content += fmt.Sprintf(htmlProjectOther, "需求概况", vv.Detail)
  363. }
  364. }
  365. articles = append(articles, &Article{
  366. Title: fmt.Sprintf("%s_%s_采购意向", ymd, kk),
  367. Content: fmt.Sprintf(htmlBody, content+htmlBottom),
  368. })
  369. }
  370. return true
  371. })
  372. //
  373. if len(articles) > 0 {
  374. list := g.List{}
  375. for _, v := range articles {
  376. list = append(list, g.Map{
  377. "article_type": "news",
  378. "title": v.Title,
  379. //"author":AUTHOR,
  380. //"digest":DIGEST,
  381. "content": v.Content,
  382. //"content_source_url":CONTENT_SOURCE_URL,
  383. "thumb_media_id": g.Config().MustGet(ctx, "mediaId").String(),
  384. //"need_open_comment":0,
  385. //"only_fans_can_comment":0,
  386. //"pic_crop_235_1":X1_Y1_X2_Y2,
  387. //"pic_crop_1_1":X1_Y1_X2_Y2
  388. })
  389. }
  390. g.Log().Info(ctx, "提交草稿数据", len(articles), "条", gconv.String(list))
  391. result, err := g.Client().ContentType("application/json").Post(ctx, "https://api.weixin.qq.com/cgi-bin/draft/add?access_token="+getAccessToken(ctx), g.Map{"articles": list})
  392. if err != nil {
  393. g.Log().Error(ctx, "提交草稿出错", err)
  394. return
  395. }
  396. defer result.Close()
  397. g.Log().Info(ctx, "新增草稿箱请求返回结果", result.ReadAllString())
  398. } else {
  399. g.Log().Info(ctx, "没有需要提交草稿的数据")
  400. }
  401. g.Log().Info(ctx, "新增草稿箱任务结束。。。")
  402. }
  403. func getAccessToken(ctx context.Context) string {
  404. code := g.Config().MustGet(ctx, "appid").String()
  405. var repl string
  406. client, err := rpc.DialHTTP("tcp", g.Config().MustGet(ctx, "wxTokenRpc").String())
  407. if err != nil {
  408. g.Log().Error(ctx, code, err)
  409. return repl
  410. }
  411. defer client.Close()
  412. err = client.Call("WxTokenRpc.GetAccessToken", code, &repl)
  413. if err != nil {
  414. g.Log().Error(ctx, code, err)
  415. return repl
  416. }
  417. if repl == "" {
  418. g.Log().Error(ctx, code, "未获取到accessToken")
  419. } else {
  420. g.Log().Info(ctx, code, "获取到accessToken", repl)
  421. }
  422. return repl
  423. }
  424. func filterHtml(text string) string {
  425. doc, err := html.Parse(strings.NewReader(text))
  426. if err != nil {
  427. text = regexp.MustCompile(`<[^>]+>`).ReplaceAllString(text, "")
  428. } else {
  429. var textBuilder strings.Builder
  430. var traverse func(*html.Node)
  431. traverse = func(n *html.Node) {
  432. if n.Type == html.TextNode {
  433. textBuilder.WriteString(n.Data)
  434. } else if n.Type == html.ElementNode && n.Data == "br" {
  435. textBuilder.WriteString("<br/>")
  436. }
  437. for c := n.FirstChild; c != nil; c = c.NextSibling {
  438. traverse(c)
  439. }
  440. }
  441. traverse(doc)
  442. text = textBuilder.String()
  443. }
  444. text = strings.ReplaceAll(text, `\t`, " ")
  445. text = strings.ReplaceAll(text, `\n`, " ")
  446. text = allSpaceReg.ReplaceAllString(text, " ")
  447. text = spaceReg.ReplaceAllString(text, " ")
  448. text = brReg.ReplaceAllString(text, "<br/>")
  449. text = strings.TrimSpace(text)
  450. return text
  451. }
  452. func detailCapture(text string) string {
  453. text = gstr.SubStrRune(text, 0, g.Config().MustGet(gctx.New(), "template.detailLen").Int())
  454. text = strings.TrimSuffix(text, "<br/")
  455. text = strings.TrimSuffix(text, "<br")
  456. text = strings.TrimSuffix(text, "<b")
  457. text = strings.TrimSuffix(text, "<")
  458. text = strings.TrimSpace(text)
  459. return text
  460. }