王山 8 年之前
当前提交
c1020bbb08
共有 100 个文件被更改,包括 16582 次插入0 次删除
  1. 10 0
      .gitignore
  2. 4 0
      README.md
  3. 1 0
      src/0f442ec701f99e220688f5e12b2c0118.txt
  4. 3136 0
      src/city.json
  5. 103 0
      src/config.json
  6. 28 0
      src/jfw/active/active.go
  7. 71 0
      src/jfw/active/saofu.go
  8. 478 0
      src/jfw/active/zqluckdraw.go
  9. 21 0
      src/jfw/active/zqluckdraw_test.go
  10. 12 0
      src/jfw/config/config.go
  11. 27 0
      src/jfw/config/zqluckdraw.go
  12. 6 0
      src/jfw/dataRepair/config.json
  13. 96 0
      src/jfw/dataRepair/main.go
  14. 24 0
      src/jfw/filter/filter.go
  15. 193 0
      src/jfw/filter/logfilter.go
  16. 16 0
      src/jfw/followpush/src/config.json
  17. 11 0
      src/jfw/followpush/src/config/config.go
  18. 71 0
      src/jfw/followpush/src/filterdata/filterdata.go
  19. 14 0
      src/jfw/followpush/src/followpush/config.json
  20. 138 0
      src/jfw/followpush/src/followpush/datastruct.go
  21. 127 0
      src/jfw/followpush/src/followpush/followpush.go
  22. 18 0
      src/jfw/followpush/src/followpush/followpush_test.go
  23. 369 0
      src/jfw/followpush/src/followpush/push.go
  24. 116 0
      src/jfw/followpush/src/listdb/listdb.go
  25. 47 0
      src/jfw/followpush/src/main.go
  26. 27 0
      src/jfw/followpush/src/main_test.go
  27. 122 0
      src/jfw/followpush/src/rpcpush/findData.go
  28. 31 0
      src/jfw/followpush/src/rpcpush/rpcpush.go
  29. 23 0
      src/jfw/followpush/src/timetask/clearredis.go
  30. 18 0
      src/jfw/followpush/src/tools/mongo.go
  31. 1 0
      src/jfw/followpush/src/tools/tools.go
  32. 76 0
      src/jfw/followpush/src/weixinrpc/weixinrpc.go
  33. 99 0
      src/jfw/front/dealinfo.go
  34. 832 0
      src/jfw/front/follow.go
  35. 886 0
      src/jfw/front/front.go
  36. 78 0
      src/jfw/front/jyshare.go
  37. 267 0
      src/jfw/front/pay.go
  38. 175 0
      src/jfw/front/shorturl.go
  39. 2073 0
      src/jfw/front/swordfish.go
  40. 81 0
      src/jfw/front/userlog.go
  41. 256 0
      src/jfw/front/wxkeyset.go
  42. 87 0
      src/jfw/jyutil/jyutil.go
  43. 87 0
      src/jfw/jyutil/sessionkeep.go
  44. 37 0
      src/jfw/jyutil/sort.go
  45. 0 0
      src/jfw/push/src/config.json
  46. 1 0
      src/jfw/push/src/filter.json
  47. 1 0
      src/jfw/push/src/fix.json
  48. 100 0
      src/jfw/push/src/main.go
  49. 2 0
      src/jfw/push/src/qfw/push/bid/bid.go
  50. 119 0
      src/jfw/push/src/qfw/push/bid/bid_test.go
  51. 28 0
      src/jfw/push/src/qfw/push/bid/bidpushjob.go
  52. 51 0
      src/jfw/push/src/qfw/push/bid/fixpushjob.go
  53. 138 0
      src/jfw/push/src/qfw/push/cache.go
  54. 84 0
      src/jfw/push/src/qfw/push/cache_test.go
  55. 142 0
      src/jfw/push/src/qfw/push/dfa/interestanalysis.go
  56. 45 0
      src/jfw/push/src/qfw/push/dfa/interestanalysis_test.go
  57. 315 0
      src/jfw/push/src/qfw/push/dopush/dopush.go
  58. 213 0
      src/jfw/push/src/qfw/push/dopush/dopushes.go
  59. 160 0
      src/jfw/push/src/qfw/push/dopush/dopushmgo
  60. 75 0
      src/jfw/push/src/qfw/push/dopush/filterdata.go
  61. 8 0
      src/jfw/push/src/qfw/push/job.go
  62. 101 0
      src/jfw/push/src/qfw/push/listdb/listdb.go
  63. 11 0
      src/jfw/push/src/qfw/push/pushconfig.go
  64. 34 0
      src/jfw/push/src/qfw/push/rpcpush/rpcpush.go
  65. 54 0
      src/jfw/push/src/qfw/push/weixincall.go
  66. 61 0
      src/jfw/push/src/qfw/push/wxcall_test.go
  67. 106 0
      src/jfw/rpcfollow/rpc.go
  68. 104 0
      src/jfw/tag/msg.go
  69. 11 0
      src/jfw/tag/tag.go
  70. 164 0
      src/jfw/timetask/fishindex.go
  71. 131 0
      src/jfw/timetask/followtimetask.go
  72. 7 0
      src/jfw/timetask/timetask.go
  73. 76 0
      src/jfw/tools/checkholiday.go
  74. 15 0
      src/jfw/tools/checkwxbrowser.go
  75. 83 0
      src/jfw/tools/extractarea.go
  76. 18 0
      src/jfw/tools/mongo.go
  77. 136 0
      src/jfw/tools/rpccall.go
  78. 2 0
      src/jfw/tools/tools.go
  79. 11 0
      src/jfw/tools/tools_test.go
  80. 26 0
      src/jfw/weixin/src/apiclient_cert.pem
  81. 28 0
      src/jfw/weixin/src/apiclient_key.pem
  82. 53 0
      src/jfw/weixin/src/config.json
  83. 11 0
      src/jfw/weixin/src/config/config.go
  84. 22 0
      src/jfw/weixin/src/config/zqluckdraw.go
  85. 1189 0
      src/jfw/weixin/src/github.com/wizjin/weixin/weixin.go
  86. 106 0
      src/jfw/weixin/src/github.com/wizjin/weixin/wxssl.go
  87. 238 0
      src/jfw/weixin/src/jrpc/jrpc.go
  88. 54 0
      src/jfw/weixin/src/main.go
  89. 90 0
      src/jfw/weixin/src/oauth/oauth.go
  90. 19 0
      src/jfw/weixin/src/rootca.pem
  91. 76 0
      src/jfw/weixin/src/tools/checkholiday.go
  92. 18 0
      src/jfw/weixin/src/tools/mongo.go
  93. 10 0
      src/jfw/weixin/src/tools/redis.go
  94. 26 0
      src/jfw/weixin/src/tools/rpccall.go
  95. 1 0
      src/jfw/weixin/src/tools/tools.go
  96. 165 0
      src/jfw/weixin/src/wx/checkuser.go
  97. 130 0
      src/jfw/weixin/src/wx/checkwords.go
  98. 104 0
      src/jfw/weixin/src/wx/getcount.go
  99. 1170 0
      src/jfw/weixin/src/wx/wx.go
  100. 46 0
      src/jfw/weixin/src/wx/wxjssdk.go

+ 10 - 0
.gitignore

@@ -0,0 +1,10 @@
+pkg
+bin
+.project
+extend
+*/pkg
+*.exe
+*.log
+*/src/src
+*.data
+*/bin

+ 4 - 0
README.md

@@ -0,0 +1,4 @@
+剑鱼招标订阅
+微信和pc端功能
+weixin sdk https://github.com/wizjin/weixin
+web用xweb框架

+ 1 - 0
src/0f442ec701f99e220688f5e12b2c0118.txt

@@ -0,0 +1 @@
+0f442ec701f99e220688f5e12b2c0118

+ 3136 - 0
src/city.json

@@ -0,0 +1,3136 @@
+{
+    "city": [
+        "金银川镇",
+        "郑东新",
+        "信阳客整所",
+        "北屯",
+        "鼓楼",
+        "辽河",
+        "南大港",
+        "北京",
+        "密云",
+        "延庆",
+        "天津",
+        "宁河",
+        "静海",
+        "蓟县",
+        "河北",
+        "石家庄",
+        "井陉",
+        "正定",
+        "栾城",
+        "行唐",
+        "灵寿",
+        "高邑",
+        "深泽",
+        "赞皇",
+        "无极县",
+        "平山县",
+        "元氏",
+        "赵县",
+        "辛集市",
+        "藁城",
+        "晋州",
+        "新乐",
+        "鹿泉",
+        "唐山",
+        "滦县",
+        "滦南",
+        "乐亭",
+        "迁西",
+        "玉田",
+        "唐海",
+        "遵化",
+        "迁安市",
+        "秦皇岛",
+        "青龙县",
+        "青龙满族自治县",
+        "昌黎",
+        "抚宁",
+        "卢龙",
+        "邯郸",
+        "临漳",
+        "成安",
+        "大名县",
+        "涉县",
+        "磁县",
+        "肥乡",
+        "永年",
+        "邱县",
+        "鸡泽",
+        "广平县",
+        "馆陶",
+        "魏县",
+        "曲周",
+        "武安",
+        "邢台",
+        "临城",
+        "内丘",
+        "柏乡县",
+        "隆尧",
+        "任县",
+        "南和",
+        "宁晋",
+        "巨鹿",
+        "新河县",
+        "广宗",
+        "平乡",
+        "威县",
+        "清河",
+        "临西",
+        "南宫",
+        "沙河市",
+        "保定",
+        "满城",
+        "清苑",
+        "涞水",
+        "阜平",
+        "徐水",
+        "定兴",
+        "唐县",
+        "高阳",
+        "容城",
+        "涞源",
+        "望都",
+        "安新",
+        "易县",
+        "曲阳",
+        "蠡县",
+        "顺平",
+        "博野",
+        "雄县",
+        "涿州",
+        "定州",
+        "安国",
+        "高碑店",
+        "张家口",
+        "宣化",
+        "张北",
+        "康保",
+        "沽源",
+        "尚义",
+        "蔚县",
+        "阳原",
+        "怀安",
+        "万全县",
+        "怀来",
+        "涿鹿",
+        "赤城",
+        "崇礼",
+        "承德",
+        "兴隆",
+        "平泉县",
+        "滦平",
+        "隆化",
+        "丰宁",
+        "宽城满族自治县",
+        "宽城县",
+        "围场",
+        "沧州",
+        "沧县",
+        "青县",
+        "东光县",
+        "海兴",
+        "盐山",
+        "肃宁",
+        "南皮",
+        "吴桥县",
+        "献县",
+        "孟村回族自治县",
+        "孟村县",
+        "泊头",
+        "任丘",
+        "黄骅",
+        "河间市",
+        "廊坊",
+        "固安",
+        "永清",
+        "香河",
+        "大城县",
+        "文安",
+        "大厂回族自治县",
+        "大厂县",
+        "霸州",
+        "三河",
+        "衡水",
+        "枣强",
+        "武邑",
+        "武强",
+        "饶阳",
+        "安平",
+        "故城县",
+        "景县",
+        "阜城县",
+        "冀州",
+        "深州",
+        "山西",
+        "太原",
+        "清徐",
+        "阳曲",
+        "娄烦",
+        "古交",
+        "大同市",
+        "阳高",
+        "天镇",
+        "广灵",
+        "灵丘",
+        "浑源",
+        "左云",
+        "阳泉",
+        "平定",
+        "盂县",
+        "长治",
+        "襄垣",
+        "屯留",
+        "平顺",
+        "黎城",
+        "壶关",
+        "长子",
+        "武乡",
+        "沁县",
+        "沁源",
+        "潞城",
+        "晋城",
+        "沁水",
+        "阳城",
+        "陵川",
+        "泽州",
+        "高平",
+        "朔州",
+        "山阴",
+        "应县",
+        "右玉",
+        "怀仁",
+        "晋中",
+        "榆社",
+        "左权县",
+        "和顺",
+        "昔阳",
+        "寿阳",
+        "太谷",
+        "祁县",
+        "平遥",
+        "灵石",
+        "介休",
+        "运城",
+        "临猗",
+        "万荣",
+        "闻喜",
+        "稷山",
+        "新绛",
+        "绛县",
+        "垣曲",
+        "夏县",
+        "平陆",
+        "芮城",
+        "永济",
+        "河津市",
+        "忻州",
+        "定襄",
+        "五台县",
+        "代县",
+        "繁峙",
+        "宁武",
+        "静乐",
+        "神池",
+        "五寨",
+        "岢岚",
+        "河曲",
+        "保德",
+        "偏关",
+        "原平",
+        "临汾",
+        "曲沃",
+        "翼城",
+        "襄汾",
+        "洪洞",
+        "古县",
+        "安泽",
+        "浮山",
+        "吉县",
+        "乡宁",
+        "大宁",
+        "隰县",
+        "永和",
+        "蒲县",
+        "汾西",
+        "侯马",
+        "霍州",
+        "吕梁",
+        "文水",
+        "交城",
+        "兴县",
+        "临县",
+        "柳林",
+        "石楼",
+        "岚县",
+        "方山县",
+        "中阳县",
+        "交口县",
+        "孝义市",
+        "汾阳",
+        "内蒙古",
+        "呼和浩特",
+        "土默特左旗",
+        "托克托",
+        "和林格尔",
+        "清水河",
+        "武川",
+        "包头",
+        "土默特右旗",
+        "固阳",
+        "达尔罕茂明安联合旗",
+        "乌海",
+        "赤峰",
+        "阿鲁科尔沁旗",
+        "巴林左旗",
+        "巴林右旗",
+        "林西",
+        "克什克腾旗",
+        "翁牛特旗",
+        "喀喇沁旗",
+        "宁城",
+        "敖汉旗",
+        "通辽",
+        "科尔沁左翼中旗",
+        "科尔沁左翼后旗",
+        "开鲁",
+        "库伦旗",
+        "奈曼旗",
+        "扎鲁特旗",
+        "霍林郭勒",
+        "鄂尔多斯",
+        "达拉特旗",
+        "准格尔旗",
+        "鄂托克前旗",
+        "鄂托克旗",
+        "杭锦旗",
+        "乌审旗",
+        "伊金霍洛旗",
+        "呼伦贝尔",
+        "阿荣旗",
+        "莫力达瓦达斡尔族自治旗",
+        "鄂伦春自治旗",
+        "鄂温克族自治旗",
+        "陈巴尔虎旗",
+        "新巴尔虎左旗",
+        "新巴尔虎右旗",
+        "满洲里",
+        "牙克石",
+        "扎兰屯",
+        "额尔古纳",
+        "根河",
+        "巴彦淖尔",
+        "五原",
+        "磴口",
+        "乌拉特前旗",
+        "乌拉特中旗",
+        "乌拉特后旗",
+        "杭锦后旗",
+        "乌兰察布",
+        "卓资",
+        "化德",
+        "商都县",
+        "兴和县",
+        "凉城",
+        "察哈尔右翼前旗",
+        "察哈尔右翼中旗",
+        "察哈尔右翼后旗",
+        "四子王旗",
+        "丰镇",
+        "兴安盟",
+        "乌兰浩特",
+        "阿尔山",
+        "科尔沁右翼前旗",
+        "科尔沁右翼中旗",
+        "扎赉特旗",
+        "突泉",
+        "锡林郭勒盟",
+        "二连浩特",
+        "锡林浩特",
+        "阿巴嘎旗",
+        "苏尼特左旗",
+        "苏尼特右旗",
+        "东乌珠穆沁旗",
+        "西乌珠穆沁旗",
+        "太仆寺旗",
+        "镶黄旗",
+        "正镶白旗",
+        "正蓝旗",
+        "多伦",
+        "阿拉善盟",
+        "阿拉善左旗",
+        "阿拉善右旗",
+        "额济纳旗",
+        "辽宁",
+        "沈阳",
+        "辽中",
+        "康平",
+        "法库",
+        "新民市",
+        "大连",
+        "长海",
+        "瓦房店",
+        "普兰店",
+        "庄河",
+        "鞍山",
+        "台安县",
+        "岫岩",
+        "海城市",
+        "抚顺",
+        "新宾",
+        "清原",
+        "本溪",
+        "桓仁",
+        "丹东",
+        "宽甸",
+        "东港市",
+        "凤城",
+        "锦州",
+        "黑山县",
+        "义县",
+        "凌海",
+        "北镇",
+        "营口",
+        "盖州",
+        "大石桥市",
+        "阜新",
+        "彰武",
+        "辽阳",
+        "灯塔市",
+        "盘锦",
+        "大洼",
+        "盘山",
+        "铁岭",
+        "西丰",
+        "昌图",
+        "调兵山",
+        "开原",
+        "朝阳市",
+        "建平",
+        "喀喇沁",
+        "北票",
+        "凌源",
+        "葫芦岛",
+        "绥中",
+        "建昌",
+        "兴城",
+        "吉林",
+        "长春",
+        "农安",
+        "九台",
+        "榆树",
+        "德惠",
+        "吉林",
+        "永吉",
+        "蛟河",
+        "桦甸",
+        "舒兰",
+        "磐石",
+        "四平",
+        "梨树县",
+        "伊通",
+        "公主岭",
+        "双辽",
+        "辽源",
+        "东丰",
+        "东辽",
+        "通化",
+        "辉南",
+        "柳河",
+        "梅河口",
+        "集安",
+        "白山市",
+        "抚松",
+        "靖宇",
+        "长白",
+        "临江",
+        "松原",
+        "前郭尔罗斯",
+        "长岭",
+        "乾安",
+        "扶余",
+        "白城",
+        "镇赉",
+        "通榆",
+        "洮南",
+        "大安市",
+        "延边州",
+        "延吉",
+        "图们",
+        "敦化",
+        "珲春",
+        "龙井市",
+        "和龙",
+        "汪清",
+        "安图县",
+        "黑龙江",
+        "哈尔滨",
+        "依兰县",
+        "方正县",
+        "宾县",
+        "巴彦",
+        "木兰县",
+        "通河县",
+        "延寿",
+        "双城",
+        "尚志",
+        "五常市",
+        "齐齐哈尔",
+        "龙江县",
+        "依安",
+        "泰来",
+        "甘南",
+        "富裕县",
+        "克山县",
+        "克东县",
+        "拜泉",
+        "讷河",
+        "鸡西市",
+        "鸡东县",
+        "虎林",
+        "密山",
+        "鹤岗",
+        "萝北",
+        "绥滨",
+        "双鸭山",
+        "集贤",
+        "友谊县",
+        "宝清",
+        "饶河",
+        "大庆",
+        "肇州",
+        "肇源",
+        "林甸",
+        "杜尔伯特",
+        "伊春市",
+        "嘉荫",
+        "铁力",
+        "佳木斯",
+        "桦南",
+        "桦川",
+        "汤原",
+        "抚远",
+        "同江市",
+        "富锦",
+        "七台河",
+        "勃利",
+        "牡丹江",
+        "东宁",
+        "林口",
+        "绥芬河",
+        "海林",
+        "宁安",
+        "穆棱",
+        "黑河",
+        "嫩江",
+        "逊克",
+        "孙吴",
+        "北安",
+        "五大连池",
+        "绥化",
+        "望奎",
+        "兰西",
+        "青冈",
+        "庆安",
+        "明水",
+        "绥棱",
+        "安达",
+        "肇东",
+        "海伦",
+        "呼玛",
+        "塔河",
+        "漠河",
+        "上海",
+        "崇明",
+        "江苏",
+        "南京",
+        "溧水",
+        "高淳",
+        "无锡",
+        "江阴",
+        "宜兴",
+        "徐州",
+        "丰县",
+        "沛县",
+        "铜山",
+        "睢宁",
+        "新沂",
+        "邳州",
+        "常州",
+        "溧阳",
+        "金坛",
+        "苏州",
+        "常熟",
+        "张家港",
+        "昆山",
+        "吴江",
+        "太仓",
+        "南通",
+        "海安",
+        "如东",
+        "启东",
+        "如皋",
+        "通州",
+        "海门",
+        "连云港",
+        "赣榆",
+        "东海",
+        "灌云",
+        "灌南",
+        "淮安",
+        "涟水",
+        "洪泽",
+        "洪泽县",
+        "盱眙",
+        "金湖",
+        "盐城",
+        "响水",
+        "滨海县",
+        "阜宁",
+        "射阳",
+        "建湖",
+        "东台",
+        "大丰",
+        "扬州",
+        "宝应",
+        "仪征",
+        "高邮",
+        "江都",
+        "镇江",
+        "丹阳",
+        "扬中",
+        "句容",
+        "泰州",
+        "兴化",
+        "靖江",
+        "泰兴市",
+        "泰兴镇",
+        "姜堰",
+        "宿迁",
+        "沭阳",
+        "泗阳",
+        "泗洪",
+        "浙江",
+        "杭州",
+        "桐庐",
+        "淳安",
+        "建德",
+        "富阳",
+        "临安",
+        "宁波",
+        "象山",
+        "宁海",
+        "余姚",
+        "慈溪",
+        "奉化",
+        "温州",
+        "洞头",
+        "永嘉",
+        "平阳",
+        "苍南",
+        "文成县",
+        "泰顺",
+        "瑞安",
+        "乐清",
+        "嘉兴",
+        "嘉善",
+        "海盐",
+        "海宁",
+        "平湖",
+        "桐乡",
+        "湖州",
+        "德清",
+        "长兴",
+        "安吉",
+        "绍兴",
+        "新昌",
+        "诸暨",
+        "上虞",
+        "嵊州",
+        "金华",
+        "武义",
+        "浦江",
+        "磐安",
+        "兰溪",
+        "义乌",
+        "东阳",
+        "永康",
+        "衢州",
+        "常山",
+        "开化",
+        "龙游",
+        "江山市",
+        "舟山",
+        "岱山",
+        "嵊泗",
+        "台州",
+        "玉环",
+        "三门县",
+        "天台县",
+        "仙居",
+        "温岭",
+        "临海",
+        "丽水",
+        "青田",
+        "缙云",
+        "遂昌",
+        "松阳",
+        "云和",
+        "庆元",
+        "景宁",
+        "龙泉市",
+        "安徽",
+        "合肥",
+        "长丰",
+        "肥东",
+        "肥西",
+        "芜湖",
+        "繁昌",
+        "南陵",
+        "蚌埠",
+        "怀远",
+        "五河",
+        "固镇",
+        "淮南",
+        "凤台",
+        "马鞍山",
+        "当涂",
+        "含山",
+        "和县",
+        "淮北",
+        "濉溪",
+        "铜陵",
+        "安庆",
+        "怀宁",
+        "枞阳",
+        "潜山",
+        "太湖县",
+        "宿松",
+        "望江",
+        "岳西",
+        "桐城",
+        "黄山",
+        "歙县",
+        "休宁",
+        "黟县",
+        "祁门",
+        "滁州",
+        "来安",
+        "全椒",
+        "定远",
+        "凤阳",
+        "天长市",
+        "明光市",
+        "阜阳",
+        "临泉",
+        "太和县",
+        "阜南",
+        "颍上",
+        "界首",
+        "宿州",
+        "砀山",
+        "萧县",
+        "灵璧",
+        "泗县",
+        "巢湖",
+        "庐江",
+        "无为县",
+        "六安",
+        "寿县",
+        "霍邱",
+        "舒城",
+        "金寨",
+        "霍山",
+        "亳州",
+        "涡阳",
+        "蒙城",
+        "利辛",
+        "池州",
+        "东至县",
+        "石台",
+        "青阳",
+        "宣城",
+        "郎溪",
+        "广德",
+        "泾县",
+        "绩溪",
+        "旌德",
+        "宁国市",
+        "福建",
+        "福州",
+        "闽侯",
+        "连江",
+        "罗源",
+        "闽清",
+        "永泰县",
+        "平潭",
+        "福清",
+        "长乐",
+        "厦门",
+        "莆田",
+        "仙游",
+        "三明",
+        "明溪",
+        "清流",
+        "宁化",
+        "大田",
+        "尤溪",
+        "沙县",
+        "将乐",
+        "泰宁",
+        "建宁",
+        "永安",
+        "泉州",
+        "惠安县",
+        "安溪",
+        "永春",
+        "德化",
+        "金门",
+        "石狮",
+        "晋江",
+        "南安",
+        "漳州",
+        "云霄",
+        "漳浦",
+        "诏安",
+        "长泰",
+        "东山县",
+        "南靖",
+        "平和县",
+        "华安",
+        "龙海",
+        "南平",
+        "顺昌",
+        "浦城",
+        "光泽",
+        "松溪",
+        "政和县",
+        "邵武",
+        "武夷山",
+        "建瓯",
+        "建阳",
+        "龙岩",
+        "长汀",
+        "永定",
+        "上杭",
+        "武平",
+        "连城",
+        "漳平",
+        "宁德",
+        "霞浦",
+        "古田",
+        "屏南",
+        "寿宁",
+        "周宁",
+        "柘荣",
+        "福安",
+        "福鼎",
+        "江西",
+        "南昌",
+        "新建",
+        "安义",
+        "进贤",
+        "景德镇",
+        "浮梁",
+        "乐平市",
+        "萍乡",
+        "莲花",
+        "上栗",
+        "芦溪",
+        "九江市",
+        "武宁",
+        "修水县",
+        "永修",
+        "德安",
+        "星子县",
+        "都昌",
+        "湖口县",
+        "彭泽",
+        "瑞昌",
+        "新余",
+        "分宜",
+        "鹰潭",
+        "余江",
+        "贵溪",
+        "赣州",
+        "赣县",
+        "信丰",
+        "大余",
+        "上犹",
+        "崇义",
+        "安远",
+        "龙南县",
+        "定南",
+        "全南",
+        "宁都",
+        "于都",
+        "兴国县",
+        "会昌",
+        "寻乌",
+        "石城",
+        "瑞金",
+        "南康",
+        "吉安",
+        "吉水",
+        "峡江",
+        "新干",
+        "永丰",
+        "泰和县",
+        "遂川",
+        "万安县",
+        "安福县",
+        "永新县",
+        "井冈山",
+        "宜春",
+        "奉新",
+        "万载县",
+        "上高",
+        "宜丰",
+        "靖安",
+        "铜鼓",
+        "丰城",
+        "樟树",
+        "高安",
+        "抚州",
+        "南城",
+        "黎川",
+        "南丰",
+        "崇仁",
+        "乐安",
+        "宜黄",
+        "金溪",
+        "资溪",
+        "东乡县",
+        "广昌",
+        "上饶",
+        "广丰",
+        "玉山",
+        "铅山",
+        "横峰",
+        "弋阳",
+        "余干",
+        "鄱阳",
+        "万年县",
+        "婺源",
+        "德兴",
+        "山东",
+        "济南",
+        "平阴",
+        "济阳",
+        "商河县",
+        "章丘",
+        "青岛",
+        "胶州",
+        "即墨",
+        "平度市",
+        "胶南",
+        "莱西",
+        "淄博",
+        "桓台",
+        "高青",
+        "沂源",
+        "枣庄",
+        "滕州",
+        "东营市",
+        "东营",
+        "垦利",
+        "利津",
+        "广饶",
+        "烟台",
+        "长岛县",
+        "龙口市",
+        "莱阳",
+        "莱州",
+        "蓬莱",
+        "招远",
+        "栖霞市",
+        "海阳",
+        "潍坊",
+        "临朐",
+        "昌乐",
+        "青州",
+        "诸城",
+        "寿光",
+        "安丘",
+        "高密",
+        "昌邑市",
+        "济宁",
+        "微山",
+        "鱼台",
+        "金乡",
+        "嘉祥",
+        "汶上",
+        "泗水",
+        "梁山",
+        "曲阜",
+        "兖州",
+        "邹城",
+        "泰安",
+        "宁阳",
+        "东平",
+        "新泰",
+        "肥城",
+        "威海",
+        "文登",
+        "荣成",
+        "乳山",
+        "日照",
+        "五莲",
+        "莒县",
+        "莱芜",
+        "临沂",
+        "沂南",
+        "郯城",
+        "沂水",
+        "苍山",
+        "费县",
+        "平邑",
+        "莒南",
+        "蒙阴",
+        "临沭",
+        "德州",
+        "陵县",
+        "宁津",
+        "庆云",
+        "临邑",
+        "齐河",
+        "平原",
+        "夏津",
+        "武城",
+        "乐陵",
+        "禹城",
+        "聊城",
+        "阳谷",
+        "莘县",
+        "茌平",
+        "东阿",
+        "冠县",
+        "高唐",
+        "临清",
+        "滨州",
+        "惠民",
+        "阳信",
+        "无棣",
+        "沾化",
+        "博兴",
+        "邹平",
+        "菏泽",
+        "曹县",
+        "单县",
+        "成武",
+        "巨野",
+        "郓城",
+        "鄄城",
+        "定陶",
+        "东明县",
+        "河南",
+        "郑州",
+        "管城",
+        "中牟",
+        "巩义",
+        "荥阳",
+        "新密",
+        "新郑",
+        "登封",
+        "开封",
+        "杞县",
+        "通许",
+        "尉氏",
+        "兰考",
+        "洛阳",
+        "孟津",
+        "新安",
+        "栾川",
+        "嵩县",
+        "汝阳",
+        "宜阳",
+        "洛宁",
+        "伊川",
+        "偃师",
+        "平顶山",
+        "宝丰",
+        "叶县",
+        "鲁山",
+        "郏县",
+        "舞钢",
+        "汝州",
+        "安阳",
+        "汤阴",
+        "滑县",
+        "内黄",
+        "林州",
+        "鹤壁",
+        "浚县",
+        "淇县",
+        "新乡",
+        "获嘉",
+        "原阳",
+        "延津",
+        "封丘",
+        "长垣",
+        "卫辉",
+        "辉市",
+        "焦作",
+        "修武",
+        "博爱",
+        "武陟",
+        "温县",
+        "沁阳",
+        "孟州",
+        "濮阳",
+        "清丰",
+        "南乐",
+        "范县",
+        "台前",
+        "许昌",
+        "鄢陵",
+        "襄城县",
+        "禹州",
+        "长葛",
+        "漯河",
+        "舞阳",
+        "临颍",
+        "三门峡",
+        "渑池",
+        "陕县",
+        "卢氏",
+        "义马",
+        "灵宝",
+        "南阳",
+        "南召",
+        "方城",
+        "西峡",
+        "镇平县",
+        "内乡",
+        "淅川",
+        "社旗",
+        "唐河",
+        "新野",
+        "桐柏",
+        "邓州",
+        "商丘",
+        "民权",
+        "睢县",
+        "宁陵",
+        "柘城",
+        "虞城",
+        "夏邑",
+        "永城",
+        "信阳",
+        "罗山",
+        "光山",
+        "新县",
+        "商城",
+        "固始",
+        "潢川",
+        "淮滨",
+        "息县",
+        "周口",
+        "扶沟",
+        "西华",
+        "商水",
+        "沈丘",
+        "郸城",
+        "淮阳",
+        "太康",
+        "鹿邑",
+        "项城",
+        "驻马店",
+        "西平",
+        "上蔡",
+        "平舆",
+        "正阳",
+        "确山",
+        "泌阳",
+        "汝南",
+        "遂平",
+        "新蔡",
+        "济源",
+        "湖北",
+        "武汉",
+        "黄石市",
+        "阳新县",
+        "大冶",
+        "十堰",
+        "郧县",
+        "郧西",
+        "竹山",
+        "竹溪",
+        "房县",
+        "丹江口",
+        "宜昌",
+        "远安",
+        "兴山",
+        "秭归",
+        "长阳",
+        "五峰",
+        "宜都",
+        "当阳",
+        "枝江",
+        "襄阳",
+        "南漳",
+        "谷城",
+        "保康",
+        "老河口",
+        "枣阳",
+        "宜城",
+        "鄂州",
+        "荆门",
+        "京山县",
+        "沙洋",
+        "钟祥",
+        "屈家岭",
+        "孝感",
+        "孝昌",
+        "大悟",
+        "云梦",
+        "应城",
+        "安陆",
+        "汉川",
+        "荆州",
+        "公安县",
+        "监利",
+        "江陵",
+        "石首",
+        "洪湖",
+        "松滋",
+        "黄冈市",
+        "团风县",
+        "红安",
+        "罗田",
+        "英山",
+        "浠水",
+        "蕲春",
+        "黄梅",
+        "麻城",
+        "武穴",
+        "咸宁",
+        "嘉鱼",
+        "通城",
+        "崇阳",
+        "通山",
+        "赤壁",
+        "随州",
+        "随县",
+        "广水",
+        "恩施州",
+        "恩施",
+        "利川",
+        "建始县",
+        "巴东",
+        "宣恩",
+        "咸丰",
+        "来凤",
+        "鹤峰",
+        "仙桃",
+        "潜江",
+        "天门市",
+        "神农架",
+        "江汉油田",
+        "湖南",
+        "长沙",
+        "望城",
+        "宁乡",
+        "浏阳",
+        "株洲",
+        "攸县",
+        "茶陵",
+        "炎陵",
+        "醴陵",
+        "湘潭",
+        "湘乡",
+        "韶山",
+        "衡阳",
+        "衡南",
+        "衡山",
+        "衡东",
+        "祁东",
+        "耒阳",
+        "常宁",
+        "邵阳",
+        "邵东",
+        "新邵",
+        "隆回",
+        "洞口县",
+        "绥宁",
+        "新宁县",
+        "城步",
+        "武冈",
+        "岳阳",
+        "华容县",
+        "湘阴",
+        "平江",
+        "汨罗",
+        "临湘",
+        "常德",
+        "安乡县",
+        "汉寿",
+        "澧县",
+        "临澧",
+        "桃源县",
+        "石门",
+        "津市市",
+        "张家界",
+        "慈利",
+        "桑植",
+        "益阳",
+        "南县",
+        "桃江",
+        "安化",
+        "沅江",
+        "郴州",
+        "桂阳",
+        "宜章",
+        "永兴",
+        "嘉禾",
+        "临武",
+        "汝城",
+        "桂东",
+        "安仁",
+        "资兴",
+        "永州",
+        "祁阳",
+        "东安县",
+        "双牌",
+        "道县",
+        "江永",
+        "宁远",
+        "蓝山",
+        "新田",
+        "江华",
+        "怀化",
+        "中方县",
+        "沅陵",
+        "辰溪",
+        "溆浦",
+        "会同",
+        "麻阳",
+        "新晃",
+        "芷江",
+        "靖州",
+        "通道县",
+        "通道侗族自治县",
+        "洪江",
+        "娄底",
+        "双峰",
+        "新化",
+        "冷水江",
+        "涟源",
+        "湘西",
+        "吉首",
+        "泸溪",
+        "凤凰县",
+        "花垣",
+        "保靖",
+        "古丈",
+        "永顺",
+        "龙山县",
+        "广东",
+        "广州",
+        "增城",
+        "从化",
+        "韶关",
+        "始兴",
+        "仁化",
+        "翁源",
+        "乳源",
+        "新丰",
+        "乐昌",
+        "南雄",
+        "深圳",
+        "珠海",
+        "汕头",
+        "南澳",
+        "佛山",
+        "江门市",
+        "台山市",
+        "开平市",
+        "鹤山市",
+        "恩平",
+        "湛江",
+        "遂溪",
+        "徐闻",
+        "廉江",
+        "雷州",
+        "吴川",
+        "茂名",
+        "电白",
+        "高州",
+        "化州",
+        "信宜",
+        "肇庆",
+        "广宁",
+        "怀集",
+        "封开",
+        "德庆",
+        "高要",
+        "四会市",
+        "惠州",
+        "博罗",
+        "惠东",
+        "龙门县",
+        "梅州",
+        "梅县",
+        "大埔",
+        "丰顺",
+        "五华县",
+        "平远",
+        "蕉岭",
+        "兴宁市",
+        "汕尾",
+        "海丰",
+        "陆河",
+        "陆丰",
+        "河源",
+        "紫金县",
+        "龙川",
+        "连平",
+        "和平县",
+        "东源",
+        "阳江市",
+        "阳西",
+        "阳东",
+        "阳春",
+        "清远",
+        "佛冈",
+        "阳山",
+        "连山县",
+        "连山壮族瑶族自治县",
+        "连南",
+        "清新",
+        "英德",
+        "连州",
+        "东莞",
+        "中山市",
+        "潮州",
+        "潮安",
+        "饶平",
+        "揭阳",
+        "揭东",
+        "揭西",
+        "惠来",
+        "普宁",
+        "云浮",
+        "新兴",
+        "郁南",
+        "云安",
+        "罗定",
+        "广西",
+        "南宁",
+        "武鸣",
+        "隆安",
+        "马山",
+        "上林",
+        "宾阳",
+        "横县",
+        "柳州",
+        "柳江",
+        "柳城",
+        "鹿寨",
+        "融安",
+        "融水",
+        "三江侗族自治县",
+        "三江县",
+        "桂林",
+        "阳朔",
+        "临桂",
+        "灵川",
+        "全州",
+        "兴安",
+        "永福",
+        "灌阳",
+        "龙胜",
+        "资源县",
+        "平乐",
+        "荔蒲",
+        "恭城",
+        "梧州",
+        "苍梧",
+        "藤县",
+        "蒙山",
+        "岑溪",
+        "北海市",
+        "北海经济开发",
+        "合浦",
+        "防城港",
+        "上思",
+        "东兴市",
+        "钦州",
+        "灵山县",
+        "浦北",
+        "贵港",
+        "平南",
+        "桂平",
+        "玉林",
+        "容县",
+        "陆川",
+        "博白",
+        "兴业县",
+        "北流",
+        "百色",
+        "田阳",
+        "田东",
+        "平果县",
+        "德保",
+        "靖西",
+        "那坡",
+        "凌云县",
+        "乐业县",
+        "田林县",
+        "西林",
+        "隆林",
+        "贺州",
+        "昭平",
+        "钟山县",
+        "富川",
+        "河池",
+        "南丹",
+        "天峨",
+        "凤山",
+        "东兰",
+        "罗城",
+        "环江",
+        "巴马",
+        "都安",
+        "大化",
+        "宜州",
+        "来宾",
+        "忻城",
+        "象州",
+        "武宣",
+        "金秀",
+        "合山",
+        "崇左",
+        "扶绥",
+        "宁明",
+        "龙州",
+        "大新县",
+        "天等",
+        "凭祥",
+        "海南",
+        "三沙",
+        "海口",
+        "三亚",
+        "五指山",
+        "琼海",
+        "儋州",
+        "文昌",
+        "万宁",
+        "东方市",
+        "定安",
+        "屯昌",
+        "澄迈",
+        "临高",
+        "白沙县",
+        "白沙黎族自治县",
+        "昌江自治县",
+        "乐东",
+        "陵水",
+        "保亭",
+        "琼中",
+        "重庆",
+        "綦江",
+        "潼南",
+        "铜梁",
+        "大足",
+        "荣昌",
+        "璧山",
+        "梁平",
+        "城口县",
+        "丰都",
+        "垫江",
+        "武隆",
+        "忠县",
+        "开县",
+        "云阳",
+        "奉节",
+        "巫山",
+        "巫溪",
+        "石柱土家族自治",
+        "秀山土家族苗族自治",
+        "酉阳土家族苗族自治",
+        "彭水苗族土家族自治",
+        "四川",
+        "成都",
+        "金堂",
+        "双流",
+        "郫县",
+        "大邑",
+        "蒲江",
+        "新津",
+        "都江堰",
+        "彭州",
+        "邛崃",
+        "崇州",
+        "自贡",
+        "荣县",
+        "富顺",
+        "攀枝花",
+        "米易",
+        "盐边",
+        "泸州",
+        "泸县",
+        "合江",
+        "叙永",
+        "古蔺",
+        "南部县",
+        "德阳",
+        "中江",
+        "罗江",
+        "广汉",
+        "什邡",
+        "绵竹",
+        "绵阳",
+        "三台县",
+        "盐亭",
+        "安县",
+        "梓潼",
+        "北川羌族自治",
+        "平武",
+        "江油",
+        "广元",
+        "旺苍",
+        "青川",
+        "剑阁",
+        "苍溪",
+        "遂宁",
+        "蓬溪",
+        "射洪",
+        "大英",
+        "内江",
+        "威远",
+        "资中",
+        "隆昌",
+        "乐山",
+        "犍为",
+        "井研",
+        "夹江",
+        "沐川",
+        "峨边彝族自治",
+        "马边彝族自治",
+        "峨眉山",
+        "南充",
+        "营山",
+        "蓬安",
+        "仪陇",
+        "西充",
+        "阆中",
+        "眉山",
+        "仁寿",
+        "彭山",
+        "洪雅",
+        "丹棱",
+        "青神",
+        "宜宾",
+        "南溪",
+        "江安",
+        "长宁县",
+        "高县",
+        "珙县",
+        "筠连",
+        "兴文",
+        "屏山",
+        "广安",
+        "岳池",
+        "武胜",
+        "邻水",
+        "华蓥",
+        "达州",
+        "达县",
+        "宣汉",
+        "开江县",
+        "大竹县",
+        "渠县",
+        "万源市",
+        "雅安市",
+        "名山县",
+        "荥经",
+        "汉源",
+        "石棉县",
+        "天全",
+        "芦山",
+        "宝兴",
+        "巴中",
+        "通江",
+        "南江县",
+        "平昌",
+        "资阳",
+        "安岳",
+        "乐至县",
+        "简阳",
+        "阿坝州",
+        "汶川",
+        "理县",
+        "茂县",
+        "松潘",
+        "九寨沟",
+        "金川县",
+        "小金县",
+        "黑水县",
+        "马尔康",
+        "壤塘",
+        "阿坝",
+        "若尔盖",
+        "红原",
+        "甘孜",
+        "康定",
+        "泸定",
+        "丹巴",
+        "九龙县",
+        "雅江",
+        "道孚",
+        "炉霍",
+        "新龙",
+        "德格",
+        "白玉县",
+        "石渠",
+        "色达",
+        "理塘",
+        "巴塘",
+        "乡城县",
+        "稻城",
+        "得荣",
+        "凉山",
+        "西昌",
+        "木里",
+        "盐源",
+        "德昌",
+        "会理",
+        "会东",
+        "宁南",
+        "普格",
+        "布拖",
+        "金阳",
+        "昭觉",
+        "喜德",
+        "冕宁",
+        "越西",
+        "甘洛",
+        "美姑",
+        "雷波",
+        "贵州",
+        "贵阳",
+        "开阳",
+        "息烽",
+        "修文",
+        "清镇",
+        "六盘水",
+        "水城",
+        "盘县",
+        "遵义",
+        "桐梓",
+        "绥阳",
+        "正安",
+        "道真",
+        "务川",
+        "凤冈",
+        "湄潭",
+        "余庆",
+        "习水",
+        "赤水",
+        "仁怀",
+        "安顺",
+        "平坝",
+        "普定",
+        "镇宁",
+        "关岭",
+        "紫云县",
+        "紫云苗族布依族自治县",
+        "毕节",
+        "大方县",
+        "黔西",
+        "金沙县",
+        "织金",
+        "纳雍",
+        "威宁",
+        "赫章",
+        "铜仁",
+        "江口县",
+        "玉屏",
+        "石阡",
+        "思南",
+        "印江",
+        "德江",
+        "沿河",
+        "松桃",
+        "黔西南",
+        "兴义",
+        "兴仁",
+        "普安",
+        "晴隆",
+        "贞丰",
+        "望谟",
+        "册亨",
+        "安龙县",
+        "黔东南",
+        "凯里市",
+        "黄平县",
+        "施秉",
+        "三穗",
+        "镇远县",
+        "岑巩",
+        "天柱",
+        "锦屏",
+        "剑河",
+        "台江县",
+        "台江",
+        "黎平",
+        "榕江",
+        "从江",
+        "雷山",
+        "麻江",
+        "丹寨",
+        "黔南布",
+        "都匀",
+        "福泉",
+        "荔波",
+        "贵定",
+        "瓮安",
+        "独山县",
+        "平塘",
+        "罗甸",
+        "长顺",
+        "龙里",
+        "惠水",
+        "三都",
+        "云南",
+        "昆明",
+        "呈贡",
+        "晋宁",
+        "富民县",
+        "宜良",
+        "石林",
+        "嵩明",
+        "禄劝",
+        "寻甸",
+        "安宁",
+        "曲靖",
+        "马龙",
+        "陆良",
+        "师宗",
+        "罗平县",
+        "富源",
+        "会泽",
+        "沾益",
+        "宣威",
+        "玉溪",
+        "江川",
+        "澄江",
+        "通海",
+        "华宁",
+        "易门",
+        "峨山",
+        "新平",
+        "元江",
+        "保山",
+        "施甸",
+        "腾冲",
+        "龙陵",
+        "昌宁",
+        "昭通",
+        "鲁甸",
+        "巧家",
+        "盐津",
+        "大关县",
+        "永善",
+        "绥江",
+        "镇雄",
+        "彝良",
+        "威信",
+        "水富",
+        "丽江",
+        "玉龙县",
+        "玉龙纳西族自治县",
+        "永胜",
+        "华坪",
+        "宁蒗",
+        "普洱",
+        "宁洱",
+        "墨江",
+        "景东县",
+        "景东彝族自治县",
+        "景谷",
+        "镇沅",
+        "江城自治县",
+        "江城县",
+        "孟连",
+        "澜沧",
+        "西盟",
+        "临沧",
+        "凤庆",
+        "云县",
+        "永德",
+        "镇康",
+        "双江",
+        "耿马",
+        "沧源",
+        "楚雄",
+        "双柏",
+        "牟定",
+        "南华",
+        "姚安",
+        "大姚",
+        "永仁",
+        "元谋",
+        "武定",
+        "禄丰",
+        "红河",
+        "个旧",
+        "开远",
+        "蒙自",
+        "屏边",
+        "建水",
+        "石屏",
+        "弥勒",
+        "泸西",
+        "元阳",
+        "金平自治县",
+        "金平县",
+        "绿春",
+        "河口自治县",
+        "文山",
+        "砚山",
+        "西畴",
+        "麻栗坡",
+        "马关县",
+        "丘北县",
+        "广南",
+        "富宁",
+        "西双版纳",
+        "景洪",
+        "勐海",
+        "勐腊",
+        "大理白族自治州",
+        "大理州",
+        "大理市",
+        "漾濞",
+        "祥云",
+        "宾川",
+        "弥渡",
+        "南涧",
+        "巍山",
+        "永平",
+        "云龙",
+        "洱源",
+        "剑川",
+        "鹤庆",
+        "德宏",
+        "瑞丽",
+        "潞西",
+        "梁河",
+        "盈江",
+        "陇川",
+        "怒江",
+        "泸水",
+        "福贡",
+        "贡山",
+        "兰坪",
+        "迪庆",
+        "香格里拉",
+        "德钦",
+        "维西",
+        "西藏",
+        "拉萨",
+        "林周",
+        "当雄",
+        "尼木",
+        "曲水",
+        "堆龙德庆",
+        "达孜",
+        "墨竹工卡",
+        "昌都",
+        "江达",
+        "贡觉",
+        "类乌齐",
+        "丁青",
+        "察雅",
+        "八宿",
+        "左贡",
+        "芒康",
+        "洛隆",
+        "边坝",
+        "乃东",
+        "扎囊",
+        "贡嘎",
+        "桑日",
+        "琼结",
+        "曲松",
+        "措美",
+        "洛扎",
+        "加查",
+        "隆子",
+        "错那",
+        "浪卡子",
+        "日喀则",
+        "南木林",
+        "江孜",
+        "定日",
+        "萨迦",
+        "拉孜",
+        "昂仁",
+        "谢通门",
+        "白朗",
+        "仁布",
+        "康马",
+        "定结",
+        "仲巴",
+        "亚东",
+        "吉隆",
+        "聂拉木",
+        "萨嘎",
+        "岗巴",
+        "那曲",
+        "嘉黎",
+        "比如县",
+        "聂荣县",
+        "安多县",
+        "申扎",
+        "索县",
+        "班戈",
+        "巴青",
+        "尼玛",
+        "普兰",
+        "札达",
+        "噶尔",
+        "日土",
+        "革吉",
+        "改则",
+        "措勤",
+        "林芝市",
+        "林芝县",
+        "工布江达",
+        "米林",
+        "墨脱",
+        "波密",
+        "察隅",
+        "朗县",
+        "陕西",
+        "西安",
+        "蓝田",
+        "周至",
+        "户县",
+        "高陵",
+        "铜川",
+        "宜君",
+        "宝鸡",
+        "凤翔县",
+        "岐山",
+        "扶风",
+        "眉县",
+        "陇县",
+        "千阳",
+        "麟游",
+        "凤县",
+        "太白县",
+        "咸阳",
+        "三原县",
+        "泾阳",
+        "乾县",
+        "礼泉",
+        "永寿",
+        "彬县",
+        "长武",
+        "旬邑",
+        "淳化",
+        "武功县",
+        "兴平",
+        "渭南",
+        "潼关",
+        "大荔",
+        "合阳",
+        "澄城",
+        "蒲城",
+        "白水",
+        "富平",
+        "韩城",
+        "华阴",
+        "延安",
+        "延长县",
+        "延川",
+        "子长",
+        "安塞",
+        "志丹",
+        "吴起",
+        "甘泉",
+        "富县",
+        "洛川",
+        "宜川",
+        "黄龙",
+        "黄陵",
+        "汉中",
+        "南郑县",
+        "城固",
+        "洋县",
+        "西乡",
+        "勉县",
+        "宁强",
+        "略阳",
+        "镇巴",
+        "留坝",
+        "佛坪",
+        "榆林",
+        "神木",
+        "府谷",
+        "横山",
+        "靖边",
+        "定边",
+        "绥德",
+        "米脂",
+        "佳县",
+        "吴堡",
+        "清涧",
+        "子洲",
+        "安康",
+        "汉阴",
+        "石泉",
+        "宁陕",
+        "紫阳",
+        "岚皋",
+        "平利",
+        "镇坪",
+        "旬阳",
+        "白河县",
+        "商洛",
+        "洛南",
+        "丹凤",
+        "商南",
+        "山阳",
+        "镇安县",
+        "柞水",
+        "甘肃",
+        "兰州",
+        "东乡自治县",
+        "永登",
+        "皋兰",
+        "榆中",
+        "嘉峪关",
+        "金昌",
+        "永昌",
+        "白银市",
+        "靖远",
+        "会宁",
+        "景泰县",
+        "天水",
+        "清水县",
+        "秦安",
+        "甘谷",
+        "武山",
+        "张家川",
+        "武威",
+        "民勤",
+        "古浪",
+        "天祝",
+        "张掖",
+        "肃南",
+        "民乐县",
+        "临泽",
+        "高台",
+        "山丹",
+        "平凉",
+        "泾川",
+        "灵台",
+        "崇信",
+        "华亭",
+        "庄浪",
+        "静宁",
+        "酒泉",
+        "金塔",
+        "瓜州",
+        "肃北",
+        "阿克塞",
+        "玉门",
+        "敦煌",
+        "庆阳",
+        "庆城",
+        "环县",
+        "华池",
+        "合水县",
+        "正宁",
+        "宁县",
+        "镇原县",
+        "定西",
+        "通渭",
+        "陇西",
+        "渭源",
+        "临洮",
+        "漳县",
+        "岷县",
+        "陇南",
+        "成县",
+        "文县",
+        "宕昌",
+        "康县",
+        "西和",
+        "礼县",
+        "徽县",
+        "两当县",
+        "临夏",
+        "康乐县",
+        "永靖",
+        "广河",
+        "和政",
+        "积石山",
+        "合作市",
+        "临潭",
+        "卓尼",
+        "舟曲",
+        "迭部",
+        "玛曲",
+        "碌曲",
+        "夏河",
+        "青海",
+        "共和县",
+        "西宁",
+        "大通自治县",
+        "大通县",
+        "湟中",
+        "湟源",
+        "平安县",
+        "民和",
+        "乐都",
+        "河南蒙古族自治县",
+        "互助土族自治县",
+        "互助县",
+        "化隆",
+        "循化",
+        "海北州",
+        "海北藏族自治州",
+        "门源",
+        "祁连",
+        "海晏",
+        "刚察",
+        "黄南",
+        "同仁",
+        "尖扎",
+        "泽库",
+        "同德",
+        "贵德",
+        "兴海",
+        "贵南",
+        "果洛",
+        "玛沁",
+        "班玛",
+        "甘德",
+        "达日",
+        "久治",
+        "玛多",
+        "玉树",
+        "杂多",
+        "称多",
+        "治多",
+        "囊谦",
+        "曲麻莱",
+        "海西州",
+        "海西蒙古族藏族自治州",
+        "格尔木",
+        "德令哈",
+        "乌兰",
+        "都兰",
+        "天峻",
+        "宁夏",
+        "银川",
+        "永宁",
+        "贺兰",
+        "灵武",
+        "石嘴山",
+        "平罗",
+        "吴忠",
+        "盐池",
+        "同心县",
+        "青铜峡",
+        "固原",
+        "西吉",
+        "隆德",
+        "泾源",
+        "彭阳",
+        "中卫",
+        "中宁",
+        "海原",
+        "新疆",
+        "乌鲁木齐",
+        "克拉玛依",
+        "吐鲁番",
+        "鄯善",
+        "托克逊",
+        "哈密",
+        "巴里坤",
+        "伊吾",
+        "昌吉",
+        "阜康",
+        "呼图壁",
+        "玛纳斯",
+        "奇台",
+        "吉木萨尔",
+        "木垒哈萨克",
+        "博尔塔拉",
+        "博乐",
+        "精河",
+        "温泉县",
+        "巴音郭楞",
+        "库尔勒",
+        "轮台",
+        "尉犁",
+        "若羌",
+        "且末",
+        "焉耆",
+        "和静",
+        "和硕",
+        "博湖",
+        "阿克苏",
+        "温宿",
+        "库车",
+        "沙雅",
+        "新和县",
+        "拜城",
+        "乌什",
+        "阿瓦提",
+        "柯坪",
+        "克孜勒",
+        "阿图什",
+        "阿克陶",
+        "阿合奇",
+        "乌恰",
+        "喀什",
+        "疏附",
+        "疏勒",
+        "英吉沙",
+        "泽普",
+        "莎车",
+        "叶城",
+        "麦盖提",
+        "岳普湖",
+        "伽师",
+        "巴楚",
+        "塔什",
+        "和田",
+        "墨玉",
+        "皮山",
+        "洛浦",
+        "策勒",
+        "于田县",
+        "民丰",
+        "伊犁",
+        "伊宁",
+        "奎屯",
+        "察布查尔锡伯",
+        "霍城",
+        "巩留",
+        "新源",
+        "昭苏",
+        "特克斯",
+        "尼勒克",
+        "塔城",
+        "乌苏",
+        "额敏",
+        "沙湾",
+        "托里",
+        "裕民",
+        "和布克赛尔",
+        "阿勒泰",
+        "布尔津",
+        "富蕴",
+        "福海",
+        "哈巴河",
+        "青河",
+        "吉木乃",
+        "石河子",
+        "阿拉尔",
+        "图木舒克",
+        "五家渠",
+        "台湾",
+        "台北",
+        "高雄",
+        "基隆",
+        "台中市",
+        "台南",
+        "新竹",
+        "嘉义",
+        "宜兰",
+        "桃园市",
+        "苗栗",
+        "彰化",
+        "南投",
+        "云林",
+        "屏东",
+        "台东",
+        "花莲",
+        "澎湖",
+        "香港",
+        "澳门",
+        "东城",
+        "西城",
+        "崇文",
+        "宣武",
+        "丰台",
+        "石景山",
+        "海淀",
+        "门头沟",
+        "房山",
+        "顺义",
+        "昌平",
+        "大兴",
+        "怀柔",
+        "平谷",
+        "河西",
+        "滨海新",
+        "南开",
+        "红桥",
+        "塘沽",
+        "汉沽",
+        "大港",
+        "东丽",
+        "西青",
+        "津南",
+        "北辰",
+        "武清",
+        "宝坻",
+        "井陉矿",
+        "裕华",
+        "路南",
+        "路北",
+        "古冶",
+        "丰南",
+        "丰润",
+        "海港",
+        "山海关",
+        "北戴河",
+        "邯山",
+        "丛台",
+        "复兴",
+        "峰峰矿",
+        "北市",
+        "南市",
+        "下花园",
+        "双滦",
+        "鹰手营子矿",
+        "运河",
+        "安次",
+        "广阳",
+        "桃城",
+        "小店",
+        "迎泽",
+        "杏花岭",
+        "尖草坪",
+        "万柏林",
+        "晋源",
+        "南郊",
+        "新荣",
+        "朔城",
+        "平鲁",
+        "榆次",
+        "盐湖",
+        "忻府",
+        "尧都",
+        "离石",
+        "玉泉",
+        "赛罕",
+        "东河",
+        "昆都仑",
+        "石拐",
+        "白云矿",
+        "九原",
+        "海勃湾",
+        "乌达",
+        "红山",
+        "元宝山",
+        "松山",
+        "科尔沁",
+        "东胜",
+        "海拉尔",
+        "临河",
+        "集宁",
+        "沈河",
+        "大东",
+        "皇姑",
+        "苏家屯",
+        "东陵",
+        "沈北新",
+        "于洪",
+        "西岗",
+        "沙河口",
+        "甘井子",
+        "旅顺口",
+        "金州",
+        "立山",
+        "千山",
+        "新抚",
+        "东洲",
+        "望花",
+        "顺城",
+        "溪湖",
+        "明山",
+        "南芬",
+        "元宝",
+        "振兴",
+        "振安",
+        "古塔",
+        "凌河",
+        "站前",
+        "西市",
+        "鲅鱼圈",
+        "老边",
+        "新邱",
+        "太平",
+        "清河门",
+        "细河",
+        "白塔",
+        "文圣",
+        "宏伟",
+        "弓长岭",
+        "太子河",
+        "双台子",
+        "兴隆台",
+        "银州",
+        "双塔",
+        "龙城",
+        "龙港",
+        "南票",
+        "杨家杖子开发",
+        "南关",
+        "二道",
+        "绿园",
+        "双阳",
+        "龙潭",
+        "船营",
+        "丰满",
+        "东昌",
+        "东昌府",
+        "二道江",
+        "八道江",
+        "江源",
+        "宁江",
+        "洮北",
+        "道里",
+        "南岗",
+        "道外",
+        "平房",
+        "松北",
+        "香坊",
+        "呼兰",
+        "阿城",
+        "龙沙",
+        "建华",
+        "铁锋",
+        "昂昂溪",
+        "富拉尔基",
+        "碾子山",
+        "梅里斯达斡尔族",
+        "鸡冠",
+        "恒山",
+        "滴道",
+        "城子河",
+        "麻山",
+        "工农",
+        "尖山",
+        "岭东",
+        "四方台",
+        "萨尔图",
+        "龙凤",
+        "让胡路",
+        "红岗",
+        "南岔",
+        "友好",
+        "翠峦",
+        "新青",
+        "美溪",
+        "金山屯",
+        "五营",
+        "乌马河",
+        "汤旺河",
+        "带岭",
+        "乌伊岭",
+        "上甘岭",
+        "东风",
+        "桃山",
+        "茄子河",
+        "阳明",
+        "爱民",
+        "爱辉",
+        "北林",
+        "大兴安岭",
+        "加格达奇",
+        "松岭",
+        "新林",
+        "呼中",
+        "黄浦",
+        "徐汇",
+        "静安",
+        "闸北",
+        "虹口",
+        "杨浦",
+        "闵行",
+        "嘉定",
+        "浦东新",
+        "金山 ",
+        "松江",
+        "青浦",
+        "奉贤",
+        "玄武",
+        "白下",
+        "秦淮",
+        "建邺",
+        "下关",
+        "浦口",
+        "雨花台",
+        "江宁",
+        "六合",
+        "崇安",
+        "南长",
+        "北塘",
+        "锡山",
+        "惠山",
+        "滨湖",
+        "九里",
+        "贾汪",
+        "泉山",
+        "天宁",
+        "钟楼",
+        "戚墅堰",
+        "新北",
+        "武进",
+        "沧浪",
+        "金阊",
+        "虎丘",
+        "吴中",
+        "相城",
+        "崇川",
+        "港闸",
+        "新浦",
+        "楚州",
+        "淮阴",
+        "清浦",
+        "亭湖",
+        "盐都",
+        "广陵",
+        "邗江",
+        "维扬",
+        "京口",
+        "润州",
+        "丹徒",
+        "海陵",
+        "高港",
+        "宿城",
+        "宿豫",
+        "上城",
+        "下城",
+        "江干",
+        "拱墅",
+        "滨江",
+        "萧山",
+        "余杭",
+        "海曙",
+        "江东",
+        "北仑",
+        "镇海",
+        "鄞州",
+        "鹿城",
+        "龙湾",
+        "瓯海",
+        "南湖",
+        "秀洲",
+        "吴兴",
+        "南浔",
+        "越城",
+        "婺城",
+        "金东",
+        "柯城",
+        "衢江",
+        "定海",
+        "椒江",
+        "黄岩",
+        "路桥",
+        "莲都",
+        "瑶海",
+        "庐阳",
+        "蜀山",
+        "新站",
+        "包河",
+        "镜湖",
+        "镜湖新",
+        "弋江",
+        "鸠江",
+        "三山",
+        "蚌山",
+        "禹会",
+        "淮上",
+        "田家庵",
+        "谢家集",
+        "八公山",
+        "潘集",
+        "金家庄",
+        "花山",
+        "雨山",
+        "博望新",
+        "杜集",
+        "相山",
+        "烈山",
+        "铜官山",
+        "狮子山",
+        "迎江",
+        "大观",
+        "宜秀",
+        "屯溪",
+        "徽州",
+        "琅琊",
+        "南谯",
+        "颍州",
+        "颍东",
+        "颍泉",
+        "埇桥",
+        "居巢",
+        "金安",
+        "裕安",
+        "谯城",
+        "贵池",
+        "宣州",
+        "仓山",
+        "马尾",
+        "晋安",
+        "思明",
+        "海沧",
+        "湖里",
+        "集美",
+        "同安",
+        "同安县",
+        "翔安",
+        "城厢",
+        "涵江",
+        "荔城",
+        "秀屿",
+        "梅列",
+        "三元",
+        "鲤城",
+        "丰泽",
+        "洛江",
+        "泉港",
+        "芗城",
+        "龙文",
+        "延平",
+        "新罗",
+        "蕉城",
+        "东湖",
+        "青云谱",
+        "湾里",
+        "青山湖",
+        "珠山",
+        "安源",
+        "湘东",
+        "庐山",
+        "浔阳",
+        "渝水",
+        "月湖",
+        "章贡",
+        "吉州",
+        "青原",
+        "袁州",
+        "临川",
+        "信州",
+        "历下",
+        "槐荫",
+        "天桥",
+        "历城",
+        "长清",
+        "市南",
+        "市北",
+        "四方",
+        "黄岛",
+        "崂山",
+        "李沧",
+        "城阳",
+        "淄川",
+        "张店",
+        "博山",
+        "临淄",
+        "周村",
+        "薛城",
+        "峄城",
+        "台儿庄",
+        "山亭",
+        "芝罘",
+        "福山",
+        "牟平",
+        "莱山",
+        "潍城",
+        "寒亭",
+        "坊子",
+        "奎文",
+        "任城",
+        "泰山",
+        "岱岳",
+        "环翠",
+        "岚山",
+        "莱城",
+        "钢城",
+        "兰山",
+        "罗庄",
+        "德城",
+        "东昌府",
+        "滨城",
+        "牡丹",
+        "中原",
+        "二七",
+        "金水",
+        "上街",
+        "惠济",
+        "龙亭",
+        "顺河回族",
+        "禹王台",
+        "金明",
+        "西工",
+        "瀍河回族",
+        "涧西",
+        "吉利",
+        "洛龙",
+        "卫东",
+        "石龙",
+        "湛河",
+        "文峰",
+        "北关",
+        "殷都",
+        "龙安",
+        "山城",
+        "淇滨",
+        "红旗",
+        "卫滨",
+        "凤泉",
+        "牧野",
+        "中站",
+        "马村",
+        "华龙",
+        "魏都",
+        "源汇",
+        "郾城",
+        "召陵",
+        "湖滨",
+        "宛城",
+        "卧龙",
+        "梁园",
+        "睢阳",
+        "浉河",
+        "平桥",
+        "川汇",
+        "驿城",
+        "江岸",
+        "江汉",
+        "硚口",
+        "汉阳",
+        "武昌",
+        "洪山",
+        "东西湖",
+        "汉南",
+        "蔡甸",
+        "江夏",
+        "黄陂",
+        "新洲",
+        "东湖开发",
+        "武汉经济开发",
+        "黄石港",
+        "西塞山",
+        "下陆",
+        "铁山",
+        "黄石经济开发",
+        "茅箭",
+        "张湾",
+        "十堰经济开发",
+        "武当山旅游经济特",
+        "西陵",
+        "伍家岗",
+        "点军",
+        "猇亭",
+        "夷陵",
+        "樊城",
+        "隆中风景",
+        "高新开发",
+        "鱼梁洲开发",
+        "梁子湖",
+        "鄂城",
+        "葛店开发",
+        "鄂州经济开发",
+        "东宝",
+        "掇刀",
+        "荆门经济开发",
+        "孝南",
+        "沙市",
+        "荆州经济开发",
+        "黄州",
+        "龙感湖管理",
+        "咸安",
+        "曾都",
+        "芙蓉",
+        "天心",
+        "岳麓",
+        "开福",
+        "雨花",
+        "荷塘",
+        "芦淞",
+        "石峰",
+        "天元",
+        "雨湖",
+        "岳塘",
+        "珠晖",
+        "雁峰",
+        "石鼓",
+        "蒸湘",
+        "南岳",
+        "双清",
+        "大祥",
+        "北塔",
+        "岳阳楼",
+        "云溪",
+        "君山",
+        "武陵",
+        "鼎城",
+        "武陵源",
+        "赫山",
+        "北湖",
+        "苏仙",
+        "零陵",
+        "冷水滩",
+        "鹤城",
+        "娄星",
+        "荔湾",
+        "越秀",
+        "海珠",
+        "天河",
+        "黄埔",
+        "番禺",
+        "花都",
+        "南沙",
+        "萝岗",
+        "武江",
+        "浈江",
+        "曲江",
+        "罗湖",
+        "福田",
+        "宝安",
+        "龙岗",
+        "盐田",
+        "香洲",
+        "斗门",
+        "斗门镇",
+        "金湾",
+        "龙湖",
+        "濠江",
+        "潮阳",
+        "潮南",
+        "澄海",
+        "禅城",
+        "南海",
+        "顺德",
+        "三水",
+        "高明",
+        "蓬江",
+        "江海",
+        "新会",
+        "赤坎",
+        "霞山",
+        "坡头",
+        "麻章",
+        "茂南",
+        "茂港",
+        "端州",
+        "鼎湖",
+        "惠城",
+        "惠阳",
+        "梅江",
+        "源城",
+        "清城",
+        "枫溪",
+        "湘桥",
+        "榕城",
+        "云城",
+        "青秀",
+        "江南",
+        "西乡塘",
+        "良庆",
+        "邕宁",
+        "鱼峰",
+        "柳南",
+        "柳北",
+        "秀峰",
+        "叠彩",
+        "七星",
+        "雁山",
+        "万秀",
+        "蝶山",
+        "长洲",
+        "银海",
+        "铁山港",
+        "港口",
+        "防城",
+        "钦南",
+        "钦北",
+        "港北",
+        "港南",
+        "覃塘",
+        "玉州",
+        "右江",
+        "八步",
+        "金城江",
+        "兴宾",
+        "江洲",
+        "秀英",
+        "龙华",
+        "琼山",
+        "美兰",
+        "洋浦经济开发",
+        "万州",
+        "涪陵",
+        "渝中",
+        "大渡口",
+        "沙坪坝",
+        "九龙坡",
+        "南岸",
+        "北碚",
+        "万盛",
+        "渝北",
+        "巴南",
+        "黔江",
+        "长寿",
+        "江津",
+        "合川",
+        "南川",
+        "永川",
+        "北部新",
+        "锦江",
+        "青羊",
+        "金牛",
+        "武侯",
+        "成华",
+        "龙泉驿",
+        "青白江",
+        "新都",
+        "温江",
+        "自流井",
+        "贡井",
+        "沿滩",
+        "仁和",
+        "江阳",
+        "纳溪",
+        "龙马潭",
+        "旌阳",
+        "涪城",
+        "游仙",
+        "元坝",
+        "朝天",
+        "船山",
+        "安居",
+        "五通桥",
+        "金口河",
+        "顺庆",
+        "高坪",
+        "嘉陵",
+        "东坡",
+        "翠屏",
+        "通川",
+        "雨城",
+        "巴州",
+        "雁江",
+        "南明",
+        "云岩",
+        "花溪",
+        "乌当",
+        "小河",
+        "金阳新",
+        "六枝特",
+        "红花岗",
+        "汇川",
+        "新浦新",
+        "西秀",
+        "黄果树开发",
+        "七星关",
+        "碧江",
+        "万山特",
+        "凯里经济开发",
+        "盘龙",
+        "官渡",
+        "西山",
+        "东川",
+        "麒麟",
+        "红塔",
+        "隆阳",
+        "昭阳",
+        "古城",
+        "思茅",
+        "临翔",
+        "山南市",
+        "阿里地",
+        "碑林",
+        "莲湖",
+        "灞桥",
+        "未央",
+        "雁塔",
+        "阎良",
+        "临潼",
+        "沣渭新",
+        "王益",
+        "印台",
+        "耀州",
+        "渭滨",
+        "金台",
+        "陈仓",
+        "秦都",
+        "渭城",
+        "临渭",
+        "宝塔",
+        "汉台",
+        "榆阳",
+        "汉滨",
+        "商州",
+        "杨凌示范",
+        "七里河",
+        "西固",
+        "红古",
+        "平川",
+        "秦州",
+        "麦积",
+        "凉州",
+        "甘州",
+        "崆峒",
+        "肃州",
+        "西峰",
+        "安定",
+        "武都",
+        "海东",
+        "兴庆",
+        "西夏",
+        "金凤",
+        "大武口",
+        "惠农",
+        "利通",
+        "红寺堡",
+        "原州",
+        "沙坡头",
+        "天山",
+        "沙依巴克",
+        "水磨沟",
+        "头屯河",
+        "达坂城",
+        "米东",
+        "独山子",
+        "白碱滩",
+        "乌尔禾",
+        "中西",
+        "九龙城",
+        "观塘",
+        "深水埗",
+        "湾仔",
+        "黄大仙",
+        "油尖旺",
+        "离岛",
+        "葵青",
+        "西贡",
+        "沙田",
+        "屯门",
+        "荃湾",
+        "元朗",
+        "花地玛堂",
+        "圣安多尼堂",
+        "大堂",
+        "望德堂",
+        "风顺堂",
+        "嘉模堂",
+        "圣方济各堂"
+    ]
+}

+ 103 - 0
src/config.json

@@ -0,0 +1,103 @@
+{
+    "mongodbServers": "192.168.3.18:27080",
+    "mongodbPoolSize": "5",
+    "mongodbName": "qfw",
+    "influxaddr": "http://192.168.3.207:8086",
+    "influxdb": "jy_logs",
+    "elasticsearch": "http://192.168.3.18:9800",
+    "elasticPoolSize": 30,
+    "redisaddrs": "other=192.168.3.14:3379,push=192.168.3.14:3379,sso=192.168.3.14:3379",
+    "webport": "8089",
+    "webrpcport": "84",
+    "weixinrpc": "127.0.0.1:83",
+    "cacheflag": false,
+    "agreement": "http",
+    "webdomain": "http://webws.qmx.top",
+    "redirect": {
+        "searchinfo": "/swordfish/search",
+        "rssset": "/wxkeyset/keyset/index",
+        "viewdemo": "/front/viewdemo",
+        "wxpushlist": "/wxpush/bidinfo/%s",
+        "share": "/swordfish/guide/share",
+        "myfollow": "/follow/list",
+        "pcmyfollow": "/follow/set/list/%s",
+        "feedback": "/swordfish/feedback",
+        "wxpushfollowlist": "/follow/notice/%s/%s",
+        "community": "http://www.myfans.cc/30a78e9b78",
+        "quickly": "http://mp.weixin.qq.com/s?__biz=MzIyNTM1NDUyNw==&mid=100000001&idx=1&sn=58e6008f0577d529a56c9ed78724decf&scene=0#wechat_redirect",
+        "useskill": "http://mp.weixin.qq.com/mp/homepage?__biz=MzIyNTM1NDUyNw==&hid=1&sn=f9e98da1975f85011ee138a4ee5cfbe8#wechat_redirect",
+        "about": "/swordfish/about",
+        "followset": "/follow/set/%s/%s",
+        "myfeedbacks": "/swordfish/myFeedbacks",
+        "zqluckdraw": "/active/zqLuckdraw"
+    },
+    "jy_activeset": {
+        "activitystartcode": "3201000000",
+        "activityendcode": "3201999999",
+        "code": "promation",
+        "jy_extend": {
+            "activitystartcode": "3202100000",
+            "activityendcode": "3202199999"
+        },
+        "jy_leaflets": {
+            "activitystartcode": "3202200000",
+            "activityendcode": "3202299999"
+        },
+        "jy_zqluckdraw": {
+            "activitystartcode": "3202300000",
+            "activityendcode": "3202399999"
+        }
+    },
+    "followProject": 10,
+    "pushRpc": "127.0.0.1:8766",
+    "followLimitDay": 30,
+    "followTipMsg": {
+        "title": "您关注的项目《%s》将于%s开标!",
+        "remark": "您收到这条信息是因为您之前设置了项目开标提醒,如果您不再需要此类提醒,可点击下方“详情”修改提醒设置。"
+    },
+    "followPushRpc": "127.0.0.1:8759",
+    "userPoolSize": 1000,
+    "recommendThreshold": 0.5,
+    "msg_server": "123.56.236.148:7070",
+    "rewardText": [
+        "感觉不错?就给努力的剑鱼君打个赏吧!",
+        "憋说话!赏我!",
+        "说好的一起投标,你却偷偷用剑鱼……",
+        "“赏”就一个字 我只说一次",
+        "厉害了word鱼!",
+        "看官赏点银子偶会更卖力噢!",
+        "各位客官,快来赏点吧!",
+        "夸我还是含蓄点的好,赏一个吧~",
+        "爱,就供养;喜欢,就打赏!",
+        "请简单粗暴地爱我!",
+        "最喜欢你一言不合就打赏的样子了~~~么么!",
+        "支持剑鱼,不问度娘,欢迎打赏,情深意长!",
+        "剑鱼君正在埋头钻研,感觉累累哒,求各位打赏!"
+    ],
+    "advertText": [
+        "与其一个人孤单  不如一群人狂欢   赶快加入剑鱼社区",
+        "加入剑鱼招标社区  和投标大神做朋友",
+        "来剑鱼招标社区  找最合拍的伙伴",
+        "剑鱼招标社区,让沟通连接你我。",
+        "剑鱼社区,分享你的知识、解决问题、行业资源交换",
+        "勇敢发声,和老司机一起畅聊招投标。",
+        "春风十里,不如来剑鱼招标社区!",
+        "只要心相通,相隔千里也能畅快交流。",
+        "弹指一挥间,剑鱼因你而不同!",
+        "携手同行,认真对待每一次招投标"
+    ],
+    "advertImg": "/images/advert.png",
+    "advertName": "推荐",
+    "advertUrl": "/swordfish/about",
+    "wxJianyu": {
+        "appid": "wx5b1c6e7cc4dac0e4",
+        "appsecret": "b026103ffebd2291b3edb7a269612112",
+        "pay": {
+            "mchid": "1293231901",
+            "key": "top2015top2015",
+            "attachmsg": "剑鱼打赏",
+            "bodymsg": "剑鱼-招标信息打赏",
+            "detailmsg": "招标推送信息[%s] 打赏%s元钱"
+        }
+    }
+}

+ 28 - 0
src/jfw/active/active.go

@@ -0,0 +1,28 @@
+package active
+
+import (
+	"github.com/go-xweb/xweb"
+	tools "jfw/tools"
+	"qfw/util"
+)
+
+type Active struct {
+	*xweb.Action
+	zqLuckdraw              xweb.Mapper `xweb:"/active/zqLuckdraw"`                     //中秋节抽奖活动
+	zqLuckdrawSave          xweb.Mapper `xweb:"/active/zqLuckdrawSave"`                 //中秋节抽奖活动保存
+	zqLuckdrawWinningrecord xweb.Mapper `xweb:"/active/zqLuckdrawWinningrecord/(.*)"`   //中秋节抽奖活动中奖纪录
+	zqLuckdrawShareInc      xweb.Mapper `xweb:"/active/zqLuckdrawShareInc"`             //分享完之后还可以抽奖
+	zqLuckdrawShare         xweb.Mapper `xweb:"/active/zqLuckdrawShare/(\\w+)/([^.]*)"` //分享
+	zqLuckdrawControl       xweb.Mapper `xweb:"/active/zqLuckdrawControl"`              //控制
+	saofuImage              xweb.Mapper `xweb:"/active/saofu/image"`                    //扫福
+	saofuIndex              xweb.Mapper `xweb:"/active/saofu"`                          //扫福
+
+}
+
+var mongodb = tools.MQFW
+var se util.SimpleEncrypt = util.SimpleEncrypt{Key: "topnet2015topnet2015"}
+
+func init() {
+	//添加模块解析
+	xweb.AddAction(&Active{})
+}

+ 71 - 0
src/jfw/active/saofu.go

@@ -0,0 +1,71 @@
+package active
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"math/rand"
+	"net/http"
+	"sync/atomic"
+)
+
+var picarry = []string{}
+var length int32
+
+//搜百度图片
+func down(start int, tpl string) {
+	resp, err := http.Get(fmt.Sprintf(tpl, start))
+	if err != nil {
+		fmt.Println(err.Error())
+		return
+	}
+	bs, _ := ioutil.ReadAll(resp.Body)
+	resp.Body.Close()
+	data := map[string]interface{}{}
+	json.Unmarshal(bs, &data)
+	//
+	if data["data"] == nil {
+		//fmt.Println("下载到第", start, "处,不让下了")
+		return
+	}
+	list := data["data"].([]interface{})
+	for _, v := range list {
+		item := v.(map[string]interface{})
+		if pic, ok := item["middleURL"]; ok {
+			picurl := pic.(string)
+			picarry = append(picarry, picurl)
+			atomic.AddInt32(&length, 1)
+		}
+
+	}
+}
+
+//
+func init11() {
+	go func() {
+		//书法类
+		tpl := "http://images.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&is=&fp=result&queryWord=福字书法&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&z=&ic=0&word=福字书法&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&pn=%d&rn=30&gsm=5a&1484871068561="
+		for i := 0; i < 20; i++ {
+			down(i*30+5, tpl)
+		}
+		//年画类
+		tpl = "http://images.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&is=&fp=result&queryWord=福字年画&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&z=&ic=0&word=福字年画&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&pn=%d&rn=30&gsm=5a&1484871068561="
+		for i := 0; i < 20; i++ {
+			down(i*30+5, tpl)
+		}
+		//手写类
+		tpl = "http://images.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&is=&fp=result&queryWord=福字手写&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&z=&ic=0&word=福字手写&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&pn=%d&rn=30&gsm=5a&1484871068561="
+		for i := 0; i < 20; i++ {
+			down(i*30+5, tpl)
+		}
+		//fmt.Printf("共收集图片%d 张\n", length)
+	}()
+}
+func (a *Active) SaofuImage() {
+	pos := (int)(rand.Int31n(length - 1))
+	url := picarry[pos]
+	http.Redirect(a.ResponseWriter, a.Request, url, 302)
+}
+func (a *Active) SaofuIndex() error {
+	return a.Render("/active/saofu.html")
+}

+ 478 - 0
src/jfw/active/zqluckdraw.go

@@ -0,0 +1,478 @@
+package active
+
+import (
+	"fmt"
+	"gopkg.in/mgo.v2/bson"
+	"jfw/config"
+	"jfw/wx"
+	"log"
+	"math/rand"
+	"qfw/util"
+	"time"
+)
+
+var thankYou string = "谢谢参与"
+var personPrizes map[string][]string = make(map[string][]string)
+var todayPrize map[string]int = make(map[string]int)
+var todaySign string
+var controlFlag bool
+var zqluckdrawChan chan bool = make(chan bool, 1)
+
+func init() {
+	now := time.Now()
+	todaySign = fmt.Sprint(now.Year()) + fmt.Sprint(now.Month()) + fmt.Sprint(now.Day())
+	thankYou = config.ZqLuckdraw.Weights[0]["text"].(string)
+	//活动期间,把数据库中的中奖数量保存到内存
+	if isActiveing() {
+		recordPrizeCount()
+	}
+}
+
+//是否在活动中。。。
+func isActiveing() bool {
+	now := time.Now()
+	return now.After(time.Unix(config.ZqLuckdraw.StartDate, 0)) && now.Before(time.Unix(config.ZqLuckdraw.EndDate, 0))
+}
+
+//
+func (a *Active) ZqLuckdrawControl() {
+	openId, _ := a.GetSession("s_m_openid").(string)
+	if openId != "" && isManager(openId) {
+		if controlFlag {
+			controlFlag = false
+			a.Write("状态关闭!")
+		} else {
+			controlFlag = true
+			a.Write("状态开启!")
+		}
+	} else {
+		a.Write("请登录!")
+	}
+}
+
+//是否是管理员
+func isManager(openId string) bool {
+	for _, v := range config.ZqLuckdraw.MIds {
+		if v == openId {
+			return true
+		}
+	}
+	return false
+}
+
+//抽奖
+func (a *Active) ZqLuckdraw() error {
+	if !isActiveing() {
+		log.Println("luckdrawError:中秋节抽奖活动已结束!")
+		return a.Redirect("/active/zqLuckdrawShare/nowinning/-1")
+	}
+	openId, _ := a.GetSession("s_m_openid").(string)
+	if openId == "" {
+		return a.Redirect("/active/zqLuckdrawShare/nowinning/-1")
+	}
+	//判读是取消关注
+	data, ok := mongodb.Find("jy_subscribe", bson.M{"s_m_openid": openId}, `{"l_date":-1}`, `{"s_event":1}`, false, 0, 1)
+	if !ok || data == nil || len(*data) == 0 {
+		return a.Redirect("/active/zqLuckdrawShare/nowinning/-1")
+	}
+	event, _ := (*data)[0]["s_event"].(string)
+	if event == "" || event == "unsubscribe" {
+		return a.Redirect("/active/zqLuckdrawShare/nowinning/-1")
+	}
+	frequency := getSurplusNum(openId, true)
+	myPrizes := getPrize(openId, frequency)
+	var prizes []string
+	for _, v := range config.ZqLuckdraw.Weights {
+		prizes = append(prizes, v["text"].(string))
+	}
+	//
+	var winningPrize string
+	winningQuery := bson.M{
+		"s_openid": openId,
+		"i_status": 1,
+		"s_code":   config.ZqLuckdraw.ActiveCode,
+	}
+	res, ok := mongodb.FindOneByField("jy_active", winningQuery, `{"s_prize":1}`)
+	if ok && nil != res && len(*res) > 0 {
+		winningPrize, _ = (*res)["s_prize"].(string)
+	}
+	a.T["winningPrize"] = winningPrize
+	a.T["prizes"] = prizes
+	a.T["myPrizes"] = myPrizes
+	a.T["frequency"] = frequency
+	a.T["name"] = config.ZqLuckdraw.ActiveName
+	a.T["sign"] = se.EncodeString(openId)
+	a.T["signature"] = wx.SignJSSDK(a.Site() + a.Url())
+	a.T["openid"] = se.EncodeString(openId)
+	a.T["startDate"] = config.ZqLuckdraw.StartDate
+	a.T["endDate"] = config.ZqLuckdraw.EndDate
+	a.T["claimsStartDate"] = config.ZqLuckdraw.ClaimsStartDate
+	a.T["claimsEndDate"] = config.ZqLuckdraw.ClaimsEndDate
+	a.T["thankYou"] = thankYou
+	return a.Render("/active/zqluckdraw.html")
+}
+
+//分享增加次数
+func (a *Active) ZqLuckdrawShareInc() error {
+	if !isActiveing() {
+		a.ServeJson(bson.M{"flag": -2, "prize": ""})
+		return nil
+	}
+	openId, _ := a.GetSession("s_m_openid").(string)
+	//session失效
+	if openId == "" {
+		a.ServeJson(bson.M{"flag": -1, "prize": ""})
+		return nil
+	}
+	var flag int
+	var prizes []string
+	now := time.Now()
+	query := bson.M{
+		"s_openid": openId,
+		"s_code":   config.ZqLuckdraw.ShareCode,
+		"i_year":   now.Year(),
+		"i_month":  now.Month(),
+		"i_day":    now.Day(),
+	}
+	if mongodb.Count("jy_active", query) == 0 {
+		nickName, _ := a.GetSession("s_nickname").(string)
+		id := mongodb.Save("jy_active", bson.M{
+			"s_openid":     openId,
+			"s_name":       config.ZqLuckdraw.ActiveName,
+			"s_code":       config.ZqLuckdraw.ShareCode,
+			"i_year":       now.Year(),
+			"i_month":      now.Month(),
+			"i_day":        now.Day(),
+			"l_createtime": now.Unix(),
+			"s_nickname":   nickName,
+		})
+		if len(id) > 0 {
+			flag = 1
+			prizes = getPrize(openId, getSurplusNum(openId, false))
+			//取最后n个
+			prizes = prizes[len(prizes)-config.ZqLuckdraw.Inc:]
+		}
+	}
+	a.ServeJson(bson.M{"flag": flag, "prize": prizes, "inc": config.ZqLuckdraw.Inc})
+	return nil
+}
+
+//中奖保存
+func (a *Active) ZqLuckdrawSave() error {
+	var flag int
+	openId, _ := a.GetSession("s_m_openid").(string)
+	if !isActiveing() {
+		flag = -1
+		log.Println("luckdrawError:中秋节抽奖活动已结束!")
+	} else if openId == "" {
+		flag = -3
+		log.Println("luckdrawError:session中没有获取到openId!")
+	} else {
+		myPrizes := personPrizes[openId]
+		if len(myPrizes) == 0 {
+			flag = -2
+			log.Println("luckdrawError:中没有获取到中奖记录!")
+			a.ServeJson(bson.M{"flag": flag})
+			return nil
+		}
+		prize := myPrizes[0]
+		personPrizes[openId] = myPrizes[1:]
+		if mongodb.Count("user", bson.M{"s_m_openid": openId, "i_appid": 2}) == 0 {
+			log.Println("luckdrawError:没有找到该用户", openId)
+			flag = -4
+		} else if getSurplusNum(openId, true) == 0 {
+			log.Println("luckdrawError:今天没有抽奖次数了!")
+			flag = -5
+		} else {
+			now := time.Now()
+			data := make(bson.M)
+			data["s_openid"] = openId
+			data["s_prize"] = prize
+			if prize == thankYou {
+				data["i_status"] = 0
+			} else {
+				data["i_status"] = 1
+			}
+			_, data["i_grade"] = getPrizeInfo(prize)
+			data["s_name"] = config.ZqLuckdraw.ActiveName
+			data["s_code"] = config.ZqLuckdraw.ActiveCode
+			data["l_createtime"] = now.Unix()
+			data["i_year"] = now.Year()
+			data["i_month"] = now.Month()
+			data["i_day"] = now.Day()
+			data["s_nickname"], _ = a.GetSession("s_nickname").(string)
+			if len(mongodb.Save("jy_active", data)) > 0 {
+				flag = 1
+			} else {
+				log.Println("luckdrawError:保存失败:", data)
+			}
+		}
+	}
+	a.ServeJson(bson.M{"flag": flag})
+	return nil
+}
+
+//中奖记录
+func (a *Active) ZqLuckdrawWinningrecord(openId string) {
+	query := bson.M{
+		"s_openid": se.DecodeString(openId),
+		"i_status": 1,
+		"s_code":   config.ZqLuckdraw.ActiveCode,
+	}
+	res, ok := mongodb.FindOneByField("jy_active", query, `{"s_prize":1}`)
+	if ok && nil != res && len(*res) > 0 {
+		a.T["data"] = res
+	}
+	a.T["name"] = config.ZqLuckdraw.ActiveName
+	a.T["signature"] = wx.SignJSSDK(a.Site() + a.Url())
+	a.T["openid"] = openId
+	a.Render("/active/winningrecord.html")
+}
+
+//分享出去的页面
+func (a *Active) ZqLuckdrawShare(reqType, openId string) error {
+	if reqType == "" || openId == "" {
+		return a.Render("/_error.html")
+	} else if reqType == "winning" {
+		now := time.Now()
+		query := bson.M{
+			"i_year":   now.Year(),
+			"i_month":  now.Month(),
+			"i_day":    now.Day(),
+			"s_code":   config.ZqLuckdraw.ActiveCode,
+			"i_status": 1,
+			"s_openid": se.DecodeString(openId),
+		}
+		data, ok := mongodb.FindOneByField("jy_active", query, `{"s_nickname":1,"s_prize":1}`)
+		if !ok || data == nil || len(*data) == 0 {
+			a.T["flag"] = 0
+		} else {
+			a.T["flag"] = 1
+			a.T["nickName"] = (*data)["s_nickname"]
+			a.T["prize"] = (*data)["s_prize"]
+		}
+	} else if reqType == "nowinning" {
+		a.T["flag"] = 0
+	} else {
+		return a.Render("/_error.html")
+	}
+	a.T["signature"] = wx.SignJSSDK(a.Site() + a.Url())
+	a.T["openId"] = openId
+	var shareId = a.GetString("id")
+	if len(shareId) == 0 {
+		shareId = config.ZqLuckdraw.ShareId
+	}
+	a.T["shareId"] = se.EncodeString(shareId)
+	return a.Render("/active/share.html")
+}
+
+//获取奖品
+func getPrize(openId string, count int) []string {
+	var prizes []string
+	if count <= 0 {
+		return prizes
+	}
+	myPrizes := personPrizes[openId]
+	//判读之前是否中过奖,如果中过奖直接设置为不中奖
+	if count == len(myPrizes) { //获取数量和内存数一样
+		prizes = myPrizes
+	} else if count > len(myPrizes) { //获取数量大于内存数
+		prizes = myPrizes
+		isWinning := isWinning(openId, myPrizes)
+		for i := len(myPrizes); i < count; i++ {
+			prize := thankYou
+			//获取新的奖品
+			if !isWinning {
+				prize = getNewPrize()
+				if prize != thankYou {
+					isWinning = true
+				}
+			}
+			prizes = append(prizes, prize)
+		}
+		personPrizes[openId] = prizes
+	} else { //获取数量小于内存数
+		prizes = personPrizes[openId][0:count]
+	}
+	return prizes
+}
+
+//获取新的奖品
+func getNewPrize() string {
+	zqluckdrawChan <- true
+	defer func() {
+		<-zqluckdrawChan
+	}()
+	clearPrizeCount()
+	//获取新的奖品
+	array := config.ZqLuckdraw.Weights
+	weightValue := getWeightRandom(array)
+	prize := array[weightValue]["text"].(string)
+	if prize == thankYou {
+		return thankYou
+	}
+	//一天只能中奖n多次
+	surplus, grade := getPrizeInfo(prize)
+	average := getLimitNum(surplus)
+	log.Println("今天可以中奖===", average)
+	if todayPrize[prize] >= average {
+		return thankYou
+	}
+	dbCount := mongodb.Count("jy_active", bson.M{"i_grade": grade, "s_code": config.ZqLuckdraw.ActiveCode})
+	var memoryCount int
+	for _, prizes := range personPrizes {
+		for _, v := range prizes {
+			if v == prize {
+				memoryCount++
+			}
+		}
+	}
+	if dbCount+memoryCount >= surplus {
+		return thankYou
+	}
+	if prize != thankYou {
+		todayPrize[prize] = todayPrize[prize] + 1
+	}
+	return prize
+}
+
+//根据权重随机获取数组的索引
+func getWeightRandom(array []map[string]interface{}) int {
+	var weightSum, stepWeightSum float64
+	for _, v := range array {
+		weightSum += v["proportion"].(float64)
+	}
+	randVal := rand.New(rand.NewSource(time.Now().UnixNano())).Float64()
+	for i := 0; i < len(array); i++ {
+		stepWeightSum += array[i]["proportion"].(float64)
+		if randVal <= stepWeightSum/weightSum {
+			return i
+		}
+	}
+	return 0
+}
+
+//记录今天的中奖数量
+func recordPrizeCount() {
+	now := time.Now()
+	query := bson.M{
+		"i_year":   now.Year(),
+		"i_month":  now.Month(),
+		"i_day":    now.Day(),
+		"s_code":   config.ZqLuckdraw.ActiveCode,
+		"i_status": 1,
+	}
+	list, ok := mongodb.Find("jy_active", query, `{"s_prize":-1}`, `{"s_prize":1}`, false, -1, -1)
+	if ok && list != nil && len(*list) > 0 {
+		for _, v := range *list {
+			prize, _ := v["s_prize"].(string)
+			todayPrize[prize] = todayPrize[prize] + 1
+		}
+	}
+}
+
+//清空今天的中奖数量
+func clearPrizeCount() {
+	now := time.Now()
+	newTodaySign := fmt.Sprint(now.Year()) + fmt.Sprint(now.Month()) + fmt.Sprint(now.Day())
+	if todaySign != newTodaySign {
+		todaySign = newTodaySign
+		todayPrize = make(map[string]int)
+	}
+}
+
+//获取某个奖品信息
+func getPrizeInfo(text string) (int, int) {
+	array := config.ZqLuckdraw.Weights
+	for _, v := range array {
+		if v["text"].(string) == text {
+			return util.IntAll(v["count"]), util.IntAll(v["grade"])
+		}
+	}
+	return 0, 0
+}
+
+//这个人是否中过奖
+func winningIsExist(openId string) bool {
+	return mongodb.Count("jy_active", bson.M{"s_openid": openId, "s_code": config.ZqLuckdraw.ActiveCode, "i_status": 1}) > 0
+}
+
+//获取剩余次数
+func getSurplusNum(openId string, flag bool) (frequency int) {
+	now := time.Now()
+	query := bson.M{
+		"s_openid": openId,
+		"i_year":   now.Year(),
+		"i_month":  now.Month(),
+		"i_day":    now.Day(),
+		"s_code":   config.ZqLuckdraw.ActiveCode,
+	}
+	count := mongodb.Count("jy_active", query) //今天已经抽奖的次数
+	var count1 int
+	if flag {
+		query1 := bson.M{
+			"s_openid": openId,
+			"s_code":   config.ZqLuckdraw.ShareCode,
+			"i_year":   now.Year(),
+			"i_month":  now.Month(),
+			"i_day":    now.Day(),
+		}
+		count1 = mongodb.Count("jy_active", query1) //今天是否分享
+	}
+	if count < config.ZqLuckdraw.Count+config.ZqLuckdraw.Inc {
+		frequency = config.ZqLuckdraw.Count - count
+		if frequency < 0 {
+			frequency = 0
+		}
+		if count1 > 0 || !flag {
+			frequency += config.ZqLuckdraw.Inc
+		}
+	}
+	return
+}
+
+//是否已经中过奖
+func isWinning(openId string, myPrizes []string) bool {
+	if controlFlag {
+		return true
+	}
+	//看是否中过奖
+	for _, v := range myPrizes {
+		if v != thankYou {
+			return true
+		}
+	}
+	return winningIsExist(openId)
+}
+
+func getLimitNum(countPrize int) int {
+	n := time.Unix(config.ZqLuckdraw.StartDate, 0)
+	start := time.Date(n.Year(), n.Month(), n.Day(), 0, 0, 0, 0, time.Local)
+	//
+	m := time.Unix(config.ZqLuckdraw.EndDate, 0)
+	end := time.Date(m.Year(), m.Month(), m.Day(), m.Hour(), m.Minute(), m.Second()+1, m.Nanosecond(), time.Local)
+	//活动进行的总天数
+	countDay := int(end.Sub(start).Hours() / 24)
+	//今天凌晨
+	now := time.Now()
+	todayStart := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local)
+	surplusDay := int(end.Sub(todayStart).Hours() / 24)
+	if countDay > countPrize {
+		if countDay-countPrize >= surplusDay {
+			return 0
+		} else {
+			return 1
+		}
+	} else {
+		dayAverage := countPrize / countDay
+		otherDayAverage := countPrize % countDay
+		if surplusDay <= otherDayAverage {
+			dayAverage++
+		}
+		if dayAverage == 0 {
+			dayAverage = 1
+		}
+		return dayAverage
+	}
+	return 1
+}

+ 21 - 0
src/jfw/active/zqluckdraw_test.go

@@ -0,0 +1,21 @@
+package active
+
+import (
+	"fmt"
+	_ "jfw/config"
+	"testing"
+	"time"
+)
+
+func Test_getPrize(t *testing.T) {
+	timer1 := time.NewTicker(1 * time.Nanosecond)
+	for i := 0; i < 100; i++ {
+		select {
+		case <-timer1.C:
+			prize := getNewPrize()
+			if prize != "谢谢参与" {
+				fmt.Println(prize)
+			}
+		}
+	}
+}

+ 12 - 0
src/jfw/config/config.go

@@ -0,0 +1,12 @@
+package config
+
+import (
+	"qfw/util"
+)
+
+var Sysconfig map[string]interface{}
+var Seoconfig map[string]interface{}
+
+func init() {
+	util.ReadConfig(&Sysconfig)
+}

+ 27 - 0
src/jfw/config/zqluckdraw.go

@@ -0,0 +1,27 @@
+package config
+
+import (
+	"qfw/util"
+)
+
+//系统配置
+type zqLuckdraw struct {
+	Weights         []map[string]interface{} `json:"weights"`
+	StartDate       int64                    `json:"startDate"`
+	EndDate         int64                    `json:"endDate"`
+	ActiveName      string                   `json:"activeName"`
+	ActiveCode      string                   `json:"activeCode"`
+	ClaimsStartDate int64                    `json:"claimsStartDate"`
+	ClaimsEndDate   int64                    `json:"claimsEndDate"`
+	MIds            []string                 `json:"mIds"`
+	ShareId         string                   `json:"shareId"`
+	ShareCode       string                   `json:"shareCode"`
+	Count           int                      `json:"count"`
+	Inc             int                      `json:"inc"`
+}
+
+var ZqLuckdraw zqLuckdraw
+
+func init() {
+	util.ReadConfig("./zqluckdraw.json", &ZqLuckdraw)
+}

+ 6 - 0
src/jfw/dataRepair/config.json

@@ -0,0 +1,6 @@
+{
+    "mongodbServers": "10.172.242.243:27080",
+    "mongodbPoolSize": "2",
+    "mongodbName": "qfw",
+	"weixinrpcport":""
+}

+ 96 - 0
src/jfw/dataRepair/main.go

@@ -0,0 +1,96 @@
+package main
+
+import (
+	. "jfw/tools"
+	"log"
+	"qfw/util"
+	"strings"
+	"time"
+
+	_ "gopkg.in/mgo.v2/bson"
+)
+
+type M map[string]interface{}
+
+func main() {
+	sess := MQFW.GetMgoConn()
+	defer MQFW.DestoryMongoConn(sess)
+	cur := sess.DB(MQFW.DbName).C("user").Find(&M{
+		"o_jy_msgset": M{"$exists": true},
+		"o_jy":        M{"$exists": false},
+		//"_id":         bson.ObjectIdHex("572c6641c9ebc24ce6000004"),
+	}).Iter()
+	j := 0
+	for tmp := make(map[string]interface{}); cur.Next(tmp); j++ {
+		defer util.Catch()
+		obj := tmp["o_jy_msgset"]
+		newObj := map[string]interface{}{
+			"s_scope": "A",
+		}
+		if obj != nil {
+			/**
+			解析旧配置到新配置中,合并配置
+			**/
+			keys := map[string]bool{}
+			scopes := map[string]bool{}
+			times := time.Now().Unix()
+			for _, types := range []string{"bid", "tender"} {
+				if objM, ok := obj.(map[string]interface{}); ok {
+					if objM[types] != nil {
+						if objMb, ok1 := objM[types].(map[string]interface{}); ok1 {
+							util.Try(func() {
+								a_key := util.ObjArrToStringArr(objMb["a_key"].([]interface{}))
+								for _, v := range a_key {
+									keys[v] = true
+								}
+							}, func(e interface{}) {})
+							s_scope := util.ObjToString(objMb["s_scope"])
+							for _, v := range strings.Split(s_scope, ",") {
+								scopes[v] = true
+							}
+							ttime := util.Int64All(objMb["l_modifydate"])
+							if ttime > times {
+								times = ttime
+							}
+						}
+					}
+				}
+			}
+			newObj["l_modifydate"] = times
+			if scopes["A"] {
+				newObj["s_scope"] = "A"
+			} else {
+				newObj["s_scope"] = func(m map[string]bool) string {
+					s := []string{}
+					for kk, _ := range m {
+						s = append(s, kk)
+					}
+					st1 := strings.Join(s, ",")
+					if st1 == "" {
+						st1 = "A"
+					}
+					return st1
+				}(scopes)
+			}
+			newObj["a_key"] = func(m map[string]bool) (res []string) {
+				num := 0
+				res = []string{}
+				for kk, _ := range m {
+					num++
+					if num > 10 {
+						break
+					}
+					res = append(res, kk)
+				}
+				return
+			}(keys)
+			log.Println(j, "====", newObj, tmp["_id"], obj)
+			sess.DB(MQFW.DbName).C("user").UpdateId(tmp["_id"], &M{
+				"$set": M{
+					"o_jy": newObj,
+				},
+			})
+		}
+		tmp = make(map[string]interface{})
+	}
+}

+ 24 - 0
src/jfw/filter/filter.go

@@ -0,0 +1,24 @@
+package filter
+
+import (
+	"regexp"
+	"time"
+
+	"github.com/go-xweb/xweb"
+)
+
+func init() {
+	xweb.AddFilter(&logfilter{App: xweb.RootApp(), SessionName: "s_nickname"})
+	//增加session过滤
+	sessionfiletr := &xweb.LoginFilter{App: xweb.RootApp(), SessionName: "s_m_openid",
+		AnonymousUrls: make([]*regexp.Regexp, 0),
+		AskLoginUrls:  make([]*regexp.Regexp, 0),
+		Redirect:      "/error.html",
+	}
+	sessionfiletr.AddAskLoginUrls("^/member")
+	xweb.AddFilter(sessionfiletr)
+	go func() {
+		time.Sleep(1 * time.Minute)
+		SaveLogTask()
+	}()
+}

+ 193 - 0
src/jfw/filter/logfilter.go

@@ -0,0 +1,193 @@
+/**
+日志过滤器
+记录网站所有访问和请求
+2015-7-2
+任政
+**/
+package filter
+
+import (
+	"jfw/tools"
+	"log"
+	"net"
+	"net/http"
+	"net/url"
+	"strings"
+	"sync"
+	"time"
+
+	"github.com/go-xweb/xweb"
+)
+
+//日志过滤器
+type logfilter struct {
+	App         *xweb.App
+	SessionName string
+}
+
+//继承过滤器方法
+func (l *logfilter) Do(w http.ResponseWriter, req *http.Request) bool {
+	req.Proto = GetIp(req)
+	go func() {
+		addLog(l, w, req)
+	}()
+	return true
+}
+
+//定时保存日志
+func SaveLogTask() {
+	lock.Lock()
+	if len(arr) >= 1 {
+		tmp := arr
+		arr = make([]map[string]interface{}, 0)
+		go func() {
+			log.Println("timer..save..visit..log", len(tmp))
+			tools.MQFW.SaveBulk("jy_logs", tmp...)
+		}()
+	}
+	lock.Unlock()
+	time.AfterFunc(5*time.Minute, SaveLogTask)
+}
+
+//内存缓存日志数量,超过此数量存库
+var nc = 200
+
+//内存缓存日志map
+var arr = make([]map[string]interface{}, 0)
+
+//对map的同步
+var lock sync.Mutex
+
+//用线程处理,增加日志
+const (
+	URL = "127.0.0.1-www.zhaobiao.info"
+)
+
+func addLog(l *logfilter, w http.ResponseWriter, req *http.Request) {
+	session := l.App.SessionManager.Session(req, w)
+	userName := session.Get(l.SessionName)
+	hasSession := (userName != nil)
+	timeNow := time.Now()
+	agent := req.Header.Get("user-agent")
+	ref := req.Referer()
+	if session.Get("brefer") == nil {
+		bexist := false
+		rurl, err := url.ParseRequestURI(ref)
+		if err == nil && rurl != nil {
+			host := rurl.Host
+			if host != "" {
+				host = strings.Split(host, ":")[0]
+				if strings.Index(URL, host) < 0 && strings.Index(host, "qmx.top") < 0 {
+					bexist = true
+					session.Set("brefer", 1)
+					session.Set("referhost", host)
+				}
+			}
+		}
+		if !bexist {
+			session.Set("brefer", -1)
+		}
+	}
+	s_url := req.RequestURI
+	date := timeNow.Unix()
+	logs := map[string]interface{}{
+		"l_date":     date,
+		"s_ip":       req.Proto,
+		"s_refer":    ref,
+		"i_year":     timeNow.Year(),
+		"i_month":    timeNow.Month(),
+		"i_day":      timeNow.Day(),
+		"i_hour":     timeNow.Hour(),
+		"i_minutes":  timeNow.Minute(),
+		"s_describe": req.Form,
+		"s_client":   agent,
+		"s_os":       GetOS(agent),
+		"s_browse":   GetBrowse(agent),
+		"s_method":   req.Method,
+		"s_url":      s_url,
+	}
+	if hasSession {
+		logs["s_m_openid"] = session.Get("s_m_openid")
+		logs["s_username"] = userName
+	}
+	lock.Lock()
+	arr = append(arr, logs)
+	if len(arr) >= nc || s_url == "/sl" {
+		tmp := arr
+		arr = make([]map[string]interface{}, 0)
+		go func() {
+			log.Println("save..visit..log", len(tmp))
+			tools.MQFW.SaveBulk("jy_logs", tmp...)
+		}()
+	}
+	lock.Unlock()
+}
+
+//获取平台类型
+func GetOS(useros string) string {
+	osVersion := "其他"
+	if strings.Contains(useros, "NT 6.0") {
+		osVersion = "Windows Vista/Server 2008"
+	} else if strings.Contains(useros, "NT 5.2") {
+		osVersion = "Windows Server 2003"
+	} else if strings.Contains(useros, "NT 5.1") {
+		osVersion = "Windows XP"
+	} else if strings.Contains(useros, "NT 5") {
+		osVersion = "Windows 2000"
+	} else if strings.Contains(useros, "Mac") {
+		osVersion = "Mac"
+	} else if strings.Contains(useros, "Unix") {
+		osVersion = "UNIX"
+	} else if strings.Contains(useros, "Linux") {
+		osVersion = "Linux"
+	} else if strings.Contains(useros, "SunOS") {
+		osVersion = "SunOS"
+	} else if strings.Contains(useros, "NT 6.3") {
+		osVersion = "Window8"
+	} else if strings.Contains(useros, "NT 6.1") {
+		osVersion = "Window7"
+	} else if strings.Contains(useros, "NT 10.0") {
+		osVersion = "Window10"
+	}
+	return osVersion
+}
+
+//获取浏览器类型
+func GetBrowse(userbrowser string) string {
+	browserVersion := "其他"
+	if strings.Contains(userbrowser, "MSIE") {
+		browserVersion = "IE"
+	} else if strings.Contains(userbrowser, "Firefox") {
+		browserVersion = "Firefox"
+	} else if strings.Contains(userbrowser, "Chrome") {
+		browserVersion = "Chrome"
+	} else if strings.Contains(userbrowser, "Safari") {
+		browserVersion = "Safari"
+	} else if strings.Contains(userbrowser, "rv:11.0") {
+		browserVersion = "IE11"
+	}
+
+	return browserVersion
+}
+
+//获取请求ip
+func GetIp(req *http.Request) string {
+	if req == nil {
+		return ""
+	}
+	ip_for := req.Header.Get("x-forwarded-for")
+	ip_client := req.Header.Get("http_client_ip")
+	ip_addr := req.Header.Get("Remote_addr")
+	un := "unknown"
+	if (ip_for != un) && (len(strings.TrimSpace(ip_for)) > 0) {
+		return ip_for
+	}
+	if (ip_client != un) && (len(strings.TrimSpace(ip_client)) > 0) {
+		return ip_client
+	}
+	if (ip_addr != un) && (len(strings.TrimSpace(ip_addr)) > 0) {
+		return ip_addr
+	}
+	ip, _, _ := net.SplitHostPort(req.RemoteAddr)
+	return ip
+}

+ 16 - 0
src/jfw/followpush/src/config.json

@@ -0,0 +1,16 @@
+{
+    "durationMinutes": "3",
+    "elasticPoolSize": 30,
+    "elasticsearch": "http://192.168.3.18:9800",
+    "lastid": "58579a5a012a9abbe627de52",
+    "mongodbName": "qfw",
+    "mongodbPoolSize": "20",
+    "mongodbServers": "192.168.3.18:27080",
+    "redisServers": "sso=192.168.3.14:1379,other=192.168.3.14:1379,push=192.168.3.14:3379",
+    "rpcPort": "8759",
+    "viewDomain": "192.168.3.78",
+    "weixinRpcServer": "127.0.0.1:80",
+    "wxcontent": "剑鱼推送",
+    "wxgroup": "关注项目",
+    "wxtitle": "您关注的项目《%s》有新的公告信息!"
+}

+ 11 - 0
src/jfw/followpush/src/config/config.go

@@ -0,0 +1,11 @@
+package config
+
+import (
+	"qfw/util"
+)
+
+var Sysconfig map[string]interface{}
+
+func init() {
+	util.ReadConfig(&Sysconfig)
+}

+ 71 - 0
src/jfw/followpush/src/filterdata/filterdata.go

@@ -0,0 +1,71 @@
+package filterdata
+
+import (
+	"encoding/json"
+	"log"
+	"qfw/util/redis"
+)
+
+var FilterData *filterDataEntity
+
+type filterDataEntity struct {
+	Array  []string
+	OpenId string
+}
+
+func init() {
+	FilterData = &filterDataEntity{
+		Array: []string{},
+	}
+}
+
+//获取数据
+func (fde *filterDataEntity) Start(openid string) {
+	if openid == "" {
+		return
+	}
+	fde.OpenId = openid
+	data := redis.Get("push", "push_"+openid)
+	if data == nil {
+		return
+	}
+	b, err := json.Marshal(data)
+	if err != nil {
+		log.Println("从redis中取出的数据转成byte数组出错!")
+		return
+	}
+	var array []string
+	if json.Unmarshal(b, &array) != nil {
+		log.Println("byte数组转成string数组出错!")
+		return
+	}
+	fde.Array = array
+}
+
+//判断数据是否存在
+func (fde *filterDataEntity) IsExists(_id string) bool {
+	if _id == "" {
+		return false
+	}
+	for _, v := range fde.Array {
+		if _id == v {
+			return true
+		}
+	}
+	//log.Println(fde.Array)
+	fde.Array = append(fde.Array, _id)
+	return false
+}
+
+//添加数据
+func (fde *filterDataEntity) End(flag int) {
+	if fde.OpenId != "" && flag == 0 {
+		if len(fde.Array) > 0 {
+			redis.Put("push", "push_"+fde.OpenId, fde.Array, -1)
+		} else {
+			redis.Del("push", "push_"+fde.OpenId)
+		}
+	}
+	fde.Array = []string{}
+	fde.OpenId = ""
+}

+ 14 - 0
src/jfw/followpush/src/followpush/config.json

@@ -0,0 +1,14 @@
+{
+    "durationMinutes": "1",
+    "lastid": "168418e062684db687cb0624",
+    "mongodbName": "qfw",
+    "mongodbPoolSize": "20",
+    "mongodbServers": "192.168.3.18:27080",
+    "redisServers": "sso=192.168.3.14:1379,other=192.168.3.14:1379,push=192.168.3.14:3379",
+    "rpcPort": "8759",
+    "viewDomain": "192.168.3.78",
+    "weixinRpcServer": "127.0.0.1:80",
+    "wxcontent": "剑鱼推送",
+    "wxgroup": "关注项目",
+    "wxtitle": "您关注的项目《%s》有新的公告信息!"
+}

+ 138 - 0
src/jfw/followpush/src/followpush/datastruct.go

@@ -0,0 +1,138 @@
+package followpush
+
+import (
+	"qfw/util"
+	"tools"
+)
+
+//构建推送时需要排序的map
+type Arr []*map[string]interface{}
+
+func (a *Arr) Len() int {
+	return len(*a)
+}
+func (a *Arr) Less(i, j int) bool {
+	return util.Int64All((*(*a)[i])["publishtime"]) < util.Int64All((*(*a)[j])["publishtime"])
+}
+func (a *Arr) Swap(i, j int) {
+	tmp := (*a)[i]
+	(*a)[i] = (*a)[j]
+	(*a)[j] = tmp
+}
+
+type DFA struct {
+	Link map[string]interface{}
+}
+
+func (d *DFA) AddWord(keys ...string) {
+	if d.Link == nil {
+		d.Link = make(map[string]interface{})
+	}
+	for _, key := range keys {
+		nowMap := &d.Link
+		for i := 0; i < len(key); i++ {
+			kc := key[i : i+1]
+			if v, ok := (*nowMap)[kc]; ok {
+				nowMap, _ = v.(*map[string]interface{})
+			} else {
+				newMap := map[string]interface{}{}
+				newMap["YN"] = false
+				(*nowMap)[kc] = &newMap
+				nowMap = &newMap
+			}
+			if i == len(key)-1 {
+				(*nowMap)["YN"] = true
+				(*nowMap)["K"] = key
+			}
+		}
+	}
+}
+
+//适合一次查找
+func (d *DFA) CheckSensitiveWord(src string) string {
+	pos := 0
+	nowMap := &d.Link
+	res := ""
+	for i := 0; i < len(src); i++ {
+		word := src[i : i+1]
+		nowMap, _ = (*nowMap)[word].(*map[string]interface{})
+		if nowMap != nil { // 存在,则判断是否为最后一个
+			if pos == 0 {
+				pos = i
+			}
+			if (*nowMap)["YN"].(bool) { // 如果为最后一个匹配规则,结束循环,返回匹配标识数
+				res = util.ObjToString((*nowMap)["K"])
+				pos = 0
+				break
+			}
+		} else {
+			nowMap = &d.Link
+			if pos > 0 {
+				i = pos
+				pos = 0
+			}
+		}
+	}
+	return res
+}
+
+var titleSensitiveDFA DFA
+
+//2.用户关注标题敏感词构建
+func CreateTSM() {
+	titleSensitiveDFA = DFA{}
+	keys := []string{}
+	for k, _ := range Setting {
+		keys = append(keys, k)
+	}
+	titleSensitiveDFA.AddWord(keys...)
+}
+
+//1.用户关注普通map构建
+func UpdateUserSetting() bool {
+	//绝对相等才可以
+	//包括项目名称、项目编号
+	res := false
+	defer util.Catch()
+	//【项目编号|项目名称】*【openid】【title|项目名称】
+	////【项目编号|项目名称】[]*tmp
+	//pcode = &Setting{map[string]*map[string]string{}}
+	Setting = map[string]*[]*map[string]interface{}{}
+	sess := tools.MQFW.GetMgoConn()
+	defer tools.MQFW.DestoryMongoConn(sess)
+	cur := sess.DB(tools.MQFW.DbName).C(FOLLOW_COLLECTION).Find(&map[string]interface{}{
+		"s_openid": map[string]interface{}{
+			"$exists": true,
+		},
+		"i_ispush": map[string]interface{}{
+			"$ne": 0,
+		},
+	}).Select(map[string]interface{}{
+		"s_projectcode": 1,
+		"s_projectname": 1,
+		"s_title":       1,
+		"s_openid":      1,
+		"_id":           1,
+	}).Iter()
+	j := 0
+	for tmp := make(map[string]interface{}); cur.Next(tmp); j++ {
+		defer util.Catch()
+		mtmp := tmp
+		for _, v := range []string{"s_projectcode", "s_projectname"} {
+			pc := util.ObjToString(mtmp[v])
+			if len(pc) > 3 {
+				map1 := Setting[pc]
+				if map1 == nil {
+					map1 = &([]*map[string]interface{}{})
+					Setting[pc] = map1
+				}
+				*map1 = append(*map1, &mtmp)
+			}
+		}
+		tmp = make(map[string]interface{})
+	}
+	res = true
+	//构建敏感词
+	CreateTSM()
+	return res
+}

+ 127 - 0
src/jfw/followpush/src/followpush/followpush.go

@@ -0,0 +1,127 @@
+package followpush
+
+import (
+	. "config"
+	"log"
+	"qfw/util"
+	"sync"
+)
+
+const (
+	FOLLOW_COLLECTION = "follow_project"
+	FOLLOW_PUSH_LOG   = "follow_push_log"
+)
+
+//每次推前构建用户关注内存结构 项目名称和项目代码对应 用户组
+var Setting map[string]*[]*map[string]interface{}
+var WxTitle, WxContent, WxGroup, ViewDomain string
+var Lock = sync.Mutex{}
+
+//加密串
+var se util.SimpleEncrypt
+
+//推送不能大于200个字
+var LastLen int
+
+func init() {
+	se = util.SimpleEncrypt{Key: "topnet"}
+	WxTitle = util.ObjToString(Sysconfig["wxtitle"])
+	WxContent = util.ObjToString(Sysconfig["wxcontent"])
+	WxGroup = util.ObjToString(Sysconfig["wxgroup"])
+	ViewDomain = util.ObjToString(Sysconfig["viewDomain"])
+	LastLen = 200 - len([]rune(WxContent)) - len([]rune(WxGroup))
+}
+
+//开始查询最新的数据,推给用户
+var muser map[*map[string]interface{}]*[]*map[string]interface{}
+
+func Job() {
+	Lock.Lock()
+	defer Lock.Unlock()
+	lastid := util.ObjToString(Sysconfig["lastid"])
+	log.Println("start push follow info..", lastid)
+	pushByEs(lastid)
+}
+
+/**
+	lastObjecIid := bson.ObjectIdHex(lastid)
+	muser = map[*map[string]interface{}]*[]*map[string]interface{}{}
+	if UpdateUserSetting() {
+		sess := tools.MQFW.GetMgoConn()
+		defer tools.MQFW.DestoryMongoConn(sess)
+		//结果集
+		cur := sess.DB(tools.MQFW.DbName).C("bidding").Find(&map[string]interface{}{
+			"publishtime": bson.M{
+				"$gt": time.Now().Unix() - 10*86400,
+			},
+			"extracttype": 2,
+			"_id": bson.M{
+				"$gt": lastObjecIid,
+			},
+		}).Select(map[string]interface{}{
+			"projectcode": 1,
+			"projectname": 1,
+			"title":       1,
+			"_id":         1,
+			"publishtime": 1,
+			"area":        1,
+			"href":        1,
+			"type":        1,
+			"toptype":     1,
+			"subtype":     1,
+		}).Iter()
+		for tmp := make(map[string]interface{}); cur.Next(tmp); {
+			//优先匹配项目代码、项目名称
+			mtmp := tmp
+			for _, v := range []string{"projectcode", "projectname"} {
+				pc := util.ObjToString(tmp[v])
+				if pc != "" {
+					addInfoTouser(pc, &mtmp)
+				}
+			}
+			//再从标题中查询
+			//titleTmp := util.ObjToString(tmp["title"])
+			//titleKey := titleSensitiveDFA.CheckSensitiveWord(titleTmp)
+			//if titleKey != "" {
+			//addInfoTouser(titleKey, &mtmp)
+			//}
+			//存储最后推送id
+			tid := tmp["_id"].(bson.ObjectId)
+			if lastObjecIid.Hex() < tid.Hex() {
+				lastObjecIid = tid
+			}
+			tmp = make(map[string]interface{})
+		}
+		Sysconfig["lastid"] = lastObjecIid.Hex()
+	}
+	go push(&muser)
+}
+**/
+
+/**
+func addInfoTouser(pc string, mtmp *map[string]interface{}) {
+	pcm := Setting[pc]
+	if pcm != nil {
+		for _, userSet := range *pcm {
+			userRes := muser[userSet]
+			if userRes == nil {
+				userRes = &[]*map[string]interface{}{}
+				muser[userSet] = userRes
+				*userRes = append(*userRes, mtmp)
+			} else {
+				//查重
+				b := false
+				for _, t1 := range *userRes {
+					if (*t1)["_id"] == (*mtmp)["_id"] {
+						b = true
+						break
+					}
+				}
+				if !b && len(*userRes) < 50 {
+					*userRes = append(*userRes, mtmp)
+				}
+			}
+		}
+	}
+}
+**/

+ 18 - 0
src/jfw/followpush/src/followpush/followpush_test.go

@@ -0,0 +1,18 @@
+package followpush
+
+import (
+	"log"
+	"testing"
+	"time"
+)
+
+func Test_updateSetting(t *testing.T) {
+	UpdateUserSetting()
+	for k, v := range Setting {
+		for _, vv := range *v {
+			log.Println(k, (*vv)["s_openid"])
+		}
+	}
+	Job()
+	time.Sleep(100 * time.Second)
+}

+ 369 - 0
src/jfw/followpush/src/followpush/push.go

@@ -0,0 +1,369 @@
+package followpush
+
+import (
+	. "config"
+	"filterdata"
+	"fmt"
+	"log"
+	"qfw/util"
+	"qfw/util/elastic"
+	qrpc "qfw/util/rpc"
+	"sort"
+	"strconv"
+	"strings"
+	"sync"
+	"time"
+	"tools"
+	"weixinrpc"
+
+	"gopkg.in/mgo.v2/bson"
+)
+
+var (
+	MaxId   = `{"query":{"filtered":{"filter":{"bool":{"must":{"range":{"id":{"gt":"%s"}}}}}}},"_source":["_id","comeintime"],"sort":{"id":"desc"},"from":0,"size":1}`
+	Query   = `{ "query": { "bool": { "must": [%s], "should": [%s], "minimum_should_match": 1 } }, "_source": [ "_id","title","publishtime","area","type","toptype","subtype","projectname","projectcode","href","infoformat" ], "sort": [ { "publishtime": "desc" } ], "from": 0, "size": 50 }`
+	IDRange = `{"range":{"id":{"gt":"%s","lte":"%s"}}}`
+	TERM    = `{"term":{"%s":"%s"}}`
+	DB      = "bidding"
+)
+
+//开始推送
+func pushByEs(_id string) bool {
+	defer util.Catch()
+	resId := elastic.Get(DB, DB, fmt.Sprintf(MaxId, _id))
+	lastid := ""
+	log.Println("push-lastid", resId)
+	if resId != nil && *resId != nil && len(*resId) == 1 {
+		lastid = util.ObjToString((*resId)[0]["_id"])
+	} else {
+		log.Println("未查找到数据...", fmt.Sprintf(MaxId, _id))
+		return false
+	}
+	idrange := fmt.Sprintf(IDRange, _id, lastid)
+	sess := tools.MQFW.GetMgoConn()
+	defer tools.MQFW.DestoryMongoConn(sess)
+	cur := sess.DB(tools.MQFW.DbName).C(FOLLOW_COLLECTION).Find(&map[string]interface{}{
+		"s_openid": map[string]interface{}{
+			"$exists": true,
+		},
+		"i_ispush": map[string]interface{}{
+			"$ne": 0,
+		},
+	}).Select(map[string]interface{}{
+		"s_projectcode": 1,
+		"s_projectname": 1,
+		"s_title":       1,
+		"s_openid":      1,
+		"_id":           1,
+	}).Iter()
+	j := 0
+	for tmp := make(map[string]interface{}); cur.Next(tmp); j++ {
+		util.Try(func() {
+			scode := util.ObjToString(tmp["s_projectcode"])
+			sname := util.ObjToString(tmp["s_projectname"])
+			openid := util.ObjToString(tmp["s_openid"])
+			go FindData(tmp["_id"], util.ObjToString(tmp["s_title"]), sname, scode, openid, idrange, true, true)
+		}, func(e interface{}) {
+			log.Println(e)
+		})
+		tmp = make(map[string]interface{})
+	}
+	log.Println("push-over,user-count:", j)
+	Sysconfig["lastid"] = lastid
+	return true
+}
+
+var findpool = make(chan bool, 10)
+var Pushlock = sync.Mutex{}
+
+//不保存不推送-只保存不推送-保存推送
+func FindData(fid interface{}, title, sname, scode, openid, idrange string, bsave, bpush bool) *Arr {
+	findpool <- true
+	defer func() {
+		<-findpool
+	}()
+	q1 := []string{}
+	if scode != "" {
+		q1 = append(q1, fmt.Sprintf(TERM, "projectcode", scode))
+	}
+	if sname != "" {
+		q1 = append(q1, fmt.Sprintf(TERM, "projectname", elastic.ReplaceYH(sname)))
+	}
+	var pushArray = &Arr{}
+	if len(q1) > 0 {
+		res := elastic.Get(DB, DB, fmt.Sprintf(Query, idrange, strings.Join(q1, ",")))
+		if res != nil && *res != nil && len(*res) > 0 {
+			//顺序处理,后序会有性能瓶颈,filterdata
+			util.Try(func() {
+				if !bsave {
+					for _, info := range *res {
+						tmp := map[string]interface{}{}
+						sid := util.BsonIdToSId(info["_id"])
+						if title != sid { //title在此处传的是关注信息id
+							tmp["s_id"] = sid
+							tmp["s_eid"] = util.EncodeArticleId2ByCheck(sid)
+							tmp["s_title"] = info["title"]
+							tmp["l_publishtime"] = info["publishtime"]
+							tmp["s_province"] = info["area"]
+							tmp["s_type"] = util.ObjToString(info["type"])
+							tmp["s_toptype"] = util.ObjToString(info["toptype"])
+							tmp["s_subtype"] = util.ObjToString(info["subtype"])
+							tmp["s_projectname"] = util.ObjToString(info["projectname"])
+							tmp["s_projectcode"] = util.ObjToString(info["projectcode"])
+							tmp["s_url"] = util.ObjToString(info["href"])
+							*pushArray = append(*pushArray, &tmp)
+						}
+					}
+				} else {
+					Pushlock.Lock()
+					defer Pushlock.Unlock()
+					filterdata.FilterData.Start(openid)
+					defer filterdata.FilterData.End(0)
+					//1.组织信息、
+					//a_relationinfo s_id s_title s_projectname  s_projectcode  l_publishtime s_url
+					var ids []string
+					for _, info := range *res {
+						tmp := map[string]interface{}{}
+						sid := util.BsonIdToSId(info["_id"])
+						tmp["s_id"] = sid
+						tmp["s_eid"] = util.EncodeArticleId2ByCheck(sid)
+						tmp["s_title"] = info["title"]
+						tmp["l_publishtime"] = info["publishtime"]
+						tmp["s_province"] = info["area"]
+						tmp["s_type"] = util.ObjToString(info["type"])
+						tmp["s_toptype"] = util.ObjToString(info["toptype"])
+						tmp["s_subtype"] = util.ObjToString(info["subtype"])
+						tmp["s_projectname"] = util.ObjToString(info["projectname"])
+						tmp["s_projectcode"] = util.ObjToString(info["projectcode"])
+						tmp["s_url"] = util.ObjToString(info["href"])
+						if filterdata.FilterData.IsExists(sid) {
+							continue
+						}
+						ids = append(ids, sid)
+						*pushArray = append(*pushArray, &tmp)
+					}
+					go func() {
+						//2.推送、
+						if pushArray.Len() > 0 {
+							sort.Sort(pushArray)
+							//更新用户关注的a_relationinfo、保存到推送记录表、推送给用户
+							//log.Println(fid, ids)
+							if fid != nil && tools.MQFW.Update(FOLLOW_COLLECTION, &bson.M{
+								"_id": fid,
+							}, &bson.M{
+								"$set": bson.M{
+									"l_lastpushtime": (*((*pushArray)[0]))["l_publishtime"],
+									"a_lastpushids":  ids,
+								},
+								"$pushAll": bson.M{
+									"a_relationinfo": pushArray,
+								},
+							}, false, false) && bpush {
+								//进入推送逻辑
+								tit := sname
+								if tit == "" {
+									tit = title
+								}
+								if tit == "" {
+									tit = scode
+								}
+								if tit != "" {
+									//go func() {
+									followid := util.BsonIdToSId(fid)
+									infoid := tools.MQFW.Save(FOLLOW_PUSH_LOG, &bson.M{
+										"s_openid":       openid,
+										"a_relationinfo": pushArray,
+										"l_date":         time.Now().Unix(),
+										"s_title":        title,
+										"s_projectcode":  scode,
+										"s_projectname":  sname,
+										"s_followid":     followid,
+									})
+									if infoid != "" {
+										lastTime := util.Int64All((*(*pushArray)[0])["l_publishtime"])
+										pushtt := fmt.Sprintf(WxTitle, tit)
+										Tip1 := ""
+										minute := time.Now().Unix() - lastTime
+										if minute > -1 && minute < 61 {
+											Tip1 = fmt.Sprintf("%d秒前发布的:\n", minute)
+										} else {
+											minute = minute / 60
+											if minute < 121 {
+												if minute < 1 {
+													minute = 1
+												}
+												Tip1 = fmt.Sprintf("%d分钟前发布的:\n", minute)
+											}
+										}
+										LastTip := ""
+										pushnum := len(*pushArray)
+										if pushnum > 1 {
+											LastTip = fmt.Sprintf("...(共%d条)", pushnum)
+										}
+										LastLen = LastLen - len([]rune(pushtt)) - len([]rune(Tip1))
+										Remark := ""
+										bshow := false
+										for n := 1; n < pushnum+1; n++ {
+											Remark += fmt.Sprintf("%d %s\n", n, (*(*pushArray)[n-1])["s_title"])
+											if len([]rune(Remark)) > LastLen {
+												if n == pushnum {
+													bshow = true
+												}
+												break
+											}
+										}
+										if bshow {
+											LastTip = ""
+										}
+										go log.Println("push", openid, pushnum, tit)
+										weixinrpc.SendWinXin(&qrpc.NotifyMsg{
+											Openid:  openid,
+											Title:   fmt.Sprintf(WxTitle, tit),
+											Remark:  Tip1 + Remark + LastTip,
+											Detail:  WxContent,
+											Service: WxGroup,
+											Url:     ViewDomain + "/front/sess/" + se.EncodeString(openid+",uid,"+strconv.Itoa(int(time.Now().Unix()))+",wxpushfollowlist") + "__" + infoid + "__" + followid,
+										})
+									}
+									//}()
+								}
+								//推送结束
+							}
+						}
+					}()
+				}
+			}, func(e interface{}) {
+				log.Println("给用户推送关注信息时出错:", e)
+			})
+		}
+	}
+	return pushArray
+}
+
+/**
+//开始推送
+func push(muser *map[*map[string]interface{}]*[]*map[string]interface{}) {
+	if muser != nil && len(*muser) > 0 {
+		for userSet, infos := range *muser {
+			openid := (*userSet)["s_openid"].(string)
+			filterdata.FilterData.Start(openid)
+			util.Try(func() {
+				//1.组织信息、
+				//a_relationinfo s_id s_title s_projectname  s_projectcode  l_publishtime s_url
+				var pushArray = &Arr{}
+				var ids []string
+				if infos != nil && len(*infos) > 0 {
+					for _, info := range *infos {
+						tmp := map[string]interface{}{}
+						sid := fmt.Sprintf("%x", string((*info)["_id"].(bson.ObjectId)))
+						ids = append(ids, sid)
+						tmp["s_id"] = sid
+						tmp["s_eid"] = util.EncodeArticleId(sid)
+						tmp["s_title"] = (*info)["title"]
+						tmp["l_publishtime"] = (*info)["publishtime"]
+						tmp["s_province"] = (*info)["area"]
+						tmp["s_type"] = util.ObjToString((*info)["type"])
+						tmp["s_toptype"] = util.ObjToString((*info)["toptype"])
+						tmp["s_subtype"] = util.ObjToString((*info)["subtype"])
+						tmp["s_projectname"] = util.ObjToString((*info)["projectname"])
+						tmp["s_projectcode"] = util.ObjToString((*info)["projectcode"])
+						tmp["s_url"] = util.ObjToString((*info)["href"])
+						if filterdata.FilterData.IsExists(sid) {
+							continue
+						}
+						*pushArray = append(*pushArray, &tmp)
+					}
+				}
+				filterdata.FilterData.End(0)
+				//2.推送、
+				if pushArray.Len() > 0 {
+					sort.Sort(pushArray)
+					//更新用户关注的a_relationinfo、保存到推送记录表、推送给用户
+					if tools.MQFW.Update(FOLLOW_COLLECTION, &bson.M{
+						"_id": (*userSet)["_id"],
+					}, &bson.M{
+						"$set": bson.M{
+							"l_lastpushtime": (*((*pushArray)[0]))["l_publishtime"],
+							"a_lastpushids":  ids,
+						},
+						"$pushAll": bson.M{
+							"a_relationinfo": pushArray,
+						},
+					}, false, false) {
+						tit := util.ObjToString((*userSet)["s_projectname"])
+						if tit == "" {
+							tit = util.ObjToString((*userSet)["s_title"])
+						}
+						if tit == "" {
+							tit = util.ObjToString((*userSet)["s_projectcode"])
+						}
+						if tit != "" {
+							//go func() {
+							followid := fmt.Sprintf("%x", string((*userSet)["_id"].(bson.ObjectId)))
+							infoid := tools.MQFW.Save(FOLLOW_PUSH_LOG, &bson.M{
+								"s_openid":       openid,
+								"a_relationinfo": pushArray,
+								"l_date":         time.Now().Unix(),
+								"s_title":        util.ObjToString((*userSet)["s_title"]),
+								"s_projectcode":  util.ObjToString((*userSet)["s_projectcode"]),
+								"s_projectname":  util.ObjToString((*userSet)["s_projectname"]),
+								"s_followid":     followid,
+							})
+							if infoid != "" {
+								lastTime := util.Int64All((*(*pushArray)[0])["l_publishtime"])
+								pushtt := fmt.Sprintf(WxTitle, tit)
+								Tip1 := ""
+								minute := time.Now().Unix() - lastTime
+								if minute > -1 && minute < 61 {
+									Tip1 = fmt.Sprintf("%d秒前发布的:\n", minute)
+								} else {
+									minute = minute / 60
+									if minute < 121 {
+										if minute < 1 {
+											minute = 1
+										}
+										Tip1 = fmt.Sprintf("%d分钟前发布的:\n", minute)
+									}
+								}
+								LastTip := ""
+								pushnum := len(*pushArray)
+								if pushnum > 1 {
+									LastTip = fmt.Sprintf("...(共%d条)", pushnum)
+								}
+								LastLen = LastLen - len([]rune(pushtt)) - len([]rune(Tip1))
+								Remark := ""
+								bshow := false
+								for n := 1; n < pushnum+1; n++ {
+									Remark += fmt.Sprintf("%d %s\n", n, (*(*pushArray)[n-1])["s_title"])
+									if len([]rune(Remark)) > LastLen {
+										if n == pushnum {
+											bshow = true
+										}
+										break
+									}
+								}
+								if bshow {
+									LastTip = ""
+								}
+								go log.Println("push", openid, pushnum, tit)
+								weixinrpc.SendWinXin(&qrpc.NotifyMsg{
+									Openid:  openid,
+									Title:   fmt.Sprintf(WxTitle, tit),
+									Remark:  Tip1 + Remark + LastTip,
+									Detail:  WxContent,
+									Service: WxGroup,
+									Url:     ViewDomain + "/front/sess/" + se.EncodeString(openid+",uid,"+strconv.Itoa(int(time.Now().Unix()))+",wxpushfollowlist") + "__" + infoid + "__" + followid,
+								})
+							}
+							//}()
+						}
+					}
+				}
+			}, func(e interface{}) {
+				filterdata.FilterData.End(0)
+				log.Println("给用户推送关注信息时出错:", e)
+			})
+		}
+	}
+}
+**/

+ 116 - 0
src/jfw/followpush/src/listdb/listdb.go

@@ -0,0 +1,116 @@
+package listdb
+
+import (
+	"config"
+	"followpush"
+	"log"
+	"qfw/util"
+	"sort"
+	"sync"
+	"time"
+	"tools"
+
+	"gopkg.in/mgo.v2/bson"
+)
+
+type arr []*map[string]interface{}
+
+func (a *arr) Len() int {
+	return len(*a)
+}
+func (a *arr) Less(i, j int) bool {
+	return util.Int64All((*(*a)[i])["publishtime"]) < util.Int64All((*(*a)[j])["publishtime"])
+}
+func (a *arr) Swap(i, j int) {
+	tmp := (*a)[i]
+	(*a)[i] = (*a)[j]
+	(*a)[j] = tmp
+}
+
+type ListDB struct {
+	Lock      *sync.Mutex
+	DB        *arr
+	Stype     string
+	timestamp int64
+	Lastid    bson.ObjectId //更新的最后id
+}
+
+var fields map[string]interface{} = map[string]interface{}{
+	"type":        1,
+	"toptype":     1,
+	"subtype":     1,
+	"comeintime":  1,
+	"publishtime": 1,
+	"title":       1,
+	"projectname": 1,
+	"projectcode": 1,
+	"area":        1,
+	"href":        1,
+	"areaval":     1,
+}
+
+var Bid *ListDB
+
+func Inits() {
+	Bid = &ListDB{
+		Lock:      new(sync.Mutex),
+		DB:        &arr{},
+		timestamp: 0,
+	}
+	go Bid.update()
+}
+
+func (l *ListDB) update() {
+	l.UpdateData()
+	time.AfterFunc(3*time.Minute, l.update)
+}
+
+func (l *ListDB) UpdateData() {
+	util.Catch()
+	followpush.Lock.Lock()
+	pushlastid := config.Sysconfig["lastid"]
+	followpush.Lock.Unlock()
+	l.Lock.Lock()
+	defer l.Lock.Unlock()
+	pushOid := bson.ObjectIdHex(pushlastid.(string))
+	q := map[string]interface{}{
+		//"extracttype": bson.M{"$exists": true},
+		"extracttype": 2,
+	}
+	if l.Lastid.Valid() && l.Lastid.Hex() < pushOid.Hex() {
+		//此条件要更新
+		q["$and"] = []bson.M{
+			bson.M{"_id": bson.M{"$gt": l.Lastid}},
+			bson.M{"_id": bson.M{"$lte": pushOid}},
+		}
+	} else if !l.Lastid.Valid() {
+		//此条件是初始化数据,查询数据小于等于pushid即可
+		q["_id"] = bson.M{
+			"$lte": pushOid,
+		}
+	} else {
+		return
+	}
+	//log.Println(q, "query")
+	util.Try(func() {
+		session := tools.MQFW.GetMgoConn()
+		defer tools.MQFW.DestoryMongoConn(session)
+		//此处不排序,谁查谁排序(不索引会造成发布时间乱序)
+		query := session.DB(tools.MQFW.DbName).C("bidding").Find(q).Select(fields).Sort("publishtime").Iter()
+		for tmp := new(map[string]interface{}); query.Next(tmp); {
+			*l.DB = append(*l.DB, tmp)
+			last := (*tmp)["_id"].(bson.ObjectId)
+			if !l.Lastid.Valid() || l.Lastid.Hex() < last.Hex() {
+				l.Lastid = last
+			}
+			if l.DB.Len() > 400000 {
+				*l.DB = (*l.DB)[l.DB.Len()-400000:]
+			}
+			tmp = new(map[string]interface{})
+		}
+	}, func(e interface{}) {
+		log.Println(e)
+	})
+	sort.Sort(l.DB)
+	//log.Println(l.DB.Len())
+}

+ 47 - 0
src/jfw/followpush/src/main.go

@@ -0,0 +1,47 @@
+package main
+
+import (
+	"config"
+	"followpush"
+	"log"
+	"net"
+	"net/http"
+	_ "net/http/pprof"
+	"net/rpc"
+	"qfw/util"
+	"qfw/util/elastic"
+	"qfw/util/redis"
+	"rpcpush"
+	"time"
+	"timetask"
+)
+
+func init() {
+	redis.InitRedisBySize(config.Sysconfig["redisServers"].(string), 200, 30, 300)
+	elastic.InitElasticSize(config.Sysconfig["elasticsearch"].(string), util.IntAllDef(config.Sysconfig["elasticPoolSize"], 30))
+}
+
+func main() {
+	time.Sleep(2 * time.Second)
+	log.Println(config.Sysconfig["rpcPort"], config.Sysconfig)
+	go runJob()
+	go timetask.ClearRedis()
+	//listdb.Inits()
+	crpc := new(rpcpush.FollowPushRpc)
+	rpc.Register(crpc)
+	rpc.HandleHTTP()
+	port, _ := config.Sysconfig["rpcPort"].(string)
+	l, _ := net.Listen("tcp", ":"+port)
+	go http.Serve(l, nil)
+	log.Println("启动关注推送系统", port)
+	b := make(chan bool)
+	<-b
+}
+
+func runJob() {
+	util.Try(func() {
+		followpush.Job()
+		util.WriteSysConfig(config.Sysconfig)
+	}, func(e interface{}) {})
+	time.AfterFunc(time.Duration(util.IntAll(config.Sysconfig["durationMinutes"]))*time.Minute, runJob)
+}

+ 27 - 0
src/jfw/followpush/src/main_test.go

@@ -0,0 +1,27 @@
+package main
+
+import (
+	"log"
+	"strings"
+	"testing"
+
+	"gopkg.in/mgo.v2/bson"
+)
+
+func Test_main(t *testing.T) {
+	tmp := make(map[string]interface{})
+	tmp["ss"] = "ssss"
+	ss := tmp
+	m1 := map[string]*map[string]interface{}{}
+	m1["aa"] = &ss
+	tmp = make(map[string]interface{})
+	log.Println(m1["aa"])
+
+	log.Println(bson.ObjectIdHex("100000ee36b82b12a0000001").Hex())
+
+}
+
+func Test_1(t *testing.T) {
+	log.Println(strings.Contains("aassdf", "0"))
+
+}

+ 122 - 0
src/jfw/followpush/src/rpcpush/findData.go

@@ -0,0 +1,122 @@
+package rpcpush
+
+import (
+	"filterdata"
+	"followpush"
+	"log"
+
+	"gopkg.in/mgo.v2/bson"
+)
+
+func RpcFindFollow(projectname, projectcode, followid, thisid, openid string, flag int) *followpush.Arr {
+	bsave := false
+	var ff interface{}
+	if len(followid) > 5 {
+		bsave = true
+		ff = bson.ObjectIdHex(followid)
+	}
+	if thisid != "" && bsave {
+		log.Println(thisid, "--add follow")
+		go func() {
+			followpush.Pushlock.Lock()
+			defer followpush.Pushlock.Unlock()
+			filterdata.FilterData.Start(openid)
+			defer filterdata.FilterData.End(0)
+			filterdata.FilterData.IsExists(thisid)
+		}()
+	}
+	res := followpush.FindData(ff, thisid, projectname, projectcode, openid, "", bsave, false)
+	if flag == 1 {
+		return res
+	}
+	return nil
+}
+
+/**
+	"filterdata"
+	"fmt"
+	"followpush"
+	"listdb"
+	"log"
+	"qfw/util"
+	"sort"
+
+	"gopkg.in/mgo.v2/bson"
+	//"strings"
+	"tools"
+func RpcFindFollow(projectname, projectcode, followid, thisid, openid string, flag int) *followpush.Arr {
+	LDB := listdb.Bid
+	LDB.Lock.Lock()
+	defer LDB.Lock.Unlock()
+	res := []*map[string]interface{}{}
+	util.Catch()
+	filterdata.FilterData.Start(openid)
+	defer filterdata.FilterData.End(flag)
+	filterdata.FilterData.IsExists(thisid)
+	for k := LDB.DB.Len() - 1; k > -1; k-- {
+		tmp := *(*LDB.DB)[k]
+		mtmp := tmp
+		pc := util.ObjToString(tmp["projectcode"])
+		//titleTmp := util.ObjToString(tmp["title"])
+		by := false
+		if pc != "" && pc == projectcode {
+			by = true
+		} else {
+			pc = util.ObjToString(tmp["projectname"])
+			if pc != "" && pc == projectname {
+				by = true
+			}
+			//if !by && (projectname != "" && strings.Contains(titleTmp, projectname)) || (projectcode != "" && strings.Contains(titleTmp, projectcode)) {
+			//	by = true
+			//}
+		}
+		if by && !filterdata.FilterData.IsExists(util.BsonIdToSId(tmp["_id"])) {
+			//res[titleTmp] = &mtmp
+			res = append(res, &mtmp)
+		}
+		if len(res) > 30 {
+			break
+		}
+	}
+	log.Println(thisid, len(res), followid)
+	if len(res) > 0 {
+		var pushArray = &followpush.Arr{}
+		sort.Sort(pushArray)
+		for _, info := range res {
+			tid := fmt.Sprintf("%x", string((*info)["_id"].(bson.ObjectId)))
+			if tid != thisid {
+				tmp := map[string]interface{}{}
+				tmp["s_id"] = tid
+				tmp["s_title"] = (*info)["title"]
+				tmp["l_publishtime"] = (*info)["publishtime"]
+				tmp["s_province"] = (*info)["area"]
+				tmp["s_type"] = util.ObjToString((*info)["type"])
+				tmp["s_toptype"] = util.ObjToString((*info)["toptype"])
+				tmp["s_subtype"] = util.ObjToString((*info)["subtype"])
+				tmp["s_projectname"] = util.ObjToString((*info)["projectname"])
+				tmp["s_projectcode"] = util.ObjToString((*info)["projectcode"])
+				tmp["s_url"] = util.ObjToString((*info)["href"])
+				*pushArray = append(*pushArray, &tmp)
+			}
+		}
+		if flag == 1 {
+			return pushArray
+		}
+		//2.保存、
+		if pushArray.Len() > 0 {
+			//更新用户关注的a_relationinfo、保存给用户
+			tools.MQFW.Update(followpush.FOLLOW_COLLECTION, &bson.M{
+				"_id": bson.ObjectIdHex(followid),
+			}, &bson.M{
+				"$set": bson.M{
+					"l_lastpushtime": (*((*pushArray)[0]))["l_publishtime"],
+				},
+				"$pushAll": bson.M{
+					"a_relationinfo": pushArray,
+				},
+			}, false, false)
+		}
+	}
+	return nil
+}
+**/

+ 31 - 0
src/jfw/followpush/src/rpcpush/rpcpush.go

@@ -0,0 +1,31 @@
+package rpcpush
+
+import (
+	"encoding/json"
+	"qfw/util"
+	qrpc "qfw/util/rpc"
+)
+
+type FollowPushRpc struct {
+}
+
+//RPC调用结果预览
+func (p *FollowPushRpc) FollowPush(data *qrpc.FollowPush, Reply *[]byte) error {
+	util.Try(func() {
+		/**
+		followpush.Lock.Lock()
+		pushlastid := config.Sysconfig["lastid"]
+		followpush.Lock.Unlock()
+		lastid := bson.ObjectIdHex(pushlastid.(string))
+		dbLastid := listdb.Bid.Lastid
+		if !dbLastid.Valid() || dbLastid.Hex() < lastid.Hex() {
+			listdb.Bid.UpdateData()
+		}
+		**/
+		res := RpcFindFollow(data.ProjectName, data.ProjectCode, data.FollowId, data.InfoId, data.OpenId, data.Flag)
+		if res != nil {
+			*Reply, _ = json.Marshal([]*map[string]interface{}(*res))
+		}
+	}, func(e interface{}) {})
+	return nil
+}

+ 23 - 0
src/jfw/followpush/src/timetask/clearredis.go

@@ -0,0 +1,23 @@
+package timetask
+
+import (
+	"log"
+	"qfw/util/redis"
+	"time"
+)
+
+//每天凌晨清redis
+func ClearRedis() {
+	for {
+		time.Sleep(30 * time.Second)
+		now := time.Now()
+		//每天凌晨
+		next := time.Date(now.Year(), now.Month(), now.Day()+1, 0, 0, 0, 0, now.Location())
+		timer := time.NewTimer(next.Sub(now)) //自动删除
+		select {
+		case <-timer.C:
+			log.Println("每天凌晨清redis数据。")
+			redis.DelByCodePattern("push", "push_*")
+		}
+	}
+}

+ 18 - 0
src/jfw/followpush/src/tools/mongo.go

@@ -0,0 +1,18 @@
+package tools
+
+import (
+	. "config"
+	"qfw/util"
+	"qfw/util/mongodb"
+)
+
+var MQFW mongodb.MongodbSim
+
+func init() {
+	MQFW = mongodb.MongodbSim{
+		MongodbAddr: Sysconfig["mongodbServers"].(string),
+		Size:        util.IntAll(Sysconfig["mongodbPoolSize"]),
+		DbName:      Sysconfig["mongodbName"].(string),
+	}
+	MQFW.InitPool()
+}

+ 1 - 0
src/jfw/followpush/src/tools/tools.go

@@ -0,0 +1 @@
+package tools

+ 76 - 0
src/jfw/followpush/src/weixinrpc/weixinrpc.go

@@ -0,0 +1,76 @@
+package weixinrpc
+
+import (
+	"config"
+	"log"
+	"net/rpc"
+	"qfw/util"
+	qrpc "qfw/util/rpc"
+	"strings"
+	"time"
+	"tools"
+)
+
+var wxpool chan bool = make(chan bool, 30)
+
+//微信远程调用,实现模板发送消息
+func SendWinXin(p *qrpc.NotifyMsg) {
+	wxpool <- true
+	defer func() {
+		<-wxpool
+	}()
+	util.Try(func() {
+		client, err := rpc.DialHTTP("tcp", config.Sysconfig["weixinRpcServer"].(string))
+		defer client.Close()
+		if err != nil {
+			log.Println(err.Error())
+			return
+		}
+		var repl qrpc.RpcResult
+		err = client.Call("WeiXinRpc.SendPushMsg", p, &repl)
+		if err != nil {
+			log.Println(err.Error())
+		}
+		res := string(repl)
+		if strings.Contains(res, "[46004]") || strings.Contains(res, "[65302]") || strings.Contains(res, "[43004]") || strings.Contains(res, "[40003]") {
+			updateIsPush(p.Openid, 0)
+		}
+	}, func(e interface{}) {})
+	time.Sleep(10 * time.Millisecond)
+}
+
+//微信远程调用,实现模板发送消息
+func FollowPush(p *qrpc.NotifyMsg) {
+	wxpool <- true
+	defer func() {
+		<-wxpool
+	}()
+	util.Try(func() {
+		client, err := rpc.DialHTTP("tcp", config.Sysconfig["weixinRpcServer"].(string))
+		defer client.Close()
+		if err != nil {
+			log.Println(err.Error())
+			return
+		}
+		var repl qrpc.RpcResult
+		err = client.Call("WeiXinRpc.SendPushMsg", p, &repl)
+		if err != nil {
+			log.Println(err.Error())
+		}
+	}, func(e interface{}) {})
+	time.Sleep(10 * time.Millisecond)
+}
+
+//修改是否推送的状态
+func updateIsPush(openid string, status int) {
+	tools.MQFW.Update("user", map[string]interface{}{"s_m_openid": openid}, map[string]interface{}{
+		"$set": map[string]interface{}{
+			"i_ispush": status,
+		},
+	}, false, false)
+	tools.MQFW.Update("follow_project", map[string]interface{}{"s_openid": openid}, map[string]interface{}{
+		"$set": map[string]interface{}{
+			"i_ispush": status,
+		},
+	}, false, true)
+}

+ 99 - 0
src/jfw/front/dealinfo.go

@@ -0,0 +1,99 @@
+package front
+
+import (
+	//"encoding/json"
+	"jfw/config"
+	//"mfw/util"
+	qutil "qfw/util"
+	"regexp"
+	//"strings"
+)
+
+//var client *util.Client
+var reg = regexp.MustCompile("^[0-9a-zA-Z-.]+$")
+var reg_space = regexp.MustCompile("(?ism)(<style.*?>.*?</style>)|([.#]?\\w{1,20}\\{.*?\\})|(<.*?>)|(\\\\t)+|\\t|( +)|( +)|(" + string(rune(160)) + "+)")
+var reg_row = regexp.MustCompile("(?i)<(tr|div|p)[^>]*?>|(\\n)+")
+var reg_dh = regexp.MustCompile("[,]+")
+var reg_newdb = regexp.MustCompile("([:,、:,。.;])[,]")
+var reg_no = regexp.MustCompile("^[0-9]*$")
+var MSG_SERVER = "123.56.236.148:7070"
+var DesLen = 120
+
+func init_2() {
+	ser := qutil.ObjToString(config.Sysconfig["msg_server"])
+	if ser != "" {
+		MSG_SERVER = ser
+	}
+	//client, _ = util.StartClient(func(p *util.Packet) {
+	//异步调用
+	//}, MSG_SERVER, "剑鱼抽关键词", []int{})
+}
+
+//func DealInfo_1(obj *map[string]interface{}, coll string) {
+//	defer qutil.Catch()
+//	if (*obj)["keywords"] != nil && (*obj)["description"] != nil {
+//		return
+//	} else {
+//		(*obj)["keywords"] = ""
+//		(*obj)["description"] = ""
+//	}
+//	title := qutil.ObjToString((*obj)["title"])
+//	ret, _ := client.Call("", util.UUID(8), 4010, util.SENDTO_TYPE_RAND_RECIVER, title, 3)
+//	var m [][]string
+//	json.Unmarshal(ret, &m)
+//	arr := []string{}
+//	keyword := []string{}
+//	keywordnew := []string{}
+//	for _, tmp := range m {
+//		if reg.MatchString(tmp[0]) {
+//			arr = append(arr, tmp[0])
+//		} else {
+//			if len(arr) > 0 {
+//				str := strings.Join(arr, "")
+//				keyword = append(keyword, str)
+//				arr = []string{}
+//			}
+//			if len(tmp[0]) > 3 && (strings.HasPrefix(tmp[1], "n") || tmp[1] == "v" || tmp[1] == "vn" || strings.HasPrefix(tmp[1], "g")) {
+//				keyword = append(keyword, tmp[0])
+//			}
+//		}
+//	}
+//	for _, v := range keyword {
+//		v = reg_no.ReplaceAllString(v, "")
+//		if len(v) > 0 {
+//			keywordnew = append(keywordnew, v)
+//		}
+//	}
+//	keywords := strings.Join(keywordnew, ",")
+//	(*obj)["keywords"] = keywords
+//	content := ""
+//	if (*obj)["detail_bak"] != nil {
+//		content = qutil.ObjToString((*obj)["detail_bak"])
+//	} else {
+//		content = qutil.ObjToString((*obj)["detail"])
+//	}
+//	//内容替换
+//	content = strings.Replace(content, " ", "", -1)
+//	content = reg_space.ReplaceAllString(content, "")
+//	content = reg_row.ReplaceAllString(content, ",")
+//	content = reg_dh.ReplaceAllString(content, ",")
+//	content = reg_newdb.ReplaceAllString(content, "$1")
+//	if strings.HasPrefix(content, ",") {
+//		content = content[1:]
+//	}
+//	//log.Println(content)
+//	tc := []rune(content)
+//	ltc := len(tc)
+//	description := content
+//	if ltc > DesLen {
+//		description = string(tc[:DesLen])
+//	}
+//	(*obj)["description"] = description
+//	//保存到数据库
+//	_id := (*obj)["_id"]
+//	go mongodb.Update(coll, &map[string]interface{}{"_id": _id}, &map[string]interface{}{"$set": map[string]interface{}{
+//		"keywords":    keywords,
+//		"description": description,
+//	}}, false, false)
+//	return
+//}

+ 832 - 0
src/jfw/front/follow.go

@@ -0,0 +1,832 @@
+package front
+
+import (
+	"encoding/json"
+	"jfw/config"
+	tools "jfw/tools"
+	"jfw/wx"
+	"log"
+	"qfw/util"
+	"qfw/util/elastic"
+	"qfw/util/redis"
+	rpc "qfw/util/rpc"
+	"strings"
+	"time"
+
+	"github.com/go-xweb/xweb"
+	"gopkg.in/mgo.v2/bson"
+)
+
+type Follow struct {
+	*xweb.Action
+	ajaxReq       xweb.Mapper `xweb:"/follow/ajaxReq"` //关注项目ajax请求
+	list          xweb.Mapper `xweb:"/follow/list"`    //我关注的项目
+	add           xweb.Mapper `xweb:"/follow/add"`     //添加关注项目
+	addsave       xweb.Mapper `xweb:"/follow/addsave"`
+	set           xweb.Mapper `xweb:"/follow/set/(\\w+)/([^.]*)"`
+	notice        xweb.Mapper `xweb:"/follow/notice/(\\w+)/([^.]*)"` //项目公告
+	allNotice     xweb.Mapper `xweb:"/follow/allNotice"`             //项目公告/(.*)
+	visited       xweb.Mapper `xweb:"/follow/notice/visited"`
+	photo         xweb.Mapper `xweb:"/follow/photo/(.*)"`
+	fwsave        xweb.Mapper `xweb:"/follow/fwsave"`
+	mylist        xweb.Mapper `xweb:"/follow/mylist"`
+	checkFPStatus xweb.Mapper `xweb:"/follow/checkFPStatus"`
+	checkCStatus  xweb.Mapper `xweb:"/follow/checkCStatus"`
+	shareFW       xweb.Mapper `xweb:"/follow/shareFW/([^.]*)"`
+	pcAllNotice   xweb.Mapper `xweb:"/front/pcAllNotice"`
+}
+
+var followLimit int
+
+func init() {
+	xweb.AddAction(&Follow{})
+	followLimit = util.IntAllDef(config.Sysconfig["followProject"], 10)
+}
+
+//
+func (m *Follow) CheckFPStatus() error {
+	defer util.Catch()
+	s_id := util.DecodeArticleId2ByCheck(m.GetString("s_id"))[0]
+	follows, _ := mongodb.FindById("follow_project", s_id, `{"_id":1}`)
+	flag := "f"
+	if follows != nil && *follows != nil && len(*follows) > 0 {
+		flag = "t"
+	}
+	m.ServeJson(map[string]interface{}{
+		"flag": flag,
+	})
+	return nil
+}
+
+//
+func (m *Follow) CheckCStatus() error {
+	defer util.Catch()
+	//s_id := util.DecodeArticleId(m.GetString("s_id"))[0]
+	pname := m.GetString("pcname")
+	pcode := m.GetString("pccode")
+	userId, _ := m.GetSession("userId").(string)
+	flag := false
+	followid := ""
+	if userId != "" {
+		flag, followid = MFollow(userId, pname, pcode, "")
+		//		follows, _ := mongodb.FindOneByField("follow_project", `{"s_id":"`+s_id+`","s_openid":"`+s_openid.(string)+`"}`, `{"_id":1}`)
+		//		if follows != nil && *follows != nil && len(*follows) > 0 {
+		//			flag = "t"
+		//			followid = strings.Split(fmt.Sprintf("%s", (*follows)["_id"]), `"`)[1]
+		//		}
+	}
+	m.ServeJson(map[string]interface{}{
+		"flag":     flag,
+		"followid": followid,
+	})
+	return nil
+}
+
+//
+func (m *Follow) Mylist() {
+	defer util.Catch()
+	id := m.GetString("id")
+	id = util.DecodeArticleId2ByCheck(id)[0]
+	var flag = "F"
+	res, ok := mongodb.FindById("follow_project", id, `{"title":1}`)
+	if ok && res != nil && len(*res) > 0 {
+		flag = "T"
+	}
+	m.ServeJson(map[string]interface{}{
+		"flag": flag,
+	})
+}
+
+//关注列表关注
+func (m *Follow) Fwsave() {
+	defer util.Catch()
+	userId := m.GetSession("userId").(string)
+	openid := m.GetSession("s_m_openid").(string)
+	var status = "n"
+	var followId string
+	s_id := util.DecodeArticleId2ByCheck(m.GetString("id"))[0]
+	if mongodb.Count("follow_project", `{"s_userid":"`+userId+`"}`) >= followLimit {
+		status = "m"
+	} else if mongodb.Count("follow_project", `{"s_userid":"`+m.GetSession("userId").(string)+`","s_id":"`+s_id+`"}`) > 0 {
+		status = "e"
+	} else {
+		data := make(map[string]interface{})
+		if remind, _ := m.GetInteger("remind"); remind == 1 {
+			data["i_remind"] = 1
+		} else {
+			data["i_remind"] = 0
+		}
+		if bidopentime, err := m.GetInt("bidopentime"); err == nil {
+			data["l_bidopentime"] = bidopentime
+		}
+		if remindtime, err := m.GetInt("remindtime"); err == nil {
+			data["l_remindtime"] = remindtime
+		}
+		projectname := m.GetString("projectname")
+		data["s_projectname"] = projectname
+		data["s_userid"] = userId
+		data["s_openid"] = openid
+		data["l_createtime"] = time.Now().Unix()
+		data["s_title"] = projectname
+		data["s_id"] = s_id
+		data["i_source"] = 3
+		data["toptype"] = "招标"
+		fields := `{"title":1,"comeintime":1,"bidopentime":1,"projectcode":1,"type":1,"href":1,"publishtime":1,"area":1,"subtype":1,"toptype":1}`
+		res, ok := mongodb.FindById("bidding", s_id, fields)
+		if ok && (res == nil || *res == nil || len(*res) == 0) {
+			res, ok = mongodb.FindById("bidding_back", s_id, fields)
+		}
+		if ok && res != nil {
+			projectcode, _ := (*res)["projectcode"].(string)
+			data["s_projectcode"] = projectcode
+			data["s_title"] = (*res)["title"]
+			data["s_type"] = (*res)["type"]
+			data["l_publishtime"] = (*res)["publishtime"]
+			data["s_area"] = (*res)["area"]
+			data["s_url"] = (*res)["href"]
+			data["s_province"] = (*res)["area"]
+			data["l_comeintime"] = (*res)["comeintime"]
+			//data["l_lastpushtime"] = (*res)["comeintime"]
+			if data["l_bidopentime"] == "" {
+				data["l_bidopentime"] = (*res)["bidopentime"]
+			}
+			mySelf := make(bson.M)
+			mySelf["s_projectname"] = projectname
+			mySelf["s_title"] = (*res)["title"]
+			mySelf["s_projectcode"] = projectcode
+			mySelf["s_url"] = (*res)["href"]
+			mySelf["s_type"] = (*res)["type"]
+			mySelf["s_subtype"] = (*res)["subtype"]
+			mySelf["s_toptype"] = (*res)["toptype"]
+			mySelf["s_province"] = (*res)["area"]
+			mySelf["s_id"] = s_id
+			mySelf["s_eid"] = util.EncodeArticleId2ByCheck(s_id)
+			mySelf["l_publishtime"] = (*res)["publishtime"]
+			data["a_relationinfo"] = []map[string]interface{}{mySelf}
+			if followId = mongodb.Save("follow_project", data); len(followId) > 0 {
+				status = "y"
+				go tools.FollowPush(&rpc.FollowPush{
+					ProjectName: projectname,
+					ProjectCode: projectcode,
+					InfoId:      s_id,
+					FollowId:    followId,
+					OpenId:      openid,
+				})
+			}
+		}
+	}
+	m.ServeJson(map[string]interface{}{
+		"status":   status,
+		"followId": util.EncodeArticleId2ByCheck(followId),
+		"infoId":   util.EncodeArticleId2ByCheck(s_id),
+	})
+}
+
+//分享关注项目
+func (m *Follow) ShareFW(tp string) error {
+	defer util.Catch()
+	tpm := strings.Split(tp, "__")
+	id := util.DecodeArticleId2ByCheck(tpm[0])[0]
+	projectname := tpm[1]
+	openid := tpm[2]
+	m.T["signature"] = wx.SignJSSDK(m.Site() + m.Url())
+	userId := m.GetSession("userId")
+	if userId != "" && userId != nil {
+		m.T["userId"] = userId.(string)
+	}
+	projectcode := ""
+	m.T["projectname"] = projectname
+	m.T["projectcode"] = projectcode
+	//userId, _ := m.GetSession("userId").(string)
+	m.T["fg"] = "F"
+	fields := `{"_id":1,"title":1,"comeintime":1,"projectcode":1,"projectname":1,"bidopentime":1,"projectcode":1,"area","toptype":1,"subtype":1,"type":1,"href":1,"publishtime":1,"area":1}`
+	data, ok := mongodb.FindById("bidding", id, fields)
+	if ok && (data == nil || *data == nil || len(*data) == 0) {
+		data, _ = mongodb.FindById("bidding_back", id, fields)
+	}
+	bidopentime := (*data)["bidopentime"]
+	if bidopentime != nil {
+		m.T["bidopentime"] = util.FormatDateWithObj(&bidopentime, "2006年01月02日 15时") + " " + convertWeekday(time.Unix(bidopentime.(int64), 0).Weekday().String())
+		m.T["l_bidopentime"] = bidopentime
+	}
+	if remindtime := (*data)["remindtime"]; remindtime != nil {
+		m.T["remindtime"] = util.FormatDateWithObj(&remindtime, "2006年01月02日 15时") + " " + convertWeekday(time.Unix(remindtime.(int64), 0).Weekday().String())
+		m.T["l_remindtime"] = remindtime
+	} else if bidopentime != nil {
+		date := time.Unix(bidopentime.(int64), 0).AddDate(0, 0, -1)
+		m.T["remindtime"] = util.FormatDate(&date, "2006年01月02日 15时") + " " + convertWeekday(date.Weekday().String())
+		m.T["l_remindtime"] = date.Unix()
+	}
+	mySelf := make(bson.M)
+	mySelf["s_projectname"] = projectname
+	mySelf["s_title"] = (*data)["title"]
+	mySelf["type"] = (*data)["type"]
+	mySelf["subtype"] = (*data)["subtype"]
+	mySelf["toptype"] = (*data)["toptype"]
+	mySelf["area"] = (*data)["area"]
+	mySelf["s_projectcode"] = projectcode
+	mySelf["s_url"] = (*data)["href"]
+	mySelf["s_id"] = id
+	mySelf["l_publishtime"] = (*data)["publishtime"]
+	if len(projectname) > 0 || len(projectcode) > 0 {
+		res, _ := tools.FollowPush(&rpc.FollowPush{
+			ProjectName: projectname,
+			ProjectCode: projectcode,
+			InfoId:      id,
+			FollowId:    "",
+			OpenId:      "",
+			Flag:        1,
+		})
+		if len(res) > 0 || len(mySelf) > 0 {
+			m.T["fg"] = "T"
+			m.T["data"] = res
+			m.T["id"] = id
+			m.T["mySelf"] = mySelf
+			m.T["openid"] = openid
+		}
+	}
+	return m.Render("/weixin/follow/shareset.html", &m.T)
+}
+
+//
+func (m *Follow) PcAllNotice() error {
+	defer util.Catch()
+	id := util.DecodeArticleId2ByCheck(m.GetString("id"))[0]
+	projectname := m.GetString("projectname")
+	projectcode := m.GetString("projectcode")
+	log.Println(projectname, "id:", id)
+	data := map[string]interface{}{}
+	if len(projectname) > 0 || len(projectcode) > 0 {
+		res, _ := tools.FollowPush(&rpc.FollowPush{
+			ProjectName: projectname,
+			ProjectCode: projectcode,
+			InfoId:      id,
+			FollowId:    "",
+			OpenId:      "",
+			Flag:        1,
+		})
+		if len(res) > 0 {
+			for _, v := range res {
+				(*v)["s_eid"] = util.EncodeArticleId2ByCheck((*v)["s_id"].(string))
+			}
+			data["res"] = res
+		}
+	}
+
+	m.ServeJson(map[string]interface{}{
+		"data": data,
+	})
+	return nil
+}
+
+//未关注项目
+func (m *Follow) Photo(tp string) error {
+	defer util.Catch()
+	tpm := strings.Split(tp, "__")
+	id := util.DecodeArticleId2ByCheck(tpm[0])[0]
+	projectname := tpm[1]
+	projectcode := tpm[2]
+	m.T["projectname"] = projectname
+	m.T["projectcode"] = projectcode
+	userId, _ := m.GetSession("userId").(string)
+	if userId != "" {
+		flag, followid := MFollow(userId, projectname, projectcode, "")
+		if flag && len(followid) > 0 {
+			m.Redirect("/follow/set/list/" + followid)
+			return nil
+		}
+	}
+	m.T["fg"] = "F"
+	fields := `{"_id":1,"title":1,"comeintime":1,"projectcode":1,"projectname":1,"bidopentime":1,"projectcode":1,"area","toptype":1,"subtype":1,"type":1,"href":1,"publishtime":1,"area":1}`
+	data, ok := mongodb.FindById("bidding", id, fields)
+	if ok && (data == nil || *data == nil || len(*data) == 0) {
+		data, _ = mongodb.FindById("bidding_back", id, fields)
+	}
+	bidopentime := (*data)["bidopentime"]
+	if bidopentime != nil {
+		m.T["bidopentime"] = util.FormatDateWithObj(&bidopentime, "2006年01月02日 15时") + " " + convertWeekday(time.Unix(bidopentime.(int64), 0).Weekday().String())
+		m.T["l_bidopentime"] = bidopentime
+	}
+	if remindtime := (*data)["remindtime"]; remindtime != nil {
+		m.T["remindtime"] = util.FormatDateWithObj(&remindtime, "2006年01月02日 15时") + " " + convertWeekday(time.Unix(remindtime.(int64), 0).Weekday().String())
+		m.T["l_remindtime"] = remindtime
+	} else if bidopentime != nil {
+		date := time.Unix(bidopentime.(int64), 0).AddDate(0, 0, -1)
+		m.T["remindtime"] = util.FormatDate(&date, "2006年01月02日 15时") + " " + convertWeekday(date.Weekday().String())
+		m.T["l_remindtime"] = date.Unix()
+	}
+	mySelf := make(bson.M)
+	mySelf["s_projectname"] = projectname
+	mySelf["s_title"] = (*data)["title"]
+	mySelf["type"] = (*data)["type"]
+	mySelf["subtype"] = (*data)["subtype"]
+	mySelf["toptype"] = (*data)["toptype"]
+	mySelf["area"] = (*data)["area"]
+	mySelf["s_projectcode"] = projectcode
+	mySelf["s_url"] = (*data)["href"]
+	mySelf["s_id"] = id
+	mySelf["s_eid"] = util.EncodeArticleId2ByCheck(id)
+	mySelf["l_publishtime"] = (*data)["publishtime"]
+	if len(projectname) > 0 || len(projectcode) > 0 {
+		res, _ := tools.FollowPush(&rpc.FollowPush{
+			ProjectName: projectname,
+			ProjectCode: projectcode,
+			InfoId:      id,
+			FollowId:    "",
+			OpenId:      m.GetSession("s_m_openid").(string),
+			Flag:        1,
+		})
+		if len(res) > 0 || len(mySelf) > 0 {
+			m.T["fg"] = "T"
+			for _, v := range res {
+				(*v)["s_eid"] = util.EncodeArticleId2ByCheck((*v)["s_id"].(string))
+			}
+			m.T["data"] = res
+			m.T["id"] = util.EncodeArticleId2ByCheck(id)
+			m.T["mySelf"] = mySelf
+		}
+	}
+	m.T["type"] = (*data)["type"]
+	m.T["subtype"] = (*data)["subtype"]
+	m.T["toptype"] = (*data)["toptype"]
+	m.T["a_lastpushids"] = (*data)["a_lastpushids"]
+	m.T["source"] = 1
+	myopenid, _ := m.Session().Get("s_m_openid").(string)
+	m.T["openid"] = se.EncodeString(myopenid)
+	mynickname, _ := m.Session().Get("s_nickname").(string)
+	myavatar, _ := m.Session().Get("s_avatar").(string)
+	m.T["nickname"] = mynickname
+	m.T["avatar"] = myavatar
+	m.T["signature"] = wx.SignJSSDK(m.Site() + m.Url())
+	return m.Render("/weixin/follow/set.html", &m.T)
+}
+
+//关注项目相关的ajax请求
+func (m *Follow) AjaxReq() {
+	defer util.Catch()
+	userId := m.GetSession("userId").(string)
+	var status = "n"
+	reqType := m.GetString("reqType")
+	var followId string
+	if reqType == "follow" { //快照页面关注
+		s_id := util.DecodeArticleId2ByCheck(m.GetString("id"))[0]
+		if mongodb.Count("follow_project", `{"s_userid":"`+userId+`"}`) >= followLimit {
+			status = "m"
+		} else if mongodb.Count("follow_project", `{"s_userid":"`+m.GetSession("userId").(string)+`","s_id":"`+s_id+`"}`) > 0 {
+			status = "e"
+		} else {
+			publishtime, _ := m.GetInt("publishtime")
+			projectname := m.GetString("projectname")
+			projectcode := m.GetString("projectcode")
+			url := m.GetString("url")
+			title := m.GetString("title")
+			area := m.GetString("area")
+			subtype := m.GetString("subtype")
+			toptype := m.GetString("toptype")
+			s_type := m.GetString("type")
+			if projectname != "" || projectcode != "" {
+				openid := m.GetSession("s_m_openid").(string)
+				data := map[string]interface{}{
+					"s_userid":      userId,
+					"s_openid":      openid,
+					"s_id":          s_id,
+					"s_url":         url,
+					"s_title":       title,
+					"l_createtime":  time.Now().Unix(),
+					"i_remind":      0,
+					"l_publishtime": publishtime,
+					"i_source":      1,
+					"s_province":    area,
+					"s_area":        area,
+					"s_type":        s_type,
+					"toptype":       "招标",
+				}
+				if comeintime, err := m.GetInt("comeintime"); err == nil {
+					data["l_comeintime"] = comeintime
+				}
+				if s_type := m.GetString("type"); s_type != "" {
+					data["s_type"] = s_type
+				}
+				if projectname != "" {
+					data["s_projectname"] = projectname
+				} else {
+					data["s_projectname"] = title
+				}
+				if projectcode != "" {
+					data["s_projectcode"] = projectcode
+				}
+				if bidopentime, err := m.GetInt("bidopentime"); err == nil && bidopentime > 0 {
+					data["l_bidopentime"] = bidopentime
+				}
+				//
+				mySelf := make(bson.M)
+				mySelf["s_projectcode"] = projectcode
+				mySelf["s_projectname"] = projectname
+				mySelf["s_title"] = title
+				mySelf["s_url"] = url
+				mySelf["s_id"] = s_id
+				mySelf["s_eid"] = util.EncodeArticleId2ByCheck(s_id)
+				mySelf["l_publishtime"] = publishtime
+				mySelf["s_province"] = area
+				mySelf["s_subtype"] = subtype
+				mySelf["s_toptype"] = toptype
+				mySelf["s_type"] = s_type
+				//
+				data["a_relationinfo"] = []map[string]interface{}{mySelf}
+				if followId = mongodb.Save("follow_project", data); len(followId) > 0 {
+					status = "y"
+					go tools.FollowPush(&rpc.FollowPush{
+						ProjectName: projectname,
+						ProjectCode: projectcode,
+						InfoId:      s_id,
+						FollowId:    followId,
+						OpenId:      openid,
+					})
+				}
+			}
+		}
+	} else if reqType == "cancel" { //取消关注
+		if data, ok := mongodb.FindOne("follow_project", `{"s_userid":"`+userId+`","_id":"`+util.DecodeArticleId2ByCheck(m.GetString("id"))[0]+`"}`); ok && data != nil {
+			(*data)["s_followid"] = util.BsonIdToSId((*data)["_id"])
+			delete(*data, "_id")
+			(*data)["i_status"] = 1
+			mongodb.Save("follow_project_back", data)
+			if mongodb.Del("follow_project", `{"s_openid":"`+(*data)["s_openid"].(string)+`","_id":"`+util.DecodeArticleId2ByCheck(m.GetString("id"))[0]+`"}`) {
+				status = "y"
+				go delRelRedis((*data)["s_openid"], (*data)["a_relationinfo"])
+			}
+		}
+	} else if reqType == "followset" {
+		id := util.DecodeArticleId2ByCheck(m.GetString("id"))[0]
+		if mongodb.Count("follow_project", map[string]interface{}{"_id": bson.ObjectIdHex(id), "s_userid": m.GetSession("userId").(string)}) == 1 {
+			data := make(map[string]interface{})
+			data["l_updatetime"] = time.Now().Unix()
+			if remind, _ := m.GetInteger("remind"); remind == 1 {
+				data["i_remind"] = 1
+			} else {
+				data["i_remind"] = 0
+			}
+			if bidopentime, err := m.GetInt("bidopentime"); err == nil {
+				data["l_bidopentime"] = bidopentime
+			}
+			if remindtime, err := m.GetInt("remindtime"); err == nil {
+				data["l_remindtime"] = remindtime
+			}
+			if mongodb.Update("follow_project", `{"_id":"`+id+`"}`, map[string]interface{}{"$set": data}, false, false) {
+				status = "y"
+			}
+		}
+	}
+	m.ServeJson(map[string]interface{}{
+		"status":   status,
+		"followId": util.EncodeArticleId2ByCheck(followId),
+	})
+}
+
+//我关注的项目
+func (m *Follow) List() error {
+	defer util.Catch()
+	datas, ok := mongodb.Find("follow_project", `{"s_userid":"`+m.GetSession("userId").(string)+`"}`, `{"l_lastpushtime":-1,"l_createtime":-1}`, `{"s_projectname":1,"s_projectcode":1,"i_remind":1,"l_lastpushtime":1,"l_createtime":1}`, false, -1, -1)
+	m.T["flag"] = false
+	if ok && datas != nil && len(*datas) > 0 {
+		for k, v := range *datas {
+			v["_id"] = util.EncodeArticleId2ByCheck(util.BsonIdToSId(v["_id"]))
+			log.Println(k, ":", v["l_createtime"])
+			if v["l_lastpushtime"] == "" || v["l_lastpushtime"] == nil {
+				v["l_lastpushtime"] = v["l_createtime"]
+			}
+		}
+		m.T["datas"] = datas
+		if len(*datas) >= followLimit {
+			m.T["flag"] = true
+		}
+	}
+	myopenid, _ := m.Session().Get("s_m_openid").(string)
+	m.T["openid"] = se.EncodeString(myopenid)
+	mynickname, _ := m.Session().Get("s_nickname").(string)
+	myavatar, _ := m.Session().Get("s_avatar").(string)
+	m.T["nickname"] = mynickname
+	m.T["avatar"] = myavatar
+	m.T["signature"] = wx.SignJSSDK(m.Site() + m.Url())
+	m.T["followLimit"] = followLimit
+	return m.Render("/weixin/follow/list.html", &m.T)
+}
+
+//添加关注项目
+func (m *Follow) Add() error {
+	myopenid, _ := m.Session().Get("s_m_openid").(string)
+	m.T["openid"] = se.EncodeString(myopenid)
+	m.T["signature"] = wx.SignJSSDK(m.Site() + m.Url())
+	return m.Render("/weixin/follow/add.html", &m.T)
+}
+
+//手动添加关注项目
+func (m *Follow) Addsave() error {
+	defer util.Catch()
+	var status = "n"
+	var id string
+	userId := m.GetSession("userId").(string)
+	if mongodb.Count("follow_project", `{"s_userid":"`+userId+`"}`) >= followLimit {
+		status = "m"
+	} else {
+		openid := m.GetSession("s_m_openid").(string)
+		if projectname := m.GetString("projectname"); projectname != "" {
+			data := map[string]interface{}{
+				"s_userid":      userId,
+				"s_openid":      openid,
+				"s_projectname": projectname,
+				"l_createtime":  time.Now().Unix(),
+				"i_remind":      0,
+				"s_title":       projectname,
+				"i_source":      2,
+				"toptype":       "招标",
+			}
+			//匹配
+			r := elastic.GetPage("bidding", "bidding", `{"TERM_projectname":"`+projectname+`"}`, `{"comeintime":-1}`, `"_id","title","comeintime","bidopentime","projectcode","type","href","publishtime","subtype","toptype","area"`, -1, -1)
+			//			r, ok := mongodb.Find("bidding", bson.M{
+			//				"projectname": projectname,
+			//			}, `{"comeintime":-1}`, `{"title":1,"comeintime":1,"bidopentime":1,"projectcode":1,"type":1,"href":1,"publishtime":1,"subtype":1,"toptype":1,"area":1}`, false, 0, -1)
+			if r == nil || *r == nil || len(*r) == 0 {
+				r, _ = mongodb.Find("bidding_back", bson.M{
+					"projectname": projectname,
+				}, `{"comeintime":-1}`, `{"title":1,"comeintime":1,"bidopentime":1,"projectcode":1,"type":1,"href":1,"publishtime":1,"subtype":1,"toptype":1,"area":1}`, false, 0, -1)
+			}
+			var matchingFlag = r != nil && len(*r) != 0
+			var projectcode, sid string
+			if matchingFlag {
+				d := (*r)[0]
+				sid = d["_id"].(string) //(d["_id"].(bson.ObjectId)).Hex()
+				data["s_id"] = sid
+				data["l_comeintime"] = d["comeintime"]
+				publishtime := d["publishtime"]
+				data["l_publishtime"] = publishtime
+				data["s_title"] = d["title"]
+				data["s_area"] = d["area"]
+				data["s_province"] = d["area"]
+				if bidopentime := d["bidopentime"]; bidopentime != nil {
+					data["l_bidopentime"] = bidopentime
+				}
+				if d["projectcode"] != nil {
+					projectcode = d["projectcode"].(string)
+					data["s_projectcode"] = projectcode
+				}
+				if s_type := d["type"]; s_type != nil {
+					data["s_type"] = s_type
+				}
+				url := d["href"].(string)
+				data["s_url"] = url
+				//
+				mySelf := make(bson.M)
+				mySelf["s_type"] = util.ObjToString(d["type"])
+				mySelf["s_toptype"] = util.ObjToString(d["toptype"])
+				mySelf["s_subtype"] = util.ObjToString(d["subtype"])
+				mySelf["s_province"] = util.ObjToString(d["area"])
+				mySelf["s_projectname"] = projectname
+				mySelf["s_title"] = d["title"]
+				mySelf["s_projectcode"] = projectcode
+				mySelf["s_url"] = url
+				mySelf["s_id"] = sid
+				mySelf["s_eid"] = util.EncodeArticleId2ByCheck(sid)
+				mySelf["l_publishtime"] = publishtime
+				data["a_relationinfo"] = []map[string]interface{}{mySelf}
+			}
+			if id = mongodb.Save("follow_project", data); len(id) > 0 {
+				status = "y"
+				go tools.FollowPush(&rpc.FollowPush{
+					ProjectName: projectname,
+					ProjectCode: projectcode,
+					InfoId:      sid,
+					FollowId:    id,
+					OpenId:      openid,
+				})
+			}
+		}
+	}
+	m.ServeJson(map[string]interface{}{
+		"status": status,
+		"id":     util.EncodeArticleId2ByCheck(id),
+	})
+	return nil
+}
+func (m *Follow) Set(tp, id string) error {
+	defer util.Catch()
+	id = util.DecodeArticleId2ByCheck(id)[0]
+	fields := `{"s_id":1,"i_source":1,"s_projectname":1,"s_projectcode":1,"s_url":1,"i_remind":1,"s_type":1,"l_bidopentime":1,"l_remindtime":1,"a_relationinfo":1,"a_visited":1,"l_lastpushtime":1,"a_lastpushids":1}`
+	data, ok := mongodb.FindOneByField("follow_project", map[string]interface{}{
+		"_id":      bson.ObjectIdHex(id),
+		"s_userid": m.GetSession("userId").(string),
+	}, fields)
+	m.T["isDel"] = false
+	if ok && (data == nil || len(*data) == 0) {
+		data, ok = mongodb.FindOneByField("follow_project_back", `{"s_followid":"`+id+`"}`, fields)
+		m.T["isDel"] = true
+	}
+	if !ok || data == nil || len(*data) == 0 {
+		return m.Render("_error.html")
+	}
+	sid, _ := (*data)["s_id"].(string)
+	m.T["_id"] = util.EncodeArticleId2ByCheck(id)
+	m.T["sid"] = util.EncodeArticleId2ByCheck(sid)
+	m.T["source"] = (*data)["i_source"]
+	m.T["projectname"] = (*data)["s_projectname"]
+	m.T["projectcode"] = (*data)["s_projectcode"]
+	m.T["url"] = (*data)["s_url"]
+	m.T["remind"] = (*data)["i_remind"]
+	m.T["type"] = (*data)["s_type"]
+	if sid != "" {
+		info, info_ok := mongodb.FindOneByField("bidding", map[string]interface{}{
+			"_id": bson.ObjectIdHex(sid),
+		}, `{"type":1,"subtype":1,"toptype":1}`)
+		if info_ok && (info == nil || *info == nil || len(*info) == 0) {
+			info, info_ok = mongodb.FindOneByField("bidding_back", map[string]interface{}{
+				"_id": bson.ObjectIdHex(sid),
+			}, `{"type":1,"subtype":1,"toptype":1}`)
+		}
+		if info_ok {
+			m.T["subtype"] = (*info)["subtype"]
+			m.T["toptype"] = (*info)["toptype"]
+			m.T["type"] = (*data)["type"]
+		}
+	}
+	m.T["relationinfo"] = (*data)["a_relationinfo"]
+	m.T["a_visited"] = (*data)["a_visited"]
+	m.T["l_lastpushtime"] = (*data)["l_lastpushtime"]
+	m.T["a_lastpushids"] = (*data)["a_lastpushids"]
+	l_bidopentime := (*data)["l_bidopentime"]
+	if l_bidopentime != nil {
+		m.T["bidopentime"] = util.FormatDateWithObj(&l_bidopentime, "2006年01月02日 15时") + " " + convertWeekday(time.Unix(util.Int64All(l_bidopentime), 0).Weekday().String())
+		m.T["l_bidopentime"] = l_bidopentime
+	}
+	if l_remindtime := (*data)["l_remindtime"]; l_remindtime != nil {
+		m.T["remindtime"] = util.FormatDateWithObj(&l_remindtime, "2006年01月02日 15时") + " " + convertWeekday(time.Unix(l_remindtime.(int64), 0).Weekday().String())
+		m.T["l_remindtime"] = l_remindtime
+	} else if l_bidopentime != nil {
+		date := time.Unix(util.Int64All(l_bidopentime), 0).AddDate(0, 0, -1)
+		m.T["remindtime"] = util.FormatDate(&date, "2006年01月02日 15时") + " " + convertWeekday(date.Weekday().String())
+		m.T["l_remindtime"] = date.Unix()
+	}
+	m.T["flag"] = true
+	myopenid, _ := m.Session().Get("s_m_openid").(string)
+	m.T["openid"] = se.EncodeString(myopenid)
+	mynickname, _ := m.Session().Get("s_nickname").(string)
+	myavatar, _ := m.Session().Get("s_avatar").(string)
+	m.T["nickname"] = mynickname
+	m.T["avatar"] = myavatar
+	m.T["signature"] = wx.SignJSSDK(m.Site() + m.Url())
+	return m.Render("/weixin/follow/set.html", &m.T)
+}
+func (m *Follow) AllNotice() error {
+	defer util.Catch()
+	id := util.DecodeArticleId2ByCheck(m.GetString("id"))[0]
+	var relationinfo []interface{}
+	fields := `{"_id":-1,"s_title":1,"s_projectcode":1,"s_projectname":1,"s_url":1,"s_id":1,"l_publishtime":1,"a_relationinfo":1,"a_visited":1,"s_id":1}`
+	data, ok := mongodb.FindById("follow_project", id, fields)
+	if ok && (data == nil || len(*data) == 0) { //以取消关注
+		data, ok = mongodb.FindOneByField("follow_project_back", `{"s_followid":"`+id+`"}`, fields)
+	}
+	if ok && data != nil {
+		if a_relationinfo := (*data)["a_relationinfo"]; a_relationinfo != nil {
+			relationinfo = a_relationinfo.([]interface{})
+			//排序
+			for x, _ := range relationinfo {
+				for y := 0; y < len(relationinfo)-x-1; y++ {
+					dt1 := util.Int64All(relationinfo[y].(map[string]interface{})["l_publishtime"])
+					dt2 := util.Int64All(relationinfo[y+1].(map[string]interface{})["l_publishtime"])
+					if dt1 > 0 && dt2 > 0 && dt1 < dt2 {
+						temp := relationinfo[y]
+						relationinfo[y] = relationinfo[y+1]
+						relationinfo[y+1] = temp
+					}
+				}
+			}
+		}
+		(*data)["a_relationinfo"] = relationinfo
+	}
+	m.ServeJson(map[string]interface{}{
+		"data": data,
+	})
+	return nil
+}
+func (m *Follow) Notice(id, followId string) error {
+	defer util.Catch()
+	data, ok := mongodb.FindById("follow_push_log", id, `{"s_followid":-1,"s_title":1,"s_projectcode":1,"s_projectname":1,"a_relationinfo":1,"a_visited":1}`)
+	if ok {
+		if a_relationinfo := (*data)["a_relationinfo"]; a_relationinfo != nil {
+			relationinfo := a_relationinfo.([]interface{})
+			for x, _ := range relationinfo {
+				s_id, _ := relationinfo[x].(map[string]interface{})["s_id"].(string)
+				relationinfo[x].(map[string]interface{})["s_id"] = util.EncodeArticleId2ByCheck(s_id)
+				for y := 0; y < len(relationinfo)-x-1; y++ {
+					dt1 := util.Int64All(relationinfo[y].(map[string]interface{})["l_publishtime"])
+					dt2 := util.Int64All(relationinfo[y+1].(map[string]interface{})["l_publishtime"])
+					if dt1 > 0 && dt2 > 0 && dt1 < dt2 {
+						temp := relationinfo[y]
+						relationinfo[y] = relationinfo[y+1]
+						relationinfo[y+1] = temp
+					}
+				}
+			}
+			(*data)["a_relationinfo"] = relationinfo
+		}
+	}
+	m.T["id"] = id
+	m.T["data"] = data
+	myopenid, _ := m.Session().Get("s_m_openid").(string)
+	m.T["openid"] = se.EncodeString(myopenid)
+	mynickname, _ := m.Session().Get("s_nickname").(string)
+	myavatar, _ := m.Session().Get("s_avatar").(string)
+	m.T["nickname"] = mynickname
+	m.T["avatar"] = myavatar
+	m.T["signature"] = wx.SignJSSDK(m.Site() + m.Url())
+	return m.Render("/weixin/follow/notice.html", &m.T)
+}
+func (m *Follow) Visited() error {
+	defer util.Catch()
+	id := m.GetString("id")
+	id = util.DecodeArticleId2ByCheck(id)[0]
+	reqType, _ := m.GetInteger("type")
+	var collection string
+	if reqType == 1 {
+		collection = "follow_project"
+	} else if reqType == 2 {
+		collection = "follow_push_log"
+	} else {
+		return nil
+	}
+	mongodb.Update(collection, `{"_id":"`+id+`"}`, map[string]interface{}{
+		"$addToSet": map[string]interface{}{"a_visited": m.GetString("sid")},
+	}, false, false)
+	return nil
+}
+func convertWeekday(weekday string) string {
+	if weekday == "Sunday" {
+		weekday = "日"
+	} else if weekday == "Monday" {
+		weekday = "一"
+	} else if weekday == "Tuesday" {
+		weekday = "二"
+	} else if weekday == "Wednesday" {
+		weekday = "三"
+	} else if weekday == "Thursday" {
+		weekday = "四"
+	} else if weekday == "Friday" {
+		weekday = "五"
+	} else if weekday == "Saturday" {
+		weekday = "六"
+	}
+	return "周" + weekday
+}
+
+//删除redis相关数据
+func delRelRedis(openid interface{}, relationinfo interface{}) {
+	defer util.Catch()
+	oid, _ := openid.(string)
+	if oid == "" || relationinfo == nil {
+		return
+	}
+	key := "push_" + oid
+	data := redis.Get("push", key)
+	if data == nil {
+		return
+	}
+	b, err := json.Marshal(data)
+	if err != nil {
+		log.Println("data---从redis中取出的数据转成byte数组出错!")
+		return
+	}
+	var array []string
+	if json.Unmarshal(b, &array) != nil {
+		log.Println("data---byte数组转成string数组出错!")
+		return
+	}
+	//
+	r, e := json.Marshal(relationinfo)
+	if e != nil {
+		log.Println("relationinfo---从redis中取出的数据转成byte数组出错!")
+		return
+	}
+	var riArray []map[string]interface{}
+	if json.Unmarshal(r, &riArray) != nil {
+		log.Println("relationinfo---byte数组转成string数组出错!")
+		return
+	}
+	var arrayTmp []string
+	for _, v := range array {
+		var flag bool
+		for _, ri := range riArray {
+			sid, _ := ri["s_id"].(string)
+			if v == sid {
+				flag = true
+				break
+			}
+		}
+		if !flag {
+			arrayTmp = append(arrayTmp, v)
+		}
+	}
+	if len(arrayTmp) == 0 {
+		redis.Del("push", key)
+	} else {
+		redis.Put("push", key, arrayTmp, -1)
+	}
+}

+ 886 - 0
src/jfw/front/front.go

@@ -0,0 +1,886 @@
+package front
+
+import (
+	"fmt"
+	"jfw/config"
+	"jfw/filter"
+	"jfw/jyutil"
+	"jfw/tools"
+	"jfw/wx"
+	"log"
+	"math/rand"
+	"qfw/util"
+	"qfw/util/elastic"
+	"qfw/util/redis"
+	"strconv"
+	"strings"
+	"sync"
+	"time"
+
+	"github.com/go-xweb/httpsession"
+	"golang.org/x/net/websocket"
+
+	"github.com/go-xweb/xweb"
+	"gopkg.in/mgo.v2/bson"
+)
+
+type userPool struct {
+	lock    sync.Mutex
+	openIds []string
+	user    map[string]*map[string]interface{}
+}
+type Front struct {
+	*xweb.Action
+	getLoginNum        xweb.Mapper `xweb:"/front/getLoginNum/(.*)"` //
+	login              xweb.Mapper `xweb:"/front/login/(.*)"`       //登录
+	hasSign            xweb.Mapper `xweb:"/front/hasSign"`          //是否登录
+	signOut            xweb.Mapper `xweb:"/front/signOut"`          //注销
+	sess               xweb.Mapper `xweb:"/front/sess/(.*)"`        //微信跳转session登录
+	viewdemo           xweb.Mapper `xweb:"/front/viewdemo"`         //微信跳转session登录
+	getpage            xweb.Mapper `xweb:"/swordfish/getpage"`
+	wxsearch           xweb.Mapper `xweb:"/swordfish/search"`             //剑鱼微信查询
+	wxsearchlist       xweb.Mapper `xweb:"/swordfish/searchlist"`         //剑鱼微信查询结果展示
+	wxsearchlistPaging xweb.Mapper `xweb:"/swordfish/searchlist/paging"`  //剑鱼微信查询结果展示--分页
+	delWxHistorySearch xweb.Mapper `xweb:"/swordfish/delWxHistorySearch"` //剑鱼微信删除历史搜索
+	visitRedirect      xweb.Mapper `xweb:"/visit/redirect"`               //剑鱼跳转访问请求,后续统计
+	//iknow           xweb.Mapper `xweb:"/visit/iknow"`          //剑鱼跳转访问请求,后续统计
+	ajaxReq          xweb.Mapper `xweb:"/member/swordfish/ajaxReq"`
+	wxpushView       xweb.Mapper `xweb:"/wxpush/wxpushview"`        //推送结果预览
+	wxpushViewPaging xweb.Mapper `xweb:"/wxpush/wxpushview/paging"` //推送结果预览--分页
+	guide            xweb.Mapper `xweb:"/swordfish/guide/(.*)"`
+	share            xweb.Mapper `xweb:"/swordfish/share/([^.]*)"`
+	wxprotocol       xweb.Mapper `xweb:"/swordfish/wxprotocol"` //微信剑鱼协议
+	delc             xweb.Mapper `xweb:"/delcache/(.+)"`        //删除模板缓存
+	wxpushListInfo   xweb.Mapper `xweb:"/wxpush/bidinfo/(.*)"`  //推送列表
+	feedback         xweb.Mapper `xweb:"/swordfish/feedback"`   //意见反馈
+	wxpushAjaxReq    xweb.Mapper `xweb:"/wxpush/bid/ajaxReq"`
+	newSordfish      xweb.Mapper `xweb:"/"`                                  //剑鱼pc首页
+	searchinfolist   xweb.Mapper `xweb:"/swordfish/searchinfolist(.*).html"` //剑鱼pc查询
+	about            xweb.Mapper `xweb:"/swordfish/about"`
+	shareabout       xweb.Mapper `xweb:"/swordfish/shareabout/([^.]*)"`
+	getIndexData     xweb.Mapper `xweb:"/front/index.*"`
+	urlrecord        xweb.Mapper `xweb:"/front/urlrecord.*"`
+	isrecord         xweb.Mapper `xweb:"/front/isrecord/(.*)"`
+	praise           xweb.Mapper `xweb:"/swordfish/praise"`
+	aboutsearch      xweb.Mapper `xweb:"/swordfish/aboutsearch"`
+	aboutSR          xweb.Mapper `xweb:"/swordfish/aboutsearchresult"`
+	delOL            xweb.Mapper `xweb:"/swordfish/delovertimelist"` //手动删除30天无更新数据
+	pcAjaxReq        xweb.Mapper `xweb:"/front/pcAjaxReq"`
+	pcVisitRedirect  xweb.Mapper `xweb:"/pcdetail/(.*).html"` //pc剑鱼跳转访问请求,后续统计
+	//aiknow          xweb.Mapper `xweb:"/about/iknow"`
+	myFeedbacks    xweb.Mapper `xweb:"/swordfish/myFeedbacks"`
+	wxkeywords     xweb.Mapper `xweb:"/swordfish/wxkeywords"`
+	wxscope        xweb.Mapper `xweb:"/swordfish/wxscope"`
+	wxerr          xweb.Mapper `xweb:"/mob/err"`
+	getRecomKWs    xweb.Mapper `xweb:"/member/getRecomKWs"`          //获取推荐关键词
+	behaviorRecord xweb.Mapper `xweb:"/member/behaviorRecord"`       //记录用户点击的关键词
+	tSGuide        xweb.Mapper `xweb:"/front/tenderSubscribe/guide"` //引导页
+	transfer       xweb.Mapper `xweb:"/front/transfer"`              //原文链接中转
+	lifirst        xweb.Mapper `xweb:"/front/lifirst"`               //第一次访问
+	jyblog         xweb.Mapper `xweb:"/jyblog/index([^.]*).html"`    //剑鱼博客
+	jybdetail      xweb.Mapper `xweb:"/jyblog/([^.]*).html"`         //剑鱼博客三级页
+	blogpraise     xweb.Mapper `xweb:"/jyblog/blogpraise"`           //剑鱼博客三级页点赞
+	searchResult   xweb.Mapper `xweb:"/list/(\\w+)/(\\w+).html"`     //剑鱼分类 地区结果列表
+	encrypt        xweb.Mapper `xweb:"/share/encrypt"`               //分享三级页加密
+	//	onekey            xweb.Mapper `xweb:"/swordfish/onekey"`             //一键报告
+	historypush       xweb.Mapper `xweb:"/swordfish/historypush"`        //历时推送记录
+	historypushPaging xweb.Mapper `xweb:"/swordfish/historypush/paging"` //历时推送记录--分页
+	aboutus           xweb.Mapper `xweb:"/front/aboutus.html"`           //关于我们
+
+	/********************wxkeyset:v1.8**************************/
+	wxrssset        xweb.Mapper `xweb:"/swordfish/page"`
+	wxKeysetAjaxReq xweb.Mapper `xweb:"/wxkeyset/ajaxReq"`           //订阅词ajax请求
+	wxKeyset        xweb.Mapper `xweb:"/wxkeyset/keyset/(\\w+)"`     //订阅词设置
+	subscribe       xweb.Mapper `xweb:"/front/subscribe.html"`       //电脑版剑鱼设置
+	gethotkey       xweb.Mapper `xweb:"/front/gethotkey"`            //获取热词
+	rediskw         xweb.Mapper `xweb:"/front/rediskw"`              //pc订阅词存redis
+	notin           xweb.Mapper `xweb:"/notin/page"`                 //未登录用户访问三级页中转页
+	topics          xweb.Mapper `xweb:"/promotional/topics.html"`    //SEM推广
+	mobtopics       xweb.Mapper `xweb:"/promotional/mobtopics.html"` //移动端专题推广
+	saveuserlogs    xweb.Mapper `xweb:"/front/saveuserlogs"`         //保存用户关注日志
+	followinfo      xweb.Mapper `xweb:"/front/followinfo"`           //redis用户关注日志
+}
+
+var sewx util.SimpleEncrypt //微信的加密方法
+var mongodb = tools.MQFW
+var urlMap map[string]interface{}
+
+//var userPoolSize = 1000
+//var up userPool
+var se = util.SE //移到tools中
+var payCallBackChan chan bool = make(chan bool, 1)
+
+func init() {
+	sewx = util.SimpleEncrypt{Key: "topnet"}
+	xweb.AddAction(&Front{})
+	xweb.AddAction(&Short{})
+	urlMap = config.Sysconfig["redirect"].(map[string]interface{})
+	//userPoolSize = util.IntAllDef(config.Sysconfig["userPoolSize"], userPoolSize)
+	//up.user = make(map[string]*map[string]interface{})
+}
+
+//移动端专题推广
+func (f *Front) Mobtopics() error {
+	return f.Render("/active/mobtopics.html")
+}
+
+//SEM推广
+func (f *Front) Topics() error {
+	var shareid = f.GetString("id")
+	if len(shareid) == 0 {
+		shareid = fmt.Sprintf("%s%d", config.Seoconfig["jySEMtgy"].(string)+fmt.Sprintf("%d", time.Now().UnixNano())[7:14], rand.Intn(9))
+	}
+	f.T["logid"] = config.Seoconfig["jySEMtgy"].(string)
+	f.T["shareid"] = se.EncodeString(shareid)
+	f.DisableHttpCache()
+	lastBids := elastic.GetPage("bidding", "bidding", "{}", `{"publishtime":-1}`, `"_id","title","publishtime","toptype","subtype","type","area","href"`, 0, 18)
+	if lastBids != nil && len(*lastBids) > 0 {
+		lbnHtml, olHtml := structureLastBidsHtml(lastBids)
+		f.T["lbnHtml"] = lbnHtml
+		f.T["olHtml"] = olHtml
+	}
+
+	return f.Render("/active/topics.html", &f.T)
+}
+
+//
+func (f *Front) Notin() error {
+	ref := f.GetSession("referer")
+	if ref != nil && ref != "" {
+		f.T["ref"] = ref.(string)
+	}
+	var shareid = f.GetString("id")
+	if len(shareid) == 0 {
+		shareid = fmt.Sprintf("%s%d", config.Seoconfig["jysskzy"].(string)+fmt.Sprintf("%d", time.Now().UnixNano())[7:14], rand.Intn(9))
+	}
+	f.T["logid"] = config.Seoconfig["jysskzy"].(string)
+	f.T["shareid"] = se.EncodeString(shareid)
+	return f.Render("/pc/notin.html", &f.T)
+}
+
+//
+func ServeWs(ws *websocket.Conn) {
+	var err error
+	var shareid = ""
+	var openid = ""
+	for {
+		var allShareid string
+		if err = websocket.Message.Receive(ws, &allShareid); err != nil {
+			log.Println("websocket接受失败!")
+			break
+		}
+		shareidlist := strings.Split(allShareid, "___")
+		if allShareid != "" && len(shareidlist) > 1 {
+			shareidnum := strings.Split(allShareid, "___")[0]
+			shareidkop := strings.Split(allShareid, "___")[1]
+			//log.Println(shareidnum + "----" + shareidkop)
+			shareid = shareidnum
+			openid = redis.GetStr("sso", "p_share_"+se.DecodeString(shareidnum))
+			if openid == "" {
+				openid = redis.GetStr("sso", "p_share_"+se.DecodeString(shareidkop))
+				shareid = shareidkop
+			}
+			if openid == "" {
+				shareid = ""
+			}
+		}
+		if err = websocket.Message.Send(ws, shareid); err != nil {
+			log.Println("websocket发送失败!")
+			break
+		}
+	}
+}
+
+//
+func (f *Front) Followinfo() error {
+	var pid = f.GetString("pid")
+	var kid = f.GetString("kid")
+	var oid = f.GetString("oid")
+	oldData := redis.Get("sso", "p_userdata_"+se.DecodeString(oid))
+	var Rurl = util.ObjToString(f.GetSession("RURL"))
+	var Rref = f.GetString("rref")
+	rheader := f.Request.Header
+	rhdua := "" //UA
+	if len(rheader["User-Agent"]) > 0 {
+		rhdua = rheader["User-Agent"][0]
+	}
+	userData := make(map[string]interface{})
+	userData["Ros"] = filter.GetOS(rhdua)
+	userData["Rip"] = filter.GetIp(f.Request)
+	userData["Rbrowse"] = filter.GetBrowse(rhdua)
+	if f.GetSession("RReferer") == nil || util.ObjToString(f.GetSession("RReferer")) == "" {
+		if Rref == "" {
+			f.SetSession("RReferer", Rurl)
+		} else {
+			f.SetSession("RReferer", Rref)
+		}
+	}
+	userData["RURL"] = Rurl
+	modulelist := config.Seoconfig["module"].(map[string]interface{})
+	for k, v := range modulelist {
+		if strings.Contains(Rurl, k) {
+			f.SetSession("RModule", v)
+		}
+	}
+	if f.GetSession("RModule") == nil || util.ObjToString(f.GetSession("RModule")) == "" {
+		f.SetSession("RModule", "首页")
+	}
+	//活动页模块
+	if len(Rurl) > 1 && strings.Contains(Rurl, "id=") {
+		activeCode := strings.Split(Rurl, "id=")[1]
+		//首先查看是否从百度等搜索引擎过来的referer
+		sourcelist := config.Seoconfig["source"].(map[string]interface{})
+		for k, v := range sourcelist {
+			if strings.Contains(activeCode, k) {
+				if f.GetSession("RSource") == nil {
+					f.SetSession("RSource", v)
+					f.SetSession("RModule", activeCode+"活动页")
+					f.SetSession("RActiveCode", activeCode)
+				}
+			}
+		}
+	}
+	userData["RModule"] = f.GetSession("RModule")
+	userData["RActiveCode"] = f.GetSession("RActiveCode")
+	userData["RReferer"] = f.GetSession("RReferer")
+	if f.GetSession("RSource") == nil || util.ObjToString(f.GetSession("RSource")) == "" {
+		//首先查看是否从百度等搜索引擎过来的referer
+		refererlist := config.Seoconfig["referer"].(map[string]interface{})
+		for k, v := range refererlist {
+			if strings.Contains(Rref, k) {
+				if f.GetSession("RSource") == nil {
+					f.SetSession("RSource", v)
+				}
+			}
+		}
+		if f.GetSession("RSource") == nil || util.ObjToString(f.GetSession("RSource")) == "" {
+			f.SetSession("RSource", "剑鱼网站")
+		}
+	}
+	userData["RSource"] = f.GetSession("RSource")
+	userData["Rparamkey"] = f.GetSession("paramkey")
+	userData["Rparampublishtime"] = f.GetSession("parampublishtime")
+	userData["Rparamarea"] = f.GetSession("paramarea")
+	userData["Rparaminfotype"] = f.GetSession("paraminfotype")
+	userData["Rprojectname"] = f.GetSession("projectname")
+	if oldData != nil {
+		redis.Put("sso", "p_userdata_"+se.DecodeString(pid), oldData, 780)
+		redis.Put("sso", "p_userdata_"+se.DecodeString(kid), oldData, 780)
+	} else {
+		redis.Put("sso", "p_userdata_"+se.DecodeString(pid), userData, 780)
+		redis.Put("sso", "p_userdata_"+se.DecodeString(kid), userData, 780)
+	}
+	f.ServeJson(map[string]string{
+		"result": "ok",
+	})
+	return nil
+}
+
+//
+func (f *Front) Saveuserlogs() error {
+	defer util.Catch()
+	shareid := f.GetString("shareid")
+	openid := redis.GetStr("sso", "p_userlogs_"+se.DecodeString(shareid))
+	//forid := ""
+	//if shareid != "" {
+	//	forid = se.DecodeString(shareid)[:2]
+	//}
+	s_paramkey := ""
+	s_parampublishtime := ""
+	s_paramarea := ""
+	s_paraminfotype := ""
+	s_paramprojectname := ""
+	s_paramkey = util.ObjToString(f.GetSession("paramkey"))
+	s_parampublishtime = util.ObjToString(f.GetSession("parampublishtime"))
+	s_paramarea = util.ObjToString(f.GetSession("paramarea"))
+	s_paraminfotype = util.ObjToString(f.GetSession("paraminfotype"))
+	s_paramprojectname = util.ObjToString(f.GetSession("projectname"))
+	logdata := &UserLog{}
+	log.Println("保存新用户日志:", openid)
+	if len(openid) > 0 && openid != "" {
+		logdata.s_openid = openid
+		logdata.s_shareid = se.DecodeString(shareid)
+		logdata.s_nickname = util.ObjToString(f.GetSession("nickname"))
+		logdata.s_url = util.ObjToString(f.GetSession("RURL"))
+		logdata.s_model = util.ObjToString(f.GetSession("RModule"))
+		logdata.s_activecode = util.ObjToString(f.GetSession("RActiveCode"))
+		logdata.s_referrer = util.ObjToString(f.GetSession("RReferer"))
+		logdata.s_usersource = util.ObjToString(f.GetSession("RSource"))
+		logdata.i_action = 1
+		logdata.s_paramkey = s_paramkey
+		logdata.s_parampublishtime = s_parampublishtime
+		logdata.s_paramarea = s_paramarea
+		logdata.s_paraminfotype = s_paraminfotype
+		logdata.s_paramprojectname = s_paramprojectname
+		logdata.s_os = util.ObjToString(f.GetSession("Ros"))
+		logdata.s_ip = util.ObjToString(f.GetSession("Rip"))
+		logdata.s_browse = util.ObjToString(f.GetSession("Rbrowse"))
+		//SaveUserLogs(logdata)
+	}
+	f.ServeJson(map[string]string{
+		"result": "ok",
+	})
+	return nil
+}
+
+//
+func (f *Front) Login(key string) error {
+	shareid := se.DecodeString(key)
+	openid := redis.GetStr("sso", "p_share_"+shareid)
+	if openid != "" {
+		f.SetSession("openid", openid)
+		redisheadimg := redis.Get("other", "newUser-"+openid)
+		if redisheadimg == nil {
+			redisheadimg = ""
+		}
+		user, _ := mongodb.FindOneByField("user", `{"s_m_openid":"`+openid+`"}`, `{"s_nickname":1,"s_headimage":1}`)
+		if user != nil && len(*user) > 0 {
+			f.ServeJson(map[string]string{
+				"result":       "ok",
+				"s_nickname":   fmt.Sprint((*user)["s_nickname"]),
+				"s_headimage":  fmt.Sprint((*user)["s_headimage"]),
+				"redisheadimg": fmt.Sprint(redisheadimg),
+			})
+			(*user)["shareid"] = shareid
+			f.SetSession("nickname", fmt.Sprint((*user)["s_nickname"]))
+			f.SetSession("user", *user)
+		} else {
+			f.ServeJson(map[string]string{
+				"result": "fail",
+			})
+		}
+	} else {
+		f.ServeJson(map[string]string{
+			"result": "fail",
+		})
+	}
+	return nil
+}
+
+//用户是否登录
+func (f *Front) HasSign() error {
+	tmp := f.Session().Get("user")
+	openid := f.GetSession("openid")
+	if openid == nil {
+		openid = ""
+	}
+	redisheadimg := redis.Get("other", "newUser-"+openid.(string))
+	if redisheadimg == nil {
+		redisheadimg = ""
+	}
+	if user, ok := tmp.(map[string]interface{}); ok {
+		f.Session().Set("user", user)
+		f.ServeJson(map[string]string{
+			"result":       "ok",
+			"s_nickname":   fmt.Sprint(user["s_nickname"]),
+			"s_headimage":  fmt.Sprint(user["s_headimage"]),
+			"redisheadimg": fmt.Sprint(redisheadimg),
+		})
+	}
+	return nil
+}
+
+//用户注销
+func (f *Front) SignOut() error {
+	sess := f.GetSession("user")
+	if user, ok := sess.(map[string]interface{}); ok {
+		shareid := fmt.Sprint(user["shareid"])
+		f.DelSession("user")
+		f.DelSession("openid")
+		f.ServeJson("ok")
+		redis.Del("sso", "p_share_"+shareid)
+		//重新生成二维码,
+		id, _ := strconv.Atoi(shareid)
+		tools.GetShareQR(uint32(id))
+		//
+		//		expires := time.Now().Add(-2 * time.Hour)
+		//		log.Println("exp:", expires)
+		//		cookie := &http.Cookie{
+		//			Name:     "userid_secure",
+		//			Value:    "",
+		//			Path:     "/",
+		//			HttpOnly: true,
+		//			MaxAge:   0,
+		//			Expires:  expires,
+		//		}
+		//		log.Println("cookie:", cookie)
+		//		f.SetCookie(cookie)
+	}
+	return nil
+}
+
+//微信公众号获取数字
+func (f *Front) GetLoginNum(prestr string) error {
+	shareid := fmt.Sprintf("%s%d", prestr+fmt.Sprintf("%d", time.Now().UnixNano())[7:14], rand.Intn(9))
+	shareidot := fmt.Sprintf("%d", util.Int64All(shareid)-1)
+	log.Println("登录获取shareid:", shareid)
+	f.ServeJson(map[string]string{
+		"num":   se.EncodeString(shareid),
+		"numot": se.EncodeString(shareidot),
+	})
+	return nil
+}
+
+//
+func (m *Front) Lifirst() {
+	defer util.Catch()
+	myopenid, _ := m.Session().Get("s_m_openid").(string)
+	if m.GetSession("shfirst") == "F" && myopenid != "" {
+		mongodb.Update("user", bson.M{"s_m_openid": myopenid}, bson.M{"$set": bson.M{"s_shfirst": "N"}}, false, false)
+	}
+}
+
+//一键报告
+func (m *Front) Onekey() error {
+	defer util.Catch()
+	fkid := util.DecodeArticleId2ByCheck(m.GetString("fkid"))[0]
+	//onekeyid := redis.Get("other", "onekey_"+fkid)
+	flag := "N"
+	data := make(map[string]interface{})
+	//if onekeyid == nil {
+	if fkid != "" {
+		//intdata, _ := mongodb.FindOne("interaction", `{"s_fkid":"`+fkid+`","i_type":7}`)
+		//if len(*intdata) == 0 {
+		userId, _ := m.GetSession("userId").(string)
+		if userId != "" {
+			userInfo, _ := mongodb.FindById("user", userId, nil)
+			if (*userInfo)["s_nickname"] != nil { //昵称
+				data["s_username"] = (*userInfo)["s_nickname"].(string)
+			} else if (*userInfo)["s_name"] != nil { //s_name
+				data["s_username"] = (*userInfo)["s_name"].(string)
+			}
+			if (*userInfo)["s_name"] != nil {
+				data["s_submitname"] = (*userInfo)["s_name"].(string)
+			}
+			data["s_submitid"] = userId
+		}
+		data["i_type"] = 7
+		value := m.GetString("value")
+		value = strings.Replace(value, "'", "&qpos;", 1)
+		if len([]rune(value)) > 200 {
+			value = util.SubString(value, 0, 200)
+		}
+		data["s_remark"] = "剑鱼-一键报告排版问题\n" + value
+		data["s_mop"] = m.GetString("mop") //来源:移动端or电脑端
+		data["s_title"] = m.GetString("title")
+		data["i_status"] = 0
+		data["l_submitdate"] = time.Now().Unix()
+		data["s_source"] = m.GetString("source")
+		data["s_primaryhref"] = m.GetString("primaryhref")
+		data["s_fkid"] = fkid
+		id := mongodb.Save("interaction", data)
+		if len(id) > 0 {
+			flag = "Y"
+		}
+		//} else if (*intdata)["i_status"] == 1 {
+		//			set := make(map[string]interface{})
+		//			set["i_status"] = 0
+		//			set["s_opinion"] = ""
+		//			set["s_auditname"] = ""
+		//			set["s_editorname"] = ""
+		//			set["l_submitdate"] = time.Now().Unix()
+		//			id := mongodb.Update("interaction", `{"s_fkid":"`+fkid+`","i_type":7}`, &map[string]interface{}{"$set": set}, false, false)
+		//			if id {
+		//				flag = "Y"
+		//			}
+		//}
+		redis.Put("other", "onekey_"+fkid, fkid, 10*60)
+	}
+	//}
+	m.ServeJson(map[string]interface{}{
+		"flag": flag,
+	})
+	return nil
+}
+
+//
+func (m *Front) Encrypt() error {
+	defer util.Catch()
+	id := m.GetString("id")
+	s_openid := m.GetSession("s_m_openid")
+	flag := "F"
+	var sid_openid string
+	util.Try(func() {
+		if id != "" && s_openid != nil {
+			id = util.DecodeArticleId2ByCheck(id)[0]
+			sid_openid = util.EncodeArticleId2ByCheck(id, s_openid.(string))
+			flag = "T"
+		}
+	}, func(e interface{}) {
+		log.Println("分享短地址加密出错", e)
+	})
+	m.ServeJson(map[string]interface{}{
+		"flag":       flag,
+		"sid_openid": sid_openid,
+	})
+	return nil
+}
+
+//
+func (m *Front) Wxkeywords() error {
+	myopenid, _ := m.Session().Get("s_m_openid").(string)
+	m.T["openid"] = se.EncodeString(myopenid)
+	mynickname, _ := m.Session().Get("s_nickname").(string)
+	myavatar, _ := m.Session().Get("s_avatar").(string)
+	m.T["nickname"] = mynickname
+	m.T["avatar"] = myavatar
+	m.T["signature"] = wx.SignJSSDK(m.Site() + m.Url())
+	return m.Render("/weixin/wxkeywords.html", &m.T)
+}
+
+//
+func (m *Front) Wxerr() error {
+	return m.Render("/_err.html")
+}
+
+//
+func (m *Front) Wxscope() error {
+	myopenid, _ := m.Session().Get("s_m_openid").(string)
+	m.T["openid"] = se.EncodeString(myopenid)
+	mynickname, _ := m.Session().Get("s_nickname").(string)
+	myavatar, _ := m.Session().Get("s_avatar").(string)
+	m.T["nickname"] = mynickname
+	m.T["avatar"] = myavatar
+	m.T["signature"] = wx.SignJSSDK(m.Site() + m.Url())
+	return m.Render("/weixin/wxscope.html", &m.T)
+}
+
+//查找用户并创建session
+func FindUserAndCreateSess(openid string, sess *httpsession.Session) (ok bool) {
+	_person, ok := tools.MQFW.FindOne("user",
+		bson.M{"s_m_openid": openid, "i_ispush": bson.M{"$ne": 0}})
+	if ok && *_person != nil && len(*_person) > 0 {
+		person := *_person
+		//加入session
+		//		if person["i_know"] != nil {
+		//			sess.Set("iknow", person["i_know"])
+		//		}
+		if person["i_shareknow"] != nil {
+			sess.Set("shareknow", person["i_shareknow"])
+		}
+		sess.Set("userId", (person["_id"].(bson.ObjectId)).Hex())
+		sess.Set("s_m_openid", person["s_m_openid"])
+		sess.Set("s_nickname", person["s_nickname"])
+		sess.Set("s_avatar", person["s_avatar"])
+	}
+	return
+}
+
+//微信跳转创建session
+func (m *Front) Sess(ostr string) error {
+	defer util.Catch()
+	strs := strings.Split(ostr, "__")
+	str := strings.Split(sewx.DecodeString(strs[0]), ",")
+	if len(str) == 4 {
+		openid := str[0]
+		ok := FindUserAndCreateSess(openid, m.Session())
+		if ok {
+			actionurl := util.ObjToString(urlMap[str[3]])
+			if actionurl != "" {
+				if len(strs) > 1 {
+					//支持多个参数%s..
+					actionurl = fmt.Sprintf(actionurl, func(tmps []string) []interface{} {
+						res := make([]interface{}, len(tmps))
+						for k, v := range tmps {
+							res[k] = v
+						}
+						return res
+					}(strs[1:])...)
+				}
+				m.Redirect(actionurl)
+			} else {
+				if !ok {
+					log.Println("数据库连接超时!", openid)
+				} else {
+					log.Println("解析结果:", str, ",actionurl为空")
+				}
+				m.Render("_error.html")
+			}
+		} else {
+			go tools.SaveAbnormal(openid)
+			log.Println("没有找到该用户:", openid)
+			m.Render("_error.html")
+		}
+	} else {
+		log.Println("解析出错,解析结果:", str)
+		m.Render("_error.html")
+	}
+	return nil
+}
+
+//删除模板缓存
+func (d *Front) Delc(url string) {
+	defer util.Catch()
+	d.App.TemplateMgr.CacheDelete(strings.Replace(url, "GG", "/", 1))
+	d.WriteBytes([]byte("ok,清除路径:" + url))
+}
+
+func (m *Front) Viewdemo() {
+	m.Redirect("/swordfish/guide/-1")
+}
+
+//剑鱼用户协议
+func (m *Front) Wxprotocol() error {
+	return m.Render("/weixin/wxprotocol.html")
+}
+
+//此方法已经不用
+//func (m *Front) Iknow() {
+//	defer util.Catch()
+//	if m.GetSession("userId") != nil {
+//		mongodb.Update("user", bson.M{"_id": bson.ObjectIdHex(m.GetSession("userId").(string))}, `{"$set":{"i_know":1}}`, false, false)
+//		m.SetSession("iknow", 1)
+//		m.Write("1")
+//	} else {
+//		m.Write("0")
+//	}
+//}
+
+//此方法已经不用
+//func (m *Front) Aiknow() {
+//	defer util.Catch()
+//	if m.GetSession("userId") != nil {
+//		mongodb.Update("user", bson.M{"_id": bson.ObjectIdHex(m.GetSession("userId").(string))}, `{"$set":{"i_shareknow":1}}`, false, false)
+//		m.SetSession("shareknow", 1)
+//		m.Write("1")
+//	} else {
+//		m.Write("0")
+//	}
+//}
+
+//推送列表
+func (m *Front) WxpushListInfo(_id string) error {
+	defer util.Catch()
+	if m.Session().Get("s_m_openid") == nil {
+		return m.Redirect("/swordfish/share/-1")
+	}
+	tmp, ok := mongodb.FindById("wxpush", _id, nil)
+	if ok {
+		(*tmp)["bmatch"] = true
+		m.T["data"] = &tmp
+	} else {
+		(*tmp)["bmatch"] = false
+		m.T["data"] = &tmp
+
+	}
+	m.T["_id"] = _id
+	m.T["signature"] = wx.SignJSSDK(m.Site() + m.Url())
+	myopenid, _ := m.Session().Get("s_m_openid").(string)
+	m.T["openid"] = se.EncodeString(myopenid)
+	mynickname, _ := m.Session().Get("s_nickname").(string)
+	myavatar, _ := m.Session().Get("s_avatar").(string)
+	m.T["nickname"] = mynickname
+	m.T["avatar"] = myavatar
+	return m.Render("/weixin/wxpush.html", &m.T)
+}
+func (m *Front) Feedback() error {
+	defer util.Catch()
+	myopenid, _ := m.Session().Get("s_m_openid").(string)
+	m.T["openid"] = se.EncodeString(myopenid)
+	m.T["signature"] = wx.SignJSSDK(m.Site() + m.Url())
+	userId, _ := m.GetSession("userId").(string)
+	list, _ := mongodb.Find("interaction", bson.M{"s_submitid": userId}, `{"l_submitdate":-1}`, `{"s_remark":1,"l_submitdate":1,"s_opinion":1,"i_status":1}`, false, 0, 200)
+	m.T["list"] = list
+	fkid := m.GetString("fkid")
+	if fkid != "" {
+		fkid = util.DecodeArticleId2ByCheck(fkid)[0]
+	}
+	util.ReadConfig(&config.Sysconfig)
+	_, m.T["advertText"] = getRewardText()
+	m.T["advertImg"] = config.Sysconfig["advertImg"]
+	m.T["advertName"] = config.Sysconfig["advertName"]
+	m.T["advertUrl"] = config.Sysconfig["advertUrl"]
+	m.T["fkid"] = fkid
+	return m.Render("/weixin/feedback.html", &m.T)
+}
+
+func (m *Front) GetIndexData() {
+	defer util.Catch()
+	redis_obj := redis.Get("other", "sw_index")
+	var one map[string]interface{}
+	if redis_obj != nil {
+		one = redis_obj.(map[string]interface{})
+		log.Println("newpage from the cache...")
+	} else {
+		rs, err := mongodb.Find("swordfish_index", nil, `{"_id":-1}`, nil, false, 0, 1)
+		if err {
+			one = (*rs)[0]
+			one["i_site"] = util.IntAll(one["i_entsite"]) + util.IntAll(one["i_govsite"])
+			avg := util.IntAll(one["i_bidmonth"])/21 + util.IntAll(one["i_bidmonth"])%21
+			one["i_avg"] = avg
+			one["i_my"] = util.IntAll(one["i_entsite"]) * 35 / 100
+			one["i_hy"] = util.IntAll(one["i_entsite"]) * 15 / 100
+			one["i_zb"] = util.IntAll(one["i_entsite"]) - util.IntAll(one["i_my"]) - util.IntAll(one["i_hy"])
+			redis.Put("other", "sw_index", one, 60*60*2)
+		} else {
+			m.ServeJson("n")
+		}
+	}
+	userid := m.GetSession("userId")
+	if userid != nil {
+		sess := mongodb.GetMgoConn()
+		defer mongodb.DestoryMongoConn(sess)
+		var res bson.M
+		sess.DB("qfw").C("wxpush").Pipe([]bson.M{bson.M{"$match": bson.M{"s_uid": userid}},
+			bson.M{"$group": bson.M{"_id": "sum", "totalAmount": bson.M{"$sum": "$i_size"}}}}).One(&res)
+		one["i_me"] = util.IntAll(res["totalAmount"])
+	}
+	m.ServeJson(one)
+}
+
+func (m *Front) Urlrecord() {
+	m.Render("/weixin/urlrecord.html")
+}
+func (m *Front) Isrecord(name string) {
+	defer util.Catch()
+	querystr := `{"$or":[{"s_name":"%s"},{"s_url":"%s"}]}`
+	if name != "" {
+		rs, _ := mongodb.Find("bidurlinfo", fmt.Sprintf(querystr, name, name), nil, nil, false, -1, -1)
+		if len(*rs) == 0 {
+			m.Write("f")
+		} else {
+			m.Write("y")
+		}
+	} else {
+		m.Write("n")
+	}
+}
+
+//招标订阅向导
+func (f *Front) TSGuide() error {
+	defer util.Catch()
+	s_openId := f.GetSession("s_m_openid")
+	if s_openId == nil {
+		return f.Redirect("/swordfish/share/-1")
+	}
+	openId, _ := s_openId.(string)
+	if f.Method() == "GET" {
+		if !isInTSguide(openId) {
+			return f.Redirect("/wxkeyset/keyset/index")
+		}
+		f.T["signature"] = wx.SignJSSDK(f.Site() + f.Url())
+		return f.Render("/weixin/wxtsguide.html")
+	} else {
+		reqType := f.GetString("reqType")
+		result := make(bson.M)
+		if reqType == "save" {
+			var keyMaps []map[string]interface{}
+			keyWord := f.GetSlice("keyWord")
+			for _, v := range keyWord {
+				vs := processKeyword(v)
+				if vs == nil {
+					continue
+				}
+				keyMaps = append(keyMaps, map[string]interface{}{
+					"key": vs,
+				})
+				if len(keyMaps) >= 10 {
+					break
+				}
+			}
+			saveData := bson.M{
+				"o_jy.a_key":        keyMaps,
+				"o_jy.l_modifydate": time.Now().Unix(),
+				"i_ts_guide":        2,
+			}
+			result["flag"] = mongodb.Update("user", bson.M{"s_m_openid": openId}, bson.M{"$set": saveData}, false, false)
+		} else if reqType == "over" {
+			mongodb.Update("user", bson.M{"s_m_openid": openId}, bson.M{"$set": bson.M{"i_ts_guide": 1}}, false, false)
+		} else if reqType == "preview" {
+			result["data"] = elastic.GetByNgram(INDEX, TYPE, strings.Split(f.GetString("key"), " "), "", FINDF, `{"publishtime":-1}`, `"_id","title","publishtime","toptype","subtype","type","area","href","areaval"`, 0, 10)
+		}
+		f.ServeJson(result)
+	}
+	return nil
+}
+
+func isInTSguide(openId string) bool {
+	if openId == "" {
+		return false
+	}
+	data, ok := mongodb.FindOneByField("user", bson.M{"s_m_openid": openId}, `{"o_jy":1,"i_ts_guide":1}`)
+	if ok {
+		o_jy, _ := (*data)["o_jy"].(map[string]interface{})
+		i_ts_guide := util.IntAll((*data)["i_ts_guide"])
+		if i_ts_guide == 2 || (i_ts_guide == 0 && len(o_jy) == 0) {
+			return true
+		}
+	}
+	return false
+}
+
+//查看原文中转
+func (f *Front) Transfer() error {
+	return f.Redirect(f.GetString("url"))
+}
+
+//关于我们
+func (f *Front) Aboutus() error {
+	if ret := redis.Get("other", "aboutus"); ret != nil {
+		return f.SetBody([]byte(ret.(string)))
+	} else {
+		var shareid = f.GetString("id")
+		if len(shareid) == 0 {
+			shareid = fmt.Sprintf("%s%d", config.Seoconfig["jygywmy"].(string)+fmt.Sprintf("%d", time.Now().UnixNano())[7:14], rand.Intn(9))
+		}
+		f.T["logid"] = config.Seoconfig["jygywmy"].(string)
+		f.T["shareid"] = se.EncodeString(shareid)
+		content, _ := f.Render4Cache("/pc/aboutus.html", &f.T)
+		redis.Put("other", "aboutus", string(content), -1)
+		return f.SetBody(content)
+	}
+}
+
+func (f *Front) Gethotkey() error {
+	keys := []interface{}{}
+	tmp := redis.Get("sso", "jy_hotkeys")
+	if tmp != nil {
+		keys = tmp.([]interface{})
+	} else {
+		list, _ := mongodb.Find("user", `{"o_jy.a_key":{$exists:true}}`, nil, `{"o_jy":1}`, false, -1, -1)
+		keymap := map[string]int{}
+		for _, user := range *list {
+			if o_jy, ok := user["o_jy"].(map[string]interface{}); ok {
+				a_key, _ := o_jy["a_key"].([]interface{})
+				for _, v := range a_key {
+					if tmp, ok := v.(map[string]interface{}); ok {
+						keys := tmp["key"].([]interface{})
+						for _, val := range keys {
+							keymap[fmt.Sprint(val)] += 1
+						}
+					}
+				}
+			}
+		}
+		keylist := []*jyutil.ObjectMap{}
+		for k, v := range keymap {
+			obj := &jyutil.ObjectMap{}
+			obj.Key = k
+			obj.Num = v
+			keylist = append(keylist, obj)
+		}
+		keylist = jyutil.SortMap(keylist)
+		for k, v := range keylist {
+			if k < 6 {
+				keys = append(keys, v.Key)
+			} else {
+				break
+			}
+		}
+		redis.Put("sso", "jy_hotkeys", keys, 7*24*60*60)
+	}
+	f.ServeJson(keys)
+	return nil
+}
+
+//
+
+func (f *Front) Rediskw() error {
+	skw := f.GetString("skw")
+	num := f.GetString("num")
+	if skw != "" && num != "" {
+		num = se.DecodeString(num)
+		redis.Put("sso", "pc_subscribe_"+num, skw, 15*60)
+	}
+	return nil
+}

+ 78 - 0
src/jfw/front/jyshare.go

@@ -0,0 +1,78 @@
+package front
+
+import (
+	"fmt"
+	jutil "jfw/jyutil"
+	"jfw/tools"
+	"log"
+	"qfw/util/redis"
+	"strconv"
+	"strings"
+
+	"github.com/go-xweb/xweb"
+)
+
+type Jyshare struct {
+	*xweb.Action
+	wxshare xweb.Mapper `xweb:"/front/wxshare/([^.]*)"` //微信分享图片
+	share   xweb.Mapper `xweb:"/front/share/([^.]*)"`   //电脑版微信二维码
+}
+
+func init() {
+	xweb.AddAction(&Jyshare{})
+}
+
+//电脑版微信二维码
+func (m *Jyshare) Share(pid string) error {
+	var pngdata = []byte{}
+	pid = se.DecodeString(pid)
+	log.Println("Share", pid)
+	if ret, _ := redis.GetBytes("sso", fmt.Sprintf("p_share_%s", pid)); ret != nil {
+		pngdata = *ret
+	} else {
+		id, _ := strconv.Atoi(pid)
+		tools.GetShareQR(uint32(id))
+		tmp, err := redis.GetBytes("sso", fmt.Sprintf("p_share_%d", id))
+		if err == nil {
+			pngdata = *tmp
+		} else {
+			log.Println("get share err:", id, err.Error())
+		}
+	}
+	w := m.ResponseWriter
+	w.Header().Set("Cache-Control", "no-cache,no-store,must-revalidate")
+	w.Header().Set("Prama", "no-cache")
+	w.Header().Set("Expires", "0")
+	w.Header().Set("Content-Type", "image/png")
+	w.Write(pngdata)
+	return nil
+}
+
+//取微信分享图片
+func (m *Jyshare) Wxshare(openids string) error {
+	wxid_code := strings.Split(openids, "__")
+	var openid = ""
+	var jy_code = ""
+	if len(wxid_code) > 0 {
+		openid = wxid_code[0]
+		jy_code = wxid_code[1]
+	}
+	var pngdata = []byte{}
+	openid = se.DecodeString(openid)
+	pid := jutil.FindMyShareId(jy_code, openid)
+	if ret, _ := redis.GetBytes("sso", fmt.Sprintf("p_share_%s", pid)); ret != nil {
+		pngdata = *ret
+	} else {
+		id, _ := strconv.Atoi(pid)
+		tools.GetShareQR(uint32(id))
+		tmp, _ := redis.GetBytes("sso", fmt.Sprintf("p_share_%d", id))
+		pngdata = *tmp
+	}
+	w := m.ResponseWriter
+	w.Header().Set("Cache-Control", "no-cache,no-store,must-revalidate")
+	w.Header().Set("Prama", "no-cache")
+	w.Header().Set("Expires", "0")
+	w.Header().Set("Content-Type", "image/png")
+	w.Write(pngdata)
+	return nil
+}

+ 267 - 0
src/jfw/front/pay.go

@@ -0,0 +1,267 @@
+package front
+
+import (
+	"encoding/json"
+	"encoding/xml"
+	"fmt"
+	"io/ioutil"
+	"jfw/config"
+	"jfw/tools"
+	"jfw/wx"
+	"log"
+	"net/http"
+	"qfw/util"
+	"strings"
+	"time"
+
+	"github.com/SKatiyar/qr"
+	"github.com/go-xweb/xweb"
+)
+
+type Pay struct {
+	*xweb.Action
+	redirectOauth xweb.Mapper `xweb:"/front/pay/redirect"`    //微信支付授权
+	getUserInfo   xweb.Mapper `xweb:"/front/pay/getUserInfo"` //微信支付获取用户信息
+	payIndex      xweb.Mapper `xweb:"/weixin/pay/index"`      //微信支付页面
+	wxPay         xweb.Mapper `xweb:"/weixin/pay/pay"`        //微信支付
+	payCallback   xweb.Mapper `xweb:"/weixin/pay/callback"`   //微信支付回调
+	qr            xweb.Mapper `xweb:"/front/pay/qr/([^.]*)"`  //生成二维码图片
+}
+
+var jianyuConfig map[string]interface{}
+var payConfig map[string]interface{}
+
+func init() {
+	xweb.AddAction(&Pay{})
+	jianyuConfig = config.Sysconfig["wxJianyu"].(map[string]interface{})
+	payConfig = jianyuConfig["pay"].(map[string]interface{})
+}
+
+//转发
+func (p *Pay) RedirectOauth() {
+	param := p.GetString("source") + "_" + p.GetString("id")
+	webdomain := config.Sysconfig["webdomain"].(string)
+	openId, _ := p.GetSession("payOpenId").(string)
+	if openId == "" {
+		openId, _ = p.GetSession("s_m_openid").(string)
+	}
+	if openId != "" {
+		p.SetSession("payOpenId", openId)
+		p.Redirect(webdomain+"/weixin/pay/index?param="+param, http.StatusFound)
+		return
+	}
+	urlstr := "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + jianyuConfig["appid"].(string) + "&redirect_uri=" + webdomain + "/front/pay/getUserInfo?param=" + param + "&response_type=code&scope=snsapi_base&state=1#wechat_redirect"
+	p.Redirect(urlstr, http.StatusFound)
+}
+
+//取用户信息,取完以后再跳转到实际支付页面
+func (p *Pay) GetUserInfo() {
+	api_url := "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code"
+	//取用户信息
+	recturl := fmt.Sprintf(api_url, jianyuConfig["appid"].(string), jianyuConfig["appsecret"].(string), p.GetString("code"))
+	resp, err := http.Get(recturl)
+	defer resp.Body.Close()
+	if err != nil {
+		fmt.Println(err.Error())
+		return
+	}
+	bs, _ := ioutil.ReadAll(resp.Body)
+	resp.Body.Close()
+	data := map[string]interface{}{}
+	json.Unmarshal(bs, &data)
+	openid, _ := data["openid"].(string)
+	//传入openid,进入支付页面,正常情况下应该存入session
+	p.SetSession("payOpenId", openid)
+	p.Redirect("/weixin/pay/index?param="+p.GetString("param"), http.StatusFound)
+}
+func (p *Pay) PayIndex() error {
+	param := p.GetString("param")
+	params := strings.Split(param, "_")
+	id := util.DecodeArticleId2ByCheck(params[1])[0]
+	title := ""
+	if id != "" {
+		data, ok := mongodb.FindById("bidding", id, `{"title":1}`)
+		if ok && (data == nil || *data == nil || len(*data) == 0) {
+			data, ok = mongodb.FindById("bidding_back", id, `{"title":1}`)
+		}
+		if ok && data != nil && len(*data) > 0 {
+			title, _ = (*data)["title"].(string)
+		}
+	}
+	p.T["source"] = params[0]
+	p.T["id"] = id
+	p.T["title"] = title
+	p.T["signature"] = wx.SignJSSDK(p.Site() + p.Url())
+	return p.Render("/weixin/pay.html", &p.T)
+}
+func (p *Pay) WxPay() error {
+	openId, _ := p.GetSession("payOpenId").(string)
+	if openId == "" {
+		p.ServeJson(map[string]interface{}{
+			"status": 0,
+		})
+		return nil
+	}
+	reqType := p.GetString("reqType")
+	if reqType == "getPrepayId" {
+		tf, _ := p.GetFloat("totalfee")
+		if tf <= 0 {
+			p.ServeJson(map[string]interface{}{
+				"status": 0,
+			})
+			return nil
+		}
+		totalfee := util.IntAll(tf * 100)
+		title := p.GetString("title")
+		tradeno := "jy-" + fmt.Sprint(time.Now().Unix()) + util.Uuid(12)
+		//获取预订单号
+		ret, err := tools.GetPrepayId(map[string]string{
+			"attachmsg":  payConfig["attachmsg"].(string),
+			"bodymsg":    payConfig["bodymsg"].(string),
+			"detailmsg":  fmt.Sprintf(payConfig["detailmsg"].(string), title, fmt.Sprint(float64(totalfee)/100)),
+			"useropenid": openId,
+			"tradeno":    tradeno,
+			"userip":     p.IP(),
+			"totalfee":   fmt.Sprint(totalfee),
+			"mchid":      payConfig["mchid"].(string),
+			"key":        payConfig["key"].(string),
+			"notifyUrl":  config.Sysconfig["webdomain"].(string) + "/weixin/pay/callback",
+		})
+		if err != nil || ret == nil {
+			p.ServeJson(map[string]interface{}{
+				"status": -1,
+			})
+			return nil
+		}
+		prepayId, _ := (*ret)["prepayid"].(string)
+		if prepayId == "" {
+			p.ServeJson(map[string]interface{}{
+				"status": (*ret)["status"],
+			})
+			return nil
+		}
+		nonceStr := util.Uuid(32)
+		timestamp := time.Now().Unix()
+		sign := util.WxSign(fmt.Sprintf("appId=%s&nonceStr=%s&package=%s&signType=%s&timeStamp=%d&key=%s", jianyuConfig["appid"].(string), nonceStr, "prepay_id="+prepayId, "MD5", timestamp, payConfig["key"].(string)))
+		//保存记录
+		now := time.Now()
+		mongodb.Save("reward", map[string]interface{}{
+			"s_source":     p.GetString("source"),
+			"s_prepayid":   prepayId,
+			"s_title":      title,
+			"s_id":         p.GetString("id"),
+			"l_createtime": now.Unix(),
+			"l_updatetime": now.Unix(),
+			"s_openid":     openId,
+			"i_year":       now.Year(),
+			"i_month":      now.Month(),
+			"i_day":        now.Day(),
+			"i_hour":       now.Hour(),
+			"i_minute":     now.Minute(),
+			"i_status":     0,
+			"i_totalfee":   totalfee,
+			"s_tradeno":    tradeno,
+			"i_operation":  0,
+		})
+		p.ServeJson(map[string]interface{}{
+			"status":    1,
+			"appId":     jianyuConfig["appid"].(string),
+			"timestamp": fmt.Sprint(timestamp),
+			"signType":  "MD5",
+			"sign":      sign,
+			"nonceStr":  nonceStr,
+			"prepayId":  "prepay_id=" + prepayId,
+		})
+	} else if reqType == "updateOrder" {
+		operation, _ := p.GetInteger("operation")
+		prepayId := p.GetString("prepayId")
+		if operation == 1 || operation == -1 {
+			mongodb.Update("reward", `{"s_openid":"`+openId+`","s_prepayid":"`+prepayId+`"}`, map[string]interface{}{
+				"$set": map[string]interface{}{
+					"i_operation":  operation,
+					"l_updatetime": time.Now().Unix(),
+				},
+			}, false, false)
+		}
+	}
+	return nil
+}
+func (p *Pay) PayCallback() {
+	payCallBackChan <- true
+	defer func() {
+		<-payCallBackChan
+	}()
+	by := p.Body()
+	fmt.Println("回调通知参数", string(by))
+	ret := struct {
+		Appid         string `xml:"appid"`
+		Attach        string `xml:"attach"`
+		BankType      string `xml:"bank_type"`
+		CashFee       int    `xml:"cash_fee"`
+		FeeType       string `xml:"fee_type"`
+		IsSubscribe   string `xml:"is_subscribe"`
+		MchId         string `xml:"mch_id"`
+		NonceStr      string `xml:"nonce_str"`
+		OpenId        string `xml:"openid"`
+		OutTradeNo    string `xml:"out_trade_no"`
+		ResultCode    string `xml:"result_code"`
+		ReturnCode    string `xml:"return_code"`
+		Sign          string `xml:"sign"`
+		TimeEnd       string `xml:"time_end"`
+		TotalFee      string `xml:"total_fee"`
+		TradeType     string `xml:"trade_type"`
+		TransactionId string `xml:"transaction_id"` //微信支付订单号
+	}{}
+	err := xml.Unmarshal(by, &ret)
+	if err == nil {
+		if ret.ReturnCode == "SUCCESS" && ret.Appid == jianyuConfig["appid"].(string) && ret.MchId == payConfig["mchid"].(string) {
+			sign := util.WxSign("appid=%s&attach=%s&bank_type=%s&cash_fee=%d&fee_type=%s&is_subscribe=%s&mch_id=%s&nonce_str=%s&openid=%s&out_trade_no=%s&result_code=%s&return_code=%s&time_end=%s&total_fee=%s&trade_type=%s&transaction_id=%s&key=%s", ret.Appid, ret.Attach, ret.BankType, ret.CashFee, ret.FeeType, ret.IsSubscribe, ret.MchId, ret.NonceStr, ret.OpenId, ret.OutTradeNo, ret.ResultCode, ret.ReturnCode, ret.TimeEnd, ret.TotalFee, ret.TradeType, ret.TransactionId, payConfig["key"].(string))
+			if ret.Sign == sign {
+				query := map[string]interface{}{
+					"s_openid":   ret.OpenId,
+					"s_tradeno":  ret.OutTradeNo,
+					"i_totalfee": ret.CashFee,
+					"i_status":   0,
+				}
+				mongodb.Update("reward", query, map[string]interface{}{
+					"$set": map[string]interface{}{
+						"i_status":        1,
+						"s_timeend":       ret.TimeEnd,
+						"l_updatetime":    time.Now().Unix(),
+						"s_transactionid": ret.TransactionId,
+					},
+				}, false, false)
+				p.Write(`<xml>
+			  	<return_code><![CDATA[SUCCESS]]></return_code>
+			  	<return_msg><![CDATA[OK]]></return_msg>
+			</xml>`)
+				return
+			} else {
+				log.Println("支付回调签名校验未通过!")
+			}
+		} else {
+			log.Println("支付回调参数校验未通过!")
+		}
+	} else {
+		log.Println(err.Error())
+	}
+	p.Write(`<xml>
+			<return_code><![CDATA[FAIL]]></return_code>
+			<return_msg><![CDATA[ERROR]]></return_msg>
+		</xml>`)
+	return
+}
+
+//生成企业黄页二维码图片
+func (c *Pay) Qr(code string) error {
+	w := c.ResponseWriter
+	w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
+	w.Header().Set("Pragma", "no-cache")
+	w.Header().Set("Expires", "0")
+	w.Header().Set("Content-Type", "image/png")
+	data := fmt.Sprintf("%s/front/pay/redirect?id=%s&source=p", config.Sysconfig["webdomain"].(string), code)
+	r, _ := qr.Encode(data, qr.M)
+	pngdat := r.PNG()
+	_, err := w.Write(pngdat)
+	return err
+}

+ 175 - 0
src/jfw/front/shorturl.go

@@ -0,0 +1,175 @@
+package front
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"jfw/config"
+	"jfw/tools"
+	"jfw/wx"
+	"log"
+	"math/rand"
+	"net/http"
+	"qfw/util"
+	elastic "qfw/util/elastic"
+	"qfw/util/redis"
+	"regexp"
+	"strings"
+	"time"
+
+	"github.com/go-xweb/xweb"
+)
+
+var Wxoauth, Wxoauthinfo string
+
+func init() {
+	Wxoauth = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=` + jianyuConfig["appid"].(string) + `&redirect_uri=%s&response_type=code&scope=snsapi_base&state=%s#wechat_redirect`
+	Wxoauthinfo = `https://api.weixin.qq.com/sns/oauth2/access_token?appid=` + jianyuConfig["appid"].(string) + `&secret=` + jianyuConfig["appsecret"].(string) + `&code=%s&grant_type=authorization_code`
+}
+
+type Short struct {
+	*xweb.Action
+	article xweb.Mapper `xweb:"/article/(\\w+)/(.*).html"` //([pm])
+}
+
+func (s *Short) Article(clientType, id string) error {
+	//先判断是否有session
+	if s.Session().Get("userId") == nil {
+		if s.GetString("state") == "wx" {
+			//微信跳回来的
+			code := s.GetString("code")
+			if code != "" {
+				openid := Getopenid(code)
+				if openid != "" {
+					FindUserAndCreateSess(openid, s.Session())
+				}
+			}
+		} else {
+			if tools.CheckWxBrowser(s.Request) {
+				//所有参数都不再使用,跳到微信验证用户
+				s.Redirect(fmt.Sprintf(Wxoauth, s.Site()+s.Request.URL.Path, "wx"), 302)
+			}
+		}
+	}
+
+	regex, _ := regexp.Compile("(Android|Mobile)")
+	log.Println("(Android|Mobile)", len(regex.FindAllString(s.Header("User-Agent"), -1)) > 0)
+	if len(regex.FindAllString(s.Header("User-Agent"), -1)) > 0 {
+		surl := s.GetString("url")
+		kds := s.GetString("keywords")
+		log.Println(kds)
+		var shareopenid, sid string
+		sid_openid := util.DecodeArticleId2ByCheck(id)
+		if len(sid_openid) > 1 {
+			sid = sid_openid[0]
+			shareopenid = sid_openid[1]
+		} else {
+			sid = sid_openid[0]
+		}
+		//if res := redis.Get("other", "jywxdetail_"+sid+kds); res != nil {
+		//	return s.SetBody([]byte(res.(string)))
+		//} else {
+		s.T["keywords"] = kds
+		if shareopenid != "" {
+			s.T["shareopenid"] = shareopenid
+		}
+		myopenid, _ := s.Session().Get("s_m_openid").(string)
+		if myopenid == "" {
+			myopenid = shareopenid
+			s.T["openid"] = myopenid //"-1"
+		} else {
+			s.T["openid"] = se.EncodeString(myopenid) //"-1"
+		}
+		mynickname, _ := s.GetSession("s_nickname").(string)
+		myavatar, _ := s.GetSession("s_avatar").(string)
+		s.T["nickname"] = mynickname
+		s.T["avatar"] = myavatar
+		s.T["signature"] = wx.SignJSSDK(s.Site() + s.Url())
+		userId, _ := s.GetSession("userId").(string)
+		var obj map[string]interface{}
+		obj = wxvisitD(sid, surl, userId)
+		if len(obj) > 0 {
+			//获取打赏文案
+			util.ReadConfig(&config.Sysconfig)
+			s.T["rewardText"], s.T["advertText"] = getRewardText()
+			s.T["advertImg"] = config.Sysconfig["advertImg"]
+			s.T["advertName"] = config.Sysconfig["advertName"]
+			s.T["advertUrl"] = config.Sysconfig["advertUrl"]
+			s.T["obj"] = obj
+			content, _ := s.Render4Cache("/weixin/wxinfocontent.html", &s.T)
+			//	redis.Put("other", "jywxdetail_"+sid+kds, string(content), 60*60*2)
+			return s.SetBody(content)
+			//}
+		}
+		if surl != "" {
+			s.Redirect(surl)
+		}
+	} else {
+		sid := util.DecodeArticleId2ByCheck(id)[0]
+		log.Println("sid", sid)
+		kds := s.GetString("kds")
+		res := redis.Get("other", "jypcdetail_"+sid+kds)
+		var shareid = s.GetString("id")
+		if len(shareid) == 0 {
+			shareid = fmt.Sprintf("%s%d", config.Seoconfig["jysskzy"].(string)+fmt.Sprintf("%d", time.Now().UnixNano())[8:14], rand.Intn(9))
+		}
+		s.T["logid"] = config.Seoconfig["jysskzy"].(string)
+		s.T["shareid"] = se.EncodeString(shareid)
+		if res == nil || res == "" {
+			s.T["keywords"] = kds
+			s.DisableHttpCache()
+			data := elastic.GetByIdField("bidding", "bidding", sid, `"href"`)
+			if data == nil || len(*data) == 0 {
+				return s.Render("/_error.html")
+			}
+			href, _ := (*data)["href"].(string)
+			href = strings.Replace(href, "\n", "", -1)
+			if href != "" && !strings.HasPrefix(href, "http") {
+				href = "http://" + href
+			}
+			po, bo, wo, obj := pcVRT(sid)
+			if obj != nil && len(obj) > 0 {
+				if len(po) > 0 {
+					s.T["projectOther"] = po
+				}
+				if len(bo) > 0 {
+					s.T["buyerOther"] = bo
+				}
+				if len(wo) > 0 {
+					s.T["winnerOther"] = wo
+				}
+				obj["url"] = href
+				s.T["obj"] = obj
+				if obj["projectname"] != nil {
+					s.SetSession("projectname", obj["projectname"])
+				}
+				content, _ := s.Render4Cache("/pc/biddetail.html", &s.T)
+				redis.Put("other", "jypcdetail_"+sid+kds, string(content), 60*60*2)
+				return s.SetBody(content)
+			}
+		} else {
+			return s.SetBody([]byte(res.(string)))
+		}
+	}
+	return nil
+}
+
+//获取用户openid
+func Getopenid(code string) (openid string) {
+	defer util.Catch()
+	recturl := fmt.Sprintf(Wxoauthinfo, code)
+	log.Println(recturl)
+	resp, err := http.Get(recturl)
+	defer resp.Body.Close()
+	if err != nil {
+		fmt.Println(err.Error())
+		return
+	}
+	bs, _ := ioutil.ReadAll(resp.Body)
+	resp.Body.Close()
+	data := map[string]interface{}{}
+	json.Unmarshal(bs, &data)
+	log.Println(data)
+	openid, _ = data["openid"].(string)
+	return
+}

+ 2073 - 0
src/jfw/front/swordfish.go

@@ -0,0 +1,2073 @@
+package front
+
+import (
+	"encoding/base64"
+	"encoding/json"
+	"fmt"
+	"html/template"
+	"jfw/config"
+	"jfw/tools"
+	"jfw/wx"
+	"log"
+	"math/rand"
+	"qfw/util"
+	"qfw/util/elastic"
+	"qfw/util/redis"
+	"regexp"
+	"strconv"
+	"strings"
+	"sync"
+	"time"
+
+	"github.com/go-xweb/xweb"
+	. "github.com/thinxer/go-word2vec"
+	"gopkg.in/mgo.v2/bson"
+)
+
+const (
+	bidSearch_pageSize    = 50 //招标搜索分页--每页显示数量
+	bidSearch_maxPageSize = 10 //招标搜索分页--最大页数
+	wx_maxPageNum         = 20
+	wx_pageSize           = 50
+	bidSearch_field       = `"_id","title","publishtime","toptype","subtype","type","area","href","detail"`
+	bidSearch_sort        = `{"publishtime":-1}`
+)
+
+var M *Model
+var recomKWChan chan bool = make(chan bool, 1)
+var listlock = &sync.Mutex{}
+var clearHtml *regexp.Regexp
+
+func init() {
+	M, _ = Load("./zb.bin")
+	clearHtml, _ = regexp.Compile("<[^>]*>")
+}
+
+//剑鱼推送三级页点赞
+func (m *Front) Praise() error {
+	defer util.Catch()
+	var id = m.GetString("id")
+	id = util.DecodeArticleId2ByCheck(id)[0]
+	aosup, _ := m.GetInteger("aosup")
+	aosdown, _ := m.GetInteger("aosdown")
+	var flag = "F"
+	//userid := m.GetSession("userId")
+	i_upno := 0
+	i_downno := 0
+	util.Try(func() {
+		if len(id) > 0 {
+			sign := true
+			aobj, ok := mongodb.FindById("bidding", id, `{"i_upno":1,"i_downno":1}`)
+			if ok && (aobj == nil || *aobj == nil || len(*aobj) == 0) {
+				sign = false
+				aobj, ok = mongodb.FindById("bidding_back", id, `{"i_upno":1,"i_downno":1}`)
+			}
+			obj := *aobj
+			if ok && obj != nil {
+				if obj["i_upno"] != nil {
+					i_upno = obj["i_upno"].(int)
+				}
+				if obj["i_downno"] != nil {
+					i_downno = obj["i_downno"].(int)
+				}
+				i_upno = i_upno + aosup
+				i_downno = i_downno + aosdown
+				set := make(map[string]interface{})
+				set["i_upno"] = i_upno
+				set["i_downno"] = i_downno
+				if mongodb.Update(util.If(sign, "bidding", "bidding_back").(string), `{"_id":"`+id+`"}`, &map[string]interface{}{"$set": set}, false, false) {
+					flag = "T"
+				}
+
+			}
+		}
+	}, func(e interface{}) {
+		log.Println("文章点赞出错", e)
+	})
+	m.ServeJson(map[string]interface{}{
+		"flag": flag,
+	})
+	return nil
+}
+
+//剑鱼pc首页
+func (m *Front) NewSordfish() error {
+	ispc, _ := m.GetInteger("ispc")
+	var shareid = m.GetString("id")
+	if len(shareid) == 0 {
+		shareid = fmt.Sprintf("%s%d", config.Seoconfig["jysy"].(string)+fmt.Sprintf("%d", time.Now().UnixNano())[8:14], rand.Intn(9))
+	} else {
+		redis.Del("other", "jypcindex")
+	}
+	m.T["logid"] = config.Seoconfig["jysy"].(string)
+	m.T["shareid"] = se.EncodeString(shareid)
+	regex, _ := regexp.Compile("(Android|Mobile)")
+	if ispc == 0 && len(regex.FindAllString(m.Header("User-Agent"), -1)) > 0 {
+		return m.Render("/pc/mobileindex.html", &m.T)
+	} else {
+		if ret := redis.Get("other", "jypcindex"); ret != nil {
+			return m.SetBody([]byte(ret.(string)))
+		} else {
+			m.DisableHttpCache()
+			lastBids := elastic.GetPage("bidding", "bidding", "{}", `{"publishtime":-1}`, `"_id","title","publishtime","toptype","subtype","type","area","href"`, 0, 18)
+			if lastBids != nil && len(*lastBids) > 0 {
+				lbnHtml, olHtml := structureLastBidsHtml(lastBids)
+				m.T["lbnHtml"] = lbnHtml
+				m.T["olHtml"] = olHtml
+			}
+			content, _ := m.Render4Cache("/pc/index.html", &m.T)
+			redis.Put("other", "jypcindex", string(content), 60*60*2)
+			return m.SetBody(content)
+		}
+	}
+	return m.Render("/pc/index.html", &m.T)
+}
+
+func structureLastBidsHtml(lastBidNews *[]map[string]interface{}) (string, string) {
+	var tmp int = 0
+	var olCount int = 0
+	var lbnHtml string = ""
+	var olHtml string = `<ol class="carousel-indicators">`
+	for i := 0; i < len(*lastBidNews); i++ {
+		lastBidNew := (*lastBidNews)[i]
+		if tmp == 0 {
+			olHtml += `<li data-target="#lastBidNews" data-slide-to="` + strconv.Itoa(olCount) + `"`
+			if olCount == 0 {
+				olHtml += ` class="active"`
+			}
+			olHtml += `></li>`
+			olCount++
+		}
+		tmp++
+		if tmp == 1 {
+			lbnHtml += `<div class="item`
+			if i == 0 {
+				lbnHtml += ` active`
+			}
+			lbnHtml += `"><ul>`
+		}
+		lbnHtml += `<li><div><a onclick="noIn(this),_czc.push(['_trackEvent', '最新招标信息', '点击扫码', 'lastBidNews'])" dataListId="` + util.EncodeArticleId2ByCheck(util.ObjToString(lastBidNew["_id"])) + `" target="_blank">` + strconv.Itoa(tmp) + `.&nbsp;&nbsp;` + util.ObjToString(lastBidNew["title"]) + `</a></div><div>`
+		area, _ := lastBidNew["area"].(string)
+		area = strings.TrimSpace(area)
+		finalType, _ := lastBidNew["subtype"].(string)
+		if finalType == "" {
+			finalType = util.ObjToString(lastBidNew["toptype"])
+		}
+		if finalType == "" {
+			finalType = util.ObjToString(lastBidNew["type"])
+			if finalType == "bid" {
+				finalType = "中标"
+			} else if finalType == "tender" {
+				finalType = "招标"
+			} else {
+				finalType = ""
+			}
+		}
+		stpadd, areaadd := classify(finalType, area)
+		if area != "" && area != "A" {
+			lbnHtml += `<span class="com-area"><a href="/list/area/` + areaadd + `.html">` + area + `</a></span>`
+		}
+		if finalType != "" {
+			lbnHtml += `<span class="com-type"><a href="/list/stype/` + stpadd + `.html">` + finalType + `</a></span>`
+		}
+		publishtime, _ := lastBidNew["publishtime"].(float64)
+		if publishtime != 0 {
+			diff := util.TimeDiff(time.Unix(util.Int64All(publishtime), 0))
+			if diff != "" {
+				lbnHtml += `<span class="com-time" data-value="` + fmt.Sprint(util.Int64All(publishtime)) + `"><i class="glyphicon bofangjilu"></i>` + diff + `</span>`
+			}
+		}
+		lbnHtml += `</div></li>`
+		if tmp == 6 || i == len(*lastBidNews)-1 {
+			tmp = 0
+			lbnHtml += `</ul></div>`
+		}
+	}
+	if olCount > 1 {
+		olHtml += `</ol>`
+	}
+	return lbnHtml, olHtml
+}
+
+func detailLikeRpc(Interest []string, res *[]map[string]interface{}, flag bool) (mcontent map[string]interface{}) {
+	//defer util.Catch()
+	str := fmt.Sprintf("<div>根据您设置的关键词(%s),给您推送以下招标信息:</div>", strings.Join(Interest, ";"))
+	//发送内容组合
+	i := 0
+	o_pushinfo := map[string]interface{}{}
+	var details []string
+	v := *res
+	bmatch := false
+	for _, k2 := range v {
+		title := strings.Replace(k2["title"].(string), "\n", "", -1)
+		province := util.ObjToString(k2["area"])
+		if province != "" && province != "A" {
+			title = `[<span class='area'>` + province + `</span>]` + title
+		}
+		i++
+		str += "<div class='tslist'><span class='xh'>" + fmt.Sprintf("%d", i) + ".</span><a class='bt' target='_blank' sid='" + util.EncodeArticleId2ByCheck(util.BsonIdToSId(k2["_id"])) + "' eid='" + util.EncodeArticleId2ByCheck(util.BsonIdToSId(k2["_id"])) + "'  href='" + k2["href"].(string) + "'>" + title + "</a></div>"
+		o_pushinfo[strconv.Itoa(i)] = map[string]interface{}{
+			"publishtime": k2["publishtime"],
+			"stype":       k2["type"],
+			"topstype":    k2["toptype"],
+			"substype":    k2["subtype"],
+		}
+		if flag {
+			highlight, _ := k2["highlight"].(map[string][]string)
+			detail := ""
+			if len(highlight["detail"]) > 0 {
+				detail = highlight["detail"][0]
+			}
+			details = append(details, detail)
+		}
+	}
+	mcontent = map[string]interface{}{}
+	if len(o_pushinfo) > 0 {
+		bmatch = true
+		mcontent["o_pushinfo"] = o_pushinfo
+		mcontent["s_content"] = str
+		mcontent["s_words"] = Interest
+	}
+	mcontent["bmatch"] = bmatch
+	if flag {
+		mcontent["details"] = details
+	}
+	return
+}
+
+//跳转到pc查询剑鱼信息列表
+func (m *Front) Searchinfolist(p string) error {
+	defer util.Catch()
+	var shareid = m.GetString("id")
+	if len(shareid) == 0 {
+		shareid = fmt.Sprintf("%s%d", config.Seoconfig["jysslby"].(string)+fmt.Sprintf("%d", time.Now().UnixNano())[8:14], rand.Intn(9))
+	}
+	m.T["logid"] = config.Seoconfig["jysslby"].(string)
+	m.T["shareid"] = se.EncodeString(shareid)
+	keywords := m.GetString("keywords")
+
+	searchvalue := m.GetString("searchvalue")
+	area := m.GetString("area")
+	publishtime := m.GetString("publishtime")
+	toptype := m.GetString("toptype")
+	subtype := m.GetString("subtype")
+	selectType := m.GetString("selectType")
+	selectTypesess := m.GetSession("selectType")
+	if selectTypesess != nil && selectTypesess != "" {
+		selectType = selectTypesess.(string)
+	}
+	if selectType == "" {
+		selectType = "all"
+	}
+	var status = 1
+	var count int64
+	var list *[]map[string]interface{}
+	pages := make([]interface{}, 0)
+	if len(searchvalue) > 0 {
+		count, list = getBidSearchData(searchvalue, area, publishtime, subtype, 0, true, selectType)
+	} else if m.Method() == "POST" {
+		status = 2
+		count, list = getLastNewsData(searchvalue, area, publishtime, subtype, 0, true)
+	} else {
+		status = 2
+		if redis.Get("other", "index_list") == nil {
+			intns := make([]int, 0)
+			for i := 0; i < 10; i++ {
+				if len(intns) == 0 {
+					intns = append(intns, rand.Intn(100))
+				} else {
+					for {
+						v := rand.Intn(intns[(i-1)] + 100)
+						if v-intns[(i-1)] >= 30 {
+							intns = append(intns, v)
+							break
+						}
+					}
+				}
+			}
+			for i := 0; i < 10; i++ {
+				count, list = getLastNewsData(searchvalue, area, publishtime, subtype, intns[i], true)
+				for k, v := range *list {
+					v["k"] = (k + 1) + i*50
+					t := time.Unix(util.Int64All(v["publishtime"]), 0)
+					v["timetemp"] = fmt.Sprint(util.Int64All(v["publishtime"]))
+					v["_id"] = util.EncodeArticleId2ByCheck(v["_id"].(string))
+					v["time"] = util.TimeDiff(t)
+					var stp = ""
+					if v["subtype"] != nil {
+						stp, _ = v["subtype"].(string)
+					} else {
+						stp = ""
+					}
+					if stp == "" && v["toptype"] != nil {
+						stp, _ = v["toptype"].(string)
+					}
+					area, _ := v["area"].(string)
+					v["stypeadd"], v["areaadd"] = classify(stp, area)
+				}
+				pages = append(pages, list)
+			}
+			m.DisableHttpCache()
+			redis.Put("other", "index_list", pages, 60*60*2)
+		}
+	}
+
+	if status == 2 && m.Method() == "GET" {
+		pages := redis.Get("other", "index_list").([]interface{})
+		p := util.IntAll(p)
+		if p <= 0 || p > 10 {
+			p = 1
+		}
+
+		if p-1 <= 0 {
+			m.T["prev"] = 1
+		} else {
+			m.T["prev"] = p - 1
+		}
+		if p+1 >= 11 {
+			m.T["next"] = 10
+		} else {
+			m.T["next"] = p + 1
+		}
+		m.T["cur"] = p
+		m.T["list"] = pages[p-1]
+		return m.Render("/pc/bidsearch_static.html", &m.T)
+	} else {
+		if list != nil {
+			for _, v := range *list {
+				v["_id"] = util.EncodeArticleId2ByCheck(v["_id"].(string))
+				stp, _ := v["subtype"].(string)
+				if stp == "" {
+					stp, _ = v["toptype"].(string)
+				}
+				area, _ := v["area"].(string)
+				v["stypeadd"], v["areaadd"] = classify(stp, area)
+				//正文匹配检索关键词
+				highlight, _ := v["highlight"].(map[string][]string)
+				detail := ""
+				for _, val := range highlight["detail"] {
+					detail += clearHtml.ReplaceAllString(val, "")
+				}
+				v["detail"] = detail
+			}
+		}
+		m.T["list"] = list
+		isopen, _ := m.GetInteger("isopen")
+		m.T["isopen"] = isopen
+		m.T["area"] = area
+		m.T["publishtime"] = publishtime
+		m.T["timeslot"] = m.GetString("timeslot")
+		m.T["toptype"] = toptype
+		m.T["subtype"] = subtype
+		m.T["count"] = count
+		m.T["status"] = status
+		m.T["keywords"] = keywords
+		m.T["searchvalue"] = searchvalue
+		m.T["selectType"] = selectType
+		m.T["login"] = m.Session().Get("user")
+		m.SetSession("paramkey", keywords)
+		if publishtime == "lately-7" {
+			m.SetSession("parampublishtime", "最近7天")
+		} else if publishtime == "lately-30" {
+			m.SetSession("parampublishtime", "最近30天")
+		} else if publishtime == "thisyear" {
+			m.SetSession("parampublishtime", "去年")
+		} else {
+			m.SetSession("parampublishtime", publishtime)
+		}
+		m.SetSession("paramarea", area)
+		if subtype != "" {
+			m.SetSession("paraminfotype", subtype)
+		} else {
+			m.SetSession("paraminfotype", toptype)
+		}
+		return m.Render("/pc/bidsearch.html", &m.T)
+	}
+}
+
+//ajax分页请求
+func (m *Front) PcAjaxReq() {
+	reqType := m.GetString("reqType")
+	//获取最新招标信息
+	if reqType == "lastBids" {
+		m.ServeJson(map[string]interface{}{
+			"list": elastic.GetPage("bidding", "bidding", "{}", `{"publishtime":-1}`, `"_id","title","publishtime","toptype","subtype","type","area","href"`, 0, 18),
+		})
+		return
+	} else if reqType == "rewardText" {
+		rewardText, _ := getRewardText()
+		m.Write(rewardText)
+		return
+	}
+	currentPage, _ := m.GetInteger("pageNumber")
+	if currentPage > bidSearch_maxPageSize {
+		currentPage = bidSearch_maxPageSize
+	}
+	start := (currentPage - 1) * bidSearch_pageSize
+	area := m.GetString("area")
+	subtype := m.GetString("subtype")
+	searchvalue := m.GetString("searchvalue")
+	publishtime := m.GetString("publishtime")
+	selectType := m.GetString("selectType")
+	m.SetSession("selectType", selectType)
+	fmt.Println(selectType)
+
+	var list *[]map[string]interface{}
+	var count int64
+	status, _ := m.GetInteger("status")
+	if reqType == "filter" {
+		if status == 1 {
+			count, list = getBidSearchData(searchvalue, area, publishtime, subtype, 0, true, selectType)
+		} else if status == 2 {
+			count, list = getLastNewsData(searchvalue, area, publishtime, subtype, 0, true)
+		}
+	} else if reqType == "bidSearch" {
+		_, list = getBidSearchData(searchvalue, area, publishtime, subtype, start, false, selectType)
+	} else if reqType == "lastNews" {
+		_, list = getLastNewsData(searchvalue, area, publishtime, subtype, start, false)
+	}
+	if list != nil && len(*list) > 0 {
+		for _, v := range *list {
+			if v["_id"] != nil {
+				v["_id"] = util.EncodeArticleId2ByCheck(v["_id"].(string))
+			}
+
+			stp, ok := v["subtype"].(string)
+			if ok && stp == "" {
+				stp = v["toptype"].(string)
+			}
+			area, ok := v["area"].(string)
+			v["stypeadd"], v["areaadd"] = classify(stp, area)
+			//正文匹配检索关键词
+			highlight, _ := v["highlight"].(map[string][]string)
+			detail := ""
+			for _, val := range highlight["detail"] {
+				detail += clearHtml.ReplaceAllString(val, "")
+			}
+			v["detail"] = detail
+		}
+	}
+	m.ServeJson(map[string]interface{}{
+		"list":  list,
+		"count": count,
+	})
+}
+
+/**
+**页面搜索
+**/
+func getBidSearchData(searchvalue, area, publishtime, subtype string, start int, isGetCount bool, selectType string) (count int64, list *[]map[string]interface{}) {
+	query := getBidSearchQuery(area, publishtime, subtype)
+	//selectType:全文搜索(all)、标题搜索(title)
+	var qstr string
+	qstr = elastic.GetNgramQuery(searchvalue, query, `"title","detail"` /*FINDF*/)
+	if isGetCount && qstr != "" {
+		count = elastic.Count(INDEX, TYPE, qstr)
+	}
+	if !isGetCount || count > 0 {
+		var repl *[]map[string]interface{}
+		if selectType == "all" {
+			//全文搜索
+			repl = elastic.GetByNgramOther(INDEX, TYPE, searchvalue, query, `"title","detail"` /*FINDF*/, bidSearch_sort, bidSearch_field, start, bidSearch_pageSize, true, false, 115)
+		} else {
+			//标题搜索
+			repl = elastic.GetByNgram(INDEX, TYPE, searchvalue, query, `"title"` /*FINDF*/, bidSearch_sort, bidSearch_field, start, bidSearch_pageSize)
+		}
+		if repl != nil && *repl != nil && len(*repl) > 0 {
+			list = repl
+		}
+	}
+	limitCount := int64(bidSearch_pageSize * bidSearch_maxPageSize)
+	if count > limitCount {
+		count = limitCount
+	}
+	return
+}
+func getLastNewsData(searchvalue, area, publishtime, subtype string, start int, isGetCount bool) (count int64, list *[]map[string]interface{}) {
+	//最新招标信息
+	query := getBidSearchQuery(area, publishtime, subtype)
+	strquery := `{"query":{"bool":{"must":[` + query + `],"must_not":[],"should":[],"minimum_should_match" : 1}}}`
+	if isGetCount {
+		count = elastic.Count(INDEX, TYPE, strquery)
+		//count = elastic.Count(INDEX, TYPE, elastic.MakeQuery(query, "", "", -1, -1))
+	}
+	if !isGetCount || count > 0 {
+		//repl := elastic.GetPage(INDEX, TYPE, query, bidSearch_sort, bidSearch_field, start, bidSearch_pageSize)
+		if query == "{}" {
+			query = ""
+		}
+		repl := elastic.GetByNgramOther(INDEX, TYPE, searchvalue, query, `"title","detail"` /*FINDF*/, bidSearch_sort, bidSearch_field, start, bidSearch_pageSize, true, false, 115)
+		if repl != nil && *repl != nil && len(*repl) > 0 {
+			list = repl
+		}
+	}
+	limitCount := int64(bidSearch_pageSize * bidSearch_maxPageSize)
+	if count > limitCount {
+		count = limitCount
+	}
+	return
+}
+func getBidSearchQuery(area, publishtime, subtype string) string {
+	query := ``
+	if area != "" {
+		query += `{"terms":{"area":[`
+		for k, v := range strings.Split(area, ",") {
+			if k > 0 {
+				query += `,`
+			}
+			query += `"` + v + `"`
+		}
+		query += `]}}`
+	}
+	if publishtime != "" {
+		if len(query) > 0 {
+			query += ","
+		}
+		starttime, endtime := "", ""
+		now := time.Now()
+		if publishtime == "lately-7" { //最近7天
+			starttime = fmt.Sprint(time.Date(now.Year(), now.Month(), now.Day()-7, 0, 0, 0, 0, time.Local).Unix())
+		} else if publishtime == "lately-30" { //最近30天
+			starttime = fmt.Sprint(time.Date(now.Year(), now.Month(), now.Day()-30, 0, 0, 0, 0, time.Local).Unix())
+		} else if publishtime == "thisyear" { //去年
+			starttime = fmt.Sprint(time.Date(now.Year()-1, 1, 1, 0, 0, 0, 0, time.Local).Unix())
+			endtime = fmt.Sprint(time.Date(now.Year(), 1, 1, 0, 0, 0, 0, time.Local).Unix())
+		} else {
+			starttime = strings.Split(publishtime, "_")[0]
+			endtime = strings.Split(publishtime, "_")[1]
+			etTime := time.Now()
+			if endtime != "" {
+				et, _ := strconv.ParseInt(endtime, 0, 64)
+				etTime = time.Unix(et, 0)
+			}
+			endtime = fmt.Sprint(time.Date(etTime.Year(), etTime.Month(), etTime.Day()+1, 0, 0, 0, 0, time.Local).Unix())
+		}
+		query += `{"range":{"publishtime":{`
+		if starttime != "" {
+			query += `"gte":` + starttime
+		}
+		if starttime != "" && endtime != "" {
+			query += `,`
+		}
+		if endtime != "" {
+			query += `"lt":` + endtime
+		}
+		query += `}}}`
+	}
+	if subtype != "" {
+		if len(query) > 0 {
+			query += ","
+		}
+		query += `{"terms":{"subtype":[`
+		for k, v := range strings.Split(subtype, ",") {
+			if k > 0 {
+				query += `,`
+			}
+			query += `"` + v + `"`
+		}
+		query += `]}}`
+	}
+	return query
+}
+func getLastNewsQuery(area, publishtime, subtype string) string {
+	query := ``
+	if area != "" {
+		query += `"area":{"$in":[`
+		for k, v := range strings.Split(area, ",") {
+			if k > 0 {
+				query += `,`
+			}
+			query += `"` + v + `"`
+		}
+		query += `]}`
+	}
+	if publishtime != "" {
+		if len(query) > 0 {
+			query += ","
+		}
+		starttime, endtime := "", ""
+		now := time.Now()
+		if publishtime == "lately-7" { //最近7天
+			starttime = fmt.Sprint(time.Date(now.Year(), now.Month(), now.Day()-7, 0, 0, 0, 0, time.Local).Unix())
+		} else if publishtime == "lately-30" { //最近30天
+			starttime = fmt.Sprint(time.Date(now.Year(), now.Month(), now.Day()-30, 0, 0, 0, 0, time.Local).Unix())
+		} else if publishtime == "thisyear" { //去年
+			starttime = fmt.Sprint(time.Date(now.Year()-1, 1, 1, 0, 0, 0, 0, time.Local).Unix())
+			endtime = fmt.Sprint(time.Date(now.Year(), 1, 1, 0, 0, 0, 0, time.Local).Unix())
+		} else {
+			starttime = strings.Split(publishtime, "_")[0]
+			endtime = strings.Split(publishtime, "_")[1]
+			et, _ := strconv.ParseInt(endtime, 0, 64)
+			etTime := time.Unix(et, 0)
+			endtime = fmt.Sprint(time.Date(etTime.Year(), etTime.Month(), etTime.Day()+1, 0, 0, 0, 0, time.Local).Unix())
+		}
+		if starttime != "" && endtime != "" {
+			query += `"$and":[{"publishtime":{"$gte":` + starttime + `}},{"publishtime":{"$lt":` + endtime + `}}]`
+		} else if starttime != "" && endtime == "" {
+			query += `"publishtime":{"$gte":` + starttime + `}`
+		} else if starttime == "" && endtime != "" {
+			query += `"publishtime":{"$lt":` + endtime + `}`
+		}
+	}
+	if subtype != "" {
+		if len(query) > 0 {
+			query += ","
+		}
+		query += `"subtype":{"$in":[`
+		for k, v := range strings.Split(subtype, ",") {
+			if k > 0 {
+				query += `,`
+			}
+			query += `"` + v + `"`
+		}
+		query += `]}`
+	}
+	query = `{` + query + `}`
+	return query
+}
+
+//进入订阅页面
+func (m *Front) Wxrssset() error {
+	myopenid, _ := m.Session().Get("s_m_openid").(string)
+	if isInTSguide(myopenid) {
+		return m.Redirect("/front/tenderSubscribe/guide")
+	}
+	m.T["openid"] = se.EncodeString(myopenid)
+	m.T["signature"] = wx.SignJSSDK(m.Site() + m.Url())
+	mynickname, _ := m.Session().Get("s_nickname").(string)
+	myavatar, _ := m.Session().Get("s_avatar").(string)
+	m.T["nickname"] = mynickname
+	m.T["avatar"] = myavatar
+	return m.Render("/weixin/wxrssset.html", &m.T)
+}
+
+//进入订阅页面
+func (m *Front) Getpage() error {
+	defer util.Catch()
+	s_type := ""
+	openid := m.Session().Get("s_m_openid")
+	var msgset map[string]interface{}
+	if openid != nil {
+		one, _ := mongodb.FindOneByField("user", `{"s_m_openid":"`+openid.(string)+`"}`, `{"o_jy":1}`)
+		msg := (*one)["o_jy"]
+		if msg != "" && msg != nil {
+			msgset = msg.(map[string]interface{})
+		}
+		s_type = "tender"
+	}
+	m.ServeJson(map[string]interface{}{
+		"msgset": msgset,
+		"s_type": s_type,
+	})
+	return nil
+}
+
+//微信搜索界面
+func (m *Front) Wxsearch() error {
+	defer util.Catch()
+	shname := m.GetSession("shname")
+	toptype := m.GetSession("toptype")
+	subtype := m.GetSession("subtype")
+	scope := m.GetSession("scope")
+	publishtime := m.GetSession("publishtime")
+	if shname != "" && shname != nil {
+		m.T["shname"] = shname
+	}
+	m.T["toptype"] = toptype
+	m.T["subtype"] = subtype
+	m.T["scope"] = scope
+	m.T["publishtime"] = publishtime
+	m.T["signature"] = wx.SignJSSDK(m.Site() + m.Url())
+	mynickname, _ := m.Session().Get("s_nickname").(string)
+	myavatar, _ := m.Session().Get("s_avatar").(string)
+	m.T["nickname"] = mynickname
+	m.T["avatar"] = myavatar
+	myopenid, _ := m.Session().Get("s_m_openid").(string)
+	m.T["openid"] = se.EncodeString(myopenid)
+	if userId := m.GetSession("userId"); userId != nil {
+		one, _ := mongodb.FindOneByField("user", bson.M{"_id": bson.ObjectIdHex(userId.(string))}, `{"o_jy":1}`)
+		history := redis.GetStr("other", "s_"+userId.(string))
+		arrs := strings.Split(history, ",")
+		if history == "" {
+			arrs = make([]string, 0)
+		}
+		l := len(arrs) - 1
+		for i := 0; i < len(arrs)/2; i++ {
+			tmp := arrs[l-i]
+			arrs[l-i] = arrs[i]
+			arrs[i] = tmp
+		}
+		m.T["history"] = arrs
+		if one != nil && len(*one) > 0 {
+			o_jy, _ := (*one)["o_jy"].(map[string]interface{})
+			a_key, _ := o_jy["a_key"].([]interface{})
+			var keys []interface{}
+			for _, v := range a_key {
+				keyMap, _ := v.(map[string]interface{})
+				key, _ := keyMap["key"].([]interface{})
+				keys = append(keys, key)
+			}
+			m.T["msgset"] = keys
+		}
+	}
+	return m.Render("/weixin/wxsearch.html", &m.T)
+}
+
+//剑鱼微信查询结果页面
+func (m *Front) Wxsearchlist() error {
+	defer util.Catch()
+	keywords := m.GetString("searchname")
+	searchvalue := m.GetString("searchvalue")
+	toptype := m.GetString("toptype")
+	subtype := m.GetString("subtype")
+	scope := m.GetString("scope")
+	publishtime := m.GetString("publishtime")
+	selectType := m.GetString("selectType")
+	m.SetSession("shname", keywords)
+	m.SetSession("toptype", toptype)
+	m.SetSession("subtype", subtype)
+	m.SetSession("scope", scope)
+	m.SetSession("publishtime", publishtime)
+	var list *[]map[string]interface{}
+	if userid := m.GetSession("userId"); userid != nil {
+		if len(keywords) > 0 {
+			if selectType == "" { //默认设置为全文搜索
+				selectType = "all"
+			}
+			list = getWxsearchlistData(keywords, searchvalue, scope, publishtime, subtype, 1, selectType)
+
+			r := redis.GetStr("other", "s_"+userid.(string))
+			arrs := strings.Split(r, ",")
+			if r == "" {
+				arrs = make([]string, 0)
+			}
+			var historyFlag = 0
+			for _, v := range arrs {
+				if v == strings.Trim(keywords, " ") {
+					historyFlag = 1
+					break
+				}
+			}
+			if historyFlag != 1 {
+				arrs = append(arrs, keywords)
+				if len(arrs) > 5 {
+					arrs = arrs[1:6]
+				}
+				redis.Del("other", "s_"+userid.(string))
+				redis.Put("other", "s_"+userid.(string), strings.Join(arrs, ","), -1)
+			}
+		}
+	}
+	m.T["list"] = list
+	m.T["pageSize"] = wx_pageSize
+	m.T["keywords"] = keywords
+	m.T["searchvalue"] = searchvalue
+	m.T["toptype"] = toptype
+	m.T["subtype"] = subtype
+	m.T["scope"] = scope
+	m.T["publishtime"] = publishtime
+	//搜索列表增加分享
+	m.T["signature"] = wx.SignJSSDK(m.Site() + m.Url())
+	myopenid, _ := m.Session().Get("s_m_openid").(string)
+	m.T["openid"] = se.EncodeString(myopenid)
+	mynickname, _ := m.Session().Get("s_nickname").(string)
+	myavatar, _ := m.Session().Get("s_avatar").(string)
+	m.T["nickname"] = mynickname
+	m.T["avatar"] = myavatar
+	return m.Render("/weixin/wxsearchlist.html", &m.T)
+}
+
+//搜索结果,ajax分页请求
+func (m *Front) WxsearchlistPaging() {
+	defer util.Catch()
+	userid := m.GetSession("userId")
+	var list *[]map[string]interface{}
+	pageNum, _ := m.GetInteger("pageNum")
+	if userid != nil && pageNum <= wx_maxPageNum {
+		keywords := strings.Trim(m.GetString("searchname"), " ")
+		searchvalue := m.GetString("searchvalue")
+		subtype := m.GetString("subtype")
+		scope := m.GetString("scope")
+		publishtime := m.GetString("publishtime")
+		selectType := m.GetString("selectType")
+		list = getWxsearchlistData(keywords, searchvalue, scope, publishtime, subtype, pageNum, selectType)
+	}
+	m.ServeJson(map[string]interface{}{
+		"list":        list,
+		"hasNextPage": list != nil && len(*list) == wx_pageSize && pageNum < wx_maxPageNum,
+	})
+}
+
+//微信端删除历史搜索
+func (m *Front) DelWxHistorySearch() {
+	defer util.Catch()
+	//定义一个无用参数作为返回值
+	var rt string = "rt"
+	userId := m.GetSession("userId")
+	history := redis.GetStr("other", "s_"+userId.(string))
+	if len(history) > 0 || history != "" {
+		redis.Del("other", "s_"+userId.(string))
+	}
+	m.ServeJson(map[string]interface{}{
+		"rt": rt,
+	})
+}
+
+//微信端搜索
+func getWxsearchlistData(keywords, searchvalue, scope, publishtime, subtype string, pageNum int, selectType string) (list *[]map[string]interface{}) {
+	field := `"_id","title","publishtime","toptype","subtype","type","area","href"`
+	sort := `{"publishtime":-1}`
+	query := getBidSearchQuery(scope, publishtime, subtype) //scope是地区对应传进的areas, stype是类型对应scope
+	if len(keywords) > 0 {
+		if selectType == "all" { //全文搜索
+			list = elastic.GetByNgramOther(INDEX, TYPE, searchvalue, query, `"title","detail"`, sort, field, (pageNum-1)*wx_pageSize, wx_pageSize, true, false, 100)
+		} else { //标题搜索
+			list = elastic.GetByNgram(INDEX, TYPE, searchvalue, query, `"title"` /*FINDF*/, sort, field, (pageNum-1)*wx_pageSize, wx_pageSize)
+		}
+
+		if list != nil {
+			for _, v := range *list {
+				v["_id"] = util.EncodeArticleId2ByCheck(util.ObjToString(v["_id"]))
+			}
+		}
+	}
+	return list
+}
+
+//查看原文跳转
+func (m *Front) VisitRedirect() {
+	fmt.Sprintln("^6^^^^^")
+	defer util.Catch()
+	sid := m.GetString("id")
+	//	regex, _ := regexp.Compile("(Android|Mobile)")
+	//	if len(regex.FindAllString(m.Header("User-Agent"), -1)) <= 0 {
+	//		m.Redirect("/article/p/" + sid + ".html")
+	//	}
+	surl := m.GetString("url")
+	sds := m.GetString("keywords")
+	m.T["keywords"] = sds
+	shareopenid := m.GetString("openid")
+	if shareopenid != "" {
+		m.T["shareopenid"] = shareopenid
+	}
+	myopenid, _ := m.Session().Get("s_m_openid").(string)
+	if myopenid == "" {
+		myopenid = shareopenid
+		m.T["openid"] = myopenid //"-1"
+	} else {
+		m.T["openid"] = se.EncodeString(myopenid) //"-1"
+	}
+	mynickname, _ := m.Session().Get("s_nickname").(string)
+	myavatar, _ := m.Session().Get("s_avatar").(string)
+	m.T["nickname"] = mynickname
+	m.T["avatar"] = myavatar
+	m.T["signature"] = wx.SignJSSDK(m.Site() + m.Url())
+	userId, _ := m.GetSession("userId").(string)
+	var obj map[string]interface{}
+	obj = wxvisitD(sid, surl, userId)
+	if len(obj) > 0 {
+		//获取打赏文案
+		m.T["rewardText"], m.T["advertText"] = getRewardText()
+		m.T["obj"] = obj
+		m.Render("/weixin/wxinfocontent.html", &m.T)
+		return
+	}
+	if surl != "" {
+		m.Redirect(surl)
+	}
+}
+func wxvisitD(sid, surl, userId string) (objdata map[string]interface{}) {
+	defer util.Catch()
+	var obj map[string]interface{}
+	if len(sid) > 5 {
+		aobj, ok := mongodb.FindById("bidding", sid, nil)
+		if ok && (aobj == nil || *aobj == nil || len(*aobj) == 0) {
+			aobj, ok = mongodb.FindById("bidding_back", sid, nil)
+		}
+		obj = *aobj
+		if ok && obj != nil && len(obj) >= 3 {
+			obj["_id"] = util.EncodeArticleId2ByCheck(sid)
+			obj["url"] = surl
+			pt := obj["publishtime"]
+			obj["l_publishtime"] = pt
+			obj["publishtime"] = util.FormatDateWithObj(&pt, util.Date_Full_Layout)
+			//查询是否关注
+			obj["followFlag"] = false
+			obj["hasSession"] = false
+			var infoformat = obj["infoformat"]
+			if infoformat != nil && infoformat != "" {
+				obj["infoformat"] = util.IntAll(infoformat)
+			}
+			if userId != "" {
+				pcode, _ := obj["projectcode"].(string)
+				pname, _ := obj["projectname"].(string)
+				titleTmp, _ := obj["title"].(string)
+				obj["followFlag"], obj["followId"] = MFollow(userId, pname, pcode, titleTmp)
+				obj["hasSession"] = true
+			}
+			if strings.Trim(util.ObjToString(obj["detail"]), " ") == "" {
+				obj["detail"] = ""
+			}
+		}
+	}
+	return obj
+}
+
+func MFollow(userId string, pname string, pcode string, title string) (bool, string) {
+	defer util.Catch()
+	var followId string
+	followFlag := false
+	follows, ok := mongodb.Find("follow_project", `{"s_userid":"`+userId+`"}`, `{"_id":1,"s_projectname":1,"s_projectcode":1}`, nil, false, -1, -1)
+	if ok && follows != nil && len(*follows) > 0 {
+		for _, v := range *follows {
+			pc, _ := v["s_projectcode"].(string)
+			if pc != "" && pc == pcode {
+				followFlag = true
+			} else {
+				pn, _ := v["s_projectname"].(string)
+				if pn != "" && pn == pname {
+					followFlag = true
+				}
+			}
+			if followFlag {
+				followId = util.EncodeArticleId2ByCheck(util.BsonIdToSId(v["_id"]))
+				break
+			}
+		}
+	}
+	return followFlag, followId
+}
+
+//查看原文跳转
+//增加查询备份库数据、增加关键词、描述逻辑处理
+func (m *Front) PcVisitRedirect(sid string) {
+	defer util.Catch()
+	kds := m.GetString("kds")
+	m.T["keywords"] = kds
+	sid = strings.Split(sid, "_")[0]
+	//sid = util.DecodeArticleId(sid)[0]
+	var shareid = m.GetString("id")
+	if len(shareid) == 0 {
+		shareid = fmt.Sprintf("%s%d", config.Seoconfig["jysskzy"].(string)+fmt.Sprintf("%d", time.Now().UnixNano())[8:14], rand.Intn(9))
+	}
+	m.T["logid"] = config.Seoconfig["jysskzy"].(string)
+	m.T["shareid"] = se.EncodeString(shareid)
+	if ret := redis.Get("other", "jypcdetail_"+sid+kds); ret != nil {
+		m.SetBody([]byte(ret.(string)))
+		return
+	} else {
+		m.DisableHttpCache()
+		data := elastic.GetByIdField("bidding", "bidding", sid, `"href"`)
+		if data == nil || len(*data) == 0 {
+			m.Render("/_error.html")
+			return
+		}
+		href, _ := (*data)["href"].(string)
+		href = strings.Replace(href, "\n", "", -1)
+		if href != "" && !strings.HasPrefix(href, "http") {
+			href = "http://" + href
+		}
+		po, bo, wo, obj := pcVRT(sid)
+		if obj != nil && len(obj) > 0 {
+			if len(po) > 0 {
+				m.T["projectOther"] = po
+			}
+			if len(bo) > 0 {
+				m.T["buyerOther"] = bo
+			}
+			if len(wo) > 0 {
+				m.T["winnerOther"] = wo
+			}
+			obj["url"] = href
+			m.T["obj"] = obj
+			content, _ := m.Render4Cache("/pc/biddetail.html", &m.T)
+			redis.Put("other", "jypcdetail_"+sid+kds, string(content), 60*60*24)
+			m.SetBody(content)
+			return
+		}
+		if href != "" {
+			m.Redirect(href)
+		}
+	}
+}
+
+//pc三级页跳转
+func pcVRT(sid string) (po, bo, wo []map[string]interface{}, objdata map[string]interface{}) {
+	defer util.Catch()
+	var projectOther, buyerOther, winnerOther []map[string]interface{}
+	if len(sid) > 5 {
+		coll := "bidding"
+		obj, ok := mongodb.FindById(coll, sid, nil)
+		if ok && (obj == nil || *obj == nil || len(*obj) == 0) {
+			coll = "bidding_back"
+			obj, ok = mongodb.FindById(coll, sid, nil)
+		}
+		if ok && obj != nil && len(*obj) > 0 {
+			//DealInfo(obj, coll)
+			(*obj)["_id"] = util.EncodeArticleId2ByCheck(sid)
+			var infoformat = (*obj)["infoformat"]
+			if infoformat != nil && infoformat != "" {
+				(*obj)["infoformat"] = util.IntAll(infoformat)
+			}
+			if strings.Trim(util.ObjToString((*obj)["detail"]), " ") == "" {
+				(*obj)["detail"] = ""
+			}
+			area := (*obj)["area"].(string)
+			finalType, _ := (*obj)["subtype"].(string)
+			if finalType == "" {
+				finalType = util.ObjToString((*obj)["toptype"])
+			}
+			if finalType == "" {
+				finalType = util.ObjToString((*obj)["type"])
+				if finalType == "bid" {
+					finalType = "中标"
+				} else if finalType == "tender" {
+					finalType = "招标"
+				} else {
+					finalType = ""
+				}
+			}
+			(*obj)["stypeadd"], (*obj)["areaadd"] = classify(finalType, area)
+			//增加处理信息逻辑
+			objdata = *obj
+			queryStr := ""
+			commonQuery := func(mustquery string) *[]map[string]interface{} {
+				return elastic.GetPage("bidding", "bidding", queryStr, `{"publishtime":-1}`, `"_id","title","publishtime","toptype","subtype","type","area","href"`, 0, 11)
+			}
+			//同一个项目的其他招标信息
+			projectName, _ := (*obj)["projectname"].(string)
+			projectCode, _ := (*obj)["projectcode"].(string)
+			if projectName != "" || projectCode != "" {
+				if projectName != "" && projectCode != "" {
+					queryStr = `{"$or":[{"TERM_projectname":"` + projectName + `"},{"TERM_projectcode":"` + projectCode + `"}]}`
+				} else if projectName != "" && projectCode == "" {
+					queryStr = `{"TERM_projectname":"` + projectName + `"}`
+				} else if projectName == "" && projectCode != "" {
+					queryStr = `{"TERM_projectcode":"` + projectCode + `"}`
+				}
+				projectOther = bidDataConvert(sid, commonQuery(queryStr))
+			}
+			//同一个业主最近的其他招标信息
+			buyer, _ := (*obj)["buyer"].(string) //采购单位
+			if buyer != "" {
+				queryStr = `{"TERM_buyer":"` + buyer + `"}`
+				buyerOther = bidDataConvert(sid, commonQuery(queryStr))
+			}
+			//同一中标人最近中标的其他信息
+			subtype, _ := (*obj)["subtype"].(string) //信息类型
+			winner, _ := (*obj)["winner"].(string)   //中标人
+			if winner != "" && subtype == "中标" {
+				queryStr = `{"TERM_winner":"` + winner + `"}`
+				winnerOther = bidDataConvert(sid, commonQuery(queryStr))
+			}
+		}
+	}
+	return projectOther, buyerOther, winnerOther, objdata
+}
+
+//数据转换
+func bidDataConvert(id string, datas *[]map[string]interface{}) (array []map[string]interface{}) {
+	if datas == nil || len(*datas) == 0 {
+		return array
+	}
+	index := 0
+	for _, v := range *datas {
+		if len(array) >= 10 {
+			break
+		}
+		_id, _ := v["_id"].(string)
+		if _id == id {
+			continue
+		}
+		v["_id"] = util.EncodeArticleId2ByCheck(_id)
+		area, _ := v["area"].(string)
+		if area == "A" {
+			v["area"] = ""
+		}
+		tp, _ := v["subtype"].(string)
+		if tp == "" {
+			tp, _ = v["toptype"].(string)
+		}
+		if tp == "" {
+			tp, _ = v["type"].(string)
+			if tp == "bid" {
+				tp = "中标"
+			} else if tp == "tender" {
+				tp = "招标"
+			} else {
+				tp = ""
+			}
+		}
+		v["stypeadd"], v["areaadd"] = classify(tp, area)
+		v["type"] = tp
+		diff := ""
+		publishtime, _ := v["publishtime"].(float64)
+		if publishtime != 0 {
+			diff = util.TimeDiff(time.Unix(util.Int64All(publishtime), 0))
+			v["pbtime"] = util.Int64All(publishtime)
+		}
+		v["publishtime"] = diff
+		index++
+		v["index"] = index
+		array = append(array, v)
+	}
+	return array
+}
+
+//剑鱼保存
+func (m *Front) AjaxReq() error {
+	defer util.Catch()
+	reqType := m.GetString("reqType")
+	var flag = "n"
+	switch reqType {
+	case "feedback": //意见反馈
+		data := make(map[string]interface{})
+		userId, ok := m.GetSession("userId").(string)
+		if !ok || userId == "" {
+			break
+		}
+		userInfo, ok := mongodb.FindById("user", userId, nil)
+		if !ok {
+			break
+		}
+		data["i_type"] = 8
+		value := m.GetString("value")
+		if len([]rune(value)) > 200 {
+			value = util.SubString(value, 0, 200)
+		}
+		data["s_remark"] = value
+		if (*userInfo)["s_name"] != nil {
+			data["s_submitname"] = (*userInfo)["s_name"].(string)
+		}
+		data["s_submitid"] = userId
+		data["s_title"] = m.GetString("title")
+		data["i_status"] = 0
+		if (*userInfo)["s_nickname"] != nil { //昵称
+			data["s_username"] = (*userInfo)["s_nickname"].(string)
+		} else if (*userInfo)["s_name"] != nil { //s_name
+			data["s_username"] = (*userInfo)["s_name"].(string)
+		}
+		data["l_submitdate"] = time.Now().Unix()
+		data["s_source"] = m.GetString("source")
+		data["s_fkid"] = m.GetString("fkid")
+		id := mongodb.Save("interaction", data)
+		if len(id) > 0 {
+			flag = "y"
+		}
+		break
+	case "subscribe": //直接订阅
+		if openid := m.GetSession("s_m_openid"); openid != nil {
+			r, _ := mongodb.FindOneByField("user", bson.M{"s_m_openid": openid.(string)}, `{"o_jy":1}`)
+			o_jy, _ := (*r)["o_jy"].(map[string]interface{})
+			a_key, _ := o_jy["a_key"].([]interface{})
+			keysArray := processKeyword(m.GetString("keys"))
+			if keysArray == nil {
+				break
+			}
+			var isExists bool
+			for _, v := range a_key {
+				count := 0
+				for _, kay := range keysArray {
+					keyMap, _ := v.(map[string]interface{})
+					key, _ := keyMap["key"].([]interface{})
+					for _, ky := range key {
+						if kay == ky {
+							count++
+							break
+						}
+					}
+				}
+				if count == len(keysArray) {
+					isExists = true
+					flag = "y"
+					break
+				}
+			}
+			//如果不存在
+			if !isExists {
+				if len(a_key) >= 10 {
+					flag = "o"
+				} else {
+					if mongodb.Update("user", bson.M{"s_m_openid": openid.(string)},
+						bson.M{
+							"$push": bson.M{"o_jy.a_key": bson.M{"key": keysArray}},
+							"$set":  bson.M{"o_jy.l_modifydate": time.Now().Unix()},
+						}, false, false) {
+						flag = "y"
+					}
+				}
+			}
+		}
+		break
+	default:
+		if openid := m.GetSession("s_m_openid"); openid != nil {
+			keys := m.GetSlice("keys")
+			scopes := m.GetString("scope")
+			email := m.GetString("s_email")
+			mode, _ := m.GetInteger("mode")
+			set := make(map[string]interface{})
+			set["o_jy.a_key"] = keys
+			set["o_jy.s_scope"] = scopes
+			set["o_jy.s_email"] = email
+			if mode == 0 {
+				mode = 1
+			}
+			set["o_jy.i_mode"] = mode
+			set["o_jy.l_modifydate"] = time.Now().Unix()
+			if mongodb.Update("user", `{"s_m_openid":"`+openid.(string)+`"}`, &map[string]interface{}{"$set": set}, false, false) {
+				flag = "y"
+			}
+		}
+		break
+	}
+	m.ServeJson(map[string]interface{}{
+		"flag": flag,
+	})
+	return nil
+}
+
+const (
+	INDEX = "bidding"
+	TYPE  = "bidding"
+	FINDF = `"title"`
+)
+
+//预览结果
+func (m *Front) WxpushView() error {
+	defer util.Catch()
+	myopenid, _ := m.Session().Get("s_m_openid").(string)
+	if myopenid == "" {
+		return m.Redirect("/swordfish/share/-1")
+	}
+	a_key, list := getWxpushViewData(myopenid, 1)
+	m.T["firstPage"] = list
+	m.T["hasNextPage"] = list != nil && len(*list) == wx_pageSize
+	m.T["pageSize"] = wx_pageSize
+	m.T["a_key"] = a_key
+	mynickname, _ := m.Session().Get("s_nickname").(string)
+	myavatar, _ := m.Session().Get("s_avatar").(string)
+	m.T["nickname"] = mynickname
+	m.T["avatar"] = myavatar
+	m.T["signature"] = wx.SignJSSDK(m.Site() + m.Url())
+	m.T["openid"] = se.EncodeString(myopenid)
+	return m.Render("/weixin/resultpreview.html", &m.T)
+}
+func (m *Front) WxpushViewPaging() {
+	defer util.Catch()
+	var list *[]map[string]interface{}
+	pageNum, _ := m.GetInteger("pageNum")
+	if myopenid := m.Session().Get("s_m_openid"); myopenid != nil && pageNum <= wx_maxPageNum {
+		_, list = getWxpushViewData(myopenid.(string), pageNum)
+	}
+	m.ServeJson(map[string]interface{}{
+		"list":        list,
+		"hasNextPage": list != nil && len(*list) == wx_pageSize && pageNum < wx_maxPageNum,
+	})
+}
+func getWxpushViewData(myopenid string, pageNum int) (keys []interface{}, list *[]map[string]interface{}) {
+	if myopenid == "" {
+		return
+	}
+	tmp, ok := mongodb.FindOneByField("user", `{"s_m_openid":"`+myopenid+`"}`, `{"_id":1,"o_jy":1}`)
+	if !ok || tmp == nil || len(*tmp) == 0 {
+		return
+	}
+	o_jy := (*tmp)["o_jy"].(map[string]interface{})
+	a_key, _ := o_jy["a_key"].([]interface{})
+	if len(a_key) == 0 {
+		return
+	}
+	for _, v := range a_key {
+		keyMap, _ := v.(map[string]interface{})
+		key, _ := keyMap["key"].([]interface{})
+		keys = append(keys, key)
+	}
+	field := `"_id","title","publishtime","toptype","subtype","type","area","href","areaval"`
+	var allkeys []elastic.KeyConfig //用户配置
+	_bs, err := json.Marshal(a_key)
+	if err == nil {
+		json.Unmarshal(_bs, &allkeys)
+	}
+	list = elastic.GetResForJY(INDEX, TYPE, allkeys, "", `"title"`, `{"publishtime":"desc"}`, field, (pageNum-1)*wx_pageSize, wx_pageSize)
+	if list != nil {
+		for _, v := range *list {
+			v["_id"] = util.EncodeArticleId2ByCheck(util.ObjToString(v["_id"]))
+		}
+	}
+	return
+}
+func (m *Front) Guide(sign string) error {
+	defer util.Catch()
+	if m.Session().Get("s_m_openid") == nil {
+		return m.Redirect("/swordfish/share/-1")
+	}
+	m.T["signature"] = wx.SignJSSDK(m.Site() + m.Url())
+	mynickname, _ := m.Session().Get("s_nickname").(string)
+	myavatar, _ := m.Session().Get("s_avatar").(string)
+	m.T["nickname"] = mynickname
+	m.T["avatar"] = myavatar
+	myopenid, _ := m.Session().Get("s_m_openid").(string)
+	m.T["openid"] = se.EncodeString(myopenid)
+	m.T["sign"] = sign
+	if sign == "other" {
+		userInfo, ok := mongodb.FindById("user", m.GetSession("userId").(string), `{"o_jy_msgset":1}`)
+		if ok {
+			m.T["msgset"] = (*userInfo)["o_jy_msgset"]
+		}
+	}
+	if sign == "share" {
+		return m.Render("/weixin/wxshareguide.html", &m.T)
+	} else {
+		return m.Render("/weixin/wxindex.html", &m.T)
+	}
+}
+
+func (m *Front) Share(openids string) error {
+	defer util.Catch()
+	var openid = ""
+	var jy_code = ""
+	if openids != "-1" {
+		wxid_code := strings.Split(openids, "__")
+		if len(wxid_code) > 0 {
+			openid = wxid_code[0]
+			jy_code = wxid_code[1]
+		}
+	}
+	m.T["openid"] = openid
+	m.T["jy_code"] = jy_code
+	m.T["signature"] = wx.SignJSSDK(m.Site() + m.Url())
+	mynickname, _ := m.Session().Get("s_nickname").(string)
+	myavatar, _ := m.Session().Get("s_avatar").(string)
+	m.T["nickname"] = mynickname
+	m.T["avatar"] = myavatar
+	return m.Render("/weixin/wxshare.html", &m.T)
+}
+func (m *Front) WxpushAjaxReq() error {
+	defer util.Catch()
+	index, _ := m.GetInteger("index")
+	mongodb.Update("wxpush", `{"_id":"`+m.GetString("_id")+`"}`, map[string]interface{}{
+		"$addToSet": map[string]interface{}{"a_visitedindex": index},
+	}, false, false)
+	return nil
+}
+
+//
+func (m *Front) About() error {
+	defer util.Catch()
+	myopenid, _ := m.Session().Get("s_m_openid").(string)
+	mynickname, _ := m.Session().Get("s_nickname").(string)
+	myavatar, _ := m.Session().Get("s_avatar").(string)
+	m.T["nickname"] = mynickname
+	m.T["avatar"] = myavatar
+	if myopenid != "" {
+		m.T["A"] = "A"
+	}
+	m.T["openid"] = se.EncodeString(myopenid)
+	m.T["signature"] = wx.SignJSSDK(m.Site() + m.Url())
+	m.Render("/weixin/about.html")
+	return nil
+}
+
+//
+func (m *Front) Shareabout(openids string) error {
+	defer util.Catch()
+	var openid = ""
+	if openids != "-1" {
+		wxid_code := strings.Split(openids, "__")
+		if len(wxid_code) > 0 {
+			openid = wxid_code[0]
+		}
+	}
+	myopenid, _ := m.Session().Get("s_m_openid").(string)
+	mynickname, _ := m.Session().Get("s_nickname").(string)
+	myavatar, _ := m.Session().Get("s_avatar").(string)
+	if openid == "" {
+		m.T["openid"] = se.EncodeString(myopenid)
+	} else {
+		m.T["openid"] = openid
+	}
+	m.T["nickname"] = mynickname
+	m.T["avatar"] = myavatar
+	m.T["signature"] = wx.SignJSSDK(m.Site() + m.Url())
+	m.Render("/weixin/about.html")
+	return nil
+}
+
+//
+func (m *Front) Aboutsearch() error {
+	myopenid, _ := m.Session().Get("s_m_openid").(string)
+	m.T["openid"] = se.EncodeString(myopenid)
+	mynickname, _ := m.Session().Get("s_nickname").(string)
+	myavatar, _ := m.Session().Get("s_avatar").(string)
+	m.T["nickname"] = mynickname
+	m.T["avatar"] = myavatar
+	m.T["signature"] = wx.SignJSSDK(m.Site() + m.Url())
+	m.Render("/weixin/aboutsearch.html", &m.T)
+	return nil
+}
+
+//剑鱼微信查询保存网站
+func (m *Front) AboutSR() error {
+	defer util.Catch()
+	var flag = "N"
+	var sn = m.GetString("searchname")
+	var tp = m.GetString("tp")
+	if len(sn) > 0 {
+		switch tp {
+		case "S": //用户查询网站是否被收录
+			userId, ok := m.GetSession("userId").(string)
+			if !ok || userId == "" {
+				break
+			}
+			res, err := mongodb.FindOne("bidurlinfo", bson.M{"$or": []bson.M{bson.M{"s_name": sn}, bson.M{"s_url": sn}}})
+			if err {
+				if len(*res) > 0 {
+					flag = "T"
+				}
+			}
+			break
+		case "I": //用户提交数据
+			data := make(map[string]interface{})
+			userId, ok := m.GetSession("userId").(string)
+			if !ok || userId == "" {
+				break
+			}
+			userInfo, ok := mongodb.FindById("user", userId, nil)
+			if !ok {
+				break
+			}
+			data["i_type"] = 8
+			data["s_remark"] = sn
+			if (*userInfo)["s_name"] != nil {
+				data["s_submitname"] = (*userInfo)["s_name"].(string)
+			}
+			data["s_submitid"] = userId
+			data["s_title"] = m.GetString("title")
+			data["i_status"] = 0
+			if (*userInfo)["s_nickname"] != nil { //昵称
+				data["s_username"] = (*userInfo)["s_nickname"].(string)
+			} else if (*userInfo)["s_name"] != nil { //s_name
+				data["s_username"] = (*userInfo)["s_name"].(string)
+			}
+			data["l_submitdate"] = time.Now().Unix()
+			data["s_source"] = m.GetString("source")
+			id := mongodb.Save("interaction", data)
+			if len(id) > 0 && len(sn) > 0 {
+				flag = "T"
+			}
+			break
+		}
+	}
+	m.ServeJson(map[string]interface{}{
+		"flag": flag,
+	})
+	return nil
+}
+
+//手动删除30天无更新数据
+func (m *Front) DelOL() error {
+	defer util.Catch()
+	var ids []bson.ObjectId
+	var flag = "F"
+	var arrid = strings.Split(m.GetString("arrid"), ",")
+	if len(arrid) > 0 {
+		for _, chid := range arrid {
+			ids = append(ids, bson.ObjectIdHex(util.DecodeArticleId2ByCheck(chid)[0]))
+		}
+	}
+	userId, ok := m.GetSession("userId").(string)
+	if !ok || userId == "" || len(ids) < 1 {
+		m.ServeJson(map[string]interface{}{
+			"flag": flag,
+		})
+		return nil
+	}
+	if datas, ok := mongodb.Find("follow_project", bson.M{"_id": bson.M{"$in": ids}, "s_userid": userId}, nil, nil, false, -1, -1); ok && datas != nil {
+		for _, v := range *datas {
+			delete(v, "_id")
+			v["i_status"] = 2
+			mongodb.Save("follow_project_back", v)
+			go delRelRedis(v["s_openid"], v["a_relationinfo"])
+		}
+	}
+	if mongodb.Del("follow_project", bson.M{"_id": bson.M{"$in": ids}, "s_userid": userId}) {
+		flag = "T"
+	}
+	m.ServeJson(map[string]interface{}{
+		"flag": flag,
+	})
+	return nil
+}
+
+//获取我的反馈列表
+func (f *Front) MyFeedbacks() error {
+	userId, _ := f.GetSession("userId").(string)
+	if userId == "" {
+		return f.Render("/_error.html")
+	}
+	list, ok := mongodb.Find("interaction", bson.M{"s_submitid": userId}, `{"l_submitdate":-1}`, `{"s_remark":1,"l_submitdate":1,"s_opinion":1,"i_status":1}`, false, 0, 200)
+	if !ok {
+		return nil
+	}
+	if f.Method() == "GET" {
+		f.T["list"] = list
+		f.T["flag"] = true
+		return f.Render("/weixin/feedback.html")
+	}
+	f.ServeJson(map[string]interface{}{
+		"list": list,
+	})
+	return nil
+}
+func (f *Front) GetRecomKWs() {
+	recomKWChan <- true
+	defer func() {
+		<-recomKWChan
+	}()
+	value := f.GetString("value")
+	count, _ := f.GetInteger("count")
+	ves := strings.Split(value, " ")
+	var pairs []map[string]interface{}
+	for _, v := range ves {
+		Pw, _ := M.MostSimilar(strings.Split(v, "+"), []string{}, count)
+		for _, p := range Pw {
+			sim := p.Sim
+			word := p.Word
+			if sim < float32(config.Sysconfig["recommendThreshold"].(float64)) {
+				continue
+			}
+			if strings.HasSuffix(word, "路") || tools.DealString(word) {
+				continue
+			}
+			pairs = append(pairs, map[string]interface{}{
+				"sim":  sim,
+				"word": word,
+			})
+		}
+	}
+	f.ServeJson(pairs)
+}
+
+//记录用户行为--推荐关键词
+func (f *Front) BehaviorRecord() {
+	flag := saveBehaviorRecord(f.Action,
+		bson.M{
+			"s_word":   f.GetString("value"),
+			"s_type":   f.GetString("type"),
+			"s_source": f.GetString("source"),
+		})
+	f.ServeJson(bson.M{"flag": flag})
+}
+
+//记录用户行为
+func saveBehaviorRecord(action *xweb.Action, data bson.M) bool {
+	openId, _ := action.GetSession("s_m_openid").(string)
+	//if openId == "" {
+	//	return false
+	//}
+	nickName, _ := action.GetSession("s_nickname").(string)
+	data["s_openid"] = openId
+	data["s_nickname"] = nickName
+	data["l_createtime"] = time.Now().Unix()
+	return len(mongodb.Save("behavior", data)) > 0
+}
+
+//取得剑鱼博客的信息列表
+func (f *Front) Jyblog(param /*参数*/ string) error {
+	querymap := map[string]string{}
+	if len(param) == 0 {
+		querymap = map[string]string{
+			"perPage":     f.GetString("perPage"),
+			"currentPage": f.GetString("currentPage"),
+			"contentType": "jybk",
+			"query":       f.GetString("query"),
+		}
+	} else {
+		//反转生成map
+		paramstr := param[1:]
+		bs, _ := base64.StdEncoding.DecodeString(paramstr)
+		json.Unmarshal(bs, &querymap)
+
+	}
+	var shareid = f.GetString("id")
+	if len(shareid) == 0 {
+		shareid = fmt.Sprintf("%s%d", config.Seoconfig["jybky"].(string)+fmt.Sprintf("%d", time.Now().UnixNano())[8:14], rand.Intn(9))
+	}
+	f.T["logid"] = config.Seoconfig["jybky"].(string)
+	f.DisableHttpCache()
+	shareid = se.EncodeString(shareid)
+	data, pagination := searhWebContentblog(querymap)
+	f.Render("/pc/jyblog.html", &xweb.T{"querymap": querymap, "data": data, "pagination": pagination, "shareid": shareid})
+	return nil
+}
+
+//实际的查询剑鱼博客
+func searhWebContentblog(querymap map[string]string) (*[]map[string]interface{}, *[]interface{}) {
+	perPage, _ := strconv.Atoi(querymap["perPage"])
+	currentPage, _ := strconv.Atoi(querymap["currentPage"])
+	//修复
+	if perPage == 0 {
+		perPage = 5
+	}
+	if currentPage < 1 {
+		currentPage = 1
+	}
+	contentType := querymap["contentType"]
+	queryStr := querymap["query"]
+	str := ``
+	if queryStr != "" {
+		str = `{
+	          "match": {
+	            "s_title": {
+	              "query":"` + queryStr + `",
+	              "operator": "and"
+	            }
+	          }
+	        },{
+	          "match": {
+	            "s_content": {
+	              "query":"` + queryStr + `"
+	            }
+	          }
+	        }`
+	}
+	var tempQuery = `{"query": {
+	    "bool": {
+			"must":[{"term":{"s_contenttype":"` + contentType + `"}}],
+	      	"should": [` + str + `],
+	      	"minimum_should_match": 1
+	    }
+	  }}`
+	var query = tempQuery[:len(tempQuery)-1] +
+		`,"highlight":{
+        "pre_tags":["<span class='highlight'>"],
+        "post_tags":["</span>"],
+        "fields":{
+            "s_title":{"force_source": true}
+			,"s_content":{"force_source": true}
+        }
+    },"_source":["s_title","s_date","s_contenttype","s_content","releasetime","s_description","praise","s_source","s_pic","s_pic1","l_createdate","s_code","_id","s_author"]
+	,"from":` + fmt.Sprintf("%v", ((currentPage-1)*perPage)) + `,
+	"size":` + fmt.Sprintf("%v", perPage) +
+		`,"sort":[{"releasetime":{"order":"desc"}}] }`
+
+	total := elastic.Count("content", "content", tempQuery)
+	//查询列表数据
+	client := elastic.GetEsConn()
+	defer elastic.DestoryEsConn(client)
+	if client == nil {
+		return nil, nil
+	}
+	searchResult, err := client.Search().Index("content").Type("content").Source(query).Do()
+	if err != nil {
+		return nil, nil
+	}
+	var res []map[string]interface{}
+	if searchResult.Hits != nil {
+		resNum := len(searchResult.Hits.Hits)
+		res = make([]map[string]interface{}, resNum)
+		for i, hit := range searchResult.Hits.Hits {
+			json.Unmarshal(*hit.Source, &res[i])
+			//查询结果数据加工处理
+			for k, v := range hit.Highlight {
+				res[i][k] = v[0]
+			}
+			s_content, _ := res[i]["s_content"].(string)
+			if len(s_content) > 500 {
+				res[i]["s_content"] = ""
+			} else {
+				con, _ := regexp.Compile("^[^<]*?>")
+				content := con.ReplaceAllString(s_content, "")
+				con1, _ := regexp.Compile("<[^>]*$")
+				res[i]["s_content"] = template.HTML(con1.ReplaceAllString(content, ""))
+			}
+			s_title, _ := res[i]["s_title"].(string)
+			res[i]["s_title"] = template.HTML(s_title)
+			tmpdate, _ := res[i]["l_createdate"]
+			res[i]["l_createdate"] = util.TimeDiff(time.Unix(util.Int64All(tmpdate.(float64)), 0))
+			tmpdate1, _ := res[i]["releasetime"]
+			res[i]["releasetime"] = util.TimeDiff(time.Unix(util.Int64All(tmpdate1.(float64)), 0))
+			res[i]["_id"] = se.EncodeString(res[i]["_id"].(string))
+			res[i]["s_pic"] = config.Seoconfig["jyadd"].(string) + res[i]["s_pic"].(string)
+			res[i]["s_pic1"] = config.Seoconfig["jyadd"].(string) + res[i]["s_pic1"].(string)
+		}
+	}
+	//生成分页
+	pagination := MakePagination(perPage, currentPage, int(total), querymap, "/jyblog/index_%s.html")
+	return &res, &pagination
+}
+
+//计算分页,分页显示规则
+func MakePagination(perPage, currentPage, total int, param map[string]string, urltpl string) []interface{} {
+	var totalPages int //总页数
+	if total == 0 {
+		totalPages = 1
+	} else {
+		totalPages = (total - 1 + perPage) / perPage //总页数
+	}
+
+	ret := make([]interface{}, 3)
+	index := 0
+	prePage, nextPage := currentPage-1, currentPage+1
+	if prePage < 1 {
+		prePage = 1
+	}
+	if nextPage > totalPages {
+		nextPage = totalPages
+	}
+	param["currentPage"] = strconv.Itoa(prePage)
+	bs, _ := json.Marshal(param)
+	paramstr := base64.StdEncoding.EncodeToString(bs)
+	url := fmt.Sprintf(urltpl, paramstr)
+	iscurrent := currentPage == 1
+	ret[index] = map[string]interface{}{"page": "< 上一页", "url": url, "iscurrent": iscurrent}
+	index = index + 1
+	param["currentPage"] = strconv.Itoa(currentPage)
+	bs, _ = json.Marshal(param)
+	paramstr = base64.StdEncoding.EncodeToString(bs)
+	url = fmt.Sprintf(urltpl, paramstr)
+	iscurrent = currentPage == currentPage
+	ret[index] = map[string]interface{}{"page": currentPage, "url": url, "iscurrent": iscurrent}
+	index = index + 1
+	param["currentPage"] = strconv.Itoa(nextPage)
+	bs, _ = json.Marshal(param)
+	paramstr = base64.StdEncoding.EncodeToString(bs)
+	url = fmt.Sprintf(urltpl, paramstr)
+	iscurrent = currentPage == totalPages
+	ret[index] = map[string]interface{}{"page": "下一页 >", "url": url, "iscurrent": iscurrent}
+	return ret
+}
+
+//博客三级页
+func (f *Front) Jybdetail(_id string) error {
+	var shareid = f.GetString("id")
+	if len(shareid) == 0 {
+		shareid = fmt.Sprintf("%s%d", config.Seoconfig["jybky"].(string)+fmt.Sprintf("%d", time.Now().UnixNano())[8:14], rand.Intn(9))
+	}
+	f.T["logid"] = config.Seoconfig["jybky"].(string)
+	shareid = se.EncodeString(shareid)
+	if ret := redis.Get("other", "jyblog_"+_id); ret != nil {
+		var retlist *map[string]interface{}
+		b, _ := json.Marshal(ret)
+		json.Unmarshal(b, &retlist)
+		(*retlist)["s_content"] = template.HTML((*retlist)["s_content"].(string))
+		(*retlist)["_id"] = (*retlist)["_id"].(string)
+		f.T["data"] = retlist
+	} else {
+		id := se.DecodeString(_id)
+		r := elastic.GetByIdField("content", "content", id, `"_id","s_title","l_createdate","s_pic","s_author","s_editorname","s_contenttype","praise","releasetime","s_subcontent","s_url","s_content","s_source","s_keywords","s_description","s_contenttype"`)
+		if r != nil {
+			tmpdate1, _ := (*r)["l_createdate"]
+			(*r)["l_createdate"] = util.TimeDiff(time.Unix(util.Int64All(tmpdate1.(float64)), 0))
+			tmpdate2, _ := (*r)["releasetime"]
+			(*r)["releasetime"] = util.TimeDiff(time.Unix(util.Int64All(tmpdate2.(float64)), 0))
+			(*r)["s_content"] = template.HTML((*r)["s_content"].(string))
+			(*r)["_id"] = se.EncodeString((*r)["_id"].(string))
+			(*r)["s_pic"] = (*r)["s_pic"].(string)
+		}
+		f.DisableHttpCache()
+		redis.Put("other", "jyblog_"+_id, r, 2*60*60)
+		f.T["data"] = r
+	}
+	f.T["shareid"] = shareid
+	return f.Render("/pc/jyblogdetail.html", &f.T)
+}
+
+//
+func (f *Front) Blogpraise() error {
+	defer util.Catch()
+	id := se.DecodeString(f.GetString("id"))
+	stype := f.GetString("type")
+	flag := "F"
+	var blogflag = true
+	util.Try(func() {
+		if id != "" {
+			if stype == "up" {
+				blogflag = mongodb.Update("content", `{"_id":"`+id+`"}`, `{ "$inc" : { "praise" : 1 }}`, false, false)
+			} else if stype == "down" {
+				blogflag = mongodb.Update("content", `{"_id":"`+id+`"}`, `{ "$inc" : { "praise" : -1 }}`, false, false)
+			}
+			if blogflag {
+				flag = "T"
+				r, _ := mongodb.FindById("content", id, "")
+				elastic.UpdateNewDoc("content", "content", r)
+				redis.Del("other", "jyblog_"+f.GetString("id"))
+			}
+		}
+	}, func(e interface{}) {
+		log.Println("文章点赞出错", e)
+	})
+	f.ServeJson(map[string]interface{}{
+		"flag": flag,
+	})
+	return nil
+}
+
+//
+func getRewardText() (string, string) {
+	rewardText, _ := config.Sysconfig["rewardText"].([]interface{})
+	advertText, _ := config.Sysconfig["advertText"].([]interface{})
+	randVal := rand.New(rand.NewSource(time.Now().UnixNano())).Intn(len(rewardText))
+	if len(advertText) != 0 {
+		advVal := rand.New(rand.NewSource(time.Now().UnixNano())).Intn(len(advertText))
+		return util.ObjToString(rewardText[randVal]), util.ObjToString(advertText[advVal])
+	} else {
+		return util.ObjToString(rewardText[randVal]), ""
+	}
+}
+
+//
+func searchresulthtml(list *[]map[string]interface{}) string {
+	var listhtml = ""
+	var j = 1
+	for i := 0; i < len(*list); i++ {
+		listdata := (*list)[i]
+		j = i + 1
+		listhtml += `<li><div>` + strconv.Itoa(j) + `.</div>`
+		if listdata["title"] != "" {
+			listhtml += `<div><a onclick="noIn(this)" dataId="` + util.EncodeArticleId2ByCheck(util.ObjToString(listdata["_id"])) + `" target="_blank">` + util.ObjToString(listdata["title"]) + `</a></div><div>`
+		}
+		area, _ := listdata["area"].(string)
+		area = strings.TrimSpace(area)
+		finalType, _ := listdata["subtype"].(string)
+		if finalType == "" {
+			finalType = util.ObjToString(listdata["toptype"])
+		}
+		if finalType == "" {
+			finalType = util.ObjToString(listdata["type"])
+			if finalType == "bid" {
+				finalType = "中标"
+			} else if finalType == "tender" {
+				finalType = "招标"
+			} else {
+				finalType = ""
+			}
+		}
+		stpadd, areaadd := classify(finalType, area)
+		if area != "" && area != "A" {
+			listhtml += `<span class="com-area"><a href="/list/area/` + areaadd + `.html">` + area + `</a></span>`
+		}
+		if finalType != "" {
+			listhtml += `<span class="com-type"><a href="/list/stype/` + stpadd + `.html">` + finalType + `</a></span>`
+		}
+		publishtime, _ := listdata["publishtime"].(float64)
+		if publishtime != 0 {
+			diff := util.TimeDiff(time.Unix(util.Int64All(publishtime), 0))
+			if diff != "" {
+				listhtml += `<span class="com-time"><i class="glyphicon bofangjilu"></i>` + diff + `</span>`
+			}
+		}
+		listhtml += `</div></li>`
+	}
+	return listhtml
+}
+
+//标签查询
+func (f *Front) SearchResult(at, name string) error {
+	defer util.Catch()
+	var no = 5
+	var area = ""
+	var stype = ""
+	var startPage, currentPage, limitcount int
+	limitcount = util.IntAll(config.Seoconfig["limitcount"])
+	var res = ""
+	var seotitle = ""
+	var seokeywords = ""
+	var seodescription = ""
+	var shareid = f.GetString("id")
+	if len(shareid) == 0 {
+		shareid = fmt.Sprintf("%s%d", config.Seoconfig["jybqy"].(string)+fmt.Sprintf("%d", time.Now().UnixNano())[8:14], rand.Intn(9))
+	}
+	f.T["logid"] = config.Seoconfig["jybqy"].(string)
+	if at == "area" {
+		areamp, _ := config.Seoconfig["area"].(map[string]interface{})
+		if areamp != nil {
+			areamp1 := areamp[name].(map[string]interface{})
+			if areamp1 != nil {
+				area = areamp1["NAME"].(string)
+				seotitle = areamp1["TITLE"].(string)
+				seokeywords = areamp1["KEYWORDS"].(string)
+				seodescription = areamp1["DESCRIPTION"].(string)
+			}
+		}
+	} else if at == "stype" {
+		stypemp, _ := config.Seoconfig["stype"].(map[string]interface{})
+		if stypemp != nil {
+			stypemp1 := stypemp[name].(map[string]interface{})
+			if stypemp1 != nil {
+				stype = stypemp1["NAME"].(string)
+				seotitle = stypemp1["TITLE"].(string)
+				seokeywords = stypemp1["KEYWORDS"].(string)
+				seodescription = stypemp1["DESCRIPTION"].(string)
+			}
+		}
+	}
+	f.T["seoarea"] = area
+	f.T["seostype"] = stype
+	f.T["seotitle"] = seotitle
+	f.T["seokeywords"] = seokeywords
+	f.T["seodption"] = seodescription
+	if area == "全国" {
+		return f.Redirect("/swordfish/searchinfolist.html")
+	}
+	if area != "" || stype != "" {
+		list := redis.Get("other", "classify_"+name)
+		query1 := `{"query": {"bool": {"must":[`
+		if area != "" {
+			query1 += `{"term":{"area":"` + area + `"}}`
+		} else if stype != "" {
+			query1 += `{"term":{"subtype":"` + stype + `"}}`
+		}
+		query1 += `],"should": [],"minimum_should_match": 1}}}`
+		query := getLastNewsQuery(area, "", stype)
+		var datas *[]map[string]interface{}
+		if list == nil {
+			count := elastic.Count(INDEX, TYPE, query1)
+			r := rand.New(rand.NewSource(time.Now().UnixNano()))
+			currentPage = no
+			startPage = r.Intn(currentPage * limitcount)
+			count1 := util.IntAll(count)
+			if count1 < startPage || startPage < 0 {
+				startPage = 0
+			}
+			datas = elastic.GetPage(INDEX, TYPE, query, `{"publishtime":-1}`, `"_id","title","publishtime","toptype","subtype","type","area","href"`, startPage, limitcount)
+			redis.Put("other", "classify_"+name, datas, 2*60*60)
+		} else {
+			b, _ := json.Marshal(list)
+			json.Unmarshal(b, &datas)
+		}
+
+		res = searchresulthtml(datas)
+		f.T["res"] = res
+		f.T["area"] = area
+		f.T["stype"] = stype
+		f.SetSession("paramarea", area)
+		f.SetSession("paraminfotype", stype)
+		f.T["shareid"] = se.EncodeString(shareid)
+	}
+	return f.Render("/pc/classifylist.html", &f.T)
+}
+
+//
+func classify(stp, area string) (string, string) {
+	var areas, _ = config.Seoconfig["area"].(map[string]interface{})
+	var stypes, _ = config.Seoconfig["stype"].(map[string]interface{})
+	var tpadd = ""
+	var areaadd = ""
+	if area != "" && area != "A" {
+		for k, v := range areas {
+			if area == v.(map[string]interface{})["NAME"] {
+				areaadd = k
+			}
+		}
+	}
+
+	if stp != "" {
+		for k, v := range stypes {
+			if stp == v.(map[string]interface{})["NAME"] {
+				tpadd = k
+			}
+		}
+	}
+	return tpadd, areaadd
+}
+
+//历史推送
+func (f *Front) Historypush() error {
+	userId, _ := f.GetSession("userId").(string)
+	if userId == "" {
+		return f.Redirect("/swordfish/share/-1")
+	}
+	myopenid, _ := f.GetSession("s_m_openid").(string)
+	mynickname, _ := f.Session().Get("s_nickname").(string)
+	myavatar, _ := f.Session().Get("s_avatar").(string)
+	var thistime int64
+	var list *[]map[string]interface{}
+	var success bool
+	if userId != "" {
+		lasttime := time.Now().Local().Unix()
+		thistime, list = getHistorypush(lasttime, userId, nil, 0)
+		if list != nil && len(*list) > 0 {
+			success = true
+		}
+	}
+	f.T["data"] = list
+	f.T["thistime"] = thistime
+	f.T["success"] = success
+	f.T["nickname"] = mynickname
+	f.T["avatar"] = myavatar
+	f.T["signature"] = wx.SignJSSDK(f.Site() + f.Url())
+	f.T["openid"] = se.EncodeString(myopenid)
+	return f.Render("/weixin/historypush.html")
+}
+func (f *Front) HistorypushPaging() error {
+	lasttime, _ := f.GetInt("lasttime")
+	userId, _ := f.GetSession("userId").(string)
+	res := map[string]interface{}{}
+	res["success"] = false
+	if userId != "" && lasttime > 0 {
+		if lasttime == 1 {
+			lasttime = time.Now().Local().Unix()
+		}
+		thistime, list := getHistorypush(lasttime, userId, nil, 0)
+		if list != nil && len(*list) > 0 {
+			res["success"] = true
+			res["data"] = &list
+			res["thistime"] = thistime
+		}
+	}
+	f.ServeJson(&res)
+	return nil
+}
+func getHistorypush(lasttime int64, sid string, res []map[string]interface{}, count int) (thistime int64, list *[]map[string]interface{}) {
+	thistime = lasttime
+	if res == nil {
+		res = make([]map[string]interface{}, 0)
+	}
+	list = &res
+	tmps, ok := mongodb.Find("wxpush", &map[string]interface{}{
+		"s_uid": sid,
+		"l_date": map[string]interface{}{
+			"$lt": lasttime,
+		},
+	}, `{"l_date":-1}`, nil, false, 0, 1)
+	if ok && (*tmps) != nil && len(*tmps) == 1 && (*tmps)[0] != nil {
+		tmp := (*tmps)[0]
+		at := tmp["o_pushinfo"]
+		if at != nil {
+			ats := at.(map[string]interface{})
+			thistime = tmp["l_date"].(int64)
+			count += len(ats)
+			tmp["count"] = len(ats)
+			res = append(res, tmp)
+			list = &res
+			if count >= wx_pageSize {
+				return
+			}
+		} else {
+			return
+		}
+	} else {
+		return
+	}
+	return getHistorypush(thistime, sid, res, count)
+}
+
+//电脑端招标订阅
+func (m *Front) Subscribe() error {
+	var shareid = m.GetString("id")
+	if len(shareid) == 0 {
+		shareid = fmt.Sprintf("%s%d", config.Seoconfig["jydyy"].(string)+fmt.Sprintf("%d", time.Now().UnixNano())[8:14], rand.Intn(9))
+	}
+	m.T["logid"] = config.Seoconfig["jydyy"].(string)
+	m.T["noshareid"] = shareid
+	m.T["shareid"] = se.EncodeString(shareid)
+	myopenid, _ := m.Session().Get("s_m_openid").(string)
+	m.T["openid"] = se.EncodeString(myopenid)
+	mynickname, _ := m.Session().Get("s_nickname").(string)
+	myavatar, _ := m.Session().Get("s_avatar").(string)
+	m.T["nickname"] = mynickname
+	m.T["avatar"] = myavatar
+	m.T["signature"] = wx.SignJSSDK(m.Site() + m.Url())
+	return m.Render("/pc/subscribe.html", &m.T)
+}

+ 81 - 0
src/jfw/front/userlog.go

@@ -0,0 +1,81 @@
+// userlog
+package front
+
+import (
+	"fmt"
+	"jfw/config"
+	"qfw/util"
+	"time"
+)
+
+type UserLog struct {
+	//tag:s_openid,s_model,s_activecode,s_usersource
+	s_openid     string
+	s_nickname   string
+	s_url        string //网站地址
+	s_model      string //首页、检索页、快照页、标签页、活动页
+	s_activecode string //活动代码
+	s_referrer   string //Referrer
+	s_usersource string //用户来源:sem,seo百度,seo搜狐,seogoogle,seo其他,网站扫码
+	i_action     int    //用户取关 1关注,0取消关注
+	s_channel    string //推广渠道id--实物,扇子等固定shareid生成的二维码
+	s_shareid    string //shareid
+
+	//网站检索页参数
+	s_paramkey         string
+	s_parampublishtime string
+	s_paramarea        string
+	s_paraminfotype    string
+	s_paramprojectname string
+
+	s_os     string
+	s_ip     string
+	s_browse string
+}
+
+var addr = fmt.Sprint(config.Sysconfig["influxaddr"])
+var db = fmt.Sprint(config.Sysconfig["influxdb"])
+
+func SaveUserLogs(ulog *UserLog) {
+	date := time.Now()
+	tags := map[string]interface{}{
+		"s_openid":           ulog.s_openid,
+		"s_model":            ulog.s_model,
+		"s_activecode":       ulog.s_activecode,
+		"s_usersource":       ulog.s_usersource,
+		"s_date":             util.FormatDate(&date, util.Date_Short_Layout),
+		"s_nickname":         ulog.s_nickname,
+		"s_shareid":          ulog.s_shareid,
+		"s_url":              ulog.s_url,
+		"s_referrer":         ulog.s_referrer,
+		"i_action":           ulog.i_action,
+		"s_channel":          ulog.s_channel,
+		"s_paramkey":         ulog.s_paramkey,
+		"s_parampublishtime": ulog.s_parampublishtime,
+		"s_paramarea":        ulog.s_paramarea,
+		"s_paraminfotype":    ulog.s_paraminfotype,
+		"s_paramprojectname": ulog.s_paramprojectname,
+		"s_os":               ulog.s_os,
+		"s_ip":               ulog.s_ip,
+		"s_browse":           ulog.s_browse,
+		"i_year":             date.Year(),
+		"i_month":            getMonth(date.Month().String()),
+		"i_day":              date.Day(),
+		"i_hour":             date.Hour(),
+		"i_minute":           date.Minute(),
+		"l_time":             date.Unix(),
+	}
+	mongodb.Save("user_log", tags)
+	//fclient.Save(db, "user_log", addr, tags, fields)
+}
+func getMonth(mon string) int {
+	month := map[string]int{
+		"January": 1, "February": 2,
+		"March": 3, "April": 4,
+		"May": 5, "June": 6,
+		"July": 7, "August": 8,
+		"September": 9, "October": 10,
+		"November": 11, "December": 12,
+	}
+	return month[mon]
+}

+ 256 - 0
src/jfw/front/wxkeyset.go

@@ -0,0 +1,256 @@
+// wxkeyset
+package front
+
+import (
+	"gopkg.in/mgo.v2/bson"
+	"jfw/wx"
+	"log"
+	"qfw/util"
+	"regexp"
+	"strconv"
+	"strings"
+	"time"
+)
+
+//进入订阅词设置
+func (m *Front) WxKeyset(tpl string) error {
+	defer util.Catch()
+	myopenid, _ := m.Session().Get("s_m_openid").(string)
+	surprise := m.GetSession("surprise")
+	if myopenid == "" {
+		return m.Redirect("/swordfish/share/-1")
+	}
+	if tpl == "index" && isInTSguide(myopenid) {
+		return m.Redirect("/front/tenderSubscribe/guide")
+	}
+	if tpl == "seniorset" {
+		data, ok := mongodb.FindOneByField("user", `{"s_m_openid":"`+myopenid+`"}`, `{"o_jy":1}`)
+		if ok && data != nil && len(*data) > 0 {
+			o_jy, _ := (*data)["o_jy"].(map[string]interface{})
+			a_key, _ := o_jy["a_key"].([]interface{})
+			if len(a_key) > 0 {
+				m.T["haskeyword"] = true
+			}
+		}
+	}
+	userId, _ := m.GetSession("userId").(string)
+	s_surprise := ""
+	if tpl == "index" {
+		data, ok := mongodb.FindById("user", userId, `{"o_jy":1}`)
+		var o_jy map[string]interface{}
+		if ok && data != nil && len(*data) > 0 {
+			o_jy, _ = (*data)["o_jy"].(map[string]interface{})
+			s_surprise = util.ObjToString(o_jy["s_surprise"])
+		}
+	}
+	m.T["s_surprise"] = s_surprise
+	m.T["surprise"] = surprise
+	m.T["openid"] = se.EncodeString(myopenid)
+	m.T["signature"] = wx.SignJSSDK(m.Site() + m.Url())
+	mynickname, _ := m.Session().Get("s_nickname").(string)
+	myavatar, _ := m.Session().Get("s_avatar").(string)
+	m.T["nickname"] = mynickname
+	m.T["avatar"] = myavatar
+	return m.Render("/weixin/wxkeyset/"+tpl+".html", &m.T)
+}
+
+//ajax各种请求
+func (m *Front) WxKeysetAjaxReq() {
+	defer util.Catch()
+	userId, _ := m.GetSession("userId").(string)
+	if userId == "" {
+		m.ServeJson(map[string]interface{}{
+			"flag": false,
+		})
+		return
+	}
+	reqType := m.GetString("reqType")
+	if reqType == "getKeyset" { //获取高级设置
+		data, ok := mongodb.FindById("user", userId, `{"o_jy":1}`)
+		var o_jy map[string]interface{}
+		if ok && data != nil && len(*data) > 0 {
+			o_jy, _ = (*data)["o_jy"].(map[string]interface{})
+			a_key, _ := o_jy["a_key"].([]interface{})
+			m.SetSession("o_jy_a_key", a_key)
+		}
+		m.ServeJson(o_jy)
+		return
+	}
+	/*******************以下是修改操作******************/
+	index := ""
+	saveData := make(map[string]interface{})
+	if reqType == "saveKeyWords" { //保存关键词
+		a_key, _ := m.GetSession("o_jy_a_key").([]interface{})
+		keyWords := m.GetSlice("keyWords")
+		indexs := m.GetSlice("indexs")
+		var keyMaps []map[string]interface{}
+		//给删除的关键词加上标识
+		log.Println("keys:", a_key)
+		for k, v := range a_key {
+			key := v.(map[string]interface{})
+			flag, _ := key["flag"].(bool)
+			if !flag {
+				isExists := false
+				for _, i := range indexs {
+					if k == util.IntAll(i) {
+						isExists = true
+						break
+					}
+				}
+				if isExists {
+					key["flag"] = false
+				} else {
+					//已经被删除
+					key["flag"] = true
+				}
+			}
+			a_key[k] = key
+		}
+		m.SetSession("o_jy_a_key", a_key)
+		for k, v := range keyWords {
+			if strings.Trim(v, " ") == "" {
+				continue
+			}
+			if k >= 10 {
+				break
+			}
+			index := util.IntAll(indexs[k])
+			key := map[string]interface{}{}
+			if index < len(a_key) {
+				key, _ = a_key[index].(map[string]interface{})
+				flag, _ := key["flag"].(bool)
+				if flag {
+					key = map[string]interface{}{}
+				}
+				delete(key, "flag")
+			}
+			keys := processKeyword(v)
+			if keys != nil {
+				key["key"] = keys
+				if len(key) > 0 {
+					keyMaps = append(keyMaps, key)
+				}
+			}
+		}
+		//log.Println("keymaps:", keyMaps)
+		saveData["o_jy.a_key"] = keyMaps
+	} else if reqType == "saveArea" { //保存信息范围
+		index = m.GetString("index")
+		saveData["o_jy.a_key."+index+".area"] = m.GetSlice("area")
+	} else if reqType == "saveInfotype" { //保存信息类型
+		index = m.GetString("index")
+		saveData["o_jy.a_key."+index+".infotype"] = m.GetSlice("infotype")
+	} else if reqType == "saveNotkey" { //保存排除关键词
+		index = m.GetString("index")
+		notkey := m.GetSlice("notkey")
+		if len(notkey) > 10 {
+			notkey = notkey[0:10]
+		}
+		for k, v := range notkey {
+			notkey[k] = strings.Replace(v, " ", "", -1)
+		}
+		saveData["o_jy.a_key."+index+".notkey"] = notkey
+	} else if reqType == "saveSeniorset" { //保存高级设置
+		//推送频率:1实时 2每日一推 3自定义时间
+		ratemode, _ := m.GetInteger("ratemode")
+		if ratemode != 2 && ratemode != 3 {
+			ratemode = 1
+		}
+		saveData["o_jy.i_ratemode"] = ratemode
+		//接收方式:1微信  2邮箱 3信息+邮箱
+		mode, _ := m.GetInteger("mode")
+		if mode != 2 && mode != 3 {
+			mode = 1
+		}
+		if ratemode == 3 {
+			saveData["o_jy.i_rmstart"], _ = m.GetInteger("rmstart")
+			saveData["o_jy.i_rmend"], _ = m.GetInteger("rmend")
+		}
+		saveData["o_jy.s_email"] = strings.Trim(m.GetString("email"), " ")
+		saveData["o_jy.i_mode"] = mode
+	} else if reqType == "delKeysWord" { //
+		index = m.GetString("index")
+		delKeyWord := m.GetString("keyword")
+		log.Println("index:", index, "delKeyWord:", delKeyWord)
+		var keyMaps []map[string]interface{}
+		a_key, _ := m.GetSession("o_jy_a_key").([]interface{})
+		for k, v := range a_key {
+			if k != util.IntAll(index) {
+				keyMaps = append(keyMaps, v.(map[string]interface{}))
+			}
+		}
+		saveData["o_jy.a_key"] = keyMaps
+	} else if reqType == "setSurp" {
+		saveData["o_jy.s_surprise"] = "A"
+		m.SetSession("surprise", "A")
+	}
+	//修改操作
+	var flag bool
+	if len(saveData) > 0 {
+		saveData["o_jy.l_modifydate"] = time.Now().Unix()
+		if index == "" {
+			flag = mongodb.UpdateById("user", userId, map[string]interface{}{
+				"$set": saveData,
+			})
+		} else {
+			if intIndex, e := strconv.Atoi(index); e == nil && intIndex >= 0 && intIndex < 10 {
+				updateQuery := map[string]interface{}{
+					"_id": bson.ObjectIdHex(userId),
+					"o_jy.a_key." + index: map[string]interface{}{"$exists": 1},
+				}
+				flag = mongodb.Update("user", updateQuery, map[string]interface{}{
+					"$set": saveData,
+				}, false, false)
+			}
+		}
+	}
+	m.ServeJson(map[string]interface{}{
+		"flag": flag,
+	})
+	return
+}
+
+//高级设置
+func (m *Front) WxKeysetSeniorset() error {
+	defer util.Catch()
+	myopenid, _ := m.Session().Get("s_m_openid").(string)
+	if myopenid == "" {
+		return m.Redirect("/swordfish/share/-1")
+	}
+	m.T["openid"] = se.EncodeString(myopenid)
+	m.T["signature"] = wx.SignJSSDK(m.Site() + m.Url())
+	mynickname, _ := m.Session().Get("s_nickname").(string)
+	myavatar, _ := m.Session().Get("s_avatar").(string)
+	m.T["nickname"] = mynickname
+	m.T["avatar"] = myavatar
+	return m.Render("/weixin/wxkeyset/seniorset.html")
+}
+
+//高级设置
+func (m *Front) WxKeysetFilterset() error {
+	defer util.Catch()
+	myopenid, _ := m.Session().Get("s_m_openid").(string)
+	if myopenid == "" {
+		return m.Redirect("/swordfish/share/-1")
+	}
+	m.T["openid"] = se.EncodeString(myopenid)
+	m.T["signature"] = wx.SignJSSDK(m.Site() + m.Url())
+	mynickname, _ := m.Session().Get("s_nickname").(string)
+	myavatar, _ := m.Session().Get("s_avatar").(string)
+	m.T["nickname"] = mynickname
+	m.T["avatar"] = myavatar
+	return m.Render("/weixin/wxkeyset/filterset.html")
+}
+
+//保存入库之前,处理订阅的关键词
+func processKeyword(keyword string) []string {
+	keywordReg := regexp.MustCompile("([\\s\u3000\u2003\u00a0+,,])+")
+	spaceReg := regexp.MustCompile("\\s+")
+	keyword = keywordReg.ReplaceAllString(keyword, " ")
+	keyword = spaceReg.ReplaceAllString(keyword, " ")
+	keyword = strings.Trim(keyword, " ")
+	if keyword == "" {
+		return nil
+	}
+	return strings.Split(keyword, " ")
+}

+ 87 - 0
src/jfw/jyutil/jyutil.go

@@ -0,0 +1,87 @@
+package jyutil
+
+import (
+	"fmt"
+	"jfw/config"
+	"jfw/tools"
+	"log"
+	"qfw/util"
+	"qfw/util/redis"
+	"strconv"
+	"strings"
+	"time"
+)
+
+//计数器
+const (
+	TYPE_INVITE = iota //邀请
+	TYPE_GIVE          //赠送积分
+)
+
+var mongodb = tools.MQFW
+var se *util.SimpleEncrypt = &util.SimpleEncrypt{Key: "topnet2015topnet2015"}
+var ActivityEndCode, ActivityStartCode int64
+
+var jy_activeset map[string]interface{}
+
+func init() {
+	jy_activeset = config.Sysconfig["jy_activeset"].(map[string]interface{})
+}
+
+//
+func FindMyShareId(activecode, openid string) string {
+	defer util.Catch()
+	ret, bres := mongodb.FindOne("person_share", "{'s_openid':'"+openid+"','s_businesscode':'"+activecode+"'}")
+	var shareid string
+	if bres {
+		if (*ret)["i_shareid"] == nil {
+			if activecode != "" && jy_activeset[activecode] != nil {
+				thisact := jy_activeset[activecode].(map[string]interface{})
+				activitystartcode := util.ObjToString(thisact["activitystartcode"])
+				activityendcode := util.ObjToString(thisact["activityendcode"])
+				if activitystartcode != "" && activityendcode != "" {
+					ActivityStartCode, _ = strconv.ParseInt(activitystartcode, 10, 64)
+					ActivityEndCode, _ = strconv.ParseInt(activityendcode, 10, 64)
+				}
+			} else {
+				ActivityStartCode, _ = strconv.ParseInt(jy_activeset["activitystartcode"].(string), 10, 64)
+				ActivityEndCode, _ = strconv.ParseInt(jy_activeset["activityendcode"].(string), 10, 64)
+			}
+			id := GetShareId(TYPE_INVITE)
+			tools.GetShareQR(id)
+			shareid = fmt.Sprintf("%d", id)
+			data := make(map[string]interface{})
+			data["s_openid"] = openid
+			data["s_businesscode"] = activecode
+			data["i_shareid"] = shareid
+			data["l_timestamp"] = time.Now().Unix()
+			mongodb.Save("person_share", data)
+		} else {
+			shareid = (*ret)["i_shareid"].(string)
+		}
+	}
+	return shareid
+}
+
+//基于Redis的计数器实现
+func GetShareId(businesstype int) uint32 {
+	var tmp int64
+	if businesstype == TYPE_INVITE {
+		tmp = redis.Incr("other", "invite_counter") + ActivityStartCode
+	} else if businesstype == TYPE_GIVE {
+		tmp = redis.Incr("other", "give_counter") + ActivityStartCode
+	}
+	if tmp > ActivityEndCode {
+		log.Println(ActivityEndCode, businesstype)
+		return uint32(ActivityEndCode)
+	}
+	return uint32(tmp)
+}
+
+//过滤符号
+func ReplaceSymbol(con string, rep []string) string {
+	for _, v := range rep {
+		con = strings.Replace(con, v, "", -1)
+	}
+	return con
+}

+ 87 - 0
src/jfw/jyutil/sessionkeep.go

@@ -0,0 +1,87 @@
+package jyutil
+
+import (
+	"fmt"
+	"github.com/go-xweb/xweb"
+	"net/http"
+	"strings"
+	"time"
+)
+
+//session保持
+type SessionKeep struct {
+	SKCookieName      string //session保持,浏览器端的cookie变量名称
+	LoginedSessionVar string //服务器端的session变量名称
+	App               *xweb.App
+}
+
+//过滤器
+func (sk *SessionKeep) Do(w http.ResponseWriter, r *http.Request) bool {
+	ck, err := r.Cookie(sk.SKCookieName)
+	session := sk.App.SessionManager.Session(r, w)
+	openid := session.Get(sk.LoginedSessionVar)
+	rqu := r.RequestURI
+	enopenid := ""
+	rhd := r.Header
+	rhost := r.Host
+	rhdua := "" //UA
+	RURL := ""  //当前url
+	if len(rqu) != 1 && !strings.Contains(rqu, ".html") {
+		return true
+	}
+	if len(rhd["User-Agent"]) > 0 {
+		rhdua = rhd["User-Agent"][0]
+	}
+	session.Set("User-Agent", rhdua)
+	//当前页 模块
+	curi := r.RequestURI
+	if len(rqu) == 1 || strings.Contains(rqu, ".html") {
+		RURL = fmt.Sprintf("%s%s%s", "https://", rhost, rqu)
+		session.Set("RURL", RURL)
+	}
+	if openid != nil && openid != "" { //只有有session,就更新cookie失效时间
+		enopenid = se.EncodeString(openid.(string))
+		http.SetCookie(w, sk.cookie(enopenid))
+	} else if err == nil {
+		enopenid = ck.Value //"解密的openid"
+		deopenid := se.DecodeString(enopenid)
+		user, ok := mongodb.FindOneByField("user", `{"s_m_openid":"`+deopenid+`"}`, `{"s_nickname":1,"s_headimage":1}`)
+		if !ok || user == nil || len(*user) == 0 {
+			//查找不到此用户信息
+			return false
+		}
+		session.Set(sk.LoginedSessionVar, deopenid)
+		session.Set("user", *user)
+		http.SetCookie(w, sk.cookie(enopenid))
+	} else {
+		if strings.Contains(curi, "article/content") && strings.Contains(curi, ".html") {
+			if strings.Contains(rhdua, "baidu") || strings.Contains(rhdua, "google") || strings.Contains(rhdua, "yahoo") || strings.Contains(rhdua, "iask") || strings.Contains(rhdua, "sogou") || strings.Contains(rhdua, "soso") || strings.Contains(rhdua, "youdao") { //爬虫百度、谷歌、雅虎、新浪、搜狗、搜搜、网易有道
+				return true
+			} else if strings.Contains(rhdua, "Android") || strings.Contains(rhdua, "Mobile") {
+				//log.Println("测试微信访问三级页:222--------")
+				//TODO 微信判断
+				return true
+			} else { //只处理pc
+				session.Set("referer", r.RequestURI)
+				http.Redirect(w, r, "/notin/page", 302)
+				return false
+			}
+		}
+	}
+	return true
+}
+
+//生成cookie
+func (sk *SessionKeep) cookie(openid string) *http.Cookie {
+	maxAge := int(time.Hour * 72 / time.Second) //3天,单位秒
+	expires := time.Now().Add(72 * time.Hour)
+	cookie := &http.Cookie{
+		Name:     sk.SKCookieName,
+		Value:    openid,
+		Path:     "/",
+		HttpOnly: false,
+		MaxAge:   maxAge,
+		Expires:  expires,
+	}
+	return cookie
+}

+ 37 - 0
src/jfw/jyutil/sort.go

@@ -0,0 +1,37 @@
+// sort
+package jyutil
+
+import (
+	"sort"
+)
+
+type ObjectMap struct {
+	Key string `json:"key"` //
+	Num int    `json:"num"` //
+
+}
+type ObjSort []*ObjectMap
+
+func (list ObjSort) Len() int {
+	return len(list)
+}
+
+func (list ObjSort) Less(i, j int) bool {
+	if list[i].Num > list[j].Num {
+		return true
+	} else {
+		return false
+	}
+}
+
+func (list ObjSort) Swap(i, j int) {
+	var temp *ObjectMap = list[i]
+	list[i] = list[j]
+	list[j] = temp
+}
+
+func SortMap(list []*ObjectMap) []*ObjectMap {
+	pList := ObjSort(list)
+	sort.Sort(pList)
+	return pList
+}

文件差异内容过多而无法显示
+ 0 - 0
src/jfw/push/src/config.json


+ 1 - 0
src/jfw/push/src/filter.json

@@ -0,0 +1 @@
+["项目","中标","公告"]

+ 1 - 0
src/jfw/push/src/fix.json

@@ -0,0 +1 @@
+{"StartTime":"2016-10-31 13:59:58","fixpushhour":10,"lastid":"58579a5a012a9abbe627de52"}

+ 100 - 0
src/jfw/push/src/main.go

@@ -0,0 +1,100 @@
+/**
+ *推送服务
+ */
+package main
+
+import (
+	"log"
+	//"net"
+	//"net/http"
+	//"net/rpc"
+	"qfw/push"
+	"qfw/push/bid"
+	"qfw/push/dopush"
+	//"qfw/push/listdb"
+	_ "net/http/pprof"
+	"qfw/push/rpcpush"
+	"qfw/util"
+	"qfw/util/elastic"
+	"qfw/util/mongodb"
+	"qfw/util/redis"
+	//"strings"
+	"time"
+)
+
+//初始化
+func init() {
+	util.ReadConfig(&push.PushConfig)
+	util.ReadConfig(push.Fixconfig, &push.FixPushConfig)
+	util.ReadConfig("./filter.json", &push.FilterConfig)
+	dopush.Inits()
+	push.City = make(map[string]uint)
+	pushCite := push.PushConfig["province"].(map[string]interface{})
+	for k, v := range pushCite {
+		push.City[k] = uint(v.(float64))
+	}
+	bid.MaxPushSize = util.IntAll(push.PushConfig["maxPushSize"])
+	rpcpush.PushInfoScopeDays = util.IntAll(push.PushConfig["pushInfoScopeDays"])
+	mongodb.InitMongodbPool(util.IntAll(push.PushConfig["mgoSize"]), push.PushConfig["mgoAddr"].(string), "qfw")
+	redis.InitRedis(push.PushConfig["redisServers"].(string))
+	elastic.InitElasticSize(push.PushConfig["elasticsearch"].(string), util.IntAllDef(push.PushConfig["elasticPoolSize"], 20))
+}
+
+var jobs []push.PushJobFace = []push.PushJobFace{
+	&bid.BidPushJob{},
+}
+
+//是否正在执行定时任务
+var bJobs = true
+
+func runJob() {
+	//启动每天一推
+	h := time.Now().Hour()
+	if bJobs && h > 5 && h < 23 {
+		bJobs = false
+		for _, job := range jobs {
+			//单个job内部实现多并发,job间不再支持并发,网络压力太大了
+			util.Try(func() {
+				job.Execute()
+				util.WriteSysConfig(push.PushConfig)
+			}, func(e interface{}) {})
+		}
+		bJobs = true
+	}
+	time.AfterFunc(time.Duration(util.IntAll(push.PushConfig["durationMinutes"]))*time.Minute, runJob)
+}
+
+//主应用,定时任务
+func main() {
+	go (&bid.FixPushJob{}).Execute()
+	go runJob()
+	//开启rpc服务
+	/**
+	crpc := new(rpcpush.PushInfo)
+	rpc.Register(crpc)
+	rpc.HandleHTTP()
+	port, _ := push.PushConfig["rpcPort"].(string)
+	l, _ := net.Listen("tcp", ":"+port)
+	go http.Serve(l, nil)
+	**/
+	//go Servers()
+	log.Println("启动推送系统")
+	flag := make(chan bool)
+	<-flag
+}
+
+//补推地址
+/**
+func Servers() {
+	http.HandleFunc("/push", func(w http.ResponseWriter, r *http.Request) {
+		openid := r.FormValue("openid")
+		words := r.FormValue("words")
+		words = strings.Replace(words, " ", "+", -1)
+		pushsize := util.IntAllDef(r.FormValue("size"), 1)
+		log.Println(openid, words, pushsize)
+		res := dopush.SetBidding(openid, words, pushsize)
+		w.Write([]byte(util.If(res, "success", "fail").(string)))
+	})
+	http.ListenAndServe(":51513", nil)
+}
+**/

+ 2 - 0
src/jfw/push/src/qfw/push/bid/bid.go

@@ -0,0 +1,2 @@
+//招标、中标公告 消息推送
+package bid

+ 119 - 0
src/jfw/push/src/qfw/push/bid/bid_test.go

@@ -0,0 +1,119 @@
+package bid
+
+import (
+	"fmt"
+	"log"
+	"math/rand"
+	"net/rpc"
+	"qfw/push"
+	"qfw/util"
+	"qfw/util/mongodb"
+	qrpc "qfw/util/rpc"
+	"strings"
+	"testing"
+	"time"
+
+	"gopkg.in/mgo.v2/bson"
+)
+
+func Test_InitCache(t *testing.T) {
+	mongodb.InitMongodbPool(1, "192.168.3.14:27080", "qfw")
+	util.ReadConfig("../../../config.json", &push.PushConfig)
+	st, _ := time.Parse(util.Date_Full_Layout, push.PushConfig["startTime"].(string))
+	a := util.FormatDate(&st, util.Date_Full_Layout)
+	s := []string{"你好", "我好"}
+	log.Println(push.PushConfig["startTime"], st.Unix(), a, fmt.Sprintf("%v", s))
+
+	//push.InitCache()
+	//b := BidPushJob{}
+	//b.Execute()
+	//flag := make(chan bool)
+	//<-flag
+}
+
+func Test_Substr(t *testing.T) {
+
+	s := "你好123aaa就是"
+
+	log.Println(len(s), len([]rune(s)), util.SubString(s, 0, 10))
+
+	l1, _ := time.Parse("2006.01.02 15:04:05 ", "2015.11.20 16:20:00")
+	l2, _ := time.ParseInLocation("2006.01.02 15:04:05 ", "2015.11.20 16:20:00", time.Local)
+	log.Println("2015.11.20 16:20:00", l1.Unix(), l2.Unix(), time.Now().Unix())
+
+	r := rand.New(rand.NewSource(time.Now().UnixNano()))
+
+	n1 := r.Int63n(9)
+	n2 := r.Int63n(7200)
+	wxDate := time.Now().Local().Add(time.Duration(-n2) * time.Second).Add(time.Duration(-n1*24) * time.Hour).Format(util.Date_Full_Layout)
+
+	log.Println(wxDate)
+}
+
+func Test_1(t *testing.T) {
+	util.ReadConfig("E:/project/dev_v1.1/qfw/push/src/config.json", &push.PushConfig)
+	push.City = make(map[string]uint)
+	pushCite := push.PushConfig["province"].(map[string]interface{})
+	for k, v := range pushCite {
+		push.City[k] = uint(v.(float64))
+	}
+	a1 := push.GetChoiceCode(strings.Split("河南", ",")...)
+	a2 := push.GetChoiceCode(strings.Split("北京,河南,广东,山西,新疆", ",")...)
+	log.Println(a1, a2, a1&a2)
+}
+
+func Test_rpc(t *testing.T) {
+	log.Println("----------")
+	for i := 0; i < 30; i++ {
+		dd := qrpc.PushData{
+			Mopenid: "oJULtwzXo6EFV1Ah-XeyRBimXGM8",
+			PushType: map[string]string{
+				"tender": "招标",
+				"bid":    "中标",
+			},
+		}
+		var repl int
+		clent, _ := rpc.DialHTTP("tcp", "127.0.0.1:8766")
+		clent.Call("PushInfo.PushMsg", &dd, &repl)
+
+		log.Println(repl)
+		clent.Close()
+		time.Sleep(10 * time.Millisecond)
+	}
+
+	c := make(chan bool, 1)
+	<-c
+}
+
+func Test_aa(t *testing.T) {
+	a1 := bson.ObjectIdHex("5732a84455606732b000000c")
+	a2 := bson.ObjectIdHex("573a7d61556067173c000001")
+	//a3 := bson.ObjectIdHex("55a8597ea442ceca9e20bf1d")
+	log.Println(a1.Pid(), a1.Time().UnixNano(), a1.Counter(), a1.Hex(), a2.Pid(), a2.Time().UnixNano(), a2.Counter())
+	aa := a1.Hex() > a2.Hex()
+	var ss bson.ObjectId
+	log.Println(aa, len(a1.Hex()), ss.Valid())
+}
+
+func Test_bb(t *testing.T) {
+	s := "10000-20000-30000-40000-50000-60000-70000-80000-90000-100000-110000-120000-130000-140000,1说说说说-2说说说说-3说说说说-4说说说说-5说说说说-6说说说说-7说说说说-8说说说说-9说说说说-10说说说说-11说说说说-12说说说说-13说说说说-14说说说说-15说说说说-16说说说说-1"
+	log.Println(len(s), string([]rune("你ff444")[1:]))
+
+}
+
+func HexCompare(s1, s2 string) int {
+	v := 0
+	for i := 0; i < len(s1); i++ {
+		ss1, ss2 := s1[i:i+1], s2[i:i+1]
+		if ss1 > ss2 {
+			v = 1
+			break
+		} else if ss1 < ss2 {
+			v = -1
+			break
+		} else {
+			v = 0
+		}
+	}
+	return v
+}

+ 28 - 0
src/jfw/push/src/qfw/push/bid/bidpushjob.go

@@ -0,0 +1,28 @@
+/**
+ *招标,中标公告的推送消息,在兴趣词分析上,存在一定的难度
+ *现在采用DFA算法分析,兴趣词会构造Map结构,前期应该不会出问题。
+ *后期需要改进(用户量达到5千以上)
+ *可行策略:每日产生的新数据有限,大概不超过5000条,可以把标题全加到内存
+ *        用户兴趣词分批次加入,每次加入3000个左右的用户的兴趣词,过滤
+ *        今日产生的招标、中标数据,并完成推送,第二批载入后3000个用户的数据
+ */
+package bid
+
+import (
+	"qfw/push"
+	"qfw/push/dopush"
+)
+
+type BidPushJob struct {
+}
+
+//最大推送条数
+var MaxPushSize int
+
+//推送模式 mgo为数据库模式,es为elastic模式 var PushMode string
+
+//执行日常招标的消息推送
+func (b *BidPushJob) Execute() bool {
+	dopush.PushInfoByEs(MaxPushSize, &push.PushConfig, 1)
+	return true
+}

+ 51 - 0
src/jfw/push/src/qfw/push/bid/fixpushjob.go

@@ -0,0 +1,51 @@
+package bid
+
+/*
+*每天推送一次
+ */
+
+import (
+	"errors"
+	"log"
+	"qfw/push"
+	"qfw/push/dopush"
+	"qfw/util"
+	"time"
+)
+
+type FixPushJob struct {
+}
+
+func (b *FixPushJob) Execute() bool {
+	fixpushhour := util.IntAllDef(push.FixPushConfig["fixpushhour"], -1)
+	if fixpushhour > 0 {
+		now := time.Now()
+		h, day := now.Hour(), now.Day()
+		if h >= fixpushhour {
+			day++
+		}
+		newDate := time.Date(now.Year(), now.Month(), day, fixpushhour, 0, 50, 0, time.Local)
+		des := newDate.Unix() - now.Unix()
+		if des > 0 {
+			log.Println("start fix push after:", des)
+			time.AfterFunc(time.Duration(des)*time.Second, func() {
+				go fp()
+				ticker := time.NewTicker(time.Hour * 24)
+				go func() {
+					for _ = range ticker.C {
+						fp()
+					}
+				}()
+			})
+		}
+	} else {
+		errors.New("fixpushhour,not exists")
+	}
+	return true
+}
+
+func fp() {
+	defer util.Catch()
+	dopush.PushInfoByEs(MaxPushSize, &push.FixPushConfig, 2)
+	util.WriteSysConfig(push.Fixconfig, push.FixPushConfig)
+}

+ 138 - 0
src/jfw/push/src/qfw/push/cache.go

@@ -0,0 +1,138 @@
+/**
+ * 缓存
+ * 主要缓存用户,用户标识、用户兴趣词,用户所在位置,
+ */
+package push
+
+import (
+	"fmt"
+	"log"
+	"qfw/util"
+	"qfw/util/elastic"
+	"qfw/util/mongodb"
+	"runtime"
+	"strings"
+
+	"gopkg.in/mgo.v2/bson"
+)
+
+type MemberInterest struct {
+	Id           string   //mongoid
+	Province     string   //省份
+	ProvinceVal  uint64   //可选多个省份的处理
+	Interest     []string //用户兴趣
+	Openid       string
+	InterestDate int64
+	PushMode     int //增加推送方式 11两位,分别代表邮箱、微信
+	Ratemode     int
+	Rmstart      int
+	Rmend        int
+	Email        string
+	AllKeys      []elastic.KeyConfig
+}
+
+//初始化缓存,在每次执行任务时调用,
+func InitCache(m_openid string, bview bool) map[string]*[]*MemberInterest {
+	defer func() {
+		if r := recover(); r != nil {
+			log.Println("[E]", r)
+			for skip := 1; ; skip++ {
+				_, file, line, ok := runtime.Caller(skip)
+				if !ok {
+					break
+				}
+				go log.Printf("%v,%v\n", file, line)
+			}
+		}
+	}()
+
+	cache := make(map[string]*[]*MemberInterest)
+	q := map[string]interface{}{}
+	if m_openid != "" {
+		q = map[string]interface{}{
+			"s_m_openid": m_openid,
+		}
+	} else {
+		fixPush := util.ObjToString(PushConfig["fixPush"])
+		if len(fixPush) > 5 {
+			log.Println("推指定的人", fixPush)
+			q["s_m_openid"] = map[string]interface{}{
+				"$in": strings.Split(fixPush, ","),
+			}
+		} else {
+			q = map[string]interface{}{
+				"i_appid": 2,
+				"o_jy": map[string]interface{}{
+					"$exists": true,
+				},
+				"i_ispush": map[string]interface{}{"$ne": 0},
+			}
+		}
+	}
+	session := mongodb.GetMgoConn()
+	defer mongodb.DestoryMongoConn(session)
+	query := session.DB("qfw").C("user").Find(&q).Select(&map[string]interface{}{
+		"_id":        1,
+		"o_jy":       1,
+		"s_m_openid": 1,
+	}).Iter()
+	n := 0
+	for tmp := make(map[string]interface{}); query.Next(tmp); {
+		util.Try(func() {
+			n++
+			_id := fmt.Sprintf("%x", string(tmp["_id"].(bson.ObjectId)))
+			o_msgset := tmp["o_jy"].(map[string]interface{})
+			_a_key, _ := o_msgset["a_key"].([]interface{})
+			a_key := util.ObjArrToStringArr(_a_key)
+			s_scope := util.ObjToString(o_msgset["s_scope"])
+			mode := util.IntAll(o_msgset["i_mode"])
+			if mode > 0 && len(a_key) > 0 && len(s_scope) > 0 {
+				user := MemberInterest{
+					Id:          _id,
+					Province:    s_scope,
+					ProvinceVal: GetChoiceCode(strings.Split(s_scope, ",")...),
+					Interest:    a_key,
+					Openid:      util.ObjToString(tmp["s_m_openid"]),
+					PushMode:    mode,
+					Email:       util.ObjToString(o_msgset["s_email"]),
+				}
+				date := o_msgset["l_modifydate"]
+				if date == nil {
+					date = o_msgset["l_modifydate"]
+				}
+				if date != nil {
+					util.Try(func() {
+						user.InterestDate = date.(int64)
+					}, func(e interface{}) {})
+				}
+				for i := 0; i < len(a_key) && a_key[i] != ""; i++ {
+					var arr []*MemberInterest
+					if nil == cache[a_key[i]] {
+						arr = make([]*MemberInterest, 0)
+						cache[a_key[i]] = &arr
+					} else {
+						arr = *cache[a_key[i]]
+						cache[a_key[i]] = &arr
+					}
+					arr = append(arr, &user)
+				}
+			}
+		}, func(e interface{}) {
+			log.Println(e)
+		})
+		tmp = make(map[string]interface{})
+	}
+	log.Println("本次查询用户总数:", n)
+	return cache
+}
+
+//各省份排序,最终会占某个2进制位
+//根据传入参数,计算选择代码
+func GetChoiceCode(choice ...string) (ret uint64) {
+	for _, v := range choice {
+		if tmp, ok := City[v]; ok {
+			ret += 1 << (tmp - 1)
+		}
+	}
+	return
+}

+ 84 - 0
src/jfw/push/src/qfw/push/cache_test.go

@@ -0,0 +1,84 @@
+package push
+
+import (
+	"container/list"
+	"fmt"
+	"log"
+	"strings"
+	"sync"
+	"testing"
+	"time"
+)
+
+func Test_InitCache(t *testing.T) {
+	//mongodb.InitMongodbPool(1, "192.168.3.14:27080", "qfw")
+	//InitCache()
+	a := GetChoiceCode(strings.Split("四川", ",")...)
+	b := GetChoiceCode("福建")
+	log.Println(a, b, a&b)
+
+}
+
+func Test_mod(t *testing.T) {
+	n := 0
+	for i := 0; i < 30; i++ {
+		n %= 5
+		log.Println(n)
+		n++
+	}
+}
+
+func Test_len(t *testing.T) {
+	log.Println(len([]rune("%s")) == len([]rune("招标")))
+}
+
+func Test_list(t *testing.T) {
+	//验证list中的v值
+	li := list.New()
+	for i := 0; i < 5; i++ {
+		m1 := map[string]interface{}{
+			"a1": map[string]string{
+				"a2": "2333" + fmt.Sprintf("_%d_%d", i, i+1),
+			},
+			"a": i,
+		}
+		li.PushBack(&m1)
+	}
+	l1, l2 := list.New(), list.New()
+	for k := li.Front(); k != nil; k = k.Next() {
+		if (((*(k.Value.(*map[string]interface{})))["a"]).(int))%2 == 0 {
+			l1.PushBack(k.Value)
+		} else {
+			l2.PushBack(k.Value)
+		}
+	}
+
+	log.Println(l1.Front().Value.(*map[string]interface{}), l2.Front().Value.(*map[string]interface{}))
+}
+
+func Test_go(t *testing.T) {
+	func() {
+		var ll = sync.Mutex{}
+		for i := 0; i < 10; i++ {
+			a := 1 + (i * 10)
+			b := "11"
+			c := map[string]interface{}{
+				"ff": "111",
+				"vv": i,
+			}
+			d := &map[string]interface{}{
+				"bb": "aaa",
+				"cc": i,
+			}
+			go func(a, b, c, d interface{}) {
+				go func() {
+					ll.Lock()
+					defer ll.Unlock()
+					log.Println(a, b, c, d)
+				}()
+			}(a, b, c, d)
+		}
+
+	}()
+	time.Sleep(1000 * time.Second)
+}

+ 142 - 0
src/jfw/push/src/qfw/push/dfa/interestanalysis.go

@@ -0,0 +1,142 @@
+/**
+ *兴趣分析
+ *
+ */
+package dfa
+
+import (
+	"log"
+	"strings"
+)
+
+//DFA实现
+type DFA struct {
+	link        map[string]interface{} //存放or
+	linkAnd     map[string]int         //存放and
+	linkAndWord map[string]interface{} //存放and中的拆分词
+
+}
+
+//添加词组,用于初始化,该方法是可以调用多次的
+func (d *DFA) AddWord(words ...string) {
+	if d.link == nil {
+		d.link = make(map[string]interface{})
+		d.linkAnd = make(map[string]int)
+		d.linkAndWord = make(map[string]interface{})
+	}
+	var nowMap *map[string]interface{}
+	for _, key := range words {
+		keys := strings.Split(key, "+")
+		lenkeys := len(keys)
+		if lenkeys > 1 {
+			d.linkAnd[key] = lenkeys
+			for k := 0; k < lenkeys; k++ {
+				minKey := keys[k]
+				nowMap = &d.linkAndWord
+				for i := 0; i < len(minKey); i++ {
+					kc := minKey[i : i+1]
+					if v, ok := (*nowMap)[kc]; ok {
+						nowMap, _ = v.(*map[string]interface{})
+					} else {
+						newMap := map[string]interface{}{}
+						newMap["YN"] = "N"
+						(*nowMap)[kc] = &newMap
+						nowMap = &newMap
+					}
+					if i == len(minKey)-1 {
+						(*nowMap)["YN"] = "Y"
+						if (*nowMap)["key"] == nil {
+							(*nowMap)["key"] = make(map[string]int)
+						}
+						(*nowMap)["key"].(map[string]int)[key] = k
+					}
+				}
+			}
+		} else {
+			nowMap = &d.link
+			for i := 0; i < len(key); i++ {
+				kc := key[i : i+1]
+				if v, ok := (*nowMap)[kc]; ok {
+					nowMap, _ = v.(*map[string]interface{})
+				} else {
+					newMap := map[string]interface{}{}
+					newMap["YN"] = "N"
+					(*nowMap)[kc] = &newMap
+					nowMap = &newMap
+				}
+
+				if i == len(key)-1 {
+					(*nowMap)["YN"] = "Y"
+				}
+			}
+		}
+	}
+}
+func (d *DFA) Clear() {
+	d.link = nil
+}
+
+//从给定的内容中找出匹配上的关键词
+func (d *DFA) Analy(src string) []string {
+	if d.link == nil {
+		log.Println("请先添加词组")
+		return []string{}
+	}
+	keywords := []string{}
+	tempMap := make(map[string][]bool)
+	for i := 0; i < len(src); i++ {
+		nowMap := &d.link
+		length := 0 // 匹配标识数默认为0
+		//flag := false // 敏感词结束标识位:用于敏感词只有1位的情况
+		for j := i; j < len(src); j++ {
+			word := src[j : j+1]
+			nowMap, _ = (*nowMap)[word].(*map[string]interface{})
+			if nowMap != nil {
+				length = length + 1
+				tag, _ := (*nowMap)["YN"].(string)
+				if "Y" == tag {
+					//flag = true
+					keywords = append(keywords, src[i:i+length])
+				}
+			} else {
+				break
+			}
+		}
+		nowMap = &d.linkAndWord
+		length = 0
+		for j := i; j < len(src); j++ {
+			word := src[j : j+1]
+			nowMap, _ = (*nowMap)[word].(*map[string]interface{})
+			if nowMap != nil {
+				length = length + 1
+				tag, _ := (*nowMap)["YN"].(string)
+				if "Y" == tag {
+					mkeys := (*nowMap)["key"].(map[string]int)
+					for k, v := range mkeys {
+						tempBool := tempMap[k]
+						if tempBool == nil {
+							tempBool = make([]bool, d.linkAnd[k])
+							tempMap[k] = tempBool
+						}
+						tempBool[v] = true
+					}
+				}
+			} else {
+				break
+			}
+		}
+	}
+	for k, v := range tempMap {
+		ball := true
+		for _, m := range v {
+			if !m {
+				ball = false
+				break
+			}
+		}
+		if ball {
+			keywords = append(keywords, k)
+		}
+	}
+	return keywords
+}

+ 45 - 0
src/jfw/push/src/qfw/push/dfa/interestanalysis_test.go

@@ -0,0 +1,45 @@
+package dfa
+
+import (
+	"log"
+	"strings"
+	"testing"
+	"time"
+)
+
+var d *DFA = &DFA{}
+
+func copyMap(m map[string]int) (m2 map[string]int) {
+	m2 = make(map[string]int)
+	for k, v := range m {
+		m2[k] = v
+	}
+	return m2
+}
+
+func TestAnaly(t *testing.T) {
+	d.AddWord("办公", "办+楼", "河+省", "完+你们8")
+	log.Println(strings.Split("河+南+", "+")[2])
+	t1 := time.Now()
+	log.Println(d.Analy("这胡省锦河涛写给江泽民的信我们你们于办公楼上你完就是啊。"))
+	log.Println(time.Now().Sub(t1).Seconds())
+	d.Clear()
+	//log.Println(d.Analy("这是胡锦涛写给江泽民的信啊。"))
+
+}
+
+func Test_Label(t *testing.T) {
+	log.Println("000----")
+
+	for _, v := range []int{1, 2, 3, 4, 5} {
+		log.Println(v)
+	L1:
+		for _, vv := range []string{"a", "b", "c", "d"} {
+			log.Println(vv)
+			if vv == "add" {
+				break L1
+			}
+		}
+	}
+	log.Println("111----")
+}

+ 315 - 0
src/jfw/push/src/qfw/push/dopush/dopush.go

@@ -0,0 +1,315 @@
+package dopush
+
+import (
+	"container/list"
+	"fmt"
+	"log"
+	"qfw/push"
+	"qfw/util"
+	"qfw/util/mail"
+	"qfw/util/mongodb"
+	qrpc "qfw/util/rpc"
+	"regexp"
+	"strconv"
+	"strings"
+	"sync"
+	"time"
+)
+
+var wxsendpool = make(chan bool, 50)
+var se util.SimpleEncrypt
+var re *regexp.Regexp
+var MAXLen = 200
+var TitleLen, ContentLen, GroupLen int
+var WxTitle, WxContent, WxGroup string
+var Mailpos = 0
+var MailposLock = &sync.Mutex{}
+var MailMap = []*mail.MailAuth{}
+var MailFrom = "剑鱼招标订阅"
+var Domain = ""
+var (
+	mail_html    = ""
+	mail_content = ""
+	mail_title   = ""
+)
+
+//获取邮箱配置
+func GetSMail() (auth *mail.MailAuth) {
+	defer util.Catch()
+	MailposLock.Lock()
+	defer MailposLock.Unlock()
+	Mailpos %= len(MailMap)
+	auth = MailMap[Mailpos]
+	Mailpos++
+	time.Sleep(80 * time.Millisecond)
+	return
+}
+
+//初始化推送信息
+func Inits() {
+	//推送标题
+	mail_title = util.ObjToString(push.PushConfig["mail_title"])
+	mail_html = util.ObjToString(push.PushConfig["mail_html"])
+	mail_content = util.ObjToString(push.PushConfig["mail_content"])
+
+	WxTitle = util.ObjToString(push.PushConfig["wxtitle"])
+	//正文
+	WxContent = util.ObjToString(push.PushConfig["wxcontent"])
+	//信息类型
+	WxGroup = util.ObjToString(push.PushConfig["wxgroup"])
+	se = util.SimpleEncrypt{Key: "topnet"}
+	//html过滤
+	re, _ = regexp.Compile("<[^>]+>([^<]+)?<[^>]+>")
+	//长度计算
+	TitleLen = len([]rune(WxTitle))
+	ContentLen = len([]rune(WxContent))
+	GroupLen = len([]rune(WxGroup))
+
+	//初始化服务邮箱
+	arrMap := util.ObjArrToMapArr(push.PushConfig["mails"].([]interface{}))
+	for i := 0; i < len(arrMap); i++ {
+		MailMap = append(MailMap,
+			&mail.MailAuth{util.ObjToString(arrMap[i]["addr"]),
+				util.IntAll(arrMap[i]["port"]),
+				util.ObjToString(arrMap[i]["user"]),
+				util.ObjToString(arrMap[i]["pwd"])})
+	}
+	Domain = push.PushConfig["bidViewDomain"].(string)
+}
+
+type sortList []*map[string]interface{}
+
+func (s sortList) Len() int {
+	return len(s)
+}
+
+func (s sortList) Less(i, j int) bool {
+	defer util.Catch()
+	return util.Int64All((*s[i])["publishtime"]) > util.Int64All((*s[j])["publishtime"])
+}
+
+func (s sortList) Swap(i, j int) {
+	defer util.Catch()
+	s[i], s[j] = s[j], s[i]
+}
+
+func DealSend(k *push.MemberInterest, v *list.List, now time.Time, MaxPushSize int, ratemode int, infoTypeName string) {
+	defer util.Catch()
+	bmail := false
+	mailContent := ""
+	if k.PushMode&2 > 0 && len(k.Email) > 0 {
+		bmail = true
+	}
+	str := fmt.Sprintf("<div>根据您设置的关键词(%s),给您推送以下信息:</div>", strings.Join(k.Interest, ";"))
+	//发送内容组合
+	i := 0
+	lastInfoDate := int64(0)
+	TitleArray := []string{}
+	o_pushinfo := map[string]map[string]interface{}{}
+	publishTitle := map[string]bool{}
+	FilterData.Start(k.Openid)
+	defer FilterData.End()
+	for ks := v.Front(); ks != nil; ks = ks.Next() {
+		k2 := *(ks.Value.(*map[string]interface{}))
+		title := strings.Replace(k2["title"].(string), "\n", "", -1)
+		if !publishTitle[title] {
+			if FilterData.IsExists(util.BsonIdToSId(k2["_id"])) {
+				continue
+			}
+			publishTitle[title] = true
+			TitleArray = append(TitleArray, re.ReplaceAllString(title, "$1"))
+			i++
+			if i == 1 {
+				lastInfoDate = util.Int64All(k2["publishtime"])
+			}
+			//_sid := util.EncodeArticleId(util.BsonIdToSId(k2["_id"]))
+			//url := fmt.Sprintf("%s/pcdetail/%s.html", Domain, _sid)
+			_sid := util.EncodeArticleId2ByCheck(util.BsonIdToSId(k2["_id"]))
+			url := fmt.Sprintf("%s/article/content/%s.html", Domain, _sid)
+			if bmail { //关于邮件的处理
+				classArea := "area"
+				area := util.ObjToString(k2["area"])
+				if area == "A" {
+					area = "全国"
+				}
+				classType := "type"
+				types := util.ObjToString(k2["subtype"])
+				if types == "" {
+					types = util.ObjToString(k2["toptype"])
+					if types == "" {
+						types = "其他"
+					}
+				}
+				dates := util.LongToDate(k2["publishtime"], false)
+				//标题替换
+				otitle := util.ObjToString(k2["otitle"])
+				for _, kw := range k.Interest {
+					kws := strings.Split(kw, "+")
+					n := 0
+					otitle2 := otitle
+					for _, kwn := range kws {
+						ot := strings.Replace(otitle2, kwn, "<span class='keys'>"+kwn+"</span>", 1)
+						if ot != otitle {
+							n++
+							otitle2 = ot
+						} else {
+							break
+						}
+					}
+					if n == len(kws) {
+						otitle = otitle2
+						break
+					}
+				}
+				mailContent += fmt.Sprintf(mail_content, i, url, otitle, classArea, area, classType, types, dates)
+			}
+			//str += "<div class='tslist'><span class='xh'>" + fmt.Sprintf("%d", i) + ".</span><a class='bt' target='_blank' sid='" + _sid + "' href='" + util.ObjToString(k2["href"]) + "'>" + title + "</a></div>"
+			str += "<div class='tslist'><span class='xh'>" + fmt.Sprintf("%d", i) + ".</span><a class='bt' target='_blank' eid='" + _sid + "' href='" + util.ObjToString(k2["href"]) + "'>" + title + "</a></div>"
+			o_pushinfo[strconv.Itoa(i)] = map[string]interface{}{
+				"publishtime": k2["publishtime"],
+				"stype":       util.ObjToString(k2["type"]),
+				"topstype":    util.ObjToString(k2["toptype"]),
+				"substype":    util.ObjToString(k2["subtype"]),
+			}
+			if k2["highlight"] != nil {
+				o_pushinfo[strconv.Itoa(i)]["highlight"] = k2["highlight"]
+			}
+			if i >= MaxPushSize {
+				//限制最大信息条数
+				break
+			}
+		}
+	}
+	if i == 0 {
+		log.Println(k.Openid, "没有要推送的数据!")
+		return
+	}
+	///-----------------长度计算-----------------
+	TmpTip := ""
+	minute := now.Unix() - lastInfoDate
+	if minute > -1 && minute < 61 {
+		TmpTip = fmt.Sprintf("%d秒前发布的", minute)
+	} else {
+		minute = minute / 60
+		if minute < 121 {
+			if minute < 1 {
+				minute = 1
+			}
+			TmpTip = fmt.Sprintf("%d分钟前发布的", minute)
+		}
+	}
+
+	//2、发送微信
+	wid := ""
+	if k.PushMode&1 > 0 && len(k.Openid) > 0 {
+		Tip1 := util.If(TmpTip == "", "", TmpTip+":\n").(string)
+		LastTip := ""
+		if i > 1 {
+			LastTip = fmt.Sprintf("...(共%d条)", i)
+		}
+		LastTipLen := len([]rune(LastTip))
+		reLen := MAXLen - TitleLen - GroupLen - ContentLen - len([]rune(Tip1))
+		//if infoType == 2 {
+		//	reLen = reLen - 4
+		//}
+		WXTitle := ""
+		bshow := false
+		for n := 1; n < len(TitleArray)+1; n++ {
+			curTitle := TitleArray[n-1]
+			tmptitle := WXTitle + fmt.Sprintf("%d %s\n", n, curTitle)
+			ch := reLen - len([]rune(tmptitle))
+			if ch < LastTipLen { //加上后大于后辍,则没有完全显示
+				if ch == 0 && n == len(TitleArray) {
+					WXTitle = tmptitle
+					bshow = true
+				} else {
+					ch_1 := reLen - len([]rune(WXTitle)) - LastTipLen
+					if ch_1 > 8 {
+						curLen := len([]rune(curTitle))
+						if ch_1 > curLen {
+							ch_1 = curLen
+						}
+						WXTitle += fmt.Sprintf("%d %s\n", n, string([]rune(curTitle)[:ch_1-3]))
+					}
+				}
+
+			} else if ch == LastTipLen {
+				WXTitle = tmptitle
+				if n == len(TitleArray) {
+					bshow = true
+				}
+			} else {
+				WXTitle = tmptitle
+				if n == len(TitleArray) {
+					bshow = true
+				}
+			}
+		}
+		if bshow {
+			LastTip = ""
+		}
+		//-------------长度计算完成------------------
+		log.Println("-----", k.Openid)
+		wid = SendWeixin(k, str, now, Tip1+WXTitle+LastTip, o_pushinfo, ratemode, infoTypeName)
+	}
+
+	//3、发送邮件
+	if bmail {
+		html := fmt.Sprintf(mail_html, strings.Replace(strings.Join(k.Interest, ";"), "+", " ", -1), mailContent)
+		go SendEmail(k.Email, html, fmt.Sprintf(mail_title, infoTypeName))
+		if wid == "" { //保存信息
+			SaveSendInfo(k, now, html, o_pushinfo, ratemode)
+		}
+	}
+}
+
+//推送邮件
+func SendEmail(email, str, title string) {
+	auth := GetSMail()
+	if auth != nil {
+		mail.SendMail(auth, &mail.Message{title, MailFrom, []string{email}, str})
+	}
+}
+
+//推送微信
+func SendWeixin(k *push.MemberInterest, str string, now time.Time, Remarks string, o_pushinfo map[string]map[string]interface{}, ratemode int, infoTypeName string) string {
+	defer func() {
+		if r := recover(); r != nil {
+			fmt.Println("发送微信[E]", r)
+		}
+	}()
+	time.Sleep(time.Millisecond * 80)
+	wid := SaveSendInfo(k, now, str, o_pushinfo, ratemode)
+	wxDate := ""
+	push.SendWinXin(&qrpc.NotifyMsg{
+		Openid:  k.Openid,
+		Title:   fmt.Sprintf(WxTitle, infoTypeName),
+		Remark:  Remarks,
+		Detail:  WxContent,
+		Date:    wxDate,
+		Service: fmt.Sprintf(WxGroup, infoTypeName),
+		Url:     push.PushConfig["bidViewDomain"].(string) + "/front/sess/" + se.EncodeString(k.Openid+",uid,"+strconv.Itoa(int(time.Now().Unix()))+",wxpushlist") + "__" + wid})
+	return wid
+}
+
+//保存发送信息
+func SaveSendInfo(k *push.MemberInterest, now time.Time, str string, o_pushinfo map[string]map[string]interface{}, ratemode int) string {
+	wxpush := map[string]interface{}{
+		"s_m_openid": k.Openid,
+		"l_date":     now.Unix(),
+		"s_words":    k.Interest,
+		"s_uid":      k.Id,
+		"s_province": k.Province,
+		"a_interest": k.Interest,
+		"s_content":  str,
+		//修改字段
+		//"a_publishtime": publishTimes,
+		"o_pushinfo": o_pushinfo,
+		"i_size":     len(o_pushinfo),
+		"i_appid":    2,
+		//增加字段
+		"i_ratemode": ratemode,   //之前是拟建、招标,现在改为推送模式
+		"i_sendmode": k.PushMode, //是邮箱、微信
+	}
+	return mongodb.Save("wxpush", &wxpush)
+}

+ 213 - 0
src/jfw/push/src/qfw/push/dopush/dopushes.go

@@ -0,0 +1,213 @@
+package dopush
+
+import (
+	"container/list"
+	"encoding/json"
+	"fmt"
+	"log"
+	"qfw/push"
+	"qfw/util"
+	"qfw/util/elastic"
+	"qfw/util/mongodb"
+	"strings"
+	"time"
+
+	"gopkg.in/mgo.v2/bson"
+)
+
+const (
+	ShowField  = `"_id","title","publishtime","toptype","subtype","type","area","href","areaval","infoformat"`
+	FindField  = `"title"`
+	SortQuery  = `{"publishtime":"desc"}`
+	DB         = "bidding"
+	IDRange    = `{"range":{"id":{"gt":"%s","lte":"%s"}}},{"range":{"publishtime":{"gt": %d}}}`
+	MaxId      = `{"query":{"filtered":{"filter":{"bool":{"must":{"range":{"id":{"gt":"%s"}}}}}}},"_source":["_id","comeintime"],"sort":{"id":"desc"},"from":0,"size":1}`
+	PushTitle  = `[<span class='area'>%s</span>]%s`
+	Infoformat = `{"term":{"infoformat":%d}}`
+)
+
+var (
+	InfoMap = map[int]string{
+		1: "招标",
+		2: "拟建项目",
+	}
+	pushpool = make(chan bool, 7)
+)
+
+//开始推送
+func DoPush(k *push.MemberInterest, idrange []string, MaxPushSize int, Now time.Time, ratemode int) {
+	defer util.Catch()
+	defer func() {
+		<-pushpool
+	}()
+	//是否含有拟建项目
+	//for i := 1; i < 3; i++ {
+	//idrangeTmp := append(idrange, fmt.Sprintf(Infoformat, i))
+	var pushArray = []map[string]interface{}{}
+	res := elastic.GetResForJY(DB, DB, k.AllKeys, strings.Join(idrange, ","), FindField, SortQuery, ShowField, 0, MaxPushSize)
+	if res != nil && *res != nil && len(*res) > 0 {
+		listInfos := list.New()
+		for _, v := range *res {
+			vh := v["highlight"].(map[string][]string)
+			if len(vh) == 2 {
+				v["highlight"] = 3
+			} else {
+				if vh["title"] != nil {
+					v["highlight"] = 1
+				} else {
+					v["highlight"] = 2
+				}
+			}
+			province := util.ObjToString(v["area"])
+			v["otitle"] = v["title"]
+			if "A" != province {
+				v["title"] = fmt.Sprintf(PushTitle, province, v["title"])
+			}
+			var info = v
+			pushArray = append(pushArray, info)
+			listInfos.PushBack(&info)
+		}
+		var rtflog = false
+		h := time.Now().Hour()
+		openid := k.Openid
+		if k.Ratemode == 3 {
+			if k.Rmstart <= h && h < k.Rmend {
+				rtflog = true
+				//把暂存数据取出合并后发送,并删除此用户暂存库中的数据
+				inofsdata := mongodb.FindOne("tempush", &bson.M{"s_openid": openid})
+				if inofsdata != nil {
+					if (*inofsdata)["tmpinfos"] != nil {
+						infos := (*inofsdata)["tmpinfos"].([]interface{})
+						for _, tif := range infos {
+							tifMap := tif.(map[string]interface{})
+							listInfos.PushBack(&tifMap)
+						}
+					}
+				}
+				mongodb.Del("tempush", &bson.M{"s_openid": openid})
+			} else {
+				//自定义时间外的数据暂存数据库tempush中
+				mongodb.Update("tempush", &bson.M{"s_openid": openid}, &bson.M{
+					"$pushAll": bson.M{
+						"tmpinfos": &pushArray,
+					},
+				}, true, false)
+			}
+		} else {
+			rtflog = true
+		}
+		if rtflog {
+			DealSend(k, listInfos, Now, MaxPushSize, k.Ratemode, InfoMap[1])
+		}
+
+		//}
+	}
+}
+
+func PushInfoByEs(MaxPushSize int, Config *map[string]interface{}, i_ratemode int) (res bool) {
+	defer util.Catch()
+	st, _ := time.ParseInLocation(util.Date_Full_Layout, (*Config)["StartTime"].(string), time.Local)
+	lastTime := st.Unix()
+	_id := util.ObjToString((*Config)["lastid"])
+	log.Println("开始执行任务-i_ratemode-id-lasttime:", i_ratemode, _id, lastTime)
+	Now := time.Now()
+	session := mongodb.GetMgoConn()
+	defer mongodb.DestoryMongoConn(session)
+	idrange := []string{}
+	//获取本次查询的最大id
+	resId := elastic.Get(DB, DB, fmt.Sprintf(MaxId, _id))
+	lastid := ""
+	var comeintime interface{}
+	if resId != nil && *resId != nil && len(*resId) == 1 {
+		lastid = util.ObjToString((*resId)[0]["_id"])
+		comeintime = (*resId)[0]["comeintime"]
+	} else {
+		log.Println("未查找到数据...", fmt.Sprintf(MaxId, _id))
+		return false
+	}
+	//filter全局查询
+	idrange = append(idrange, fmt.Sprintf(IDRange, _id, lastid, lastTime-7*86400))
+	//遍历用户
+	q := map[string]interface{}{
+		"i_appid":  2,
+		"i_ispush": map[string]interface{}{"$ne": 0},
+	}
+	if i_ratemode == 1 {
+		q["o_jy.i_ratemode"] = map[string]interface{}{"$in": []int{1, 3}}
+	} else {
+		q["o_jy.i_ratemode"] = i_ratemode
+	}
+	query := session.DB("qfw").C("user").Find(&q).Select(&map[string]interface{}{
+		"_id":        1,
+		"o_jy":       1,
+		"s_m_openid": 1,
+	}).Iter()
+	//遍历所有用户放到数组
+	allPushUsers := []map[string]interface{}{}
+	for tmp := make(map[string]interface{}); query.Next(tmp); {
+		allPushUsers = append(allPushUsers, tmp)
+		tmp = make(map[string]interface{})
+	}
+	log.Println("本次推送", len(allPushUsers), "人")
+	//遍历用户数组
+	for _, tmp := range allPushUsers {
+		log.Println("openid:", tmp["s_m_openid"])
+		util.Try(func() {
+			_id := fmt.Sprintf("%x", string(tmp["_id"].(bson.ObjectId)))
+			o_msgset := tmp["o_jy"].(map[string]interface{})
+			var allkeysTemp []elastic.KeyConfig
+			_bs, err := json.Marshal(o_msgset["a_key"])
+			if err == nil {
+				json.Unmarshal(_bs, &allkeysTemp)
+			}
+			allkeys := []elastic.KeyConfig{}
+			if len(allkeysTemp) > 0 {
+				//一个字或者配置文件中的词,不推送
+				for _, vs := range allkeysTemp {
+					isFilter := false
+					vskey := strings.Replace(strings.Join(vs.Keys, ""), " ", "", -1)
+					if len([]rune(vskey)) == 1 {
+						continue
+					}
+					for _, fv := range push.FilterConfig {
+						if fv == vskey {
+							isFilter = true
+							break
+						}
+					}
+					if !isFilter {
+						allkeys = append(allkeys, vs)
+					}
+				}
+			}
+			////////////////
+			if len(allkeys) > 0 {
+				a_key := []string{}
+				for _, vs := range allkeys {
+					a_key = append(a_key, strings.Join(vs.Keys, "+"))
+				}
+				user := push.MemberInterest{
+					Id:       _id,
+					Interest: a_key,
+					PushMode: util.IntAllDef(o_msgset["i_mode"], 1),
+					Email:    util.ObjToString(o_msgset["s_email"]),
+					Openid:   util.ObjToString(tmp["s_m_openid"]),
+					Ratemode: util.IntAllDef(o_msgset["i_ratemode"], 1),
+					Rmstart:  util.IntAllDef(o_msgset["i_rmstart"], 1),
+					Rmend:    util.IntAllDef(o_msgset["i_rmend"], 1),
+					AllKeys:  allkeys,
+				}
+				pushpool <- true
+				go DoPush(&user, idrange, MaxPushSize, Now, i_ratemode)
+			}
+
+		}, func(e interface{}) {
+			log.Println(e)
+		})
+	}
+	log.Println("推送结束..", comeintime, lastid)
+	allPushUsers = []map[string]interface{}{}
+	(*Config)["StartTime"] = util.FormatDateWithObj(&comeintime, util.Date_Full_Layout)
+	(*Config)["lastid"] = lastid
+	return true
+}

+ 160 - 0
src/jfw/push/src/qfw/push/dopush/dopushmgo

@@ -0,0 +1,160 @@
+package dopush
+
+import (
+	"container/list"
+	"fmt"
+	"log"
+	"qfw/push"
+	"qfw/push/dfa"
+	"qfw/util"
+	"qfw/util/mongodb"
+	"strings"
+	"time"
+
+	"gopkg.in/mgo.v2/bson"
+)
+
+type Pjob struct {
+	Dfa         *dfa.DFA
+	Cache       *map[string]*[]*push.MemberInterest
+	MaxPushSize int
+}
+
+//构造用户兴趣词库
+func (b *Pjob) CreateUserInterestWord() {
+	b.Dfa = &dfa.DFA{}
+	words := make([]string, 0)
+	for k, _ := range *b.Cache {
+		words = append(words, k)
+	}
+	b.Dfa.AddWord(words...)
+}
+
+func (p *Pjob) DoPush(mopenid, stime string, opr int, lastTime int64, _id string) bool {
+	log.Println("开始执行任务-mgo-:", stime, _id)
+	p.Cache = new(map[string]*[]*push.MemberInterest)
+	*p.Cache = push.InitCache(mopenid, false)
+	p.CreateUserInterestWord()
+	return EachAllBidInfo(_id, lastTime, p.MaxPushSize, p.Dfa, p.Cache, opr)
+}
+
+//遍历数据并执行推送操作
+func EachAllBidInfo(_id string, lastTime int64, MaxPushSize int, dfas *dfa.DFA, cache *map[string]*[]*push.MemberInterest, pushType int) (res bool) {
+	defer func() {
+		if r := recover(); r != nil {
+			fmt.Println("推送开始[E]", r)
+		}
+	}()
+	session := mongodb.GetMgoConn()
+	defer mongodb.DestoryMongoConn(session)
+
+	var returnLastId bson.ObjectId
+	var q map[string]interface{}
+	if len(_id) != 24 {
+		log.Println("不存在最后推送信息ID,配置文件出错")
+		q = map[string]interface{}{
+			"comeintime": map[string]interface{}{
+				"$gt": lastTime,
+			},
+			"publishtime": map[string]interface{}{
+				"$gt": lastTime - 7*86400,
+			},
+			"extracttype": 1,
+		}
+	} else {
+		returnLastId = (bson.ObjectIdHex(_id))
+		q = map[string]interface{}{
+			"_id": map[string]interface{}{
+				"$gt": returnLastId,
+			},
+			"publishtime": map[string]interface{}{
+				"$gt": lastTime - 7*86400,
+			},
+			"extracttype": 1,
+		}
+	}
+	n, _ := session.DB("qfw").C("bidding").Find(&q).Count()
+	if n == 0 {
+		log.Println("执行推送任务", "没有数据。")
+		return
+	}
+	//切记publishtime 要做索引
+	query := session.DB("qfw").C("bidding").Find(&q).Sort("-publishtime").Iter()
+	userMap := &map[*push.MemberInterest]*list.List{}
+	var returnLastTime interface{}
+	var returnLastTimeLong int64 = 0
+	num := 0
+L1:
+	for tmp := new(map[string]interface{}); query.Next(tmp); num++ {
+		title := util.ObjToString((*tmp)["title"])
+		if title != "" {
+			//返回匹配到的词组
+			res := dfas.Analy(title)
+			if len(res) > 0 {
+				province := util.ObjToString((*tmp)["area"])
+				if strings.TrimSpace(province) == "" {
+					province = "A"
+				}
+				provinceVal := push.GetChoiceCode(province)
+				if "A" != province {
+					(*tmp)["title"] = `[<span class='area'>` + province + `</span>]` + title
+				}
+				for _, v := range res {
+					//根据关键词返回用户指针
+					tw := (*cache)[v]
+					if tw != nil {
+						//遍历用户加入到此条信息上
+						for _, v2 := range *tw {
+							if v2.Province == "A" || v2.ProvinceVal&provinceVal > 0 {
+								s := (*userMap)[v2]
+								if s == nil {
+									s = list.New()
+									(*userMap)[v2] = s
+								}
+								s.PushBack(tmp)
+								if pushType == 2 && s.Len() > MaxPushSize {
+									break L1
+								}
+							}
+						}
+					}
+				}
+			}
+			if (*tmp)["comeintime"] != nil {
+				ttmp := util.Int64All((*tmp)["comeintime"])
+				if ttmp > returnLastTimeLong {
+					returnLastTimeLong = ttmp
+				}
+			}
+			tmpid := (*tmp)["_id"].(bson.ObjectId)
+			if !returnLastId.Valid() || returnLastId.Hex() < tmpid.Hex() {
+				returnLastId = tmpid
+			}
+		}
+		tmp = new(map[string]interface{})
+	}
+	now := time.Now()
+	if pushType == 1 && returnLastId.Valid() {
+		returnLastTime = returnLastTimeLong
+		if returnLastTimeLong > 0 {
+			push.PushConfig["StartTime"] = util.FormatDateWithObj(&returnLastTime, util.Date_Full_Layout)
+		}
+		push.PushConfig["lastid"] = returnLastId.Hex()
+	}
+	for k, v := range *(userMap) {
+		log.Println(*k)
+		kk := *k
+		vv := *v
+		time.Sleep(50 * time.Millisecond)
+		res = true
+		wxsendpool <- true
+		go Send(&kk, &vv, now, MaxPushSize)
+	}
+	return res
+}
+
+//指定推送
+//去掉listdb后此功能失效
+func SetBidding(openid, words string, pushsize int) bool {
+	return PushInfoByEs("", openid, 0, pushsize)
+}

+ 75 - 0
src/jfw/push/src/qfw/push/dopush/filterdata.go

@@ -0,0 +1,75 @@
+package dopush
+
+import (
+	"encoding/json"
+	"log"
+	"qfw/util/redis"
+	"sync"
+)
+
+var FilterData *filterDataEntity
+
+type filterDataEntity struct {
+	Lock   *sync.Mutex
+	Array  []string
+	OpenId string
+}
+
+func init() {
+	FilterData = &filterDataEntity{
+		Lock:  new(sync.Mutex),
+		Array: []string{},
+	}
+}
+
+//获取数据
+func (fde *filterDataEntity) Start(openid string) {
+	fde.Lock.Lock()
+	if openid == "" {
+		return
+	}
+	fde.OpenId = openid
+	data := redis.Get("push", "push_"+openid)
+	if data == nil {
+		return
+	}
+	b, err := json.Marshal(data)
+	if err != nil {
+		log.Println("从redis中取出的数据转成byte数组出错!")
+		return
+	}
+	var array []string
+	if json.Unmarshal(b, &array) != nil {
+		log.Println("byte数组转成string数组出错!")
+		return
+	}
+	fde.Array = array
+}
+
+//判断数据是否存在
+func (fde *filterDataEntity) IsExists(_id string) bool {
+	if _id == "" {
+		return false
+	}
+	for _, v := range fde.Array {
+		if _id == v {
+			return true
+		}
+	}
+	fde.Array = append(fde.Array, _id)
+	return false
+}
+
+//添加数据
+func (fde *filterDataEntity) End() {
+	if fde.OpenId != "" {
+		if len(fde.Array) > 0 {
+			redis.Put("push", "push_"+fde.OpenId, fde.Array, -1)
+		} else {
+			redis.Del("push", "push_"+fde.OpenId)
+		}
+	}
+	fde.Array = []string{}
+	fde.OpenId = ""
+	fde.Lock.Unlock()
+}

+ 8 - 0
src/jfw/push/src/qfw/push/job.go

@@ -0,0 +1,8 @@
+//消息推送
+package push
+
+//推送任务接口
+type PushJobFace interface {
+	//执行动作
+	Execute() bool
+}

+ 101 - 0
src/jfw/push/src/qfw/push/listdb/listdb.go

@@ -0,0 +1,101 @@
+package listdb
+
+import (
+	"log"
+	"qfw/util"
+	"qfw/util/mongodb"
+	"sort"
+	"strings"
+	"sync"
+	"time"
+
+	"gopkg.in/mgo.v2/bson"
+)
+
+type arr []*map[string]interface{}
+
+func (a *arr) Len() int {
+	return len(*a)
+}
+func (a *arr) Less(i, j int) bool {
+	return util.Int64All((*(*a)[i])["publishtime"]) < util.Int64All((*(*a)[j])["publishtime"])
+}
+func (a *arr) Swap(i, j int) {
+	tmp := (*a)[i]
+	(*a)[i] = (*a)[j]
+	(*a)[j] = tmp
+}
+
+type ListDB struct {
+	Lock      *sync.Mutex
+	DB        *arr
+	Stype     string
+	timestamp int64
+	lastid    bson.ObjectId
+}
+
+var fields map[string]interface{} = map[string]interface{}{
+	"type":        1,
+	"toptype":     1,
+	"subtype":     1,
+	"comeintime":  1,
+	"publishtime": 1,
+	"title":       1,
+	"area":        1,
+	"href":        1,
+	"areaval":     1,
+}
+
+var Bid *ListDB
+
+//---废弃
+func __Inits() {
+	Bid = &ListDB{
+		Lock:      new(sync.Mutex),
+		DB:        &arr{},
+		timestamp: 0,
+	}
+	go Bid.update()
+}
+
+func (l *ListDB) update() {
+	l.Lock.Lock()
+	util.Try(func() {
+		var q map[string]interface{}
+		if l.lastid.Valid() {
+			q = map[string]interface{}{
+				"_id": map[string]interface{}{
+					"$gt": l.lastid,
+				},
+			}
+		}
+		session := mongodb.GetMgoConn()
+		defer mongodb.DestoryMongoConn(session)
+		//此处不排序,谁查谁排序(不索引会造成发布时间乱序)
+		query := session.DB("qfw").C("bidding").Find(q).Select(fields).Sort("publishtime").Iter()
+		for tmp := new(map[string]interface{}); query.Next(tmp); {
+			province := util.ObjToString((*tmp)["area"])
+			if strings.TrimSpace(province) == "" {
+				province = "A"
+			}
+			if "A" != province {
+				title := util.ObjToString((*tmp)["title"])
+				(*tmp)["title"] = `[<span class='area'>` + province + `</span>]` + title
+			}
+			*l.DB = append(*l.DB, tmp)
+			last := (*tmp)["_id"].(bson.ObjectId)
+			if !l.lastid.Valid() || l.lastid.Hex() < last.Hex() {
+				l.lastid = last
+			}
+			if l.DB.Len() > 400000 {
+				*l.DB = (*l.DB)[l.DB.Len()-400000:]
+			}
+			tmp = new(map[string]interface{})
+		}
+	}, func(e interface{}) {
+		log.Println(e)
+	})
+	sort.Sort(l.DB)
+	l.Lock.Unlock()
+	time.AfterFunc(10*time.Minute, l.update)
+}

+ 11 - 0
src/jfw/push/src/qfw/push/pushconfig.go

@@ -0,0 +1,11 @@
+//
+package push
+
+//配置
+var (
+	PushConfig    map[string]interface{}
+	FixPushConfig map[string]interface{}
+	Fixconfig     = "./fix.json"
+	City          map[string]uint
+	FilterConfig  []string
+)

+ 34 - 0
src/jfw/push/src/qfw/push/rpcpush/rpcpush.go

@@ -0,0 +1,34 @@
+package rpcpush
+
+//"encoding/json"
+//"log"
+//"qfw/push/bid"
+//"qfw/push/dopush"
+//"qfw/util"
+//qrpc "qfw/util/rpc"
+
+type PushInfo struct {
+}
+
+var PushInfoScopeDays int
+
+/**
+var rpcchan = make(chan bool, 50)
+
+//RPC调用结果预览
+func (p *PushInfo) ResultView(data *qrpc.PushData, Reply *[]byte) error {
+	rpcchan <- true
+	defer func() {
+		<-rpcchan
+	}()
+	util.Try(func() {
+		pj := dopush.Pjob{
+			MaxPushSize: bid.MaxPushSize,
+		}
+		mp := pj.EachInfoForView(data.Mopenid, data.Words)
+		log.Println("mp:", len((mp)))
+		*Reply, _ = json.Marshal(mp)
+	}, func(e interface{}) {})
+	return nil
+}
+**/

+ 54 - 0
src/jfw/push/src/qfw/push/weixincall.go

@@ -0,0 +1,54 @@
+//微信调用
+package push
+
+import (
+	"log"
+	"net/rpc"
+	"qfw/util"
+	"qfw/util/mongodb"
+	qrpc "qfw/util/rpc"
+	"strings"
+	"time"
+)
+
+var wxpool chan bool = make(chan bool, 30)
+
+//微信远程调用,实现模板发送消息
+func SendWinXin(p *qrpc.NotifyMsg) {
+	wxpool <- true
+	defer func() {
+		<-wxpool
+	}()
+	util.Try(func() {
+		client, err := rpc.DialHTTP("tcp", PushConfig["weixinRpcServer"].(string))
+		defer client.Close()
+		if err != nil {
+			log.Println(err.Error())
+			return
+		}
+		var repl qrpc.RpcResult
+		err = client.Call("WeiXinRpc.SendPushMsg", p, &repl)
+		if err != nil {
+			log.Println(err.Error())
+		}
+		res := string(repl)
+		if strings.Contains(res, "[46004]") || strings.Contains(res, "[65302]") || strings.Contains(res, "[43004]") || strings.Contains(res, "[40003]") {
+			updateIsPush(p.Openid, 0)
+		}
+	}, func(e interface{}) {})
+	time.Sleep(10 * time.Millisecond)
+}
+
+//修改是否推送的状态
+func updateIsPush(openid string, status int) {
+	mongodb.Update("user", map[string]interface{}{"s_m_openid": openid}, map[string]interface{}{
+		"$set": map[string]interface{}{
+			"i_ispush": status,
+		},
+	}, false, false)
+	mongodb.Update("follow_project", map[string]interface{}{"s_openid": openid}, map[string]interface{}{
+		"$set": map[string]interface{}{
+			"i_ispush": status,
+		},
+	}, false, true)
+}

+ 61 - 0
src/jfw/push/src/qfw/push/wxcall_test.go

@@ -0,0 +1,61 @@
+package push
+
+import (
+	"encoding/json"
+	"log"
+	"net/rpc"
+	"qfw/util"
+	qrpc "qfw/util/rpc"
+	"regexp"
+	"testing"
+	"time"
+)
+
+func Test_date(t *testing.T) {
+	re, _ := regexp.Compile("<[^>]+>([^<]+)<[^>]+>")
+	log.Println(re.ReplaceAllString("[<span area='ccc'>河南</span>]你就是《原梓菲》<a>dasd脸色</a>", "$2"))
+}
+
+func TestWeixinRpc(t *testing.T) {
+	PushConfig = map[string]interface{}{}
+	//PushConfig["weixinRpcServer"] = "120.25.216.197:83"
+	PushConfig["weixinRpcServer"] = "127.0.0.1:82"
+	log.Println(PushConfig)
+	SendWinXin(&qrpc.NotifyMsg{Url: "www.baidu.com?aa=1", Openid: "obEpLuMMDUYUM-zlzCbQ0MbuQOzc", Title: "[订阅消息]招投标信息推送", Remark: "请到网站个人中心查看详细.请到网站个人中心查看详细请到网站个人中心查看详细请到网站个人中心查看详细请到网站个人中心查看详细请到网站个人中心查看详细请到网站个人中心查看详细请到网站个人中心查看详细请到网站个人中心查看详细请到网站个人中心查看详细请到网站个人中心查看详细请到网站个人中心查看详细", Detail: "投标信息", Result: "2015-10-21"})
+	//coreutil.SendIdentifyTplMsg(&qrpc.NotifyMsg{Openid: "obEpLuKW516dqWdc-WT-Ra_5qigo", Remark: "审核成功", Detail: "你传的营业执照图片不清晰", Result: "认证失败"})
+	time.After(20 * time.Second)
+}
+
+func TestPushRpc(t *testing.T) {
+
+	clent, errs := rpc.DialHTTP("tcp", "127.0.0.1:8766")
+	defer clent.Close()
+	rpcData := qrpc.PushData{
+		//Mopenid: "oJULtwzXo6EFV1Ah-XeyRBimXGM8",
+		PushType: map[string]string{
+			"bid": "中标",
+		},
+		Words: "分析+公告",
+	}
+	var repl []byte
+	clent.Call("PushInfo.ResultView", &rpcData, &repl)
+	if errs == nil && repl != nil && len(repl) > 0 {
+		var mp map[string]interface{}
+		json.Unmarshal(repl, &mp)
+		log.Println(mp)
+	}
+
+}
+
+func TestPushMsg(t *testing.T) {
+	util.ReadConfig("../../config.json", &PushConfig)
+	SendWinXin(&qrpc.NotifyMsg{
+		Openid:  "o2FZ6weYKBzgA7cLLIrOz3cncw08",
+		Title:   "你",
+		Remark:  "10000-20000-30000-40000-50000-60000-70000-80000-90000-100000-110000-120000-130000-140000,1说说说说-2说说说说-3说说说说-4说说说说-5说说说说-6说说说说-7说说说说-8说说说说-9说说说说-10说说说说-11说说说说-12说说说说-13说说说说-14说说说说-15说说说说-16说说说说-17说说说说-18说说说说-19说说说说-20说说说说-21说说说说-22说说说说-23说说说说-24说说说说-25说说说说-26说说说说-27说说说说-28说说说说-29说说说说-30说说说说", //fmt.Sprintf(Remark, strings.Join(k.Interest, ";")),
+		Detail:  "cc你",
+		Date:    "2016-12-22",
+		Service: "dd你",
+		Url:     "www.baidu.com",
+	})
+}

+ 106 - 0
src/jfw/rpcfollow/rpc.go

@@ -0,0 +1,106 @@
+// rpc
+package rpcfollow
+
+import (
+	tools "jfw/tools"
+	"log"
+	"qfw/util"
+	"qfw/util/elastic"
+	frpc "qfw/util/rpc"
+	"time"
+
+	"gopkg.in/mgo.v2/bson"
+)
+
+type MyfollowRpc struct{}
+
+func (c *MyfollowRpc) MyFollowSet(param *frpc.FollowData, ret *string) error {
+	util.Try(func() {
+		*ret = setMyFollowKey(param.OpenId, param.Projectname)
+		//log.Println(param.OpenId, *ret)
+	}, func(e interface{}) {})
+	return nil
+}
+
+func setMyFollowKey(openid, projectname string) string {
+	rep := ""
+	projects, _ := tools.MQFW.Find("follow_project", `{"s_openid":"`+openid+`"}`, nil, `{"s_projectname":1}`, false, -1, -1)
+	for _, v := range *projects {
+		if v["s_projectname"] == projectname {
+			return "repeat"
+		}
+	}
+	log.Println("rep", rep)
+	if len(*projects) < 10 {
+		user, _ := tools.MQFW.FindOneByField("user", `{"s_m_openid":"`+openid+`"}`, `{"_id":1}`)
+		if projectname != "" && len(*user) > 0 {
+			data := map[string]interface{}{
+				"s_userid":      util.BsonIdToSId((*user)["_id"]),
+				"s_openid":      openid,
+				"s_projectname": projectname,
+				"l_createtime":  time.Now().Unix(),
+				"i_remind":      0,
+				"s_title":       projectname,
+				"i_source":      2,
+			}
+			//匹配
+			r := elastic.GetPage("bidding", "bidding", `{"TERM_projectname":"`+projectname+`"}`, `{"comeintime":-1}`, `"_id","title","comeintime","bidopentime","projectcode","type","href","publishtime","subtype","toptype","area"`, -1, -1)
+			if r == nil || *r == nil || len(*r) == 0 {
+				r, _ = tools.MQFW.Find("bidding_back", bson.M{
+					"projectname": projectname,
+				}, `{"comeintime":-1}`, `{"title":1,"comeintime":1,"bidopentime":1,"projectcode":1,"type":1,"href":1,"publishtime":1,"subtype":1,"toptype":1,"area":1}`, false, 0, -1)
+			}
+			var matchingFlag = r != nil && len(*r) != 0
+			var projectcode, sid string
+			if matchingFlag {
+				d := (*r)[0]
+				sid = d["_id"].(string) //(d["_id"].(bson.ObjectId)).Hex()
+				data["s_id"] = sid
+				data["s_toptype"] = d["toptype"]
+				data["l_comeintime"] = d["comeintime"]
+				publishtime := d["publishtime"]
+				data["l_publishtime"] = publishtime
+				data["s_title"] = d["title"]
+				data["s_area"] = d["area"]
+				data["s_province"] = d["area"]
+				if bidopentime := d["bidopentime"]; bidopentime != nil {
+					data["l_bidopentime"] = bidopentime
+				}
+				if d["projectcode"] != nil {
+					projectcode = d["projectcode"].(string)
+					data["s_projectcode"] = projectcode
+				}
+				if s_type := d["type"]; s_type != nil {
+					data["s_type"] = s_type
+				}
+				url := d["href"].(string)
+				data["s_url"] = url
+				//
+				mySelf := make(bson.M)
+				mySelf["s_type"] = util.ObjToString(d["type"])
+				mySelf["s_toptype"] = util.ObjToString(d["toptype"])
+				mySelf["s_subtype"] = util.ObjToString(d["subtype"])
+				mySelf["s_province"] = util.ObjToString(d["area"])
+				mySelf["s_projectname"] = projectname
+				mySelf["s_title"] = d["title"]
+				mySelf["s_projectcode"] = projectcode
+				mySelf["s_url"] = url
+				mySelf["s_id"] = sid
+				mySelf["s_eid"] = util.EncodeArticleId2ByCheck(sid)
+				mySelf["l_publishtime"] = publishtime
+				data["a_relationinfo"] = []map[string]interface{}{mySelf}
+			}
+			if id := tools.MQFW.Save("follow_project", data); len(id) > 0 {
+				rep = id
+				go tools.FollowPush(&frpc.FollowPush{
+					ProjectName: projectname,
+					ProjectCode: projectcode,
+					InfoId:      sid,
+					FollowId:    id,
+					OpenId:      openid,
+				})
+			}
+		}
+	}
+	return rep
+}

+ 104 - 0
src/jfw/tag/msg.go

@@ -0,0 +1,104 @@
+package tag
+
+import (
+	"fmt"
+	"jfw/config"
+	"qfw/util"
+	"strconv"
+	"strings"
+	"time"
+)
+
+//从json配置文件中读取值
+func Msg(mtype, key string) string {
+	return readproperty(mtype, key)
+}
+
+//
+func DateTip(date2 int64) (timedate string) {
+	timedate = "30秒前"
+	date1 := time.Now().Unix()
+	td := date1 - date2
+	//天数
+	var days = td / (24 * 3600)
+	//小时
+	var leave1 = td % (24 * 3600)
+	var hours = leave1 / 3600
+	//分钟
+	var leave2 = leave1 % 3600
+	var minutes = leave2 / 60
+	if days > 0 {
+		if days > 10 {
+			var date1fm = time.Unix(date1, 0).Format("2006-01-02")
+			var date2fm = time.Unix(date2, 0).Format("2006-01-02")
+			date1fmyear := strings.Split(date1fm, "-")[0]
+			date2fmyear := strings.Split(date2fm, "-")[0]
+			if date1fmyear > date2fmyear {
+				timedate = date2fm
+			} else {
+				timedate = time.Unix(date2, 0).Format("01-02")
+			}
+		} else {
+			timedate = strconv.FormatInt(days, 10) + "天"
+		}
+	} else if hours > 0 {
+		timedate = strconv.FormatInt(hours, 10) + "小时"
+	} else if minutes > 0 {
+		timedate = strconv.FormatInt(minutes, 10) + "分钟"
+	}
+	return
+}
+
+//
+func readproperty(mtype, key string) string {
+	switch mtype {
+	case "seo":
+		tmp := util.GetPropertie(key, config.Seoconfig)
+		if tmp == nil {
+			return ""
+		} else {
+			ret, _ := tmp.(string)
+			return ret
+		}
+	case "date":
+		fmt.Println(key, "--------")
+		timedate := "30秒前"
+		if len(key) > 0 {
+			var date1 int64
+			var date2 int64
+			date1 = time.Now().Unix()
+			date2, _ = strconv.ParseInt(key, 10, 0)
+			td := date1 - date2
+			//天数
+			var days = td / (24 * 3600)
+			//小时
+			var leave1 = td % (24 * 3600)
+			var hours = leave1 / 3600
+			//分钟
+			var leave2 = leave1 % 3600
+			var minutes = leave2 / 60
+			if days > 0 {
+				if days > 10 {
+					var date1fm = time.Unix(date1, 0).Format("2006-01-02")
+					var date2fm = time.Unix(date2, 0).Format("2006-01-02")
+					date1fmyear := strings.Split(date1fm, "-")[0]
+					date2fmyear := strings.Split(date2fm, "-")[0]
+					if date1fmyear > date2fmyear {
+						timedate = date2fm
+					} else {
+						timedate = time.Unix(date2, 0).Format("01-02")
+					}
+				} else {
+					timedate = strconv.FormatInt(days, 10) + "天"
+				}
+			} else if hours > 0 {
+				timedate = strconv.FormatInt(hours, 10) + "小时"
+			} else if minutes > 0 {
+				timedate = strconv.FormatInt(minutes, 10) + "分钟"
+			}
+		}
+		return timedate
+	default:
+		return ""
+	}
+}

+ 11 - 0
src/jfw/tag/tag.go

@@ -0,0 +1,11 @@
+package tag
+
+import (
+	"jfw/config"
+	"qfw/util"
+)
+
+//自定义标签支持,读取配置文件
+func init() {
+	util.ReadConfig("./seo.json", &config.Seoconfig)
+}

+ 164 - 0
src/jfw/timetask/fishindex.go

@@ -0,0 +1,164 @@
+package timetask
+
+import (
+	"errors"
+	"gopkg.in/mgo.v2/bson"
+	"log"
+	"qfw/util"
+	"time"
+)
+
+func fishindex() {
+	defer util.Catch()
+	othershow()
+	nowpush()
+	go func() {
+		for {
+			time.Sleep(2 * time.Second)
+			now := time.Now()
+			//每半小时
+			pushnext := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute()+40, 0, 0, now.Location())
+			pushtimer := time.NewTimer(pushnext.Sub(now)) //提醒
+			select {
+			case <-pushtimer.C:
+				nowpush()
+			}
+		}
+	}()
+	go func() {
+		for {
+			time.Sleep(10 * time.Second)
+			now := time.Now()
+			//每天凌晨
+			indexnext := time.Date(now.Year(), now.Month(), now.Day()+1, 0, 0, 0, 0, now.Location())
+			indextimer := time.NewTimer(indexnext.Sub(now))
+			select {
+			case <-indextimer.C:
+				for othershow() != nil {
+					time.Sleep(time.Minute * 10)
+				}
+			}
+		}
+	}()
+}
+
+func nowpush() {
+	defer util.Catch()
+	log.Println("start count the push today")
+	now := time.Now()
+	d := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local)
+	one, ok := mongodb.FindOne("swordfish_index", bson.M{"l_timestamp": bson.M{"$gte": d.Unix()}})
+	if !ok || one == nil || len(*one) == 0 {
+		log.Println("waiting for maindata create", "===", ok)
+	} else {
+		sess := mongodb.GetMgoConn()
+		defer mongodb.DestoryMongoConn(sess)
+		var res bson.M
+		pusherr := sess.DB("qfw").C("wxpush").Pipe([]bson.M{bson.M{"$match": bson.M{"l_date": bson.M{"$gte": d.Unix()}}},
+			bson.M{"$group": bson.M{"_id": "sum", "totalAmount": bson.M{"$sum": "$i_size"}}}}).One(&res)
+		if pusherr != nil {
+			log.Println("统计今天的推送次数出错:", pusherr)
+		} else {
+			temp := util.IntAll(res["totalAmount"])
+			if temp > 0 {
+				mongodb.Update("swordfish_index", bson.M{"_id": (*one)["_id"].(bson.ObjectId)}, bson.M{
+					"$set": bson.M{
+						"i_bidtoday": temp,
+					},
+				}, false, false)
+			} else {
+				log.Println("统计今天的推送次数异常:", temp)
+			}
+		}
+	}
+}
+
+func othershow() error {
+	defer util.Catch()
+	now := time.Now()
+	obj := map[string]interface{}{}
+	today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local)
+	//是否已经统计
+	if mongodb.Count("swordfish_index", map[string]interface{}{
+		"l_timestamp": map[string]interface{}{
+			"$gte": today.Unix(),
+		},
+	}) > 0 {
+		log.Println("已经统计当天剑鱼首页需要的数据,跳过")
+		return nil
+	} else {
+		log.Println("开始统计")
+	}
+	obj["i_bidtoday"] = 0
+	//查询政府网站
+	i_govsite, goverr := mongodb.CountByErr("bidurlinfo", map[string]interface{}{"i_type": 1})
+	if goverr != nil {
+		log.Println("统计政府网站出错:", goverr)
+		return goverr
+	}
+	obj["i_govsite"] = i_govsite
+	//查询企业网站
+	i_entsite, enterr := mongodb.CountByErr("bidurlinfo", map[string]interface{}{"i_type": 0})
+	obj["i_entsite"] = i_entsite
+	if enterr != nil {
+		log.Println("统计企业网站出错:", enterr)
+		return enterr
+	}
+	//查询剑鱼用户
+	i_user, userr := mongodb.CountByErr("user", nil)
+	obj["i_user"] = i_user
+	if userr != nil {
+		log.Println("统计剑鱼用户出错:", userr)
+		return userr
+	}
+	//最近30天
+	monthDate := time.Date(now.Year(), now.Month()-1, now.Day(), 0, 0, 0, 0, time.Local)
+	i_bidmonth, montherr := mongodb.CountByErr("bidding", map[string]interface{}{
+		"comeintime": map[string]interface{}{
+			"$gte": monthDate.Unix(),
+		},
+	})
+	obj["i_bidmonth"] = i_bidmonth
+	if montherr != nil {
+		log.Println("统计最近30天出错:", montherr)
+		return montherr
+	}
+	//推送的信息
+	//查询上次的统计结果
+	lastlist, lastok := mongodb.Find("swordfish_index", nil, `{"l_timestamp":-1}`, `{"l_timestamp":1,"i_push":1}`, false, 0, 1)
+	if lastok && lastlist != nil && len(*lastlist) == 1 {
+		newRecord := (*lastlist)[0]
+		lastTime, _ := newRecord["l_timestamp"].(int64)
+		if lastTime == 0 {
+			log.Println("统计推送的信息出错:没有获取到上一次统计时间")
+			return errors.New("error")
+		}
+		lastPushCount := util.IntAll(newRecord["i_push"])
+		//
+		sess := mongodb.GetMgoConn()
+		defer mongodb.DestoryMongoConn(sess)
+		var res bson.M
+		log.Println(lastTime)
+		pusherr := sess.DB("qfw").C("wxpush").Pipe([]bson.M{bson.M{"$match": bson.M{"l_date": bson.M{"$gte": lastTime}}},
+			bson.M{"$group": bson.M{"_id": "sum", "totalAmount": bson.M{"$sum": "$i_size"}}}}).One(&res)
+		if pusherr != nil {
+			log.Println("统计推送的信息出错:", pusherr)
+			return pusherr
+		} else {
+			obj["i_push"] = util.IntAll(res["totalAmount"]) + lastPushCount
+		}
+	} else {
+		log.Println("统计推送的信息出错:没有获取到上一次统计数据")
+		return errors.New("error")
+	}
+	//所有信息
+	i_bidall, allerr := mongodb.CountByErr("bidding", nil)
+	if allerr != nil {
+		log.Println("统计所有信息出错:", allerr)
+		return allerr
+	}
+	obj["i_bidall"] = i_bidall
+	obj["l_timestamp"] = time.Now().Unix()
+	mongodb.Save("swordfish_index", obj)
+	return nil
+}

+ 131 - 0
src/jfw/timetask/followtimetask.go

@@ -0,0 +1,131 @@
+package timetask
+
+import (
+	"fmt"
+	config "jfw/config"
+	tools "jfw/tools"
+	"qfw/util"
+	rpc "qfw/util/rpc"
+	"strconv"
+	"strings"
+	"time"
+
+	"gopkg.in/mgo.v2/bson"
+)
+
+var mongodb = tools.MQFW
+var followLimitDay int
+var se util.SimpleEncrypt
+
+func init() {
+	se = util.SimpleEncrypt{Key: "topnet"}
+	followLimitDay = util.IntAllDef(config.Sysconfig["followLimitDay"], 30)
+}
+func followtimetask() {
+	for {
+		time.Sleep(2 * time.Second)
+		now := time.Now()
+		//每小时
+		tiptnext := time.Date(now.Year(), now.Month(), now.Day(), now.Hour()+1, 0, 0, 0, now.Location())
+		tiptimer := time.NewTimer(tiptnext.Sub(now)) //提醒
+		select {
+		case <-tiptimer.C:
+			tip(tiptnext)
+		}
+	}
+
+}
+
+//开标提醒
+func tip(remindtime time.Time) {
+	defer util.Catch()
+	datas, ok := mongodb.Find("follow_project", bson.M{
+		"l_bidopentime": bson.M{"$exists": 1},
+		"i_remind":      1,
+		"$or":           []bson.M{bson.M{"toptype": "招标"}, bson.M{"i_source": 2}},
+		"l_remindtime":  remindtime.Unix(),
+	}, `{"l_bidopentime":1}`, `{"s_projectname":1,"l_bidopentime":1,"s_openid":1,"s_userid":1,"l_updatetime":1}`, false, -1, -1)
+	if !ok || datas == nil || len(*datas) == 0 {
+		return
+	}
+	var ids []bson.ObjectId
+	var tips []bson.M
+	for _, v := range *datas {
+		_id := (v["_id"].(bson.ObjectId)).Hex()
+		openId, ok := v["s_openid"].(string)
+		l_bidopentime, ok := v["l_bidopentime"].(int64)
+		l_updatetime, ok := v["l_updatetime"].(int64)
+		projectname, ok := v["s_projectname"].(string)
+		userid, _ := v["s_userid"].(string)
+		user, ok := mongodb.FindById("user", userid, `{"_id":-1,"s_nickname":1}`)
+		if !ok && user == nil {
+			continue
+		}
+		if !ok || openId == "" || l_bidopentime == 0 || projectname == "" {
+			continue
+		}
+		nickname, _ := (*user)["s_nickname"].(string) //用户昵称
+		var result rpc.RpcResult = "N"                //发送模板消息返回结果
+		var followTipMsg = config.Sysconfig["followTipMsg"].(map[string]interface{})
+		//开标时间
+		bot := time.Unix(l_bidopentime, 0)
+		//今天
+		now := time.Now()
+		//明天
+		tomorrow := now.AddDate(0, 0, 1)
+		//开标时间格式化
+		bidopen := util.FormatDate(&bot, "2006-1-2 15:04")
+		//年月日
+		bidopendate := strings.Split(bidopen, " ")[0]
+		//时分
+		bidopentime := strings.Split(bidopen, " ")[1] + " "
+		//去掉小时前的“0”
+		if strings.HasPrefix(bidopentime, "0") {
+			bidopentime = util.SubString(bidopentime, 1, 5)
+		}
+		//如果是今天
+		if now.Year() == bot.Year() && now.Month() == bot.Month() && now.Day() == bot.Day() {
+			bidopen = "今天 " + bidopentime
+		} else if tomorrow.Year() == bot.Year() && tomorrow.Month() == bot.Month() && tomorrow.Day() == bot.Day() { //如果是明天
+			bidopen = "明天 " + bidopentime
+		} else { //如果是其他时间
+			bidopen = " " + bidopendate + " " + bidopentime
+		}
+		//用户设置提醒的时间
+		ut := time.Unix(l_updatetime, 0)
+		//开标时间格式化
+		updatetime := util.FormatDate(&ut, "2006-1-2 15:04")
+		//年月日
+		utdate := strings.Split(updatetime, " ")[0]
+		//时分
+		uttime := strings.Split(updatetime, " ")[1]
+		//去掉小时前的“0”
+		if strings.HasPrefix(uttime, "0") {
+			uttime = util.SubString(uttime, 1, 4)
+		}
+		//发送模板消息
+		var title = followTipMsg["title"].(string)
+		title = fmt.Sprintf(title, projectname, bidopen)
+		err := tools.SendBidOpenMsg(&rpc.NotifyMsg{
+			Openid:  openId,
+			Title:   title,
+			Detail:  projectname,           //项目名称
+			Service: nickname,              //用户自己的昵称
+			Date:    utdate + " " + uttime, //用户设置提醒的时间
+			Remark:  followTipMsg["remark"].(string),
+			Url:     config.Sysconfig["webdomain"].(string) + "/front/sess/" + se.EncodeString(openId+",uid,"+strconv.Itoa(int(time.Now().Unix()))+",followset") + "__list__" + util.EncodeArticleId2ByCheck(_id),
+		}, &result)
+		ids = append(ids, v["_id"].(bson.ObjectId))
+		var s_err string
+		if err != nil {
+			s_err = err.Error()
+		}
+		tips = append(tips, bson.M{"l_time": time.Now().Unix(), "s_status": result, "s_error": s_err})
+	}
+	//记录提醒日志
+	if len(ids) > 0 {
+		mongodb.Update("follow_project", bson.M{"_id": bson.M{"$in": ids}}, bson.M{
+			"$pushAll": bson.M{"a_tip": tips},
+		}, false, false)
+	}
+}

+ 7 - 0
src/jfw/timetask/timetask.go

@@ -0,0 +1,7 @@
+package timetask
+
+func TaskService() {
+	//关注提醒和自动删除关注定时任务
+	go followtimetask()
+	go fishindex()
+}

+ 76 - 0
src/jfw/tools/checkholiday.go

@@ -0,0 +1,76 @@
+package tools
+
+import (
+	"time"
+	"fmt"
+	"log"
+	"io/ioutil"
+	"net/http"
+	"sync"
+	"qfw/util"
+)
+type Holidy struct{
+	isHolidy bool
+	date string
+}
+
+const(
+	URL="http://apis.baidu.com/xiaogg/holiday/holiday?d=%s"
+	KEY="188da6713cec8bf6287ee14e4ddcee26"
+)
+
+var Now=Holidy{}
+var Lock =&sync.Mutex{}
+
+func CheckNowHoliday() bool{
+	_now:=time.Now().Format("20060102")
+	if Now.date==_now{
+		return Now.isHolidy
+	}else{
+		return GetNowHoliday(_now)
+	}
+}
+
+func GetNowHoliday(_now string) bool{
+	b1,b2:=GetHolidy(_now)
+	if b2{		
+		Lock.Lock()
+		Now.date=_now
+		Now.isHolidy=b1
+		Lock.Unlock()
+	}	
+	return b1
+}
+
+func GetHolidy(yyyymmdd string) (bool,bool){
+	m,b:=GetUrl(yyyymmdd)
+	if b {
+		if m=="1"||m=="2"{
+			return true,true
+		}
+	}
+	return false,b
+}
+
+func GetUrl(date string) (m string,res bool) {
+	defer util.Catch()
+	req,err:=http.NewRequest("GET",fmt.Sprintf(URL,date),nil)
+	if err!=nil{
+		return
+	}
+	req.Header.Add("apikey",KEY)
+	resp,err:=http.DefaultClient.Do(req)
+	defer resp.Body.Close()
+	if err == nil {
+		bs, err:= ioutil.ReadAll(resp.Body)
+		if len(bs)==1{
+			m=string(bs)
+		}
+		if err != nil {
+			log.Println("err:",err)
+		}else{
+			res=true
+		}
+	}
+	return
+}

+ 15 - 0
src/jfw/tools/checkwxbrowser.go

@@ -0,0 +1,15 @@
+package tools
+
+import (
+	"net/http"
+	"strings"
+)
+
+//判断是否是微信访问
+func CheckWxBrowser(Request *http.Request) bool {
+	if strings.Index(Request.UserAgent(), "MicroMessenger") > -1 || strings.Index(Request.UserAgent(), "Wechat") > -1 {
+		return true
+	} else {
+		return false
+	}
+}

+ 83 - 0
src/jfw/tools/extractarea.go

@@ -0,0 +1,83 @@
+package tools
+
+import (
+	"fmt"
+	"qfw/util"
+)
+
+var CityConfig map[string]interface{}
+var AreaGet DFA //敏感词
+
+func init() {
+	util.ReadConfig("./city.json", &CityConfig)
+	city, _ := CityConfig["city"].([]interface{})
+	citys := []string{}
+	for _, v := range city {
+		citys = append(citys, fmt.Sprint(v))
+	}
+	AreaGet = DFA{}
+	AreaGet.AddWord(citys...)
+}
+
+type DFA struct {
+	Link map[string]interface{}
+}
+
+func DealString(title string) bool {
+	return AreaGet.CheckSensitiveWord(title)
+}
+
+func (d *DFA) AddWordAll(haskey bool, keys ...string) {
+	if d.Link == nil {
+		d.Link = make(map[string]interface{})
+	}
+	for _, key := range keys {
+		nowMap := &d.Link
+		for i := 0; i < len(key); i++ {
+			kc := key[i : i+1]
+			if v, ok := (*nowMap)[kc]; ok {
+				nowMap, _ = v.(*map[string]interface{})
+			} else {
+				newMap := map[string]interface{}{}
+				newMap["YN"] = "0"
+				(*nowMap)[kc] = &newMap
+				nowMap = &newMap
+			}
+			if i == len(key)-1 {
+				(*nowMap)["YN"] = "1"
+			}
+		}
+	}
+}
+
+func (d *DFA) AddWord(keys ...string) {
+	d.AddWordAll(true, keys...)
+}
+
+//适合一次查找
+func (d *DFA) CheckSensitiveWord(src string) bool {
+	pos := 0
+	nowMap := &d.Link
+	res := false
+	for i := 0; i < len(src); i++ {
+		word := src[i : i+1]
+		nowMap, _ = (*nowMap)[word].(*map[string]interface{})
+		if nowMap != nil { // 存在,则判断是否为最后一个
+			if pos == 0 {
+				pos = i
+			}
+			if "1" == util.ObjToString((*nowMap)["YN"]) { // 如果为最后一个匹配规则,结束循环,返回匹配标识数
+				res = true
+				pos = 0
+				break
+			}
+		} else {
+			nowMap = &d.Link
+			if pos > 0 {
+				i = pos
+				pos = 0
+			}
+		}
+	}
+	return res
+}

+ 18 - 0
src/jfw/tools/mongo.go

@@ -0,0 +1,18 @@
+package tools
+
+import (
+	. "jfw/config"
+	"qfw/util"
+	"qfw/util/mongodb"
+)
+
+var MQFW mongodb.MongodbSim
+
+func init() {
+	MQFW = mongodb.MongodbSim{
+		MongodbAddr: Sysconfig["mongodbServers"].(string),
+		Size:        util.IntAll(Sysconfig["mongodbPoolSize"]),
+		DbName:      Sysconfig["mongodbName"].(string),
+	}
+	MQFW.InitPool()
+}

+ 136 - 0
src/jfw/tools/rpccall.go

@@ -0,0 +1,136 @@
+package tools
+
+import (
+	"encoding/json"
+	config "jfw/config"
+	"log"
+	"net/rpc"
+	"qfw/util"
+	qrpc "qfw/util/rpc"
+)
+
+var rpcserver string
+var followPushRpcServer string
+
+func init() {
+	rpcserver = config.Sysconfig["weixinrpc"].(string)
+	followPushRpcServer = config.Sysconfig["followPushRpc"].(string)
+}
+
+//发送管理员模板消息
+func SendBidOpenMsg(p *qrpc.NotifyMsg, repl *qrpc.RpcResult) (err error) {
+	util.Try(func() {
+		client, e := rpc.DialHTTP("tcp", rpcserver)
+		defer client.Close()
+		if e != nil {
+			err = e
+			log.Println(p.Openid + "---" + err.Error())
+			return
+		}
+		err = client.Call("WeiXinRpc.SendBidOpenMsg", p, &repl)
+		if err != nil {
+			log.Println(p.Openid + "---" + err.Error())
+		}
+	}, func(e interface{}) {})
+	return
+}
+
+//项目更新推送
+func FollowPush(p *qrpc.FollowPush) (repls []*map[string]interface{}, err error) {
+	util.Try(func() {
+		client, e := rpc.DialHTTP("tcp", followPushRpcServer)
+		defer client.Close()
+		if e != nil {
+			err = e
+			log.Println(err.Error())
+			return
+		}
+		var repl []byte
+		err = client.Call("FollowPushRpc.FollowPush", p, &repl)
+		if err == nil && repl != nil && len(repl) > 0 {
+			var mp []*map[string]interface{}
+			json.Unmarshal(repl, &mp)
+			repls = mp
+		}
+		if err != nil {
+			log.Println(err.Error())
+		}
+	}, func(e interface{}) {})
+	return
+}
+
+//返回项目更新
+func FollowPushBack(p *qrpc.FollowPush) (err error) {
+	util.Try(func() {
+		client, e := rpc.DialHTTP("tcp", followPushRpcServer)
+		defer client.Close()
+		if e != nil {
+			err = e
+			log.Println(err.Error())
+			return
+		}
+		var repl []byte
+		err = client.Call("FollowPushRpc.FollowPush", p, &repl)
+		if err != nil {
+			log.Println(err.Error())
+		}
+	}, func(e interface{}) {})
+	return
+}
+
+//保存不正常用户
+func SaveAbnormal(openid string) {
+	util.Try(func() {
+		client, err := rpc.DialHTTP("tcp", rpcserver)
+		defer client.Close()
+		if err != nil {
+			log.Println(err.Error())
+			return
+		}
+		var repl string
+		err = client.Call("WeiXinRpc.SaveAbnormal", openid, &repl)
+		if err != nil {
+			log.Println(err.Error())
+		}
+	}, func(e interface{}) {})
+}
+
+//分享二维码图片
+func GetShareQR(url uint32) string {
+	var ret string
+	util.Try(func() {
+		client, err := rpc.DialHTTP("tcp", rpcserver)
+		defer client.Close()
+		if err != nil {
+			log.Println(err.Error())
+			return
+		}
+		err = client.Call("WeiXinRpc.GetShareQR", url, &ret)
+		if err != nil {
+			log.Println(err.Error())
+		}
+	}, func(e interface{}) {})
+	return ret
+}
+
+//取得预生成订单编号,我们的系统要控制下,别订单重复了
+func GetPrepayId(param map[string]string) (res *map[string]interface{}, e error) {
+	util.Try(func() {
+		client, err := rpc.DialHTTP("tcp", rpcserver)
+		defer client.Close()
+		if err != nil {
+			e = err
+			log.Println(err.Error())
+			return
+		}
+		var ret []byte
+		err = client.Call("WeiXinRpc.GetPrepayId", param, &ret)
+		if err != nil {
+			e = err
+			log.Println(err.Error())
+		} else {
+			json.Unmarshal(ret, &res)
+		}
+	}, func(e interface{}) {})
+	return
+}

+ 2 - 0
src/jfw/tools/tools.go

@@ -0,0 +1,2 @@
+package tools
+

+ 11 - 0
src/jfw/tools/tools_test.go

@@ -0,0 +1,11 @@
+package tools
+
+import (
+	"log"
+	"testing"
+)
+
+func Test_init(t *testing.T) {
+	s := EncodeArticleId("34567", "78910")
+	log.Println(s)
+}

+ 26 - 0
src/jfw/weixin/src/apiclient_cert.pem

@@ -0,0 +1,26 @@
+-----BEGIN CERTIFICATE-----
+MIIEbjCCA9egAwIBAgIDC653MA0GCSqGSIb3DQEBBQUAMIGKMQswCQYDVQQGEwJD
+TjESMBAGA1UECBMJR3Vhbmdkb25nMREwDwYDVQQHEwhTaGVuemhlbjEQMA4GA1UE
+ChMHVGVuY2VudDEMMAoGA1UECxMDV1hHMRMwEQYDVQQDEwpNbXBheW1jaENBMR8w
+HQYJKoZIhvcNAQkBFhBtbXBheW1jaEB0ZW5jZW50MB4XDTE1MTIwNDA3NTAwNFoX
+DTI1MTIwMTA3NTAwNFowgZ4xCzAJBgNVBAYTAkNOMRIwEAYDVQQIEwlHdWFuZ2Rv
+bmcxETAPBgNVBAcTCFNoZW56aGVuMRAwDgYDVQQKEwdUZW5jZW50MQ4wDAYDVQQL
+EwVNTVBheTEzMDEGA1UEAxQq5YyX5Lqs5ouT5pmu5Liw6IGU5L+h5oGv5bel56iL
+5pyJ6ZmQ5YWs5Y+4MREwDwYDVQQEEwgxMDk3MjUwNDCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBALeEWQ8/dHcEDQxEIEhcQyuJiZHDkdTC+4YWnXN/Mlqj
+yg/Zt5AkYfCeHXTXv8DWvJ8r+7FiyOkl9JwmNfce384Y40GnfFr1l4C9ZODz6Hzw
+hdHnaae8eClX3EGPmZc6NPbA+2jVTHOUXJ+yuFcqs9QltKEMXZpbh+VPpHS2Kk//
+xvvG+sBZdIZ8+0GCX0pu4fiIsSR/tP/u7XQ0sMP8iFB7SjKgVbe1v2/RifDVaAAk
+kcW0g25Vmckwze7koZtMnqCQtr9guc/7PnuJc7Pce9vNZlmIwLgbZ9eRvJBkeywQ
+H0WjeCrbEpj37ciuokgWOXjg0qRpkoFJFgZUjq2r250CAwEAAaOCAUYwggFCMAkG
+A1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHSJDRVMtQ0EgR2VuZXJhdGUgQ2VydGlm
+aWNhdGUiMB0GA1UdDgQWBBSfBWJzPVwgiYFjnl1yBBnyPRVQyTCBvwYDVR0jBIG3
+MIG0gBQ+BSb2ImK0FVuIzWR+sNRip+WGdKGBkKSBjTCBijELMAkGA1UEBhMCQ04x
+EjAQBgNVBAgTCUd1YW5nZG9uZzERMA8GA1UEBxMIU2hlbnpoZW4xEDAOBgNVBAoT
+B1RlbmNlbnQxDDAKBgNVBAsTA1dYRzETMBEGA1UEAxMKTW1wYXltY2hDQTEfMB0G
+CSqGSIb3DQEJARYQbW1wYXltY2hAdGVuY2VudIIJALtUlyu8AOhXMA4GA1UdDwEB
+/wQEAwIGwDAWBgNVHSUBAf8EDDAKBggrBgEFBQcDAjANBgkqhkiG9w0BAQUFAAOB
+gQAA2+iJKKkwFYGgca6+HVajCUCBSgWpU1uKleraubrF1DiUOJMFAJoJdbArId5V
+ksEO9O2zCHX7sn/kz0CL43e2NE9ejdk641GWJ1LaNHGfd/tvbgPch/cegaz6AKjn
+o6w0YbR6Q4LuEea0+PsBd7Y8fwrAr+96WwdXZElbi9Iu+g==
+-----END CERTIFICATE-----

+ 28 - 0
src/jfw/weixin/src/apiclient_key.pem

@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC3hFkPP3R3BA0M
+RCBIXEMriYmRw5HUwvuGFp1zfzJao8oP2beQJGHwnh1017/A1ryfK/uxYsjpJfSc
+JjX3Ht/OGONBp3xa9ZeAvWTg8+h88IXR52mnvHgpV9xBj5mXOjT2wPto1UxzlFyf
+srhXKrPUJbShDF2aW4flT6R0tipP/8b7xvrAWXSGfPtBgl9KbuH4iLEkf7T/7u10
+NLDD/IhQe0oyoFW3tb9v0Ynw1WgAJJHFtINuVZnJMM3u5KGbTJ6gkLa/YLnP+z57
+iXOz3HvbzWZZiMC4G2fXkbyQZHssEB9Fo3gq2xKY9+3IrqJIFjl44NKkaZKBSRYG
+VI6tq9udAgMBAAECggEBAIYF+9ys5Ot+Y+EPZ9wwtUV4fqWbhEcz1ulIXtjYjfew
+IoOM5wg36ecGMlEAt6Onr703K+uST1QmSgw3w7WkMKKdfmqWXLU5Itn8d03qE2Ib
+bj+rl3fOppra9ZpTu0G8Wq3SpkkbbWgDc9mVdhcCsq4+kzoCg+GU8xw/G8W4vBIL
+Z5pP/XKqtQi0Lvp5Qqjk/3L8y4TdInlKCT6VLppg9gTSXC3v3FBLxze+6bL8P9q1
+AX0JLlQpTWEBiv9XVMEVmI/AFRfNe3W0hjw3Z8Zb5EMIOXm8g5g1WRLTDkrqARVE
+4gZ/CYUGxtIRHYzoKKazJIQyNut4+QaPR9jbbWo0RKkCgYEA3tUSH0GTXcbkxUHh
+IhZT5rwvgtMA3BtfqKLAaCgBEZSbQpKQh4FDO+w+IBc1UVk1SCq0BuYweGZPvDEL
+CfQiNFNptRqzeyvAiYLL2mpaHcMZU7/PQBkAljDStwQkwJVvK6X/QcpN7Kr+yuPL
+RdMOe3umdGEGYULf/Z+9BmjRcEMCgYEA0tUvAHkBzbAsTsftVZ/64yUtK0V6kmJD
+J2AlFZMCbPqfyVJl81T3cUcEXB16t8hAqWlPDT1GmCs0ceraLiRgi8xqiOQx6Ekb
+2F2OF3FvDi6LgVye+fVf2m7Z+SOVcoNXaSCdVXlmXu5/aZJkI5tA0+lm2lKp+XsK
+uHGzIVRjNp8CgYAIN9F4R6HYtoofYEOeTzZ+7vfNmlyQwY2wmXgBH2CStmlXdjJQ
+XQsOvbK42kJEIscdRz3mdzzYY+WS6jgfANr2FRFz9W0jIMT0DVfZUUzLhjN77DoH
+giSMZXaP+14joB8+e9vDIGPpU5EcCHSpuZPyJ+hGqIFkuuOp5edBwkHfIwKBgApn
+XrRlREOEKAuTLhEFnfnLgdkaypuiobG1ut6/rGT4UR48JK/HR5adbIenJMEg7p/t
+hGsg1PT5XNMqRa31OZZsde1fZV8TDH62zyY1AMfwYA34JhgrlZlP0w3KRbuq44ta
+gqkPHpAcsMji4nInXSGX6O1pGt4mxjan1bEVWvf9AoGAQ0Wyy007a4LLHDq1Q6Yy
+SkHTCRifGvp2ffS/gA0Q/1ARb+GSyRXKfHP6gj4gSJwCWuueWiKFF1QghEC8eBbd
+nAJGZ2GZpfwGudM3D/WNmKVcM+RWOIhKv0qUvX8ZTcxJU2+GWoGMQ3e6KpRKPWU9
+x4hIo8fJIfT17le0YbX7VdE=
+-----END PRIVATE KEY-----

+ 53 - 0
src/jfw/weixin/src/config.json

@@ -0,0 +1,53 @@
+{
+    "mongodbServers": "192.168.3.18:27080",
+    "mongodbPoolSize": "5",
+    "mongodbName": "qfw",
+    "redisServers": "sso=192.168.3.14:3379,other=192.168.3.14:3379",
+    "weixinport": "80",
+    "weixinrpcport": "83",
+    "webrpcport": "127.0.0.1:84",
+    "webdomain": "http://wxws.qmx.top",
+    "appid": "wx5b1c6e7cc4dac0e4",
+    "apptoken": "top2015top2015",
+    "appsecret": "b026103ffebd2291b3edb7a269612112",
+    "proxysess": "http://webws.qmx.top/front/sess/%s",
+    "welcomemsg": "用剑鱼,所有功能完全免费,\n和传统的会员制说再见!\n\n<a href='%s'>点击这里</a>设置关键词,或直接回复“订阅 关键词”,如“订阅 教学设备”,您将随时随地接收招标信息!\n\n剑鱼,让投标无限可能!",
+    "tpl_push_id": "UKlMJUfby5_IsicSXEo5I28JlqPUEI2-ODTONYyFUxI",
+    "tpl_bidopen_id": "6_aiRTWS9bqG9KIZ2WUjxWdPmlHzEispmYcDkuO9b3A",
+    "tpl_managernotify_id": "NTYrV-yHFjHzlmH_gM3rDTAYl3DPJO-EXZY1C3Ntr94",
+    "weixinAutoRpl": "如果您在使用时需要帮助,可以给剑鱼君留言;\n或者<a href='http://www.myfans.cc/30a78e9b78'>点击这里</a>,进入【剑鱼招标社区】与剑鱼粉丝一起交流!\n\n您也可以点击下方菜单搜索、订阅招标信息;\n<a href='%s'>点击这里</a>设置关键词,或直接回复“订阅 关键词”,如“订阅 教学设备”,随时随地接收招标信息!",
+    "autoReplay": {
+        "title": "剑鱼招标订阅,和丟标说拜拜",
+        "url": "https://mp.weixin.qq.com/s?__biz=MzIyNTM1NDUyNw==&mid=100000001&idx=1&sn=58e6008f0577d529a56c9ed78724decf&scene=1&srcid=0628FMQzB61orhqGhfw7fOqT&key=77421cf58af4a653210128f66c0cd4486112d12cb69b77c0c8f926d725679273cf44f733f8993123ef0295c72b8946ba&ascene=0&uin=NDMwMjg4NTU1&devicetype",
+        "picUrl": "http://jy.qmx.top/images/jyzbdy.jpg",
+        "description": "呜 点击蓝字 “剑鱼招标订阅” ,可直接进入哦!"
+    },
+    "fastSubscribe": {
+        "skill": "http://mp.weixin.qq.com/mp/homepage?__biz=MzIyNTM1NDUyNw==&hid=3&sn=badf2d7da08654c58b58169e773f58f0#wechat_redirect",
+        "success": "您已成功订阅该关键词“%s”。\n您可点击下方菜单“招标订阅”查看修改订阅关键词。\n<a href='%s'>点击这里,获得更多订阅技巧。</a>",
+        "fail": "对不起,您已订阅十个关键词,请点击下方菜单“招标订阅”修改订阅关键词\n<a href='%s'>点击这里,获得更多订阅技巧。</a>",
+        "exists": "您已订阅过关键词“%s”,\n您可点击下方菜单“招标订阅”查看修改订阅关键词;\n<a href='%s'>点击这里,获得更多订阅技巧。</a>",
+        "firsttake": "您已成功订阅该关键词“%s”。\n<a href='%s'>点击这里,获得更多订阅技巧。</a>",
+        "hints": [
+            "您已成功订阅该关键词“%s”。\n您订阅的关键词较多,可能会收不到推送信息。<a href='%s'>点击这里,获得更多订阅技巧。</a>",
+            "您已成功订阅该关键词“%s”。\n您订阅的关键词字数过多,可能会收不到推送信息。<a href='%s'>点击这里,获得更多订阅技巧。</a>",
+            "您已成功订阅该关键词“%s”。\n您订阅的关键词包含特殊符号,可能会收不到推送信息。<a href='%s'>点击这里,获得更多订阅技巧。</a>",
+            "要想订阅招标信息?<a href='http://mp.weixin.qq.com/s/0OkizTK26I9mF-2W4moMxQ'>点击这里,了解如何订阅招标信息。</a>"
+        ]
+    },
+    "wufu": {
+        "title": "分分钟钟凑齐福",
+        "url": "https://www.zhaobiao.info/active/saofu",
+        "picUrl": "https://www.zhaobiao.info/images/saofu/wufu.jpg",
+        "description": "为了鱼粉们集五福,小编也是操碎了心……"
+    },
+    "headimage": "D:/goproject/jianyu/src/web/staticres",
+    "pchints": {
+        "key_success": "已为您添加订阅关键词“%s”。<a href='%s'>点击此处查看您所有的订阅关键词。</a>",
+        "key_fail": "抱歉!无法添加订阅关键词“%s”,<a href='%s'>因为您订阅的关键词,达到了系统上限,您可以点击此处删除多余的关键词,然后重新添加。</a>",
+        "project_success": "已为您添加关注项目 “%s”。<a href='%s'>点击此处查看该项目的详细信息。</a>",
+        "project_repeat": "您已关注过此项目 “%s”。<a href='%s'>点击此处查看我关注的项目列表信息。</a>",
+        "project_fail": "抱歉!无法添加项目“%s”,<a href='%s'>因为您关注的项目,达到了系统上限,您可以点击此处删除多余的项目,然后重新添加。</a>"
+    },
+	"influxdb":"http://jianyu:Topnet@20150501@wxrz.qmx.top:8086"
+}

+ 11 - 0
src/jfw/weixin/src/config/config.go

@@ -0,0 +1,11 @@
+package config
+
+import (
+	"qfw/util"
+)
+
+var Sysconfig map[string]interface{}
+
+func init() {
+	util.ReadConfig(&Sysconfig)
+}

+ 22 - 0
src/jfw/weixin/src/config/zqluckdraw.go

@@ -0,0 +1,22 @@
+package config
+
+import (
+	"qfw/util"
+)
+
+//系统配置
+type zqLuckdraw struct {
+	Welcomemsg      string                 `json:"welcomemsg"`
+	StartDate       int64                  `json:"startDate"`
+	EndDate         int64                  `json:"endDate"`
+	Replay1         map[string]interface{} `json:"replay1"`
+	Replay2         string                 `json:"replay2"`
+	ClaimsStartDate int64                  `json:"claimsStartDate"`
+	ClaimsEndDate   int64                  `json:"claimsEndDate"`
+}
+
+var ZqLuckdraw zqLuckdraw
+
+func init() {
+	util.ReadConfig("./zqluckdraw.json", &ZqLuckdraw)
+}

+ 1189 - 0
src/jfw/weixin/src/github.com/wizjin/weixin/weixin.go

@@ -0,0 +1,1189 @@
+package weixin
+
+import (
+	"bytes"
+	"crypto/sha1"
+	"encoding/json"
+	"encoding/xml"
+	"errors"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"log"
+	"mime/multipart"
+	"net/http"
+	"net/url"
+	"os"
+	"path/filepath"
+	"regexp"
+	"sort"
+	"time"
+)
+
+const (
+	// Event type
+	msgEvent          = "event"
+	EventSubscribe    = "subscribe"
+	EventUnsubscribe  = "unsubscribe"
+	EventScan         = "SCAN"
+	EventView         = "VIEW"
+	EventClick        = "CLICK"
+	EventLocation     = "LOCATION"
+	EventTemplateSent = "TEMPLATESENDJOBFINISH"
+
+	// Message type
+	MsgTypeDefault           = ".*"
+	MsgTypeText              = "text"
+	MsgTypeImage             = "image"
+	MsgTypeVoice             = "voice"
+	MsgTypeVideo             = "video"
+	MsgTypeShortVideo        = "shortvideo"
+	MsgTypeLocation          = "location"
+	MsgTypeLink              = "link"
+	MsgTypeEvent             = msgEvent + ".*"
+	MsgTypeEventSubscribe    = msgEvent + "\\." + EventSubscribe
+	MsgTypeEventUnsubscribe  = msgEvent + "\\." + EventUnsubscribe
+	MsgTypeEventScan         = msgEvent + "\\." + EventScan
+	MsgTypeEventView         = msgEvent + "\\." + EventView
+	MsgTypeEventClick        = msgEvent + "\\." + EventClick
+	MsgTypeEventLocation     = msgEvent + "\\." + EventLocation
+	MsgTypeEventTemplateSent = msgEvent + "\\." + EventTemplateSent
+
+	// Media type
+	MediaTypeImage = "image"
+	MediaTypeVoice = "voice"
+	MediaTypeVideo = "video"
+	MediaTypeThumb = "thumb"
+	// Button type
+	MenuButtonTypeKey             = "click"
+	MenuButtonTypeUrl             = "view"
+	MenuButtonTypeScancodePush    = "scancode_push"
+	MenuButtonTypeScancodeWaitmsg = "scancode_waitmsg"
+	MenuButtonTypePicSysphoto     = "pic_sysphoto"
+	MenuButtonTypePicPhotoOrAlbum = "pic_photo_or_album"
+	MenuButtonTypePicWeixin       = "pic_weixin"
+	MenuButtonTypeLocationSelect  = "location_select"
+	MenuButtonTypeMediaId         = "media_id"
+	MenuButtonTypeViewLimited     = "view_limited"
+	// Template Status
+	TemplateSentStatusSuccess      = "success"
+	TemplateSentStatusUserBlock    = "failed:user block"
+	TemplateSentStatusSystemFailed = "failed:system failed"
+	// Redirect Scope
+	RedirectURLScopeBasic    = "snsapi_base"
+	RedirectURLScopeUserInfo = "snsapi_userinfo"
+	// Weixin host URL
+	weixinHost               = "https://api.weixin.qq.com/cgi-bin"
+	weixinQRScene            = "https://api.weixin.qq.com/cgi-bin/qrcode"
+	weixinShowQRScene        = "https://mp.weixin.qq.com/cgi-bin/showqrcode"
+	weixinShortURL           = "https://api.weixin.qq.com/cgi-bin/shorturl"
+	weixinUserInfo           = "https://api.weixin.qq.com/cgi-bin/user/info"
+	weixinFileURL            = "http://file.api.weixin.qq.com/cgi-bin/media"
+	weixinTemplate           = "https://api.weixin.qq.com/cgi-bin/template"
+	weixinRedirectURL        = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s#wechat_redirect"
+	weixinUserAccessTokenURL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code"
+	weixinJsApiTicketURL     = "https://api.weixin.qq.com/cgi-bin/ticket/getticket"
+	// Max retry count
+	retryMaxN = 3
+	// Reply format
+	replyText               = "<xml>%s<MsgType><![CDATA[text]]></MsgType><Content><![CDATA[%s]]></Content></xml>"
+	replyImage              = "<xml>%s<MsgType><![CDATA[image]]></MsgType><Image><MediaId><![CDATA[%s]]></MediaId></Image></xml>"
+	replyVoice              = "<xml>%s<MsgType><![CDATA[voice]]></MsgType><Voice><MediaId><![CDATA[%s]]></MediaId></Voice></xml>"
+	replyVideo              = "<xml>%s<MsgType><![CDATA[video]]></MsgType><Video><MediaId><![CDATA[%s]]></MediaId><Title><![CDATA[%s]]></Title><Description><![CDATA[%s]]></Description></Video></xml>"
+	replyMusic              = "<xml>%s<MsgType><![CDATA[music]]></MsgType><Music><Title><![CDATA[%s]]></Title><Description><![CDATA[%s]]></Description><MusicUrl><![CDATA[%s]]></MusicUrl><HQMusicUrl><![CDATA[%s]]></HQMusicUrl><ThumbMediaId><![CDATA[%s]]></ThumbMediaId></Music></xml>"
+	replyNews               = "<xml>%s<MsgType><![CDATA[news]]></MsgType><ArticleCount>%d</ArticleCount><Articles>%s</Articles></xml>"
+	replyHeader             = "<ToUserName><![CDATA[%s]]></ToUserName><FromUserName><![CDATA[%s]]></FromUserName><CreateTime>%d</CreateTime>"
+	replyArticle            = "<item><Title><![CDATA[%s]]></Title> <Description><![CDATA[%s]]></Description><PicUrl><![CDATA[%s]]></PicUrl><Url><![CDATA[%s]]></Url></item>"
+	transferCustomerService = "<xml>" + replyHeader + "<MsgType><![CDATA[transfer_customer_service]]></MsgType></xml>"
+	reply2CustomerService   = "<xml>%s<MsgType><![CDATA[transfer_customer_service]]></MsgType></xml>"
+
+	// QR scene request
+	requestQRScene         = `{"expire_seconds":%d,"action_name":"QR_SCENE","action_info":{"scene":{"scene_id":%d}}}`
+	requestQRLimitScene    = `{"action_name":"QR_LIMIT_SCENE","action_info":{"scene":{"scene_id":%d}}}`
+	requestQRLimitSceneStr = `{"action_name":"QR_LIMIT_SCENE","action_info":{"scene":{"scene_str":%s}}}`
+)
+
+// Common message header
+type MessageHeader struct {
+	ToUserName   string
+	FromUserName string
+	CreateTime   int
+	MsgType      string
+}
+
+// Weixin request
+type Request struct {
+	MessageHeader
+	MsgId        int64
+	Content      string
+	PicUrl       string
+	MediaId      string
+	Format       string
+	ThumbMediaId string
+	LocationX    float32 `xml:"Location_X"`
+	LocationY    float32 `xml:"Location_Y"`
+	Scale        float32
+	Label        string
+	Title        string
+	Description  string
+	Url          string
+	Event        string
+	EventKey     string
+	Ticket       string
+	Latitude     float32
+	Longitude    float32
+	Precision    float32
+	Recognition  string
+	Status       string
+}
+
+// Use to reply music message
+type Music struct {
+	Title        string `json:"title"`
+	Description  string `json:"description"`
+	MusicUrl     string `json:"musicurl"`
+	HQMusicUrl   string `json:"hqmusicurl"`
+	ThumbMediaId string `json:"thumb_media_id"`
+}
+
+// Use to reply news message
+type Article struct {
+	Title       string `json:"title"`
+	Description string `json:"description"`
+	PicUrl      string `json:"picurl"`
+	Url         string `json:"url"`
+}
+
+// Use to store QR code
+type QRScene struct {
+	Ticket        string `json:"ticket"`
+	ExpireSeconds int    `json:"expire_seconds"`
+}
+
+// Custom Menu
+type Menu struct {
+	Buttons []MenuButton `json:"button,omitempty"`
+}
+
+type MenuButton struct {
+	Name       string       `json:"name"`
+	Type       string       `json:"type,omitempty"`
+	Key        string       `json:"key,omitempty"`
+	Url        string       `json:"url,omitempty"`
+	MediaId    string       `json:"media_id,omitempty"`
+	SubButtons []MenuButton `json:"sub_button,omitempty"`
+}
+
+type UserAccessToken struct {
+	AccessToken   string `json:"access_token"`
+	RefreshToken  string `json:"refresh_token"`
+	ExpireSeconds int    `json:"expires_in"`
+	OpenId        string `json:"openid"`
+	Scope         string `json:"scope"`
+	UnionId       string `json:"unionid,omitempty"`
+}
+
+type UserInfo struct {
+	Subscribe     int    `json:"subscribe,omitempty"`
+	Language      string `json:"language,omitempty"`
+	OpenId        string `json:"openid,omitempty"`
+	UnionId       string `json:"unionid,omitempty"`
+	Nickname      string `json:"nickname,omitempty"`
+	Sex           int    `json:"sex,omitempty"`
+	City          string `json:"city,omitempty"`
+	Country       string `json:"country,omitempty"`
+	Province      string `json:"province,omitempty"`
+	HeadImageUrl  string `json:"headimgurl,omitempty"`
+	SubscribeTime int64  `json:"subscribe_time,omitempty"`
+	Remark        string `json:"remark,omitempty"`
+	GroupId       int    `json:"groupid,omitempty"`
+}
+
+type TmplData map[string]TmplItem
+type TmplItem struct {
+	Value string `json:"value,omitempty"`
+	Color string `json:"color,omitempty"`
+}
+
+// Use to output reply
+type ResponseWriter interface {
+	// Get weixin
+	GetWeixin() *Weixin
+	GetUserData() interface{}
+	// Reply message
+	ReplyOK()
+	ReplyText(text string)
+	Reply2CustomerService()
+	ReplyImage(mediaId string)
+	ReplyVoice(mediaId string)
+	ReplyVideo(mediaId string, title string, description string)
+	ReplyMusic(music *Music)
+	ReplyNews(articles []Article)
+	TransferCustomerService(serviceId string)
+	// Post message
+	PostText(text string) error
+	PostImage(mediaId string) error
+	PostVoice(mediaId string) error
+	PostVideo(mediaId string, title string, description string) error
+	PostMusic(music *Music) error
+	PostNews(articles []Article) error
+	PostTemplateMessage(templateid string, url string, data TmplData) (int32, error)
+	// Media operator
+	UploadMediaFromFile(mediaType string, filepath string) (string, error)
+	DownloadMediaToFile(mediaId string, filepath string) error
+	UploadMedia(mediaType string, filename string, reader io.Reader) (string, error)
+	DownloadMedia(mediaId string, writer io.Writer) error
+}
+
+type responseWriter struct {
+	wx           *Weixin
+	writer       http.ResponseWriter
+	toUserName   string
+	fromUserName string
+}
+
+type response struct {
+	ErrorCode    int    `json:"errcode,omitempty"`
+	ErrorMessage string `json:"errmsg,omitempty"`
+}
+
+// Callback function
+type HandlerFunc func(ResponseWriter, *Request)
+
+type route struct {
+	regex   *regexp.Regexp
+	handler HandlerFunc
+}
+
+type accessToken struct {
+	token    string
+	expires  time.Time
+	lasttime *int64
+}
+
+type jsApiTicket struct {
+	ticket  string
+	expires time.Time
+}
+
+type Weixin struct {
+	token        string
+	routes       []*route
+	tokenChan    chan accessToken
+	ticketChan   chan jsApiTicket
+	userData     interface{}
+	appId        string
+	appSecret    string
+	refreshToken bool
+}
+
+// Convert qr scene to url
+func (qr *QRScene) ToURL() string {
+	return (weixinShowQRScene + "?ticket=" + qr.Ticket)
+}
+
+// Create a Weixin instance
+func New(token string, appid string, secret string) *Weixin {
+	wx := &Weixin{}
+	wx.token = token
+	wx.appId = appid
+	wx.appSecret = secret
+	wx.refreshToken = false
+	if len(appid) > 0 && len(secret) > 0 {
+		wx.tokenChan = make(chan accessToken)
+		go createAccessToken(wx.tokenChan, appid, secret, &wx.refreshToken)
+		wx.ticketChan = make(chan jsApiTicket)
+		go createJsApiTicket(wx.tokenChan, wx.ticketChan)
+	}
+	return wx
+}
+
+func NewWithUserData(token string, appid string, secret string, userData interface{}) *Weixin {
+	wx := New(token, appid, secret)
+	wx.userData = userData
+	return wx
+}
+
+func (wx *Weixin) GetAppId() string {
+	return wx.appId
+}
+
+func (wx *Weixin) GetAppSecret() string {
+	return wx.appSecret
+}
+
+func (wx *Weixin) RefreshAccessToken() {
+	wx.refreshToken = true
+	<-wx.tokenChan
+}
+
+// Register request callback.
+func (wx *Weixin) HandleFunc(pattern string, handler HandlerFunc) {
+	regex, err := regexp.Compile(pattern)
+	if err != nil {
+		panic(err)
+		return
+	}
+	route := &route{regex, handler}
+	wx.routes = append(wx.routes, route)
+}
+
+func (wx *Weixin) GetToken() string {
+	token := <-wx.tokenChan
+	return token.token
+}
+
+// Post text message
+func (wx *Weixin) PostText(touser string, text string) error {
+	var msg struct {
+		ToUser  string `json:"touser"`
+		MsgType string `json:"msgtype"`
+		Text    struct {
+			Content string `json:"content"`
+		} `json:"text"`
+	}
+	msg.ToUser = touser
+	msg.MsgType = "text"
+	msg.Text.Content = text
+	return postMessage(wx.tokenChan, &msg)
+}
+
+// Post image message
+func (wx *Weixin) PostImage(touser string, mediaId string) error {
+	var msg struct {
+		ToUser  string `json:"touser"`
+		MsgType string `json:"msgtype"`
+		Image   struct {
+			MediaId string `json:"media_id"`
+		} `json:"image"`
+	}
+	msg.ToUser = touser
+	msg.MsgType = "image"
+	msg.Image.MediaId = mediaId
+	return postMessage(wx.tokenChan, &msg)
+}
+
+// Post voice message
+func (wx *Weixin) PostVoice(touser string, mediaId string) error {
+	var msg struct {
+		ToUser  string `json:"touser"`
+		MsgType string `json:"msgtype"`
+		Voice   struct {
+			MediaId string `json:"media_id"`
+		} `json:"voice"`
+	}
+	msg.ToUser = touser
+	msg.MsgType = "voice"
+	msg.Voice.MediaId = mediaId
+	return postMessage(wx.tokenChan, &msg)
+}
+
+// Post video message
+func (wx *Weixin) PostVideo(touser string, m string, t string, d string) error {
+	var msg struct {
+		ToUser  string `json:"touser"`
+		MsgType string `json:"msgtype"`
+		Video   struct {
+			MediaId     string `json:"media_id"`
+			Title       string `json:"title"`
+			Description string `json:"description"`
+		} `json:"video"`
+	}
+	msg.ToUser = touser
+	msg.MsgType = "video"
+	msg.Video.MediaId = m
+	msg.Video.Title = t
+	msg.Video.Description = d
+	return postMessage(wx.tokenChan, &msg)
+}
+
+// Post music message
+func (wx *Weixin) PostMusic(touser string, music *Music) error {
+	var msg struct {
+		ToUser  string `json:"touser"`
+		MsgType string `json:"msgtype"`
+		Music   *Music `json:"music"`
+	}
+	msg.ToUser = touser
+	msg.MsgType = "video"
+	msg.Music = music
+	return postMessage(wx.tokenChan, &msg)
+}
+
+// Post news message
+func (wx *Weixin) PostNews(touser string, articles []Article) error {
+	var msg struct {
+		ToUser  string `json:"touser"`
+		MsgType string `json:"msgtype"`
+		News    struct {
+			Articles []Article `json:"articles"`
+		} `json:"news"`
+	}
+	msg.ToUser = touser
+	msg.MsgType = "news"
+	msg.News.Articles = articles
+	return postMessage(wx.tokenChan, &msg)
+}
+
+// Upload media from local file
+func (wx *Weixin) UploadMediaFromFile(mediaType string, fp string) (string, error) {
+	file, err := os.Open(fp)
+	if err != nil {
+		return "", err
+	}
+	defer file.Close()
+	return wx.UploadMedia(mediaType, filepath.Base(fp), file)
+}
+
+// Download media and save to local file
+func (wx *Weixin) DownloadMediaToFile(mediaId string, fp string) error {
+	file, err := os.Create(fp)
+	if err != nil {
+		return err
+	}
+	defer file.Close()
+	return wx.DownloadMedia(mediaId, file)
+}
+
+// Upload media with media
+func (wx *Weixin) UploadMedia(mediaType string, filename string, reader io.Reader) (string, error) {
+	return uploadMedia(wx.tokenChan, mediaType, filename, reader)
+}
+
+// Download media with media
+func (wx *Weixin) DownloadMedia(mediaId string, writer io.Writer) error {
+	return downloadMedia(wx.tokenChan, mediaId, writer)
+}
+
+// Get ip list
+func (wx *Weixin) GetIpList() ([]string, error) {
+	reply, err := sendGetRequest(weixinHost+"/getcallbackip?access_token=", wx.tokenChan)
+	if err != nil {
+		return nil, err
+	}
+	var result struct {
+		IpList []string `json:"ip_list"`
+	}
+	if err := json.Unmarshal(reply, &result); err != nil {
+		return nil, err
+	}
+	return result.IpList, nil
+}
+
+// Create QR scene
+func (wx *Weixin) CreateQRScene(sceneId int, expires int) (*QRScene, error) {
+	reply, err := postRequest(weixinQRScene+"/create?access_token=", wx.tokenChan, []byte(fmt.Sprintf(requestQRScene, expires, sceneId)))
+	if err != nil {
+		return nil, err
+	}
+	var qr QRScene
+	if err := json.Unmarshal(reply, &qr); err != nil {
+		return nil, err
+	}
+	return &qr, nil
+}
+
+// Create  QR limit scene
+func (wx *Weixin) CreateQRLimitScene(sceneId int) (*QRScene, error) {
+	reply, err := postRequest(weixinQRScene+"/create?access_token=", wx.tokenChan, []byte(fmt.Sprintf(requestQRLimitScene, sceneId)))
+	if err != nil {
+		return nil, err
+	}
+	var qr QRScene
+	if err := json.Unmarshal(reply, &qr); err != nil {
+		return nil, err
+	}
+	return &qr, nil
+}
+
+// Create  QR limit sceneStr
+func (wx *Weixin) CreateQRLimitSceneStr(sceneStr string) (*QRScene, error) {
+	reply, err := postRequest(weixinQRScene+"/create?access_token=", wx.tokenChan, []byte(fmt.Sprintf(requestQRLimitSceneStr, sceneStr)))
+	if err != nil {
+		return nil, err
+	}
+	var qr QRScene
+	if err := json.Unmarshal(reply, &qr); err != nil {
+		return nil, err
+	}
+	return &qr, nil
+}
+
+// Long url to short url
+func (wx *Weixin) ShortURL(url string) (string, error) {
+	var request struct {
+		Action  string `json:"action"`
+		LongUrl string `json:"long_url"`
+	}
+	request.Action = "long2short"
+	request.LongUrl = url
+	data, err := marshal(request)
+	if err != nil {
+		return "", err
+	}
+	reply, err := postRequest(weixinShortURL+"?access_token=", wx.tokenChan, data)
+	if err != nil {
+		return "", err
+	}
+	var shortUrl struct {
+		Url string `json:"short_url"`
+	}
+	if err := json.Unmarshal(reply, &shortUrl); err != nil {
+		return "", err
+	}
+	return shortUrl.Url, nil
+}
+
+// Custom menu
+func (wx *Weixin) CreateMenu(menu *Menu) error {
+	data, err := marshal(menu)
+	if err != nil {
+		return err
+	}
+	_, err = postRequest(weixinHost+"/menu/create?access_token=", wx.tokenChan, data)
+	return err
+}
+
+func (wx *Weixin) GetMenu() (*Menu, error) {
+	reply, err := sendGetRequest(weixinHost+"/menu/get?access_token=", wx.tokenChan)
+	if err != nil {
+		return nil, err
+	}
+	var result struct {
+		MenuCtx *Menu `json:"menu"`
+	}
+	if err := json.Unmarshal(reply, &result); err != nil {
+		return nil, err
+	}
+	return result.MenuCtx, nil
+}
+
+func (wx *Weixin) DeleteMenu() error {
+	_, err := sendGetRequest(weixinHost+"/menu/delete?access_token=", wx.tokenChan)
+	return err
+}
+
+// Template
+func (wx *Weixin) SetTemplateIndustry(id1 string, id2 string) error {
+	var industry struct {
+		Id1 string `json:"industry_id1,omitempty"`
+		Id2 string `json:"industry_id2,omitempty"`
+	}
+	industry.Id1 = id1
+	industry.Id2 = id2
+	data, err := marshal(industry)
+	if err != nil {
+		return err
+	}
+	_, err = postRequest(weixinTemplate+"/api_set_industry?access_token=", wx.tokenChan, data)
+	return err
+}
+
+func (wx *Weixin) AddTemplate(shortid string) (string, error) {
+	var request struct {
+		Shortid string `json:"template_id_short,omitempty"`
+	}
+	request.Shortid = shortid
+	data, err := marshal(request)
+	if err != nil {
+		return "", err
+	}
+	reply, err := postRequest(weixinTemplate+"/api_set_industry?access_token=", wx.tokenChan, data)
+	if err != nil {
+		return "", err
+	}
+	var templateId struct {
+		Id string `json:"template_id,omitempty"`
+	}
+	if err := json.Unmarshal(reply, &templateId); err != nil {
+		return "", err
+	}
+	return templateId.Id, nil
+}
+
+func (wx *Weixin) PostTemplateMessage(touser string, templateid string, url string, data TmplData) (int32, error) {
+	var msg struct {
+		ToUser     string   `json:"touser"`
+		TemplateId string   `json:"template_id"`
+		Url        string   `json:"url,omitempty"`
+		Data       TmplData `json:"data,omitempty"`
+	}
+	msg.ToUser = touser
+	msg.TemplateId = templateid
+	msg.Url = url
+	msg.Data = data
+	msgStr, err := marshal(msg)
+	if err != nil {
+		return 0, err
+	}
+	reply, err := postRequest(weixinHost+"/message/template/send?access_token=", wx.tokenChan, msgStr)
+	if err != nil {
+		return 0, err
+	}
+	var resp struct {
+		MsgId int32 `json:"msgid,omitempty"`
+	}
+	if err := json.Unmarshal(reply, &resp); err != nil {
+		return 0, err
+	}
+	return resp.MsgId, nil
+}
+
+// Create redirect url
+func (wx *Weixin) CreateRedirectURL(urlStr string, scope string, state string) string {
+	return fmt.Sprintf(weixinRedirectURL, wx.appId, url.QueryEscape(urlStr), scope, state)
+}
+
+// Get open id
+func (wx *Weixin) GetUserAccessToken(code string) (*UserAccessToken, error) {
+	resp, err := http.Get(fmt.Sprintf(weixinUserAccessTokenURL, wx.appId, wx.appSecret, code))
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+	var res UserAccessToken
+	if err := json.Unmarshal(body, &res); err != nil {
+		return nil, err
+	}
+	return &res, nil
+}
+
+// Get user info
+func (wx *Weixin) GetUserInfo(openid string) (*UserInfo, error) {
+	reply, err := sendGetRequest(fmt.Sprintf("%s?openid=%s&lang=zh_CN&access_token=", weixinUserInfo, openid), wx.tokenChan)
+	if err != nil {
+		return nil, err
+	}
+	var result UserInfo
+	if err := json.Unmarshal(reply, &result); err != nil {
+		return nil, err
+	}
+	return &result, nil
+}
+
+//Get user list
+
+type UserList struct {
+	Total int `json:"total,omitempty"`
+	Count int `json:"count,omitempty"`
+	Data  struct {
+		OpenId []string `json:"openid"`
+	} `json:"data,omitempty"`
+	Nextid string `json:"next_openid,omitempty"`
+}
+
+func (wx *Weixin) GetUserList(openid string) (*UserList, error) {
+	reply, err := sendGetRequest(fmt.Sprintf("%s/user/get?next_openid=%s&access_token=", weixinHost, openid), wx.tokenChan)
+	if err != nil {
+		return nil, err
+	}
+	var result UserList
+	if err := json.Unmarshal(reply, &result); err != nil {
+		return nil, err
+	}
+	return &result, nil
+}
+
+func (wx *Weixin) GetJsApiTicket() (string, error) {
+	for i := 0; i < retryMaxN; i++ {
+		ticket := <-wx.ticketChan
+		if time.Since(ticket.expires).Seconds() < 0 {
+			return ticket.ticket, nil
+		}
+	}
+	return "", errors.New("Get JsApi Ticket Timeout")
+}
+
+func (wx *Weixin) JsSignature(url string, timestamp int64, noncestr string) (string, error) {
+	ticket, err := wx.GetJsApiTicket()
+	if err != nil {
+		return "", err
+	}
+	h := sha1.New()
+	h.Write([]byte(fmt.Sprintf("jsapi_ticket=%s&noncestr=%s&timestamp=%d&url=%s",
+		ticket, noncestr, timestamp, url)))
+	return fmt.Sprintf("%x", h.Sum(nil)), nil
+}
+
+// Create handler func
+func (wx *Weixin) CreateHandlerFunc(w http.ResponseWriter, r *http.Request) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		wx.ServeHTTP(w, r)
+	}
+}
+
+// Process weixin request and send response.
+func (wx *Weixin) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	if !checkSignature(wx.token, w, r) {
+		http.Error(w, "", http.StatusUnauthorized)
+		return
+	}
+	// Verify request
+	if r.Method == "GET" {
+		fmt.Fprintf(w, r.FormValue("echostr"))
+		return
+	}
+	// Process message
+	data, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		log.Println("Weixin receive message failed:", err)
+		http.Error(w, "", http.StatusBadRequest)
+	} else {
+		var msg Request
+		if err := xml.Unmarshal(data, &msg); err != nil {
+			log.Println("Weixin parse message failed:", err)
+			http.Error(w, "", http.StatusBadRequest)
+		} else {
+			wx.routeRequest(w, &msg)
+		}
+	}
+	return
+}
+
+func (wx *Weixin) routeRequest(w http.ResponseWriter, r *Request) {
+	requestPath := r.MsgType
+	if requestPath == msgEvent {
+		requestPath += "." + r.Event
+	}
+	for _, route := range wx.routes {
+		if !route.regex.MatchString(requestPath) {
+			continue
+		}
+		writer := responseWriter{}
+		writer.wx = wx
+		writer.writer = w
+		writer.toUserName = r.FromUserName
+		writer.fromUserName = r.ToUserName
+		route.handler(writer, r)
+		return
+	}
+	http.Error(w, "", http.StatusNotFound)
+	return
+}
+
+func marshal(v interface{}) ([]byte, error) {
+	data, err := json.Marshal(v)
+	if err == nil {
+		data = bytes.Replace(data, []byte("\\u003c"), []byte("<"), -1)
+		data = bytes.Replace(data, []byte("\\u003e"), []byte(">"), -1)
+		data = bytes.Replace(data, []byte("\\u0026"), []byte("&"), -1)
+	}
+	return data, err
+}
+
+func checkSignature(t string, w http.ResponseWriter, r *http.Request) bool {
+	r.ParseForm()
+	var signature string = r.FormValue("signature")
+	var timestamp string = r.FormValue("timestamp")
+	var nonce string = r.FormValue("nonce")
+	strs := sort.StringSlice{t, timestamp, nonce}
+	sort.Strings(strs)
+	var str string
+	for _, s := range strs {
+		str += s
+	}
+	h := sha1.New()
+	h.Write([]byte(str))
+	return fmt.Sprintf("%x", h.Sum(nil)) == signature
+}
+
+func authAccessToken(appid string, secret string) (string, time.Duration, int64) {
+	resp, err := http.Get(weixinHost + "/token?grant_type=client_credential&appid=" + appid + "&secret=" + secret)
+	if err != nil {
+		log.Println("Get access token failed: ", err)
+	} else {
+		defer resp.Body.Close()
+		body, err := ioutil.ReadAll(resp.Body)
+		if err != nil {
+			log.Println("Read access token failed: ", err)
+		} else {
+			var res struct {
+				AccessToken string `json:"access_token"`
+				ExpiresIn   int64  `json:"expires_in"`
+			}
+			if err := json.Unmarshal(body, &res); err != nil {
+				log.Println("Parse access token failed: ", err)
+			} else {
+				//log.Printf("AuthAccessToken token=%s expires_in=%d", res.AccessToken, res.ExpiresIn)
+				return res.AccessToken, time.Duration(res.ExpiresIn * 1000 * 1000 * 1000), time.Now().Unix()
+			}
+		}
+	}
+	return "", 0, 0
+}
+
+func getJsApiTicket(c chan accessToken) (*jsApiTicket, error) {
+	reply, err := sendGetRequest(weixinJsApiTicketURL+"?type=jsapi&access_token=", c)
+	if err != nil {
+		return nil, err
+	}
+	var res struct {
+		Ticket    string `json:"ticket"`
+		ExpiresIn int64  `json:"expires_in"`
+	}
+	if err := json.Unmarshal(reply, &res); err != nil {
+		return nil, err
+	}
+	var ticket jsApiTicket
+	ticket.ticket = res.Ticket
+	ticket.expires = time.Now().Add(time.Duration(res.ExpiresIn * 1000 * 1000 * 1000))
+	return &ticket, nil
+
+}
+
+type countTocken struct {
+	allTimes int64
+	dayTimes int64
+	day      int
+}
+
+var CountT = countTocken{0, 0, time.Now().Day()}
+
+func init() {
+	go func() {
+		for {
+			if time.Now().Day() != CountT.day {
+				CountT.dayTimes = 0
+				CountT.day = time.Now().Day()
+				log.Println("wx-tocken改变统计时间:", CountT.day, ",次数:", CountT.dayTimes)
+			}
+			time.Sleep(30 * time.Second)
+		}
+	}()
+}
+
+func createAccessToken(c chan accessToken, appid string, secret string, refresh *bool) {
+	lt := int64(0)
+	token := accessToken{"", time.Now(), &lt}
+	c <- token
+	for {
+		if *refresh || (time.Now().Unix()-*token.lasttime) > 90*60 || time.Since(token.expires).Seconds() >= 0 {
+			CountT.allTimes++
+			CountT.dayTimes++
+			log.Println("获取tocken:", CountT.dayTimes)
+			*refresh = false
+			var expires time.Duration
+			var lasttime int64
+			token.token, expires, lasttime = authAccessToken(appid, secret)
+			token.expires = time.Now().Add(expires)
+			*token.lasttime = lasttime
+		}
+		c <- token
+		if CountT.dayTimes > 100 {
+			log.Println(">>-->>获取tocken%d次...<<--<<", CountT.dayTimes)
+			time.Sleep(1 * time.Minute)
+		}
+	}
+}
+
+func createJsApiTicket(cin chan accessToken, c chan jsApiTicket) {
+	ticket := jsApiTicket{"", time.Now()}
+	c <- ticket
+	for {
+		if time.Since(ticket.expires).Seconds() >= 0 {
+			t, err := getJsApiTicket(cin)
+			if err == nil {
+				ticket = *t
+			}
+		}
+		c <- ticket
+	}
+}
+
+func sendGetRequest(reqURL string, c chan accessToken) ([]byte, error) {
+	for i := 0; i < retryMaxN; i++ {
+		token := <-c
+		if time.Since(token.expires).Seconds() < 0 {
+			r, err := http.Get(reqURL + token.token)
+			if err != nil {
+				return nil, err
+			}
+			defer r.Body.Close()
+			reply, err := ioutil.ReadAll(r.Body)
+			if err != nil {
+				return nil, err
+			}
+			var result response
+			if err := json.Unmarshal(reply, &result); err != nil {
+				return nil, err
+			}
+			switch result.ErrorCode {
+			case 0:
+				return reply, nil
+			case 42001, 40001:
+				*token.lasttime = 0
+				<-c
+				log.Println("reget the tocken by ", result.ErrorCode, result.ErrorMessage)
+				continue
+			default:
+				return nil, errors.New(fmt.Sprintf("WeiXin send get request reply[%d]: %s", result.ErrorCode, result.ErrorMessage))
+			}
+		}
+	}
+	return nil, errors.New("WeiXin post request too many times:" + reqURL)
+}
+
+func postRequest(reqURL string, c chan accessToken, data []byte) ([]byte, error) {
+	for i := 0; i < retryMaxN; i++ {
+		token := <-c
+		if time.Since(token.expires).Seconds() < 0 {
+			r, err := http.Post(reqURL+token.token, "application/json; charset=utf-8", bytes.NewReader(data))
+			if err != nil {
+				return nil, err
+			}
+			defer r.Body.Close()
+			reply, err := ioutil.ReadAll(r.Body)
+			if err != nil {
+				return nil, err
+			}
+			var result response
+			if err := json.Unmarshal(reply, &result); err != nil {
+				return nil, err
+			}
+			switch result.ErrorCode {
+			case 0:
+				return reply, nil
+			case 42001, 40001:
+				*token.lasttime = 0
+				<-c
+				log.Println("reget the tocken by ", result.ErrorCode, result.ErrorMessage)
+				continue
+			default:
+				return nil, errors.New(fmt.Sprintf("WeiXin send post request reply[%d]: %s", result.ErrorCode, result.ErrorMessage))
+			}
+		}
+	}
+	return nil, errors.New("WeiXin post request too many times:" + reqURL)
+}
+
+func postMessage(c chan accessToken, msg interface{}) error {
+	data, err := marshal(msg)
+	if err != nil {
+		return err
+	}
+	_, err = postRequest(weixinHost+"/message/custom/send?access_token=", c, data)
+	return err
+}
+
+func uploadMedia(c chan accessToken, mediaType string, filename string, reader io.Reader) (string, error) {
+	reqURL := weixinFileURL + "/upload?type=" + mediaType + "&access_token="
+	for i := 0; i < retryMaxN; i++ {
+		token := <-c
+		if time.Since(token.expires).Seconds() < 0 {
+			bodyBuf := &bytes.Buffer{}
+			bodyWriter := multipart.NewWriter(bodyBuf)
+			fileWriter, err := bodyWriter.CreateFormFile("filename", filename)
+			if err != nil {
+				return "", err
+			}
+			if _, err = io.Copy(fileWriter, reader); err != nil {
+				return "", err
+			}
+			contentType := bodyWriter.FormDataContentType()
+			bodyWriter.Close()
+			r, err := http.Post(reqURL+token.token, contentType, bodyBuf)
+			if err != nil {
+				return "", err
+			}
+			defer r.Body.Close()
+			reply, err := ioutil.ReadAll(r.Body)
+			if err != nil {
+				return "", err
+			}
+			var result struct {
+				response
+				Type      string `json:"type"`
+				MediaId   string `json:"media_id"`
+				CreatedAt int64  `json:"created_at"`
+			}
+			err = json.Unmarshal(reply, &result)
+			if err != nil {
+				return "", err
+			}
+			switch result.ErrorCode {
+			case 0:
+				return result.MediaId, nil
+			case 42001: // access_token timeout and retry
+				continue
+			default:
+				return "", errors.New(fmt.Sprintf("WeiXin upload[%d]: %s", result.ErrorCode, result.ErrorMessage))
+			}
+		}
+	}
+	return "", errors.New("WeiXin upload media too many times")
+}
+
+func downloadMedia(c chan accessToken, mediaId string, writer io.Writer) error {
+	reqURL := weixinFileURL + "/get?media_id=" + mediaId + "&access_token="
+	for i := 0; i < retryMaxN; i++ {
+		token := <-c
+		if time.Since(token.expires).Seconds() < 0 {
+			r, err := http.Get(reqURL + token.token)
+			if err != nil {
+				return err
+			}
+			defer r.Body.Close()
+			if r.Header.Get("Content-Type") != "text/plain" {
+				_, err := io.Copy(writer, r.Body)
+				return err
+			}
+			reply, err := ioutil.ReadAll(r.Body)
+			if err != nil {
+				return err
+			}
+			var result response
+			if err := json.Unmarshal(reply, &result); err != nil {
+				return err
+			}
+			switch result.ErrorCode {
+			case 0:
+				return nil
+			case 42001: // access_token timeout and retry
+				continue
+			default:
+				return errors.New(fmt.Sprintf("WeiXin download[%d]: %s", result.ErrorCode, result.ErrorMessage))
+			}
+		}
+	}
+	return errors.New("WeiXin download media too many times")
+}
+
+// Format reply message header
+func (w responseWriter) replyHeader() string {
+	return fmt.Sprintf(replyHeader, w.toUserName, w.fromUserName, time.Now().Unix())
+}
+
+// Return weixin instance
+func (w responseWriter) GetWeixin() *Weixin {
+	return w.wx
+}
+
+// Return user data
+func (w responseWriter) GetUserData() interface{} {
+	return w.wx.userData
+}
+
+// Reply empty message
+func (w responseWriter) ReplyOK() {
+	w.writer.Write([]byte("success"))
+}
+
+// Reply text message
+func (w responseWriter) ReplyText(text string) {
+	msg := fmt.Sprintf(replyText, w.replyHeader(), text)
+	w.writer.Write([]byte(msg))
+}
+func (w responseWriter) Reply2CustomerService() {
+	msg := fmt.Sprintf(reply2CustomerService, w.replyHeader(), "")
+	//log.Println("repl msg:", msg)
+	w.writer.Write([]byte(msg))
+}
+
+// Reply image message
+func (w responseWriter) ReplyImage(mediaId string) {
+	msg := fmt.Sprintf(replyImage, w.replyHeader(), mediaId)
+	w.writer.Write([]byte(msg))
+}
+
+// Reply voice message
+func (w responseWriter) ReplyVoice(mediaId string) {
+	msg := fmt.Sprintf(replyVoice, w.replyHeader(), mediaId)
+	w.writer.Write([]byte(msg))
+}
+
+// Reply video message
+func (w responseWriter) ReplyVideo(mediaId string, title string, description string) {
+	msg := fmt.Sprintf(replyVideo, w.replyHeader(), mediaId, title, description)
+	w.writer.Write([]byte(msg))
+}
+
+// Reply music message
+func (w responseWriter) ReplyMusic(m *Music) {
+	msg := fmt.Sprintf(replyMusic, w.replyHeader(), m.Title, m.Description, m.MusicUrl, m.HQMusicUrl, m.ThumbMediaId)
+	w.writer.Write([]byte(msg))
+}
+
+// Reply news message (max 10 news)
+func (w responseWriter) ReplyNews(articles []Article) {
+	var ctx string
+	for _, article := range articles {
+		ctx += fmt.Sprintf(replyArticle, article.Title, article.Description, article.PicUrl, article.Url)
+	}
+	msg := fmt.Sprintf(replyNews, w.replyHeader(), len(articles), ctx)
+	w.writer.Write([]byte(msg))
+}
+
+// Transfer customer service
+func (w responseWriter) TransferCustomerService(serviceId string) {
+	msg := fmt.Sprintf(transferCustomerService, serviceId, w.fromUserName, time.Now().Unix())
+	w.writer.Write([]byte(msg))
+}
+
+// Post text message
+func (w responseWriter) PostText(text string) error {
+	return w.wx.PostText(w.toUserName, text)
+}
+
+// Post image message
+func (w responseWriter) PostImage(mediaId string) error {
+	return w.wx.PostImage(w.toUserName, mediaId)
+}
+
+// Post voice message
+func (w responseWriter) PostVoice(mediaId string) error {
+	return w.wx.PostVoice(w.toUserName, mediaId)
+}
+
+// Post video message
+func (w responseWriter) PostVideo(mediaId string, title string, desc string) error {
+	return w.wx.PostVideo(w.toUserName, mediaId, title, desc)
+}
+
+// Post music message
+func (w responseWriter) PostMusic(music *Music) error {
+	return w.wx.PostMusic(w.toUserName, music)
+}
+
+// Post news message
+func (w responseWriter) PostNews(articles []Article) error {
+	return w.wx.PostNews(w.toUserName, articles)
+}
+
+// Post template message
+func (w responseWriter) PostTemplateMessage(templateid string, url string, data TmplData) (int32, error) {
+	return w.wx.PostTemplateMessage(w.toUserName, templateid, url, data)
+}
+
+// Upload media from local file
+func (w responseWriter) UploadMediaFromFile(mediaType string, filepath string) (string, error) {
+	return w.wx.UploadMediaFromFile(mediaType, filepath)
+}
+
+// Download media and save to local file
+func (w responseWriter) DownloadMediaToFile(mediaId string, filepath string) error {
+	return w.wx.DownloadMediaToFile(mediaId, filepath)
+}
+
+// Upload media with reader
+func (w responseWriter) UploadMedia(mediaType string, filename string, reader io.Reader) (string, error) {
+	return w.wx.UploadMedia(mediaType, filename, reader)
+}
+
+// Download media with writer
+func (w responseWriter) DownloadMedia(mediaId string, writer io.Writer) error {
+	return w.wx.DownloadMedia(mediaId, writer)
+}
+
+func (wx *Weixin) PostCustomMsg(url string, obj interface{}) (bs []byte, err error) {
+	var data []byte
+	data, err = json.Marshal(obj)
+	if err != nil {
+		return
+	}
+	bs, err = postRequest(url, wx.tokenChan, data)
+	return
+}

+ 106 - 0
src/jfw/weixin/src/github.com/wizjin/weixin/wxssl.go

@@ -0,0 +1,106 @@
+package weixin
+
+import (
+	"bytes"
+	"crypto/tls"
+	"crypto/x509"
+	"encoding/json"
+	"encoding/xml"
+	"errors"
+	"io/ioutil"
+	"log"
+	"net/http"
+	"time"
+)
+
+var sslclient *http.Client
+var _tlsConfig *tls.Config
+
+//
+type ReturnXml struct {
+	XMLName xml.Name `xml:"xml"`
+	Code    string   `xml:"return_code"`
+}
+
+func InitSSLClient(cer, key, ca string) {
+	cert, err := tls.LoadX509KeyPair(cer, key)
+	if err != nil {
+		log.Println("load wechat keys fail", err)
+		return
+	}
+
+	caData, err := ioutil.ReadFile(ca)
+	if err != nil {
+		log.Println("read wechat ca fail", err)
+		return
+	}
+	pool := x509.NewCertPool()
+	pool.AppendCertsFromPEM(caData)
+
+	_tlsConfig = &tls.Config{
+		Certificates: []tls.Certificate{cert},
+		RootCAs:      pool,
+	}
+
+	tr := &http.Transport{TLSClientConfig: _tlsConfig}
+	sslclient = &http.Client{Transport: tr}
+}
+
+//Post custom message(消息结构体完全由开发人员自定义)
+func (wx *Weixin) PostXmlCustom(url string, obj interface{}) ([]byte, error) {
+	var bs []byte
+	data, err := xml.Marshal(obj)
+	if err != nil {
+		return bs, err
+	}
+	bs, err = postSSLRequest(url, wx.tokenChan, data)
+	if err != nil {
+		return bs, err
+	}
+	tmp := ReturnXml{}
+	err = xml.Unmarshal(bs, &tmp)
+	if err != nil {
+		return bs, err
+	}
+	if tmp.Code != "SUCCESS" {
+		log.Println(string(bs))
+		err = errors.New("Paybonus invalid response!")
+	}
+
+	return bs, err
+}
+
+//Post Json Data
+func (wx *Weixin) PostJsonCustom(url string, obj interface{}) ([]byte, error) {
+	var bs []byte
+	data, err := json.Marshal(obj)
+	if err != nil {
+		return bs, err
+	}
+	bs, err = postSSLRequest(url, wx.tokenChan, data)
+	return bs, err
+}
+
+//发送安全请求
+func postSSLRequest(reqURL string, c chan accessToken, data []byte) ([]byte, error) {
+	for i := 0; i < retryMaxN; i++ {
+		token := <-c
+		if time.Since(token.expires).Seconds() < 0 {
+			req, _ := http.NewRequest("POST", reqURL+token.token, bytes.NewReader(data))
+			r, err := sslclient.Do(req)
+			if err != nil {
+				log.Println("err1", err.Error())
+				return nil, err
+			}
+			defer r.Body.Close()
+			reply, err := ioutil.ReadAll(r.Body)
+			//log.Println("repl:", string(reply))
+			if err != nil {
+				log.Println("err2", err.Error())
+				return nil, err
+			}
+			return reply, nil
+		}
+	}
+	return nil, errors.New("WeiXin post request too many times:" + reqURL)
+}

+ 238 - 0
src/jfw/weixin/src/jrpc/jrpc.go

@@ -0,0 +1,238 @@
+package jrpc
+
+import (
+	"config"
+	"encoding/json"
+	"encoding/xml"
+	"fmt"
+	"log"
+	"qfw/util"
+	"qfw/util/redis"
+	qrpc "qfw/util/rpc"
+	"time"
+	"wx"
+
+	"github.com/SKatiyar/qr"
+
+	"github.com/wizjin/weixin"
+)
+
+type WeiXinRpc struct {
+	Wwx *weixin.Weixin
+}
+
+var TPL_PUSH_ID, TPL_BIDOPEN_ID, TPL_MANAGERNOTIFY_ID string
+
+func init() {
+	TPL_PUSH_ID = config.Sysconfig["tpl_push_id"].(string)
+	TPL_BIDOPEN_ID = config.Sysconfig["tpl_bidopen_id"].(string)
+	TPL_MANAGERNOTIFY_ID = config.Sysconfig["tpl_managernotify_id"].(string)
+}
+
+func (w *WeiXinRpc) SendPushMsg(param *qrpc.NotifyMsg, ret *qrpc.RpcResult) error {
+	_, err := w.Wwx.PostTemplateMessage(param.Openid, TPL_PUSH_ID, param.Url,
+		weixin.TmplData{
+			"first":    weixin.TmplItem{param.Title, ""},
+			"keyword1": weixin.TmplItem{param.Detail, ""},
+			"keyword2": weixin.TmplItem{param.Service, ""},
+			"remark":   weixin.TmplItem{param.Remark, "#0987FF"},
+		})
+	if err != nil {
+		*ret = qrpc.RpcResult(err.Error())
+		log.Println(err)
+	} else {
+		*ret = "Y"
+		log.Println("send pushmsg success!")
+	}
+	return nil
+}
+
+func (w *WeiXinRpc) SendBidOpenMsg(param *qrpc.NotifyMsg, ret *qrpc.RpcResult) error {
+	_, err := w.Wwx.PostTemplateMessage(param.Openid, TPL_BIDOPEN_ID, param.Url,
+		weixin.TmplData{
+			"first":    weixin.TmplItem{param.Title, ""},
+			"keyword1": weixin.TmplItem{param.Detail, "#0987FF"},
+			"keyword2": weixin.TmplItem{param.Service, ""},
+			"keyword3": weixin.TmplItem{param.Date, ""},
+			"remark":   weixin.TmplItem{param.Remark, ""},
+		})
+	if err != nil {
+		*ret = "N"
+		log.Println(param.Openid, err)
+	} else {
+		*ret = "Y"
+		log.Println("send bidopen success!", param.Openid)
+	}
+	return nil
+}
+
+//取得JS接口参数
+func (w *WeiXinRpc) GetJSInterfaceParam(currenturl string, ret *[]string) (err error) {
+	timestamp := time.Now().Unix()
+	noncestr := util.Uuid(32)
+	signature, err := w.Wwx.JsSignature(currenturl, timestamp, noncestr)
+	*ret = append(*ret, w.Wwx.GetAppId())
+	*ret = append(*ret, fmt.Sprintf("%d", timestamp))
+	*ret = append(*ret, noncestr)
+	if err == nil {
+		*ret = append(*ret, signature)
+	}
+	return nil
+}
+
+/**生成分享二维码
+规则:自动生成二维码,存储并存储到redis中
+	开发在使用时,先查看redis,没有的话再调用此RPC方法
+	生成的二维码有效期为一个月
+**/
+func (w *WeiXinRpc) GetShareQR(shareid uint32, ret *string) (err error) {
+	//构造自定义消息文本
+	var msg struct {
+		Expire int    `json:"expire_seconds"`
+		Name   string `json:"action_name"`
+		Info   struct {
+			Scene struct {
+				Id uint32 `json:"scene_id"`
+			} `json:"scene"`
+		} `json:"action_info"`
+	}
+	msg.Expire = 2592000
+	msg.Name = "QR_SCENE"
+	msg.Info.Scene.Id = shareid
+	data, err := w.Wwx.PostCustomMsg("https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=", &msg)
+	if err != nil {
+		*ret = "faile"
+		log.Println(err.Error())
+		return nil
+	}
+	tmp := map[string]interface{}{}
+	json.Unmarshal(data, &tmp)
+	url := tmp["url"].(string)
+	//生二维码
+	r, _ := qr.Encode(url, qr.L)
+	pngdat := r.PNG()
+	//存储到redis
+	p_share_shareid := fmt.Sprintf("p_share_%d", shareid)
+	if p_share_shareid[0:2] == "10" {
+		redis.PutBytes("sso", p_share_shareid, &pngdat, 600)
+	} else {
+		redis.PutBytes("sso", p_share_shareid, &pngdat, msg.Expire)
+	}
+	*ret = "ok"
+	return nil
+}
+
+//
+func (w *WeiXinRpc) SaveAbnormal(openid string, ret *string) (err error) {
+	log.Println(openid, "get useinfo")
+	if openid != "" {
+		go wx.SaveAbnormal(openid)
+	}
+	return nil
+}
+
+//发送管理员通知
+func (w *WeiXinRpc) SendFeedbackNotifyMsg(param *qrpc.NotifyMsg, ret *qrpc.RpcResult) error {
+	_, err := w.Wwx.PostTemplateMessage(param.Openid, TPL_MANAGERNOTIFY_ID, param.Url,
+		weixin.TmplData{
+			"first":    weixin.TmplItem{param.Title, ""},
+			"keyword1": weixin.TmplItem{param.Detail, ""},
+			"keyword2": weixin.TmplItem{param.Date, ""},
+			"remark":   weixin.TmplItem{param.Remark, "#0987FF"},
+		})
+	if err != nil {
+		log.Println(err.Error())
+	} else {
+		log.Println("send feedbackNotify success!")
+	}
+	return nil
+}
+
+//取得预生成订单编号,我们的系统要控制下,别订单重复了
+func (w *WeiXinRpc) GetPrepayId(param map[string]string, res *[]byte) error {
+	defer util.Catch()
+	attachmsg := param["attachmsg"]
+	bodymsg := param["bodymsg"]
+	detailmsg := param["detailmsg"]
+	useropenid := param["useropenid"]
+	tradeno := param["tradeno"]
+	userip := param["userip"]
+	totalfee := param["totalfee"]
+	mchid := param["mchid"]
+	key := param["key"]
+	notifyUrl := param["notifyUrl"]
+	//生成预订单
+	api_url := "https://api.mch.weixin.qq.com/pay/unifiedorder?token="
+	//匿名结构体
+	data := struct {
+		XMLName        xml.Name `xml:"xml"`
+		Appid          string   `xml:"appid"`
+		Attach         string   `xml:"attach"`
+		Body           string   `xml:"body"`
+		MchId          string   `xml:"mch_id"`           //商家ID
+		Detail         string   `xml:"detail"`           //描述
+		NonceStr       string   `xml:"nonce_str"`        //随机码
+		NotifyUrl      string   `xml:"notify_url"`       //回调URL
+		Openid         string   `xml:"openid"`           //用户OPENID
+		OutTradeNo     string   `xml:"out_trade_no"`     //商家订单编号
+		SpbillCreateIp string   `xml:"spbill_create_ip"` //用户端IP
+		TotalFee       string   `xml:"total_fee"`        //总金额
+		TradeType      string   `xml:"trade_type"`       //支付方式
+		Sign           string   `xml:"sign"`             //签名
+	}{
+		Appid:          config.Sysconfig["appid"].(string),
+		Attach:         attachmsg, //"企明星打赏",
+		Body:           bodymsg,   //  "企明星-招标信息打赏",
+		Detail:         detailmsg, //"招标推送信息[" + "xxxx文章标题" + "] 打赏" + "3元",
+		MchId:          mchid,
+		NonceStr:       util.Uuid(32),
+		NotifyUrl:      notifyUrl,
+		Openid:         useropenid, //"obEpLuKW516dqWdc-WT-Ra_5qigo",
+		OutTradeNo:     tradeno,    //"qmx201611290018",
+		SpbillCreateIp: userip,     //"123.56.236.148",
+		TotalFee:       totalfee,   //"1",
+		TradeType:      "JSAPI",
+	}
+	//计算签名
+	tmp := fmt.Sprintf("appid=%s&attach=%s&body=%s&detail=%s&mch_id=%s&nonce_str=%s&notify_url=%s&openid=%s&out_trade_no=%s&spbill_create_ip=%s&total_fee=%s&trade_type=%s&key=%s", data.Appid, data.Attach, data.Body, data.Detail, data.MchId, data.NonceStr, data.NotifyUrl, data.Openid, data.OutTradeNo, data.SpbillCreateIp, data.TotalFee, data.TradeType, key)
+	data.Sign = util.WxSign(tmp) //签名
+	bs, err := xml.Marshal(data)
+	if err != nil {
+		log.Println(err.Error())
+		return err
+	}
+	bs, err = w.Wwx.PostXmlCustom(api_url, data)
+	if err != nil {
+		log.Println(err.Error())
+		return err
+	}
+	log.Println(string(bs))
+	ret := struct {
+		ReturnCode string `xml:"return_code"`
+		ReturnMsg  string `xml:"return_msg"`
+		ResultCode string `xml:"result_code"`
+		PrepayId   string `xml:"prepay_id"`
+		ErrCode    string `xml:"err_code"`
+	}{}
+	err = xml.Unmarshal(bs, &ret)
+	if err != nil {
+		log.Println(err.Error())
+		return err
+	}
+	r := make(map[string]interface{})
+	r["status"] = -1
+	if ret.ReturnCode == "SUCCESS" {
+		//成功
+		if ret.ResultCode == "SUCCESS" {
+			r["status"] = 1
+			r["prepayid"] = ret.PrepayId
+		} else {
+			//余额不足
+			if ret.ErrCode == "NOTENOUGH" {
+				r["status"] = -2
+			}
+		}
+	}
+	*res, _ = json.Marshal(&r)
+	return nil
+}

+ 54 - 0
src/jfw/weixin/src/main.go

@@ -0,0 +1,54 @@
+package main
+
+import (
+	. "config"
+	"jrpc"
+	"log"
+	"net"
+	"net/http"
+	"net/rpc"
+	"oauth"
+	"qfw/util"
+	_ "tools"
+	"wx"
+)
+
+func main() {
+	//开启oauth和微信
+	go func() {
+		defer util.Catch()
+		http.HandleFunc("/wx/oauth/", oauth.WeixinOauth)
+		http.Handle("/wx/service", wx.Mux)
+		http.HandleFunc("/wx/createmenu_n_1", wx.CreateMenu)
+		http.HandleFunc("/wx/tmpcode/", wx.TmpCodeHandle)
+		http.HandleFunc("/wx/limitcode/", wx.LimitCodeHandle)
+		http.HandleFunc("/wx/checkuser/w/", wx.WebCheckUser)
+		http.HandleFunc("/wx/getcount/w/", wx.GetCountWeb)
+		//生成二维码
+		http.HandleFunc("/wx/yjm/", wx.YjHandle)
+		http.HandleFunc("/wx/lsm/", wx.AdvHandle)
+		http.HandleFunc("/wx/token", wx.GetToken)
+		// 设置监听的端口
+		err := http.ListenAndServe(":"+Sysconfig["weixinport"].(string), nil)
+		if err != nil {
+			log.Println("ListenAndServe: ", err)
+		}
+	}()
+	go func() {
+		wrpc := &jrpc.WeiXinRpc{Wwx: wx.Mux}
+		//在此可以注册多个Rpc服务接口
+		rpc.Register(wrpc)
+		rpc.HandleHTTP()
+		var err error
+		listen, err := net.Listen("tcp", ":"+Sysconfig["weixinrpcport"].(string))
+		if err != nil {
+			log.Println(err.Error())
+		} else {
+			http.Serve(listen, nil)
+		}
+	}()
+	log.Println("jianyu-weixin start...ok")
+
+	b := make(chan bool, 1)
+	<-b
+}

+ 90 - 0
src/jfw/weixin/src/oauth/oauth.go

@@ -0,0 +1,90 @@
+package oauth
+
+import (
+	. "config"
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"net/http"
+	"qfw/util"
+	"strconv"
+	"strings"
+	"time"
+)
+
+const (
+	OPENAUTH_URLTPL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code"
+	ERROR_TEMPLATE  = `
+<html>
+<head>
+<title>企明星-企业服务网-全国最大最真实的企业社区</title>
+<meta name="msvalidate.01" content="D5F3ADC7EB4E65FFB8BF943AD56DD1F7" />
+<meta charset="utf-8">
+<meta http-equiv="X-UA-Compatible" content="IE=edge,Chrome=1" />
+<meta name="viewport" content="width=device-width,initial-scale=1.0">
+<meta name="renderer" content="webkit">
+<meta name="Keywords" content="企明星,企业服务网,企业社区,企业服务,微官网"/>
+<meta name="Description" content="企明星是一款面向企业和个人提供服务的互联网产品,提供企业社区(企业产品服务信息发布、企业信息查询、企业关系网)、微官网、企业关系网、政策法规查询。"/>
+</head>
+<body>
+授权验证失败,请微信关注”企明星“,点击菜单进入。
+</body>
+</html>
+	`
+)
+
+var AppId, AppSecret, port, proxysess string
+var se util.SimpleEncrypt
+
+//初始化
+func init() {
+	AppId, _ = Sysconfig["appid"].(string)
+	AppSecret, _ = Sysconfig["appsecret"].(string)
+	proxysess, _ = Sysconfig["proxysess"].(string)
+	se = util.SimpleEncrypt{Key: "topnet"}
+}
+
+func WeixinOauth(w http.ResponseWriter, r *http.Request) {
+	defer util.Catch()
+	/**
+	信息查询
+	我的订阅
+	查看演示
+	**/
+	log.Println("----------------------", r.RequestURI)
+	uri := r.RequestURI
+	if strings.Index(uri, "?") < 0 {
+		return
+	}
+	start, end := strings.LastIndex(uri, "/"), strings.Index(uri, "?")
+	action := uri[start+1 : end]
+	r.ParseForm()
+	code, _ := r.Form["code"][0], r.Form["state"][0]
+	//取得openid
+	url := fmt.Sprintf(OPENAUTH_URLTPL, AppId, AppSecret, code)
+	//log.Println("request url:", url)
+	resp, err := http.Get(url)
+	defer resp.Body.Close()
+	if err == nil {
+		//取得openid,unionid
+		bs, _ := ioutil.ReadAll(resp.Body)
+		//fmt.Println("data:", string(bs))
+		tmp := map[string]interface{}{}
+		err = json.Unmarshal(bs, &tmp)
+		if err != nil {
+			fmt.Fprintln(w, ERROR_TEMPLATE)
+			return
+		}
+		openid, _ := tmp["openid"].(string)
+		unionid, _ := tmp["unionid"].(string)
+		tmpstr := openid + "," + unionid + "," + strconv.Itoa(int(time.Now().Unix())) + "," + action
+		url := fmt.Sprintf(proxysess, se.EncodeString(tmpstr))
+		log.Println("redirect::", url)
+		//跳转
+		http.Redirect(w, r, url, http.StatusFound)
+		return
+	} else {
+		fmt.Fprintln(w, ERROR_TEMPLATE)
+	}
+}

+ 19 - 0
src/jfw/weixin/src/rootca.pem

@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV
+UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy
+dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1
+MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx
+dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B
+AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f
+BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A
+cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC
+AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ
+MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm
+aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw
+ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj
+IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF
+MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA
+A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y
+7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh
+1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4
+-----END CERTIFICATE-----

+ 76 - 0
src/jfw/weixin/src/tools/checkholiday.go

@@ -0,0 +1,76 @@
+package tools
+
+import (
+	"time"
+	"fmt"
+	"log"
+	"io/ioutil"
+	"net/http"
+	"sync"
+	"qfw/util"
+)
+type Holidy struct{
+	isHolidy bool
+	date string
+}
+
+const(
+	URL="http://apis.baidu.com/xiaogg/holiday/holiday?d=%s"
+	KEY="188da6713cec8bf6287ee14e4ddcee26"
+)
+
+var Now=Holidy{}
+var Lock =&sync.Mutex{}
+
+func CheckNowHoliday() bool{
+	_now:=time.Now().Format("20060102")
+	if Now.date==_now{
+		return Now.isHolidy
+	}else{
+		return GetNowHoliday(_now)
+	}
+}
+
+func GetNowHoliday(_now string) bool{
+	b1,b2:=GetHolidy(_now)
+	if b2{		
+		Lock.Lock()
+		Now.date=_now
+		Now.isHolidy=b1
+		Lock.Unlock()
+	}	
+	return b1
+}
+
+func GetHolidy(yyyymmdd string) (bool,bool){
+	m,b:=GetUrl(yyyymmdd)
+	if b {
+		if m=="1"||m=="2"{
+			return true,true
+		}
+	}
+	return false,b
+}
+
+func GetUrl(date string) (m string,res bool) {
+	defer util.Catch()
+	req,err:=http.NewRequest("GET",fmt.Sprintf(URL,date),nil)
+	if err!=nil{
+		return
+	}
+	req.Header.Add("apikey",KEY)
+	resp,err:=http.DefaultClient.Do(req)
+	defer resp.Body.Close()
+	if err == nil {
+		bs, err:= ioutil.ReadAll(resp.Body)
+		if len(bs)==1{
+			m=string(bs)
+		}
+		if err != nil {
+			log.Println("err:",err)
+		}else{
+			res=true
+		}
+	}
+	return
+}

+ 18 - 0
src/jfw/weixin/src/tools/mongo.go

@@ -0,0 +1,18 @@
+package tools
+
+import (
+	. "config"
+	"qfw/util"
+	"qfw/util/mongodb"
+)
+
+var MQFW mongodb.MongodbSim
+
+func init() {
+	MQFW = mongodb.MongodbSim{
+		MongodbAddr: Sysconfig["mongodbServers"].(string),
+		Size:        util.IntAll(Sysconfig["mongodbPoolSize"]),
+		DbName:      Sysconfig["mongodbName"].(string),
+	}
+	MQFW.InitPool()
+}

+ 10 - 0
src/jfw/weixin/src/tools/redis.go

@@ -0,0 +1,10 @@
+package tools
+
+import (
+	. "config"
+	"qfw/util/redis"
+)
+
+func init() {
+	redis.InitRedis(Sysconfig["redisServers"].(string))
+}

+ 26 - 0
src/jfw/weixin/src/tools/rpccall.go

@@ -0,0 +1,26 @@
+// rpccall
+package tools
+
+import (
+	"log"
+	"net/rpc"
+	"qfw/util"
+	frpc "qfw/util/rpc"
+)
+
+func MyFollow(param *frpc.FollowData) string {
+	var ret string
+	util.Try(func() {
+		client, err := rpc.DialHTTP("tcp", param.Server)
+		defer client.Close()
+		if err != nil {
+			log.Println(err.Error())
+			return
+		}
+		err = client.Call("MyfollowRpc.MyFollowSet", param, &ret)
+		if err != nil {
+			log.Println(err.Error())
+		}
+	}, func(e interface{}) {})
+	return ret
+}

+ 1 - 0
src/jfw/weixin/src/tools/tools.go

@@ -0,0 +1 @@
+package tools

+ 165 - 0
src/jfw/weixin/src/wx/checkuser.go

@@ -0,0 +1,165 @@
+package wx
+
+import (
+	"log"
+	"math"
+	"qfw/util"
+	"sync"
+	"time"
+	"tools"
+)
+
+var lock = sync.Mutex{}
+var HOUR = 3
+
+type CheckJYUser struct {
+	WxTotal   int
+	MgoTotal  int
+	MgoXTotal int
+	WxMap     map[string]bool
+	MgoMap    map[string]int
+	binit     bool
+	WxToMgo   map[string]int
+	MgoToWx   map[string]bool
+}
+
+func CheckJob() {
+	go func() {
+		for {
+			NowTime := time.Now()
+			hour := NowTime.Hour()
+			if hour == HOUR {
+				StartCheckJob()
+			}
+			NowTime2 := time.Date(NowTime.Year(), NowTime.Month(), NowTime.Day()+1, HOUR, 0, 0, 0, time.Local).Unix()
+			go log.Println(NowTime2 - time.Now().Unix())
+			time.Sleep(time.Duration(NowTime2-time.Now().Unix()) * time.Second)
+		}
+	}()
+}
+
+func StartCheckJob() {
+	lock.Lock()
+	defer lock.Unlock()
+	c := NewCheck()
+	c.CheckAndRepair()
+	NilCheck(c)
+	log.Println("check over!")
+}
+
+func NewCheck() *CheckJYUser {
+	c := &CheckJYUser{}
+	c.WxMap = map[string]bool{}
+	c.MgoMap = map[string]int{}
+	c.WxToMgo = map[string]int{}
+	c.MgoToWx = map[string]bool{}
+	return c
+}
+func NilCheck(c *CheckJYUser) {
+	c.WxMap = nil
+	c.MgoMap = nil
+	c.WxToMgo = nil
+	c.MgoToWx = nil
+	c = nil
+}
+
+func (c *CheckJYUser) Init() {
+	//load mgo data into cache
+	func() {
+		log.Println("load mgo user start!")
+		sess := tools.MQFW.GetMgoConn()
+		defer tools.MQFW.DestoryMongoConn(sess)
+		q := &map[string]interface{}{"i_appid": 2}
+		sum := tools.MQFW.Count("user", q)
+		it := sess.DB(tools.MQFW.DbName).C("user").Find(q).Select(&map[string]interface{}{
+			"s_m_openid": 1,
+			"i_ispush":   1,
+		}).Iter()
+		for tmp := make(map[string]interface{}); it.Next(&tmp); {
+			c.MgoTotal++
+			ipush := 1
+			if tmp["i_ispush"] != nil && util.IntAll(tmp["i_ispush"]) == 0 {
+				ipush = -1
+			} else {
+				c.MgoXTotal++
+			}
+			c.MgoMap[util.ObjToString(tmp["s_m_openid"])] = ipush
+		}
+		if c.MgoTotal >= sum && c.MgoXTotal > 10 {
+			c.binit = true
+		}
+		log.Println("load mgo user success!")
+	}()
+
+	func() {
+		log.Println("load wx user start!")
+		err := c.GetWxUser("")
+		if err != nil {
+			log.Println("wx load err ,check exit.")
+			c.binit = false
+			return
+		}
+		if c.WxTotal < 10 || math.Abs(float64(c.WxTotal-c.MgoXTotal)) > 500 || math.Abs(float64(len(c.WxMap)-c.WxTotal)) > 20 {
+			c.binit = false
+			log.Println("load wx user failed!")
+		} else {
+			log.Println("load wx user success!")
+		}
+	}()
+	log.Println("init over", c.MgoTotal, c.MgoXTotal, c.WxTotal)
+}
+
+func (c *CheckJYUser) CheckAndRepair() {
+	c.Init()
+	if c.binit {
+		//from wx to mgo
+		for k, _ := range c.WxMap {
+			v := c.MgoMap[k]
+			if v != 1 {
+				log.Println("user", k, "wx-mgo(0:follow but nouser,-1:follow but nopush ).", v)
+				c.WxToMgo[k] = v
+			}
+		}
+
+		//from Mgo to wx
+		for k, v := range c.MgoMap {
+			if !c.WxMap[k] && v == 1 {
+				log.Println("user", k, "mgo-wx(unfollow but ispush).")
+				c.MgoToWx[k] = true
+			}
+		}
+		//repair!
+		for k, v := range c.WxToMgo {
+			if v == 0 {
+				//create user
+				user, _ := Mux.GetUserInfo(k)
+				saveUser(user, "-10")
+			} else {
+				//change user ispush
+				tools.MQFW.Update("user", "{'s_m_openid':'"+k+"'}", &map[string]interface{}{"$set": &map[string]interface{}{"i_ispush": 1}}, false, false)
+			}
+		}
+		for k, _ := range c.MgoToWx {
+			tools.MQFW.Update("user", "{'s_m_openid':'"+k+"'}", &map[string]interface{}{"$set": &map[string]interface{}{"i_ispush": 0}}, false, false)
+		}
+	}
+}
+
+func (c *CheckJYUser) GetWxUser(openid string) error {
+	list, err := Mux.GetUserList(openid)
+	if err != nil {
+		log.Println("load wx user err:", err)
+		return err
+	}
+	if openid == "" {
+		c.WxTotal = list.Total
+	}
+	for _, id := range list.Data.OpenId {
+		c.WxMap[id] = true
+	}
+	if list.Nextid != "" {
+		return c.GetWxUser(list.Nextid)
+	} else {
+		return nil
+	}
+}

+ 130 - 0
src/jfw/weixin/src/wx/checkwords.go

@@ -0,0 +1,130 @@
+/* checkkey
+剑鱼订阅设置纠错帮助消息推送
+*/
+package wx
+
+import (
+	"fmt"
+	"log"
+	qrpc "qfw/util/rpc"
+	"time"
+	"tools"
+
+	"github.com/wizjin/weixin"
+)
+
+var TPL_CHECKWORDS_ID string       //模板id
+var TPL_MSG_OPENID map[string]bool //待发送消息openid
+var USER_OPENID map[string]bool    //用户openid
+
+func init() {
+	TPL_CHECKWORDS_ID = "1OnrM4OQ997OXt8Q-E6IO7kwi2I3h1Jqaac70UD-DM8"
+}
+
+func CheckKeyWords() (content string) {
+	TPL_MSG_OPENID = make(map[string]bool)
+	USER_OPENID = make(map[string]bool)
+	ret := getProblemUserWords()
+	for _, v := range ret {
+		content += v + "<br>"
+	}
+	for k, _ := range TPL_MSG_OPENID {
+		msg := qrpc.NotifyMsg{
+			Openid:  k,
+			Remark:  "剑鱼君友情提示",
+			Result:  "Result",
+			Detail:  "关键词......,请重新设置",
+			Title:   "关键词未能成功接受推送信息",
+			Url:     "http://www.zhaobiao.info",
+			Date:    "2017年01月26",
+			Service: "剑鱼君",
+		}
+		fmt.Println(msg)
+		//sendMsg(msg)
+	}
+	log.Println(content)
+	return content
+}
+
+//提取关键词有问题的用户
+func getProblemUserWords() []string { //{ "o_jy.a_key.key.0": {$exists:1} }
+	users, _ := tools.MQFW.Find("user", `{"o_jy.a_key.key.0": {"$exists":1}}`, nil, `{"s_m_openid":1,"o_jy":1}`, false, -1, -1)
+	total := len(*users)
+	//过滤关键词,查出有问题的关键词的对应用户opendid
+	for _, user := range *users {
+		openid := fmt.Sprint(user["s_m_openid"])
+		if o_jy, ok := user["o_jy"].(map[string]interface{}); ok {
+			if a_key, ok := o_jy["a_key"].([]interface{}); ok {
+				isbreak := false
+				for _, conf := range a_key {
+					if isbreak {
+						break
+					}
+					if tmp, ok := conf.(map[string]interface{}); ok {
+						keys := tmp["key"].([]interface{})
+						if len(keys) > 2 { //关键词大于等于3
+							isbreak = true
+							break
+						}
+						for _, key := range keys { //关键词有特殊字符
+							if len(key.(string)) > 60 || !regKey.MatchString(key.(string)) {
+								isbreak = true
+								break
+							}
+						}
+					}
+				}
+				if isbreak { //关键词有问题
+					TPL_MSG_OPENID[openid] = true
+				} else { //关键词没有问题,USER_OPENID暂存剩余用户,查询推送记录
+					USER_OPENID[fmt.Sprint(user["s_m_openid"])] = true
+				}
+			}
+		}
+	}
+	errkey := len(TPL_MSG_OPENID)
+	//关键词有问题的用户,并且没有推送记录
+	l_date := time.Now().Unix() - 30*24*60*60
+	for pid, _ := range TPL_MSG_OPENID {
+		ls, _ := tools.MQFW.FindOne("wxpush", `{"s_m_openid":"`+pid+`","l_date":{"$gt":`+fmt.Sprint(l_date)+`}}`)
+		if len(*ls) > 1 {
+			delete(TPL_MSG_OPENID, pid)
+		}
+		fmt.Println("pid", pid, ls)
+	}
+	errnopush := len(TPL_MSG_OPENID)
+
+	//通过查询推送记录,判断USER_OPENID剩余用户,是否推送信息
+	nopush := 0
+	for pid, _ := range USER_OPENID {
+		ls, _ := tools.MQFW.FindOne("wxpush", `{"s_m_openid":"`+pid+`","l_date":{"$gt":`+fmt.Sprint(l_date)+`}}`)
+		if len(*ls) < 1 {
+			TPL_MSG_OPENID[pid] = true
+			nopush += 1
+		}
+	}
+	fail := len(TPL_MSG_OPENID)
+	normal := len(*users) - len(TPL_MSG_OPENID)
+	return []string{
+		"用户总数:" + fmt.Sprint(total),
+		"    有推送记录:" + fmt.Sprint(normal),
+		"    无推送记录:" + fmt.Sprint(fail),
+		"备注(用户词不规范:" + fmt.Sprint(errkey) + ",词不规范且没有推送记录:" + fmt.Sprint(errnopush) + ")",
+	}
+}
+
+//发送模板信息
+func sendMsg(msg *qrpc.NotifyMsg) {
+	_, err := Mux.PostTemplateMessage(msg.Openid,
+		TPL_CHECKWORDS_ID, msg.Url,
+		weixin.TmplData{
+			"first":    weixin.TmplItem{msg.Title, ""},
+			"keyword1": weixin.TmplItem{msg.Detail, ""},
+			"keyword2": weixin.TmplItem{msg.Date, ""},
+			//"keyword3": weixin.TmplItem{msg.Service, ""},
+			"remark": weixin.TmplItem{msg.Remark, ""},
+		})
+	if err != nil {
+		fmt.Println("err", err)
+	}
+}

+ 104 - 0
src/jfw/weixin/src/wx/getcount.go

@@ -0,0 +1,104 @@
+package wx
+
+import (
+	"encoding/json"
+	"log"
+	"net/http"
+	"time"
+	"tools"
+)
+
+const (
+	FORMAT = "2006-01-02"
+)
+
+var (
+	WX_COUNT = "https://api.weixin.qq.com/datacube/getusersummary?access_token="
+	//WX_ALLCOUNT="https://api.weixin.qq.com/datacube/getusercumulate?access_token="
+)
+
+type M map[string]interface{}
+
+//定时任务
+func CountRun(hour int) {
+	now := time.Now()
+	h, day := now.Hour(), now.Day()
+	if h >= hour {
+		day++
+	}
+	newDate := time.Date(now.Year(), now.Month(), day, hour, 0, 30, 0, time.Local)
+	des := newDate.Unix() - now.Unix()
+	if des > 0 {
+		log.Println("start user count:", des)
+		time.AfterFunc(time.Duration(des)*time.Second, func() {
+			ticker := time.NewTicker(time.Hour * 24)
+			go GetCount(nil, nil)
+			go func() {
+				for _ = range ticker.C {
+					GetCount(nil, nil)
+				}
+			}()
+		})
+	}
+}
+
+func GetCountWeb(w http.ResponseWriter, r *http.Request) {
+	r.ParseForm()
+	d := r.FormValue("d")
+	e := r.FormValue("e")
+	log.Println("get count d,e", d, e)
+	if d != "" {
+		if e == "" {
+			e = d
+		}
+		t1, err := time.Parse(FORMAT, d)
+		t2, err2 := time.Parse(FORMAT, e)
+		if err == nil && err2 == nil {
+			GetCount(&t1, &t2)
+		}
+	}
+}
+
+func GetCount(begin *time.Time, end *time.Time) {
+	d1, d2 := "", ""
+	if begin == nil {
+		now := time.Now()
+		now = now.Add(-24 * time.Hour)
+		d1 = now.Format(FORMAT)
+		d2 = d1
+	} else {
+		d1 = begin.Format(FORMAT)
+		d2 = end.Format(FORMAT)
+	}
+	log.Println("start count from wx", d1, d2)
+	//t := now.Add(-24 * time.Hour)
+	//d1 := t.Format(FORMAT)
+	bbres, err := Mux.PostCustomMsg(WX_COUNT, M{"begin_date": d1, "end_date": d2})
+	if err == nil {
+		type result struct {
+			List []struct {
+				Date       string `json:"ref_date"`
+				Source     int    `json:"user_source"`
+				Newuser    int    `json:"new_user"`
+				Canceluser int    `json:"cancel_user"`
+			} `json:"list"`
+		}
+		res := &result{}
+		json.Unmarshal(bbres, res)
+		log.Println(res)
+		if res != nil && len(res.List) > 0 {
+			for _, v := range res.List {
+				ts, _ := time.Parse(FORMAT, v.Date)
+				tools.MQFW.Save("wxcount", &map[string]interface{}{
+					"date":      v.Date,
+					"new":       v.Newuser,
+					"cancel":    v.Canceluser,
+					"source":    v.Source,
+					"timestamp": ts.Unix(),
+				})
+			}
+		}
+	} else {
+		log.Println(err)
+	}
+}

+ 1170 - 0
src/jfw/weixin/src/wx/wx.go

@@ -0,0 +1,1170 @@
+package wx
+
+import (
+	"config"
+	"encoding/json"
+	"fmt"
+	"io"
+	"math/rand"
+	"os"
+	//. "gopkg.in/mgo.v2/bson"
+	. "jfw/config"
+	"log"
+	"net/http"
+	"qfw/util"
+	"qfw/util/redis"
+	jrpc "qfw/util/rpc"
+	"regexp"
+	"strconv"
+	"strings"
+	"sync"
+	"time"
+	"tools"
+
+	"github.com/SKatiyar/qr"
+	"github.com/influxdata/influxdb-client"
+	. "github.com/wizjin/weixin"
+)
+
+var Mux *Weixin
+var se util.SimpleEncrypt
+var regKey *regexp.Regexp
+
+//锁
+var sharelock *sync.Mutex = &sync.Mutex{}
+
+func init() {
+	util.InitInfluxdb(util.ObjToString(Sysconfig["influxdb"]))
+	InitSSLClient("./apiclient_cert.pem", "./apiclient_key.pem", "./rootca.pem")
+	Mux = New(config.Sysconfig["apptoken"].(string), config.Sysconfig["appid"].(string), config.Sysconfig["appsecret"].(string))
+	// 注册关注事件的处理函数
+	//扫码事件
+	//Mux.HandleFunc(MsgTypeDefault, DefaultHandler)
+	Mux.HandleFunc(MsgTypeEventSubscribe, Subscribe)
+	Mux.HandleFunc("SCAN", ScanHandler)
+	Mux.HandleFunc(MsgTypeEventUnsubscribe, UnSubscribe)
+	Mux.HandleFunc(MsgTypeText, MsgTxtHandler)
+	se = util.SimpleEncrypt{Key: "topnet"}
+	CheckJob()
+	//关键词只能包含汉字、字母、数子
+	regKey, _ = regexp.Compile("^([0-9]|[A-Za-z]|[\u4E00-\u9FFF])+$")
+	CountRun(8)
+}
+
+//客服消息处理
+func MsgTxtHandler(w ResponseWriter, r *Request) {
+	log.Println("所有指令", r.FromUserName, r.Content) //测试信息
+	now := time.Now()
+	openid := r.FromUserName
+	user, err := Mux.GetUserInfo(openid)
+	autoReply_reg, _ := regexp.Compile("剑鱼|招标|中标|订阅|公告|项目|设置|推送")
+	if strings.Contains(strings.Replace(r.Content, " ", "", -1), "五福") {
+		wufuMsg := config.Sysconfig["wufu"].(map[string]interface{})
+		w.ReplyNews([]Article{Article{
+			Title:       wufuMsg["title"].(string),
+			Url:         wufuMsg["url"].(string),
+			PicUrl:      wufuMsg["picUrl"].(string),
+			Description: wufuMsg["description"].(string),
+		}})
+	} else if r.Content == "刮奖" && time.Now().After(time.Unix(config.ZqLuckdraw.StartDate, 0)) && time.Now().Before(time.Unix(config.ZqLuckdraw.EndDate, 0)) {
+		replayMsg := config.ZqLuckdraw.Replay1
+		url := config.Sysconfig["webdomain"].(string) + "/front/sess/" + se.EncodeString(r.FromUserName+",uid,"+strconv.Itoa(int(time.Now().Unix()))+",zqluckdraw")
+		w.ReplyNews([]Article{Article{
+			Title:       replayMsg["title"].(string),
+			Url:         url,
+			PicUrl:      config.Sysconfig["webdomain"].(string) + replayMsg["picUrl"].(string),
+			Description: replayMsg["description"].(string),
+		}})
+	} else if r.Content == "领奖" && time.Now().After(time.Unix(config.ZqLuckdraw.ClaimsStartDate, 0)) && time.Now().Before(time.Unix(config.ZqLuckdraw.ClaimsEndDate, 0)) {
+		w.ReplyText(config.ZqLuckdraw.Replay2)
+	} else if r.Content == "最剑鱼" && time.Now().Before(time.Unix(1494777599, 0)) { //2017/5/14 23:59:59
+		w.ReplyNews([]Article{Article{
+			Title:       "哪个slogan“最剑鱼”?由你来决定!",
+			Url:         "http://mp.weixin.qq.com/s/nUsXKU2dZuqb6B3s22rzCw",
+			PicUrl:      config.Sysconfig["webdomain"].(string) + "/images/wwjydy.jpg",
+			Description: "老铁们!快来为入围的剑鱼slogan投票啦~~~~~",
+		}})
+	} else if len(autoReply_reg.FindStringIndex(r.Content)) == 2 {
+		rContent := strings.TrimSpace(r.Content)
+		fastSubscribeText := config.Sysconfig["fastSubscribe"].(map[string]interface{})
+		skill := fastSubscribeText["skill"].(string)
+		hints := fastSubscribeText["hints"].([]interface{})
+		if strings.Contains(rContent, "订阅") {
+			if strings.HasPrefix(rContent, "订阅") && strings.TrimPrefix(rContent, "订阅") != "" {
+				rContent = strings.TrimPrefix(rContent, "订阅")
+				rContent = strings.TrimSpace(rContent)
+				returnValue, keyWord, hasOldKey := fastSubscribe(rContent, r.FromUserName)
+				if returnValue != 0 {
+					replyText := ""
+					if returnValue == -1 {
+						replyText = fmt.Sprintf(fastSubscribeText["fail"].(string), skill)
+					} else if returnValue == -2 {
+						replyText = fmt.Sprintf(fastSubscribeText["exists"].(string), keyWord, skill)
+					} else if returnValue == 1 {
+						if hasOldKey {
+							replyText = fmt.Sprintf(fastSubscribeText["success"].(string), keyWord, skill)
+							hint := TakeAlters(rContent, skill, hints)
+							if hint != "" {
+								replyText = hint
+							}
+						} else {
+							hint := TakeAlters(rContent, skill, hints)
+							if hint != "" {
+								replyText = hint
+							} else {
+								replyText = fmt.Sprintf(fastSubscribeText["firsttake"].(string), keyWord, skill)
+							}
+						}
+					}
+					w.ReplyText(replyText)
+				}
+			} else {
+				w.ReplyText(fmt.Sprintf(fmt.Sprint(hints[3])))
+			}
+		} else {
+			autoReplayMsg := config.Sysconfig["autoReplay"].(map[string]interface{})
+			w.ReplyNews([]Article{Article{
+				Title:       autoReplayMsg["title"].(string),
+				Url:         autoReplayMsg["url"].(string),
+				PicUrl:      autoReplayMsg["picUrl"].(string),
+				Description: autoReplayMsg["description"].(string),
+			}})
+		}
+	} else { //if now.Hour() > 17 || now.Hour() < 9 || tools.CheckNowHoliday() {
+		go tools.MQFW.Save("weixinoffline", &map[string]interface{}{
+			"s_openid":    r.FromUserName,
+			"s_msg":       r.Content,
+			"l_timestamp": now.Unix(),
+			"i_appid":     2,
+		})
+		if err == nil {
+			url := fmt.Sprintf(config.Sysconfig["proxysess"].(string), se.EncodeString(openid+","+user.UnionId+","+strconv.Itoa(int(time.Now().Unix()))+",rssset"))
+			w.ReplyText(fmt.Sprintf(config.Sysconfig["weixinAutoRpl"].(string), url))
+		}
+		log.Println("进入客服", r.FromUserName, r.Content) //测试信息
+		w.Reply2CustomerService()
+	}
+}
+
+//pc端招标订阅
+func pcSetUserKeys(user map[string]interface{}, shareid string) (map[string]interface{}, string, string) {
+	result := "fail"
+	subkey := redis.Get("sso", "pc_subscribe_"+shareid)
+	keys := strings.Split(fmt.Sprint(subkey), ";")
+	pccodepre := shareid[0:2]
+	if subkey != nil {
+		if o_jy, ok := user["o_jy"].(map[string]interface{}); ok {
+			if tmp, ok := o_jy["a_key"].([]interface{}); ok {
+				if len(tmp)+len(keys) <= 10 {
+					if pccodepre == "12" { //pc搜索关键词判重
+						if len(tmp) > 0 {
+							for _, a_key := range tmp {
+								if akey, ok := a_key.(map[string]interface{}); ok {
+									if oldkeys, ok := akey["key"].([]interface{}); ok {
+										oldkeystmp := ""
+										for _, key := range oldkeys {
+											oldkeystmp += key.(string)
+										}
+										newkeystmp := ""
+										newkeys := strings.Split(fmt.Sprint(subkey), " ")
+										for _, key := range newkeys {
+											newkeystmp += key
+										}
+										if newkeystmp == oldkeystmp {
+											result = "repeat"
+											break
+										}
+									}
+								}
+							}
+							if result != "repeat" {
+								keys := strings.Split(fmt.Sprint(subkey), " ")
+								tmp = append(tmp, map[string]interface{}{"key": keys, "notkey": []string{}, "infotype": []string{}, "area": []string{}})
+								o_jy["a_key"] = tmp
+								result = "ok"
+							}
+						} else {
+							keys := strings.Split(fmt.Sprint(subkey), " ")
+							tmp = append(tmp, map[string]interface{}{"key": keys, "notkey": []string{}, "infotype": []string{}, "area": []string{}})
+							o_jy["a_key"] = tmp
+							result = "ok"
+						}
+					} else {
+						for _, key := range keys {
+							tmp = append(tmp, map[string]interface{}{"key": []string{key}, "notkey": []string{}, "infotype": []string{}, "area": []string{}})
+						}
+						o_jy["a_key"] = tmp
+						result = "ok"
+					}
+				}
+				user["o_jy"] = o_jy
+			} else {
+				tmp := []interface{}{}
+				for _, key := range keys {
+					tmp = append(tmp, map[string]interface{}{"key": []string{key}, "notkey": []string{}, "infotype": []string{}, "area": []string{}})
+				}
+				user["o_jy"] = map[string]interface{}{"a_key": tmp}
+				result = "ok"
+			}
+		} else {
+			tmp := []interface{}{}
+			for _, key := range keys {
+				tmp = append(tmp, map[string]interface{}{"key": []string{key}, "notkey": []string{}, "infotype": []string{}, "area": []string{}})
+			}
+			user["o_jy"] = map[string]interface{}{"a_key": tmp}
+			result = "ok"
+		}
+	} else {
+		subkey = ""
+	}
+	return user, result, fmt.Sprint(subkey)
+}
+func saveUser(u *UserInfo, source string) (bool, string, string) {
+	defer util.Catch()
+	pcresult := "" //pc订阅结果
+	subkey := ""   //pc订阅关键词
+	pre := "0"
+	if len(source) >= 3 {
+		pre = source[0:2]
+	}
+	log.Println("保存用户:", subkey)
+	fg := false
+	tuser, ok := tools.MQFW.FindOneByField("user", `{"s_unionid":"`+u.UnionId+`","i_appid":2}`, `{"_id":1,"s_m_openid":1,"o_jy":1}`)
+	if !ok {
+		log.Println("save-user-error,mongodb-connection-error")
+		return fg, pcresult, subkey
+	}
+	if tuser == nil || *tuser == nil || len(*tuser) == 0 {
+		//创建全新用户
+		newUser := map[string]interface{}{}
+		newUser["s_name"] = u.Nickname
+		newUser["i_appid"] = int(2)
+		newUser["i_ispush"] = 1
+		newUser["s_m_openid"] = u.OpenId
+		newUser["s_unionid"] = u.UnionId
+		newUser["l_registedate"] = u.SubscribeTime
+		newUser["s_nickname"] = u.Nickname
+		newUser["i_sex"] = u.Sex
+		newUser["s_country"] = u.Country
+		newUser["s_province"] = u.Province
+		newUser["s_city"] = u.City
+		if pre == "10" || pre == "11" || pre == "12" || pre == "13" || pre == "14" || pre == "15" || pre == "16" || pre == "17" {
+			newUser["i_sourceid"], _ = strconv.Atoi("10000" + pre)
+		} else {
+			newUser["i_sourceid"], _ = strconv.Atoi(source)
+		}
+		headimage := downloadUserFace(u.HeadImageUrl)
+		if headimage != "" {
+			redis.Put("other", "newUser-"+u.OpenId, u.HeadImageUrl, 120)
+			newUser["s_headimage"] = headimage
+		}
+		bc, qmxid := CheckQmxUser(&newUser, u.UnionId)
+		if newUser["o_jy"] == nil {
+			newUser["i_ts_guide"] = 2
+			newUser["o_jy"] = map[string]interface{}{
+				"i_mode":     1,
+				"i_ratemode": 1,
+			}
+		}
+		if pre == "11" || pre == "12" { //11pc订阅,12pc搜索关键词
+			newUser, pcresult, subkey = pcSetUserKeys(newUser, source)
+			newUser["i_ts_guide"] = 1 //pc订阅关键词,不再走向导页
+			oj := newUser["o_jy"].(map[string]interface{})
+			oj["i_mode"] = 1
+			oj["i_ratemode"] = 1
+			oj["l_modifydate"] = time.Now().Unix()
+		}
+		_id := tools.MQFW.Save("user", newUser)
+		if _id != "" && qmxid != nil && bc {
+			go tools.MQFW.Update("user", &map[string]interface{}{
+				"_id": qmxid,
+			}, &map[string]interface{}{
+				"$set": map[string]interface{}{
+					"o_msgset.tender.i_status":       0,
+					"o_msgset.bid.i_status":          0,
+					"o_msgset.tender.i_switchstatus": 0,
+					"o_msgset.bid.i_switchstatus":    0,
+				},
+			}, false, false)
+		}
+		//查询企明星用户并更新企明星用户
+		fg = true
+	} else {
+		if (*tuser)["s_m_openid"] == nil {
+			//有小程序用户更新剑鱼用户信息
+			tools.MQFW.Update("user", &map[string]interface{}{
+				"_id": (*tuser)["_id"],
+			}, &map[string]interface{}{
+				"$set": map[string]interface{}{
+					"s_m_openid":    u.OpenId,
+					"l_registedate": u.SubscribeTime,
+					"i_sourceid":    util.IntAll(source),
+				},
+			}, false, false)
+			fg = true
+		} else { //针对取消关注的用户,关键词设置
+			*tuser, pcresult, subkey = pcSetUserKeys(*tuser, source)
+			if subkey != "" {
+				if pre == "11" || pre == "12" { //11pc订阅,12pc搜索关键词
+					tools.MQFW.Update("user",
+						&map[string]interface{}{"_id": (*tuser)["_id"]},
+						&map[string]interface{}{
+							"$set": map[string]interface{}{
+								"o_jy":       (*tuser)["o_jy"],
+								"i_ts_guide": 1,
+								"i_ispush":   1,
+							},
+						}, true, false)
+				}
+				fg = true
+			}
+		}
+	}
+	return fg, pcresult, subkey
+}
+
+//下一版这个逻辑可以删除!
+func CheckQmxUser(newUser *map[string]interface{}, UnionId string) (b bool, qmxid interface{}) {
+	tuser, _ := tools.MQFW.FindOneByField("user", `{"s_unionid":"`+UnionId+`","i_appid":{"$ne":2}}`, `{"o_msgset":1,"s_email":1}`)
+	if tuser != nil || *tuser == nil || len(*tuser) == 0 {
+		obj := (*tuser)["o_msgset"]
+		newObj := map[string]interface{}{
+			"i_mode":     1,
+			"i_ratemode": 1,
+		}
+		if obj != nil {
+			qmxid = (*tuser)["_id"]
+			keys := map[string]bool{}
+			scopes := map[string]bool{}
+			times := int64(0)
+			for _, types := range []string{"bid", "tender"} {
+				if objM, ok := obj.(map[string]interface{}); ok {
+					if objM[types] != nil {
+						if objMb, ok1 := objM[types].(map[string]interface{}); ok1 {
+							util.Try(func() {
+								a_key := util.ObjArrToStringArr(objMb["a_key"].([]interface{}))
+								for _, v := range a_key {
+									keys[v] = true
+								}
+							}, func(e interface{}) {})
+							s_scope := util.ObjToString(objMb["s_scope"])
+							for _, v := range strings.Split(s_scope, ",") {
+								scopes[v] = true
+							}
+							ttime := util.Int64All(objMb["l_modifydate"])
+							if ttime > times {
+								times = ttime
+							}
+						}
+					}
+				}
+			}
+			newObj["l_modifydate"] = times
+			area := []string{}
+			if !(scopes["A"] || scopes["全国"]) {
+				area = func(m map[string]bool) []string {
+					s := []string{}
+					for kk, _ := range m {
+						s = append(s, kk)
+					}
+					return s
+				}(scopes)
+			}
+			newObj["a_key"] = func(m map[string]bool) (res []map[string]interface{}) {
+				num := 0
+				res = []map[string]interface{}{}
+				for kk, _ := range m {
+					m1 := map[string]interface{}{}
+					num++
+					if num > 10 {
+						break
+					}
+					m1["key"] = strings.Split(kk, "+")
+					if len(area) > 0 {
+						m1["area"] = area
+					}
+					res = append(res, m1)
+				}
+				return
+			}(keys)
+			//邮件
+			smail := util.ObjToString((*tuser)["s_email"])
+			if smail != "" {
+				newObj["s_email"] = smail
+			}
+			(*newUser)["o_jy"] = newObj
+			b = true
+		}
+	}
+	return
+}
+
+//关注事件处理
+func Subscribe(w ResponseWriter, r *Request) {
+	defer util.Catch()
+	openid := r.FromUserName
+	user, err := Mux.GetUserInfo(openid)
+	var source = ""
+	if len(strings.Split(r.EventKey, "_")) == 2 {
+		source = strings.Split(r.EventKey, "_")[1]
+	}
+	if err != nil {
+		log.Println(err)
+	} else {
+		welcomemsg := config.Sysconfig["welcomemsg"].(string)
+		if time.Now().Before(time.Unix(1494777599, 0)) { //我为剑鱼代言活动 2017/5/14 23:59:59
+			welcomemsg = fmt.Sprintf(welcomemsg, "\n\n点击参与<a href='http://mp.weixin.qq.com/s/nUsXKU2dZuqb6B3s22rzCw'>“最剑鱼”slogan投票活动</a>,为你喜爱的剑鱼口号投上一票!")
+		} else {
+			//welcomemsg = strings.Replace(welcomemsg, "%s", "", -1)
+		}
+		if time.Now().After(time.Unix(config.ZqLuckdraw.StartDate, 0)) && time.Now().Before(time.Unix(config.ZqLuckdraw.EndDate, 0)) {
+			welcomemsg = config.ZqLuckdraw.Welcomemsg
+		}
+		if b, pcresult, subkey := saveUser(user, source); b {
+			pccodepre := "0" //分享过来source为空
+			if len(source) > 3 {
+				pccodepre = source[0:2]
+			}
+			if pccodepre == "12" || pccodepre == "11" { //16pc搜索关键词,17pc订阅=11,16搜索关键词=12
+				pchints := config.Sysconfig["pchints"].(map[string]interface{})
+				url := fmt.Sprintf(config.Sysconfig["proxysess"].(string), se.EncodeString(openid+","+user.UnionId+","+strconv.Itoa(int(time.Now().Unix()))+",rssset"))
+				if pcresult == "ok" {
+					w.ReplyText(fmt.Sprintf(fmt.Sprint(pchints["key_success"]), subkey, url))
+				} else if pcresult == "repeat" {
+					fastSubscribeText := config.Sysconfig["fastSubscribe"].(map[string]interface{})
+					exists := fastSubscribeText["exists"].(string)
+					w.ReplyText(fmt.Sprintf(exists, subkey, fastSubscribeText["skill"].(string)))
+				} else {
+					if subkey != "" {
+						w.ReplyText(fmt.Sprintf(fmt.Sprint(pchints["key_fail"]), subkey, url))
+					} else {
+						w.ReplyNews([]Article{Article{
+							Title:       "用剑鱼,给投标无限可能!",
+							Url:         url,
+							PicUrl:      config.Sysconfig["webdomain"].(string) + "/images/wol.png",
+							Description: "或者在聊天框回复“订阅 关键词”,如“订阅 教学设备”,来获取招标信息!",
+						}})
+						//w.ReplyText(fmt.Sprintf(welcomemsg, url))
+					}
+				}
+			} else if pccodepre == "13" { //18pc关注项目=13
+				param := new(jrpc.FollowData)
+				param.OpenId = openid
+				param.Server = fmt.Sprint(config.Sysconfig["webrpcport"])
+				param.Projectname = redis.GetStr("sso", "pc_subscribe_"+source)
+				if param.Projectname != "" {
+					rep := tools.MyFollow(param)
+					if rep == "" {
+						pchints := config.Sysconfig["pchints"].(map[string]interface{})
+						url := fmt.Sprintf(config.Sysconfig["proxysess"].(string), se.EncodeString(openid+","+user.UnionId+","+strconv.Itoa(int(time.Now().Unix()))+",myfollow"))
+						w.ReplyText(fmt.Sprintf(fmt.Sprint(pchints["project_fail"]), param.Projectname, url))
+					} else if rep == "repeat" { //重复
+						pchints := config.Sysconfig["pchints"].(map[string]interface{})
+						url := fmt.Sprintf(config.Sysconfig["proxysess"].(string), se.EncodeString(openid+","+user.UnionId+","+strconv.Itoa(int(time.Now().Unix()))+",myfollow"))
+						w.ReplyText(fmt.Sprintf(fmt.Sprint(pchints["project_repeat"]), param.Projectname, url))
+					} else {
+						pchints := config.Sysconfig["pchints"].(map[string]interface{})
+						url := fmt.Sprintf(config.Sysconfig["proxysess"].(string), se.EncodeString(openid+","+user.UnionId+","+strconv.Itoa(int(time.Now().Unix()))+",pcmyfollow")) + "__" + util.EncodeArticleId2ByCheck(rep)
+						w.ReplyText(fmt.Sprintf(fmt.Sprint(pchints["project_success"]), param.Projectname, url))
+					}
+				} else {
+					url := fmt.Sprintf(config.Sysconfig["proxysess"].(string), se.EncodeString(openid+","+user.UnionId+","+strconv.Itoa(int(time.Now().Unix()))+",rssset"))
+					//w.ReplyText(fmt.Sprintf(welcomemsg, url))
+					w.ReplyNews([]Article{Article{
+						Title:       "用剑鱼,给投标无限可能!",
+						Url:         url,
+						PicUrl:      config.Sysconfig["webdomain"].(string) + "/images/wol.png",
+						Description: "或者在聊天框回复“订阅 关键词”,如“订阅 教学设备”,来获取招标信息!",
+					}})
+				}
+			} else {
+				url := fmt.Sprintf(config.Sysconfig["proxysess"].(string), se.EncodeString(openid+","+user.UnionId+","+strconv.Itoa(int(time.Now().Unix()))+",rssset"))
+				//w.ReplyText(fmt.Sprintf(welcomemsg, url))
+				w.ReplyNews([]Article{Article{
+					Title:       "用剑鱼,给投标无限可能!",
+					Url:         url,
+					PicUrl:      config.Sysconfig["webdomain"].(string) + "/images/wol.png",
+					Description: "或者在聊天框回复“订阅 关键词”,如“订阅 教学设备”,来获取招标信息!",
+				}})
+			}
+			//正则查看是不是临时二维码
+			if digitreg.MatchString(source) {
+				//TODO 处理分享(邀请)类的二维码,记录邀请关系
+				if strings.HasPrefix(source, "32") {
+					SaveInviteLink(source, openid, false)
+				}
+			}
+		} else {
+			updateIsPush(openid, 1)
+			url := fmt.Sprintf(config.Sysconfig["proxysess"].(string), se.EncodeString(openid+","+user.UnionId+","+strconv.Itoa(int(time.Now().Unix()))+",rssset"))
+			//w.ReplyText(fmt.Sprintf(welcomemsg, url))
+			w.ReplyNews([]Article{Article{
+				Title:       "用剑鱼,给投标无限可能!",
+				Url:         url,
+				PicUrl:      config.Sysconfig["webdomain"].(string) + "/images/wol.png",
+				Description: "或者在聊天框回复“订阅 关键词”,如“订阅 教学设备”,来获取招标信息!",
+			}})
+			//正则查看是不是临时二维码
+			if digitreg.MatchString(source) {
+				//TODO 处理分享(邀请)类的二维码,记录邀请关系
+				if strings.HasPrefix(source, "32") {
+					SaveInviteLink(source, openid, true)
+				}
+			}
+		}
+		saveUserLog(source, openid)
+		redis.Put("sso", "p_share_"+source, openid, 600)
+		redis.Put("sso", "p_userlogs_"+source, openid, 600)
+	}
+	go saveSubscribe(openid, r.Event, r.EventKey, 1)
+}
+
+//
+func saveUserLog(shareId, openid string) {
+	defer util.Catch()
+	date := time.Now()
+	//var w ResponseWriter
+	//var r *Request
+	userData := redis.Get("sso", "p_userdata_"+shareId)
+	//openid := r.FromUserName
+	user, _ := Mux.GetUserInfo(openid)
+	nickname := user.Nickname
+	province := user.Province
+	action := "关注"
+	model := ""
+	referer := ""
+	paramkey := ""
+	activecode := ""
+	usersource := ""
+	os := ""
+	browse := ""
+	//infoTime := date.Unix()
+	if userData != nil {
+		infoData := map[string]interface{}{}
+		tmp, _ := json.Marshal(userData)
+		json.Unmarshal(tmp, &infoData)
+		model = util.ObjToString(infoData["RModule"])
+		referer = util.ObjToString(infoData["RReferer"])
+		usersource = util.ObjToString(infoData["RSource"])
+		paramkey = util.ObjToString(infoData["Rparamkey"])
+		activecode = util.ObjToString(infoData["RActiveCode"])
+		os = util.ObjToString(infoData["Ros"])
+		browse = util.ObjToString(infoData["Rbrowse"])
+	} else {
+		model = "首页"
+		referer = "剑鱼网站"
+		usersource = "剑鱼网站"
+	}
+	if os == "" {
+		os = "其他"
+	}
+	if browse == "" {
+		browse = "其他"
+	}
+	log.Println(browse, "shareId:", shareId, util.FormatDate(&date, util.Date_Short_Layout))
+	//timelog := time.Unix(infoTime, 0).Format("2006-01-02 15:04:05")
+	//timestamp, _ := time.Parse("2006-01-02 15:04:05", timelog) //新用户关注取关日期
+	err := util.InsertInto("jy_logs",
+		"user_log",
+		[]influxdb.Tag{
+			{Key: "province", Value: province},
+			{Key: "action", Value: action},
+			{Key: "usersource", Value: usersource},
+			{Key: "model", Value: model},
+			{Key: "s_date", Value: util.FormatDate(&date, util.Date_Short_Layout)},
+		},
+		map[string]interface{}{
+			"openid":     openid,
+			"nickname":   nickname,
+			"referer":    referer,
+			"paramkey":   paramkey,
+			"activecode": activecode,
+			"browse":     browse,
+			"os":         os,
+			"sourceid":   shareId,
+			//"s_date":     util.FormatDate(&date, util.Date_Full_Layout),
+		},
+		time.Now(),
+		"autogen",
+	)
+	if err != nil {
+		log.Println("influxdb:", err.Error())
+	} else {
+		log.Println(err, "---", nickname)
+	}
+}
+
+//保存没有不正常使用的用户
+func SaveAbnormal(openid string) {
+	defer util.Catch()
+	if openid != "" {
+		user, err := Mux.GetUserInfo(openid)
+		if err != nil {
+			log.Println(err)
+		} else {
+			saveUser(user, "")
+		}
+	}
+}
+
+//扫码事件
+func ScanHandler(w ResponseWriter, r *Request) {
+	defer util.Catch()
+	openid := r.FromUserName
+	m, b := tools.MQFW.FindOneByField("user", `{"s_m_openid":"`+openid+`"}`, `{"_id":1,"s_headimage":1,"o_jy":1}`)
+	//log.Println("SCAN:", openid, r.EventKey, b)
+	if b {
+		pccodepre := r.EventKey[0:2]
+		user, err := Mux.GetUserInfo(openid)
+		if *m == nil {
+			if err != nil {
+				log.Println(err)
+			} else {
+				if b, _, _ := saveUser(user, r.EventKey); b {
+					url := fmt.Sprintf(config.Sysconfig["proxysess"].(string), se.EncodeString(openid+","+user.UnionId+","+strconv.Itoa(int(time.Now().Unix()))+",rssset"))
+					w.ReplyText(fmt.Sprintf(config.Sysconfig["welcomemsg"].(string), url))
+				}
+			}
+		} else {
+			headimage := ""
+			if (*m)["s_headimage"] == nil || fmt.Sprint((*m)["s_headimage"]) == "" { //
+				redis.Put("other", "newUser-"+user.OpenId, user.HeadImageUrl, 120)
+				headimage = downloadUserFace(user.HeadImageUrl)
+			} else {
+				headimage = fmt.Sprint((*m)["s_headimage"])
+			}
+			if pccodepre == "13" { //103pc关注项目
+				param := new(jrpc.FollowData)
+				param.OpenId = openid
+				param.Server = fmt.Sprint(config.Sysconfig["webrpcport"])
+				param.Projectname = redis.GetStr("sso", "pc_subscribe_"+r.EventKey)
+				if param.Projectname != "" {
+					rep := tools.MyFollow(param)
+					if rep == "" {
+						pchints := config.Sysconfig["pchints"].(map[string]interface{})
+						url := fmt.Sprintf(config.Sysconfig["proxysess"].(string), se.EncodeString(openid+","+user.UnionId+","+strconv.Itoa(int(time.Now().Unix()))+",myfollow"))
+						w.ReplyText(fmt.Sprintf(fmt.Sprint(pchints["project_fail"]), param.Projectname, url))
+					} else if rep == "repeat" { //重复
+						pchints := config.Sysconfig["pchints"].(map[string]interface{})
+						url := fmt.Sprintf(config.Sysconfig["proxysess"].(string), se.EncodeString(openid+","+user.UnionId+","+strconv.Itoa(int(time.Now().Unix()))+",myfollow"))
+						w.ReplyText(fmt.Sprintf(fmt.Sprint(pchints["project_repeat"]), param.Projectname, url))
+					} else {
+						pchints := config.Sysconfig["pchints"].(map[string]interface{})
+						url := fmt.Sprintf(config.Sysconfig["proxysess"].(string), se.EncodeString(openid+","+user.UnionId+","+strconv.Itoa(int(time.Now().Unix()))+",pcmyfollow")) + "__" + util.EncodeArticleId2ByCheck(rep)
+						w.ReplyText(fmt.Sprintf(fmt.Sprint(pchints["project_success"]), param.Projectname, url))
+					}
+				}
+			} else if pccodepre == "11" || pccodepre == "12" { //101pc订阅,102pc搜索关键词
+				pcUser, pcresult, subkey := pcSetUserKeys(*m, r.EventKey)
+				go tools.MQFW.Update("user", &map[string]interface{}{
+					"s_m_openid": openid,
+				}, &map[string]interface{}{
+					"$set": map[string]interface{}{
+						"s_headimage": headimage,
+						"o_jy":        pcUser["o_jy"],
+					},
+				}, false, false)
+				if pcresult == "ok" {
+					pchints := config.Sysconfig["pchints"].(map[string]interface{})
+					url := fmt.Sprintf(config.Sysconfig["proxysess"].(string), se.EncodeString(openid+","+user.UnionId+","+strconv.Itoa(int(time.Now().Unix()))+",rssset"))
+					w.ReplyText(fmt.Sprintf(fmt.Sprint(pchints["key_success"]), subkey, url))
+				} else if pcresult == "repeat" {
+					fastSubscribeText := config.Sysconfig["fastSubscribe"].(map[string]interface{})
+					exists := fastSubscribeText["exists"].(string)
+					w.ReplyText(fmt.Sprintf(exists, subkey, fastSubscribeText["skill"].(string)))
+				} else if pcresult == "fail" && subkey != "" {
+					pchints := config.Sysconfig["pchints"].(map[string]interface{})
+					url := fmt.Sprintf(config.Sysconfig["proxysess"].(string), se.EncodeString(openid+","+user.UnionId+","+strconv.Itoa(int(time.Now().Unix()))+",rssset"))
+					w.ReplyText(fmt.Sprintf(fmt.Sprint(pchints["key_fail"]), subkey, url))
+				}
+			} else {
+				go tools.MQFW.Update("user", &map[string]interface{}{
+					"s_m_openid": openid,
+				}, &map[string]interface{}{
+					"$set": map[string]interface{}{
+						"s_headimage": headimage,
+					},
+				}, false, false)
+			}
+		}
+		if pccodepre < "32" {
+			log.Println("扫描登录:", r.EventKey)
+			redis.Put("sso", "p_share_"+r.EventKey, openid, 600)
+		}
+	} else {
+		log.Println("scan-error,mongodb-conn-error")
+	}
+}
+
+//取消关注事件
+func UnSubscribe(w ResponseWriter, r *Request) {
+	openid := r.FromUserName
+	log.Println("取关", openid)
+	//updateUser(openid)
+	updateIsPush(openid, 0)
+	updateUserLog(openid) //修改用户日志
+	go saveSubscribe(openid, r.Event, r.EventKey, 0)
+}
+
+//
+func updateUserLog(openid string) {
+	defer util.Catch()
+	if openid == "" {
+		return
+	}
+	//date := time.Now()
+	//infoTime := date.Unix()
+	nickname := ""
+	province := ""
+	action := "取关"
+	model := ""
+	referer := ""
+	paramkey := ""
+	activecode := ""
+	usersource := ""
+	os := ""
+	browse := ""
+	shareId := ""
+	err := util.Search("jy_logs",
+		func(row influxdb.Row) error {
+			model = util.ObjToString(row.ValueByName("model"))
+			referer = util.ObjToString(row.ValueByName("referer"))
+			activecode = util.ObjToString(row.ValueByName("activecode"))
+			usersource = util.ObjToString(row.ValueByName("usersource"))
+			nickname = util.ObjToString(row.ValueByName("nickname"))
+			province = util.ObjToString(row.ValueByName("province"))
+			os = util.ObjToString(row.ValueByName("os"))
+			browse = util.ObjToString(row.ValueByName("browse"))
+			paramkey = util.ObjToString(row.ValueByName("paramkey"))
+			shareId = util.ObjToString(row.ValueByName("sourceid"))
+			return nil
+		},
+		`select "model","referer","paramkey","activecode","usersource","nickname","sourceid","province","os","browse" from autogen.user_log where openid=$openid order by time desc limit 1`,
+		influxdb.Param("openid", openid),
+	)
+	if err != nil {
+		log.Println("取消关注用户信息报错:", err.Error())
+	} else {
+		//timelog := time.Unix(infoTime, 0).Format("2006-01-02 15:04:05")
+		//timestamp, _ := time.Parse("2006-01-02 15:04:05", timelog) //新用户关注取关日期
+		err := util.InsertInto("jy_logs",
+			"user_log",
+			[]influxdb.Tag{
+				{Key: "province", Value: province},
+				{Key: "action", Value: action},
+				{Key: "usersource", Value: usersource},
+				{Key: "model", Value: model},
+			},
+			map[string]interface{}{
+				"openid":     openid,
+				"nickname":   nickname,
+				"referer":    referer,
+				"paramkey":   paramkey,
+				"activecode": activecode,
+				"browse":     browse,
+				"os":         os,
+				"sourceid":   shareId,
+				//"s_date":     util.FormatDate(&date, util.Date_Full_Layout),
+			},
+			time.Now(),
+			"autogen",
+		)
+		if err != nil {
+			log.Println("influxdb11:", err.Error())
+		}
+	}
+}
+
+//
+func getMonth(mon string) int {
+	month := map[string]int{
+		"January": 1, "February": 2,
+		"March": 3, "April": 4,
+		"May": 5, "June": 6,
+		"July": 7, "August": 8,
+		"September": 9, "October": 10,
+		"November": 11, "December": 12,
+	}
+	return month[mon]
+}
+
+//
+func saveSubscribe(openid, stype, stypekey string, image int) {
+	if image == 0 {
+		tools.MQFW.Save("jy_subscribe", &map[string]interface{}{
+			"s_m_openid":  openid,
+			"s_event":     stype,
+			"s_eventkey":  stypekey,
+			"l_date":      time.Now().Unix(),
+			"s_headimage": "",
+		})
+	} else {
+		tools.MQFW.Save("jy_subscribe", &map[string]interface{}{
+			"s_m_openid": openid,
+			"s_event":    stype,
+			"s_eventkey": stypekey,
+			"l_date":     time.Now().Unix(),
+		})
+	}
+
+}
+
+//更新用户信息
+/**
+func updateUser(openid string) bool {
+	go tools.MQFW.Update("user", &map[string]interface{}{
+		"s_m_openid": openid,
+	}, &map[string]interface{}{
+		"$set": map[string]interface{}{
+			"o_jy_msgset.tender.i_status":       0,
+			"o_jy_msgset.bid.i_status":          0,
+			"o_jy_msgset.tender.i_switchstatus": 0,
+			"o_jy_msgset.bid.i_switchstatus":    0,
+		},
+	}, false, false)
+	return true
+}
+**/
+
+//创建菜单
+func CreateMenu(rw http.ResponseWriter, r *http.Request) {
+	urlstr := fmt.Sprintf("https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s/wx/oauth/%s&response_type=code&scope=snsapi_base&state=1#wechat_redirect", config.Sysconfig["appid"], config.Sysconfig["webdomain"], "%s")
+	log.Println(urlstr)
+	menu := &Menu{make([]MenuButton, 3)}
+	menu.Buttons[0].Name = "招标搜索"
+	menu.Buttons[0].Type = MenuButtonTypeUrl
+	menu.Buttons[0].Url = fmt.Sprintf(urlstr, "searchinfo")
+
+	menu.Buttons[1].Name = "招标订阅"
+	menu.Buttons[1].Type = MenuButtonTypeUrl
+	menu.Buttons[1].Url = fmt.Sprintf(urlstr, "rssset")
+
+	menu.Buttons[2].Name = "发现"
+	menu.Buttons[2].SubButtons = make([]MenuButton, 5)
+
+	menu.Buttons[2].SubButtons[0].Name = "产品更新/帮助"
+	menu.Buttons[2].SubButtons[0].Type = MenuButtonTypeUrl
+	menu.Buttons[2].SubButtons[0].Url = fmt.Sprintf(urlstr, "useskill")
+
+	menu.Buttons[2].SubButtons[1].Name = "\u2139关于剑鱼"
+	menu.Buttons[2].SubButtons[1].Type = MenuButtonTypeUrl
+	menu.Buttons[2].SubButtons[1].Url = fmt.Sprintf(urlstr, "about")
+
+	menu.Buttons[2].SubButtons[2].Name = "\u270d意见反馈"
+	menu.Buttons[2].SubButtons[2].Type = MenuButtonTypeUrl
+	menu.Buttons[2].SubButtons[2].Url = fmt.Sprintf(urlstr, "feedback")
+
+	menu.Buttons[2].SubButtons[3].Name = string(0x1F465) + "招标社区"
+	menu.Buttons[2].SubButtons[3].Type = MenuButtonTypeUrl
+	menu.Buttons[2].SubButtons[3].Url = fmt.Sprintf(urlstr, "community")
+
+	menu.Buttons[2].SubButtons[4].Name = "\u2764我关注的项目"
+	menu.Buttons[2].SubButtons[4].Type = MenuButtonTypeUrl
+	menu.Buttons[2].SubButtons[4].Url = fmt.Sprintf(urlstr, "myfollow")
+
+	err := Mux.CreateMenu(menu)
+	if err != nil {
+		log.Println(err)
+	} else {
+		log.Println("create menu success!")
+	}
+}
+
+var digitreg *regexp.Regexp = regexp.MustCompile("^\\d+$")
+
+//生成推广临时二维码
+func TmpCodeHandle(w http.ResponseWriter, r *http.Request) {
+	param := r.RequestURI[12:]
+	if !digitreg.MatchString(param) {
+		return
+	}
+	//TODO 参数存redis
+	iparam, _ := strconv.Atoi(param)
+	//构造自定义消息文本
+	var msg struct {
+		Expire int    `json:"expire_seconds"`
+		Name   string `json:"action_name"`
+		Info   struct {
+			Scene struct {
+				Id int `json:"scene_id"`
+			} `json:"scene"`
+		} `json:"action_info"`
+	}
+	msg.Expire = 1200
+	msg.Name = "QR_SCENE"
+	msg.Info.Scene.Id = iparam
+	ret, err := Mux.PostCustomMsg("https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=", &msg)
+	if err != nil {
+		log.Println(err.Error())
+	} else {
+		tmp := map[string]interface{}{}
+		json.Unmarshal(ret, &tmp)
+		url := tmp["url"].(string)
+		w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
+		w.Header().Set("Pragma", "no-cache")
+		w.Header().Set("Expires", "0")
+		w.Header().Set("Content-Type", "image/png")
+		r, _ := qr.Encode(url, qr.L)
+		pngdat := r.PNG()
+		w.Write(pngdat)
+	}
+}
+
+//web check user
+var checkFlag = 1
+
+func WebCheckUser(w http.ResponseWriter, r *http.Request) {
+	r.ParseForm()
+	u := r.FormValue("u")
+	p := r.FormValue("p")
+	if u == "rr" && p == "wxtopnettest" && checkFlag == 1 {
+		log.Println("web check wxuser!")
+		checkFlag = 0
+		go func() {
+			time.Sleep(30 * time.Minute)
+			checkFlag = 1
+		}()
+		StartCheckJob()
+		w.Write([]byte("ok."))
+	} else {
+		w.Write([]byte("err."))
+	}
+
+}
+
+//生成永久二维码
+func LimitCodeHandle(w http.ResponseWriter, r *http.Request) {
+	param := r.RequestURI[14:]
+	val, err := strconv.Atoi(param)
+	if err == nil {
+		qc, _err := Mux.CreateQRLimitScene(val)
+		if _err != nil {
+			log.Println("生永久二维码出错:", _err)
+		} else if qc != nil {
+			url := "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + qc.Ticket
+			http.Redirect(w, r, url, 301)
+		}
+	}
+}
+
+//保存用户邀请关系
+func SaveInviteLink(shareid string, myopenid string, isolduser bool) {
+	log.Println("save user invitelink ", myopenid, shareid, isolduser)
+	sharelock.Lock()
+	//对于老用户的处理
+	if isolduser { //找我自己是不是别人邀请的
+		invite, _ := tools.MQFW.FindOne("person_invitelink", `{"s_target_openid":"`+myopenid+`"}`)
+		if *invite != nil {
+			sharelock.Unlock()
+			return
+		}
+		myinvite, _ := tools.MQFW.FindOne("person_invitelink", `{"s_source_openid":"`+myopenid+`"}`)
+		if *myinvite != nil {
+			sharelock.Unlock()
+			return
+		}
+	}
+	//找邀请人信息
+
+	ret, _ := tools.MQFW.FindOne("person_share", `{"i_shareid": "`+shareid+`"}`)
+	if *ret == nil {
+		log.Println("wu share info!!!!")
+		sharelock.Unlock()
+		return
+	}
+	source_opendid := (*ret)["s_openid"]
+	data := map[string]interface{}{
+		"s_target_openid": myopenid,
+		"s_source_openid": source_opendid,
+		"l_timestamp":     time.Now().Unix(),
+		"i_shareid":       shareid,
+		"s_businesscode":  (*ret)["s_businesscode"],
+	}
+	tools.MQFW.Save("person_invitelink", data)
+
+	//存完关系,老用户就结束了
+	if isolduser {
+		sharelock.Unlock()
+		return
+	}
+	sharelock.Unlock()
+}
+
+//生成永久二维码
+func YjHandle(w http.ResponseWriter, r *http.Request) {
+	param := r.RequestURI[8:]
+	if !digitreg.MatchString(param) {
+		return
+	}
+	//TODO 参数存redis
+	iparam, _ := strconv.Atoi(param)
+	//构造自定义消息文本
+	var msg struct {
+		Name string `json:"action_name"`
+		Info struct {
+			Scene struct {
+				Id int `json:"scene_id"`
+			} `json:"scene"`
+		} `json:"action_info"`
+	}
+	msg.Name = "QR_LIMIT_SCENE"
+	msg.Info.Scene.Id = iparam
+	ret, err := Mux.PostCustomMsg("https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=", &msg)
+	if err != nil {
+		log.Println(err.Error())
+	} else {
+		tmp := map[string]interface{}{}
+		json.Unmarshal(ret, &tmp)
+		log.Println(tmp)
+		url := tmp["url"].(string)
+		w.Write([]byte(url))
+	}
+
+}
+
+//获取AccessToken
+func GetToken(w http.ResponseWriter, r *http.Request) {
+	fmt.Fprint(w, Mux.GetToken())
+}
+
+//生成推广临时二维码
+func AdvHandle(w http.ResponseWriter, r *http.Request) {
+	param := r.RequestURI[8:]
+	if !digitreg.MatchString(param) {
+		return
+	}
+	//TODO 参数存redis
+	iparam, _ := strconv.Atoi(param)
+	//构造自定义消息文本
+	var msg struct {
+		Expire int    `json:"expire_seconds"`
+		Name   string `json:"action_name"`
+		Info   struct {
+			Scene struct {
+				Id int `json:"scene_id"`
+			} `json:"scene"`
+		} `json:"action_info"`
+	}
+	msg.Expire = 2592000
+	msg.Name = "QR_SCENE"
+	msg.Info.Scene.Id = iparam
+	ret, err := Mux.PostCustomMsg("https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=", &msg)
+	if err != nil {
+		log.Println(err.Error())
+	} else {
+		tmp := map[string]interface{}{}
+		json.Unmarshal(ret, &tmp)
+		url := tmp["url"].(string)
+		w.Write([]byte(url))
+	}
+
+}
+
+//修改是否推送的状态
+func updateIsPush(openid string, status int) {
+	tools.MQFW.Update("user", map[string]interface{}{"s_m_openid": openid}, map[string]interface{}{
+		"$set": map[string]interface{}{
+			"i_ispush": status,
+		},
+	}, false, false)
+	tools.MQFW.Update("follow_project", map[string]interface{}{"s_openid": openid}, map[string]interface{}{
+		"$set": map[string]interface{}{
+			"i_ispush": status,
+		},
+	}, false, true)
+}
+
+//快速订阅关键词
+func fastSubscribe(content, openId string) (int, string, bool) {
+	user, ok := tools.MQFW.FindOneByField("user", `{"s_m_openid":"`+openId+`"}`, `{"o_jy":1}`)
+	hasOldKey := false
+	if ok && user != nil {
+		//content = strings.TrimPrefix(content, "订阅")
+		//content = strings.TrimSpace(content)
+		content = regexp.MustCompile("\\s+").ReplaceAllString(content, " ")
+		keyWord := strings.Replace(content, " ", "+", -1)
+		o_jy, _ := (*user)["o_jy"].(map[string]interface{})
+		if o_jy == nil {
+			o_jy = make(map[string]interface{})
+		}
+		a_key, _ := o_jy["a_key"].([]interface{})
+		if len(a_key) > 0 {
+			hasOldKey = true
+		}
+		//关键词达到最大数量
+		if len(a_key) >= 10 {
+			return -1, content, hasOldKey
+		}
+		//关键词已存在
+		for _, v := range a_key {
+			if tmp, ok := v.(map[string]interface{}); ok {
+				key := tmp["key"].([]interface{})
+				keys := ""
+				for _, val := range key {
+					if keys == "" {
+						keys += util.ObjToString(val)
+					} else {
+						keys += "+" + util.ObjToString(val)
+					}
+				}
+				if keys == keyWord {
+					return -2, content, hasOldKey
+				}
+			}
+		}
+		key := map[string][]string{}
+		key["key"] = strings.Split(keyWord, "+")
+		key["notkey"] = []string{}
+		key["infotype"] = []string{}
+		key["area"] = []string{}
+		a_key = append(a_key, key)
+		tools.MQFW.Update("user", `{"s_m_openid":"`+openId+`"}`, map[string]interface{}{
+			"$set": map[string]interface{}{
+				"i_ts_guide":        1,
+				"o_jy.a_key":        a_key,
+				"o_jy.l_modifydate": time.Now().Unix(),
+			},
+		}, false, false)
+		return 1, content, hasOldKey
+	}
+	return 0, content, hasOldKey
+}
+
+//订阅帮助提示
+func TakeAlters(keyWord, skill string, hints []interface{}) string {
+	keys := strings.Split(keyWord, " ")
+	if len(keys) >= 3 {
+		return fmt.Sprintf(hints[0].(string), keyWord, skill)
+	}
+	for _, v := range keys {
+		if len(v) > 59 {
+			return fmt.Sprintf(hints[1].(string), keyWord, skill)
+		}
+		if !regKey.MatchString(v) {
+			return fmt.Sprintf(hints[2].(string), keyWord, skill)
+		}
+	}
+	return ""
+}
+
+//下载微信用户头像
+func downloadUserFace(url string) string {
+	var filename string
+	tn := time.Now()
+	filename = fmt.Sprintf("/upload/%s/%s/%s/%s%d.jpg", tn.Format("2006"), tn.Format("01"), tn.Format("02"), tn.Format("20060102150405"), rand.Intn(9999)+1000)
+	go func() {
+		util.Try(func() {
+			os.MkdirAll(config.Sysconfig["headimage"].(string)+fmt.Sprintf("/upload/%s/%s/%s", tn.Format("2006"), tn.Format("01"), tn.Format("02")), 0777)
+			fi, err := os.OpenFile(config.Sysconfig["headimage"].(string)+filename, os.O_CREATE|os.O_TRUNC|os.O_SYNC|os.O_RDWR, 0x666)
+			defer fi.Close()
+			resp, err := http.Get(url)
+			defer resp.Body.Close()
+			if err == nil {
+				io.Copy(fi, resp.Body)
+			} else {
+				log.Println("download userface err:", err.Error())
+			}
+		}, func(e interface{}) {})
+	}()
+	return filename
+}

+ 46 - 0
src/jfw/weixin/src/wx/wxjssdk.go

@@ -0,0 +1,46 @@
+package wx
+
+import (
+	"config"
+	"encoding/json"
+	"fmt"
+	"log"
+	"math/rand"
+	"qfw/util/redis"
+	"time"
+)
+
+var RAND *rand.Rand
+var appid string
+
+func init() {
+	RAND = rand.New(rand.NewSource(time.Now().UnixNano()))
+	appid = config.Sysconfig["appid"].(string)
+}
+
+func GetRand() int {
+	return RAND.Intn(99999999)
+}
+
+func SignJSSDK(url string) []string {
+	var signature []string
+	var key = "wxsignature_" + url
+	if ret := redis.Get("other", key); ret != nil {
+		if d, err := json.Marshal(ret); err == nil {
+			json.Unmarshal(d, &signature)
+		}
+	}
+	if len(signature) == 0 {
+		timestamp := time.Now().Unix()
+		noncestr := fmt.Sprintf("%d", GetRand())
+		sign, err := Mux.JsSignature(url, timestamp, noncestr)
+		if err != nil {
+			log.Println(err)
+			signature = []string{"", "", "", ""}
+		} else {
+			signature = []string{appid, fmt.Sprintf("%d", timestamp), noncestr, sign}
+			redis.Put("other", key, signature, 90*60)
+		}
+	}
+	return signature
+}

部分文件因为文件数量过多而无法显示