Procházet zdrojové kódy

Merge branch 'master' into hotfix/v4.8.90.2

lianbingjie před 1 rokem
rodič
revize
ff273b99b7
22 změnil soubory, kde provedl 1033 přidání a 147 odebrání
  1. 2 1
      src/config.json
  2. 1 0
      src/jfw/modules/app/src/web/staticres/jyapp/js/common.js
  3. 1 1
      src/jfw/modules/app/src/web/templates/big-member/page_report_analysis_history.html
  4. 31 7
      src/jfw/modules/app/src/web/templates/me/appVersion.html
  5. 2 1
      src/jfw/modules/bigmember/src/config.json
  6. 1 0
      src/jfw/modules/bigmember/src/config/config.go
  7. 9 7
      src/jfw/modules/bigmember/src/entity/marketAnalysis/customizad_distribution.go
  8. 28 12
      src/jfw/modules/bigmember/src/entity/marketAnalysis/customized_analysis.go
  9. 346 24
      src/jfw/modules/bigmember/src/entity/marketAnalysis/marketAnalysisEntity.go
  10. 21 5
      src/jfw/modules/bigmember/src/entity/marketAnalysis/scaleRefineQuery.go
  11. 41 7
      src/jfw/modules/bigmember/src/entity/report.go
  12. 159 7
      src/jfw/modules/bigmember/src/service/report/marketAnalysis.go
  13. 72 40
      src/jfw/modules/bigmember/src/service/report/report.go
  14. 2 2
      src/jfw/modules/bigmember/src/util/email.go
  15. 6 5
      src/jfw/modules/publicapply/src/userbase/entity/entity.go
  16. 1 1
      src/web/staticres/common-module/collection/js/keyword-mobile.js
  17. 46 7
      src/web/staticres/common-module/diy-report/css/report-list.css
  18. 52 2
      src/web/staticres/common-module/diy-report/js/report-list.js
  19. 9 2
      src/web/staticres/common-module/report-analysis/css/report_analysis.css
  20. 83 9
      src/web/staticres/common-module/report-analysis/js/report_analysis.js
  21. 119 6
      src/web/staticres/common-module/report-analysis/js/report_analysis_history.js
  22. 1 1
      src/web/templates/big-member/wx/page_report_analysis_history.html

+ 2 - 1
src/config.json

@@ -62,7 +62,8 @@
     "wxMerge":"/weixin/frontPage/userMerge/free/index",
     "weChatUrlEnt": "/jy_mobile/project/joined/list?identity=ent",
     "weChatUrl": "/jy_mobile/project/joined/list",
-    "agencyInfo": "/jy_mobile/message/todoList?msgType=11"
+    "agencyInfo": "/jy_mobile/message/todoList?msgType=11",
+    "report_analysis": "/jyapp/big/page/report_analysis?id=%s"
   },
   "jy_activeset": {
     "activitystartcode": "3201000000",

+ 1 - 0
src/jfw/modules/app/src/web/staticres/jyapp/js/common.js

@@ -1833,6 +1833,7 @@ function afterPageInit () {
 }
 
 function compareVersion (curVersion, upVersion) {
+  if (!curVersion || !upVersion) return
   v1 = curVersion.split(".");
   v2 = upVersion.split(".");
   var maxlen = v1.length > v2.length ? v1.length : v2.length;

+ 1 - 1
src/jfw/modules/app/src/web/templates/big-member/page_report_analysis_history.html

@@ -31,7 +31,7 @@
             </van-tabs>
             <section v-show="tabActiveName === 'analysis'" class="j-main analysis-content"></section>
             <section v-show="tabActiveName === 'history'" class="j-main history-content">
-                <report-list-mobile-component ref="list" @go-report="goToAnalysis"></report-list-mobile-component>
+                <report-list-mobile-component ref="list" @go-report="goToAnalysis"  @go-delete="goDelete"></report-list-mobile-component>
             </section>
         </div>
     </div>

+ 31 - 7
src/jfw/modules/app/src/web/templates/me/appVersion.html

@@ -73,7 +73,7 @@
         <div class="j-main" id="appVersion" v-cloak>
             <div class="jy-info">
                 <div class="logo"></div>
-                <div class="version">-  For ${system} V${curVersion}  -</div>
+                <div class="version" v-show="showVersion">-  For ${system} V${appVersion}  -</div>
             </div>
             <div class="jy-list">
                 <van-cell-group>
@@ -83,7 +83,7 @@
                     <van-cell v-if="system == 'Android' && isNewApk" title="新版本检测" @click="updateApp"  :is-link="isUpdate" :value-class="isUpdate ? 'new-version' : ''" :value="isUpdate ? '发现新版本' : '已是最新版本'"></van-cell>
                 </van-cell-group>
             </div>
-            <div class="app-icp">
+            <div class="app-icp" v-show="showICP">
               客服电话:400-108- 6670
               <br>
               ICP备案号:京ICP备2021020018号-6A
@@ -97,6 +97,7 @@
     <!--E-当前页面的资源-->
     {{include "/big-member/commonjs.html"}}
     <script src='{{Cdns .Host "seo" "cdn"|SafeUrl}}/jyapp/js/common.js?v={{Msg "seo" "version"}}'></script>
+    <script src="{{Cdns .Host "seo" "cdn"|SafeUrl}}/common-module/public/js/utils.js"></script>
     <script>
         var vNode = {
             delimiters: ['${', '}'],
@@ -111,14 +112,23 @@
                 channel:'',
                 androidVersion:'',
                 getPhoneType:'',
-                isNewApk:false
+                isNewApk:false,
+                showVersion: false,
+                showICP: false,
+            },
+            computed:{
+              appVersion: function () {
+                if (this.curVersion) {
+                  return 'V' + this.curVersion
+                } else {
+                  return ''
+                }
+              },
             },
-            computed:{},
-            created () {},
             mounted() {
-                if(utils.isAndroid) {
+                if(utils.$envs.inAndroid) {
                   this.system = 'Android'
-                } else if (utils.isIos) {
+                } else if (utils.$envs.inIos) {
                   this.system = 'iPhone'
                 } else {
                   this.system = '其它'
@@ -132,8 +142,17 @@
                 if(this.isNewApk){
                   this.getVersion();
                 }
+                this.checkJyObj()
             },
             methods: {
+                setUtils: function () {
+                    this.$set(this, '$env', utils.$env)
+                    this.$set(this, '$envs', utils.$envs)
+                },
+                checkJyObj: function () {
+                    this.showVersion = utils.$envs.inApp
+                    this.showICP = utils.$envs.inApp
+                },
                 getVersion: function () {
                     var _this = this;
                     $.ajax({
@@ -206,6 +225,11 @@
         function afterJyObjInit(){
            appVersion = new Vue(vNode);
         }
+        $(function () {
+            if (!utils.$envs.inApp) {
+                afterJyObjInit()
+            }
+        })
     </script>
     {{include "/common/baiducc.html"}}
 </body>

+ 2 - 1
src/jfw/modules/bigmember/src/config.json

@@ -159,7 +159,8 @@
   "marketAnalysisPool": {
     "limit": 5,
     "timeOut": 20,
-    "projectNumLimit": 600000
+    "projectNumLimit": 600000,
+    "keyWordsCount": 300
   },
   "forecastTime": 86400,
   "isAddCacheTime": 300,

+ 1 - 0
src/jfw/modules/bigmember/src/config/config.go

@@ -49,6 +49,7 @@ type config struct {
 		Limit           int `json:"limit"`           //查询并发池
 		TimeOut         int `json:"timeOut"`         //并发池等待超时时长,单位秒
 		ProjectNumLimit int `json:"projectNumLimit"` //自定义报告限制项目个数
+		KeyWordsCount   int `json:"keyWordsCount"`   // 关键词数量附加词排除词加起来限制
 	} `json:"marketAnalysisPool"` //市场分析
 	ForecastTime   int //中标预测结果redis 缓存时间
 	IsAddCacheTime int //isadd 接口数据存储时间 + 120 内随机数

+ 9 - 7
src/jfw/modules/bigmember/src/entity/marketAnalysis/customizad_distribution.go

@@ -17,7 +17,8 @@ import (
 
 const (
 	//市场分析聚合查询
-	aggs_market_analysis = `"%s": {"range": {"field": "jgtime","ranges": [%s]},"aggs":{"project_count": {"filter": {"match_all":{}}},"project_amount":{"sum":{"field":"sortprice"}},"project_avgMoney": {"filter": {"range": {"sortprice": {"gt": 0}}},"aggs": {"avg_amount": {"avg": {"field": "sortprice"}}}} ,"buyer_count":{"cardinality":{"field":"buyer"}},"winner_count":{"cardinality":{"field":"s_winner"}}}}`
+	aggs_market_analysis = `"%s": {"range": {"field": "jgtime","ranges": [%s]},"aggs":{"project_count": {"filter": {"match_all":{}}},"project_amount":{"sum":{"field":"sortprice"}},"project_avgMoney": { "avg": {"field": "sortprice","missing": 0}} ,"buyer_count":{"cardinality":{"field":"buyer"}},"winner_count":{"cardinality":{"field":"s_winner"}}}}`
+	//aggs_market_analysis = `"%s": {"range": {"field": "jgtime","ranges": [%s]},"aggs":{"project_count": {"filter": {"match_all":{}}},"project_amount":{"sum":{"field":"sortprice"}},"project_avgMoney": {"filter": {"range": {"sortprice": {"gt": 0}}},"aggs": {"avg_amount": {"avg": {"field": "sortprice"}}}} ,"buyer_count":{"cardinality":{"field":"buyer"}},"winner_count":{"cardinality":{"field":"s_winner"}}}}`
 	//aggs_market_analysis = `"%s": {"range": {"field": "firsttime","ranges": [%s]},"aggs":{"project_count": {"filter": {"range": {"sortprice": {"gt": 0}}}},"project_amount":{"sum":{"field":"sortprice"}},"project_avgMoney": {"filter": {"range": {"sortprice": {"gt": 0}}},"aggs": {"avg_amount": {"avg": {"field": "sortprice"}}}},"buyer_count":{"filter": {"range": {"sortprice": {"gt": 0}}},"aggs": {"cardinality_buyer_count": {"cardinality": {"field": "buyer"}}}},"winner_count":{"filter": {"range": {"sortprice": {"gt": 0}}},"aggs": {"cardinality_winner_count": {"cardinality": {"field": "s_winner"}}}}}}`
 
 	//时间分布统计
@@ -237,10 +238,11 @@ type marketBuckets struct {
 		Value int `json:"value"`
 	} `json:"winner_count"`
 	ProjectAvgMoney struct {
-		DocCount  int `json:"doc_count"`
-		AvgAmount struct {
-			Value float64 `json:"value"`
-		} `json:"avg_amount"`
+		//DocCount  int `json:"doc_count"`
+		//AvgAmount struct {
+		//	Value float64 `json:"value"`
+		//} `json:"avg_amount"`
+		Value float64 `json:"value"`
 	} `json:"project_avgMoney"`
 }
 type Buckets struct {
@@ -332,7 +334,7 @@ func (mae *MarketAnalysisEntity) MarketTime() (map[string]interface{}, error) {
 		}
 		rMapData["project_count"] = Projectmarket.ProjectCount.DocCount
 		rMapData["projctamout"] = Projectmarket.ProjectAmount.Value
-		rMapData["projectavgmoney"] = Projectmarket.ProjectAvgMoney.AvgAmount.Value
+		rMapData["projectavgmoney"] = Projectmarket.ProjectAvgMoney.Value
 		rMapData["buyercount"] = Projectmarket.BuyerCount.Value
 		rMapData["winnercount"] = Projectmarket.WinnerCount.Value
 		if thisRow.Oldmarket.Buckets != nil && len(thisRow.Oldmarket.Buckets) != 0 {
@@ -340,7 +342,7 @@ func (mae *MarketAnalysisEntity) MarketTime() (map[string]interface{}, error) {
 			//环比数据
 			rMapData["projctamount_ratio"] = sequential(Projectmarket.ProjectAmount.Value, oldProjectmarket.ProjectAmount.Value)
 			rMapData["project_count_ratio"] = sequential(float64(Projectmarket.ProjectCount.DocCount), float64(oldProjectmarket.ProjectCount.DocCount))
-			rMapData["projectavgmoney_ratio"] = sequential(Projectmarket.ProjectAvgMoney.AvgAmount.Value, oldProjectmarket.ProjectAvgMoney.AvgAmount.Value)
+			rMapData["projectavgmoney_ratio"] = sequential(Projectmarket.ProjectAvgMoney.Value, oldProjectmarket.ProjectAvgMoney.Value)
 			rMapData["buyercount_ratio"] = sequential(float64(Projectmarket.BuyerCount.Value), float64(oldProjectmarket.BuyerCount.Value))
 			rMapData["winnercount_ratio"] = sequential(float64(Projectmarket.WinnerCount.Value), float64(oldProjectmarket.WinnerCount.Value))
 		}

+ 28 - 12
src/jfw/modules/bigmember/src/entity/marketAnalysis/customized_analysis.go

@@ -324,9 +324,9 @@ func ProjectScale(thisRow AreaCTop) (data []interface{}) {
 	total := thisRow.CountNot0.Count
 	buckets := thisRow.SortpriceRanges.Buckets
 	type Scale struct {
-		Name      string
-		Persent_c float64
-		Persent_a float64
+		Name      string  `bson:"Name"`
+		Persent_c float64 `bson:"Persent_c"`
+		Persent_a float64 `bson:"Persent_a"`
 	}
 	for _, v := range buckets {
 		name := v.Name
@@ -364,6 +364,7 @@ func InterToSliceString(obj interface{}) []string {
 // top10
 func (mae *MarketAnalysisEntity) ProjectTop10() (rMap map[string]interface{}, err error) {
 	finalSql := fmt.Sprintf(mae.GetCommonQuerySql(), query_top10)
+    log.Println("ProjectTop10:",finalSql)
 	hits := elastic.Get("projectset", "projectset", finalSql)
 	rMap = map[string]interface{}{}
 	bArr := []map[string]interface{}{}
@@ -513,9 +514,12 @@ func AmountCompute(thisRow AreaCTop, types string, eid map[string]string) (rData
 				rWinner = append(rWinner, rW)
 			}
 			rM["winner"] = rWinner
-
-			vv := v.AreaAmount.Amount / thisRow.Amount.Value
-			rM["area_scale"] = vv
+			if thisRow.Amount.Value != 0 {
+				vv := v.AreaAmount.Amount / thisRow.Amount.Value
+				rM["area_scale"] = vv
+			} else {
+				rM["area_scale"] = 0
+			}
 			rData = append(rData, rM)
 		}
 	} else {
@@ -544,9 +548,13 @@ func AmountCompute(thisRow AreaCTop, types string, eid map[string]string) (rData
 				rWinner = append(rWinner, rW)
 			}
 			rM["winner"] = rWinner
+			if thisRow.Amount.Value != 0 {
+				vv := v.AreaAmount.Amount / thisRow.Amount.Value
+				rM["buyclass_scale"] = vv
+			} else {
+				rM["buyclass_scale"] = 0
+			}
 
-			vv := v.AreaAmount.Amount / thisRow.Amount.Value
-			rM["buyclass_scale"] = vv
 			rData = append(rData, rM)
 		}
 	}
@@ -583,9 +591,13 @@ func CountCompute(thisRow AreaCTop, types string, eid map[string]string) (rData
 				continue
 			}
 			rM["winner"] = rWinner
+			if thisRow.Total != 0 {
+				vv := float64(v.Total) / float64(thisRow.Total)
+				rM["area_scale"] = vv
+			} else {
+				rM["area_scale"] = 0
+			}
 
-			vv := float64(v.Total) / float64(thisRow.Total)
-			rM["area_scale"] = vv
 			rData = append(rData, rM)
 		}
 	} else {
@@ -616,9 +628,13 @@ func CountCompute(thisRow AreaCTop, types string, eid map[string]string) (rData
 				continue
 			}
 			rM["winner"] = rWinner
+			if thisRow.Total != 0 {
+				vv := float64(v.Total) / float64(thisRow.Total)
+				rM["buyclass_scale"] = vv
+			} else {
+				rM["buyclass_scale"] = 0
+			}
 
-			vv := float64(v.Total) / float64(thisRow.Total)
-			rM["buyclass_scale"] = vv
 			rData = append(rData, rM)
 		}
 	}

+ 346 - 24
src/jfw/modules/bigmember/src/entity/marketAnalysis/marketAnalysisEntity.go

@@ -28,6 +28,20 @@ const (
 	ReportCacheDB      = "other"
 	ReportCacheKey     = "marketAnalysis_%s_%d"
 	ReportCacheTime    = 60 * 5
+	ReportCanceledKey  = "marketAnalysisCanceled_%s" // 取消的报告id
+	ReportCanceledTime = 60 * 60 * 24
+
+	ReportStateGenerating    = 0                             // 报告生成状态 生成中
+	ReportStateGenerated     = 1                             // 生成 成功
+	ReportStateCanceled      = 2                             // 已取消
+	ReportStateFailed        = -1                            // 生成失败
+	CollMarketScaleMain      = "marketanalysisreport_scal"   //市场规模 报告模块对应的mongo 集合名称
+	CollMarketTopProject     = "marketanalysisreport_top"    //项目规模TOP10
+	CollMarketProjectAllData = "marketanalysisreport_all"    //项目规模 地区分布 客户分布 地区客户top3
+	CollMarketScaleRefine    = "marketanalysisreport_refine" //细化市场
+	CollMarketBuyerAndWinner = "marketanalysisreport_bw"     //市场-采购单位&&中标企业
+	ValueOffline             = 1                             // 离线
+	ValueRealTime            = 2                             // 实时
 )
 
 var MarketAnalysisPool chan bool
@@ -85,11 +99,17 @@ type AnalysisRequestFormat struct {
 }
 
 type MarketAnalysisEntity struct {
-	MgoRecordId string
-	BaseParam   AnalysisRequestParam
-	FormatParam AnalysisRequestFormat
-	UId, Pid    string
-	ProjectInfo projectInfo
+	MgoRecordId   string
+	BaseParam     AnalysisRequestParam
+	FormatParam   AnalysisRequestFormat
+	UId, Pid      string
+	ProjectInfo   projectInfo
+	Offline       int // 1-离线 2-实时
+	State         int // 状态:默认0:生成中;1:已生成;2:已取消;-1:生成失败
+	MgoUserId     string
+	Phone         string // 手机号
+	PositionId    int
+	OriginalTotal int64 // 数据总数
 }
 
 type projectInfo struct {
@@ -233,7 +253,12 @@ func (mae *MarketAnalysisEntity) GetProjectInfoList() error {
 
 // SaveAnalysisRecord 保存分析记录
 func (mae *MarketAnalysisEntity) SaveAnalysisRecord() error {
-	mae.MgoRecordId = db.Mgo.Save(ReportHistoryTable, map[string]interface{}{
+	if mae.Offline == ValueRealTime {
+		mae.State = ReportStateGenerated
+	} else {
+		mae.State = ReportStateGenerating
+	}
+	data := map[string]interface{}{
 		"s_keysItems":      mae.BaseParam.KeysItemsStr,
 		"s_rangeTime":      mae.BaseParam.RangeTime,
 		"s_rangeTimeExtra": mae.BaseParam.RangeTimeExtra,
@@ -243,8 +268,18 @@ func (mae *MarketAnalysisEntity) SaveAnalysisRecord() error {
 		"s_matchingMode":   mae.BaseParam.MatchingMode,
 		"s_userId":         mae.UId,
 		"s_parentId":       mae.Pid,
+		"i_state":          mae.State,         //状态:默认0:生成中;1:已生成;2:已取消;-1:生成失败
+		"l_updateTime":     time.Now().Unix(), //生成时间 or 取消时间
 		"l_createTime":     time.Now().Unix(),
-	})
+		"i_offline":        mae.Offline,
+		"s_mgoUserId":      mae.MgoUserId,
+		"i_positionId":     mae.PositionId,
+		"s_phone":          mae.Phone,
+	}
+	if mae.OriginalTotal > 0 {
+		data["l_originalTotal"] = mae.OriginalTotal
+	}
+	mae.MgoRecordId = db.Mgo.Save(ReportHistoryTable, data)
 	if mae.MgoRecordId == "" {
 		return fmt.Errorf("分析创建异常")
 	}
@@ -269,6 +304,8 @@ func (mae *MarketAnalysisEntity) GetAnalysisFromMgoDb() error {
 	if res == nil || len(*res) == 0 {
 		return fmt.Errorf("未查询到相关数据")
 	}
+	mae.Offline = qutil.IntAll((*res)["i_offline"])
+	mae.State = qutil.IntAll((*res)["i_state"])
 	mae.BaseParam.KeysItemsStr, _ = (*res)["s_keysItems"].(string)
 	mae.BaseParam.RangeTime, _ = (*res)["s_rangeTime"].(string)
 	mae.BaseParam.RangeTimeExtra, _ = (*res)["s_rangeTimeExtra"].(string)
@@ -317,8 +354,17 @@ func (mae *MarketAnalysisEntity) GetRecordList(pageNum, PageSize int) (total int
 	if res == nil || len(*res) == 0 {
 		return
 	}
+	//用户消息开关
+	open := GetMsgOpen(mae.MgoUserId)
 	for _, row := range *res {
-		list = append(list, map[string]interface{}{
+		var status int
+		if row["i_state"] != nil {
+			status = qutil.IntAll(row["i_state"])
+			if status == ReportStateFailed {
+				status = ReportStateGenerating // 生成失败对外还是展示为生成中
+			}
+		}
+		data := map[string]interface{}{
 			"id":               util.EncodeId(mongodb.BsonIdToSId(row["_id"])),
 			"keysItems":        qutil.ObjToString(row["s_keysItems"]),
 			"area":             qutil.ObjToString(row["s_area"]),
@@ -328,7 +374,11 @@ func (mae *MarketAnalysisEntity) GetRecordList(pageNum, PageSize int) (total int
 			"s_rangeTimeExtra": qutil.ObjToString(row["s_rangeTimeExtra"]),
 			"createTime":       qutil.Int64All(row["l_createTime"]),
 			"matchingMode":     qutil.ObjToString(row["s_matchingMode"]), //项目匹配方式
-		})
+			"state":            qutil.If(row["i_state"] == nil, nil, status),
+			"updateTime":       qutil.Int64All(row["l_updateTime"]),
+			"msgOpen":          open,
+		}
+		list = append(list, data)
 	}
 	return
 }
@@ -376,30 +426,283 @@ func (mae *MarketAnalysisEntity) GetPartResult(flag int) (map[string]interface{}
 				return nil, fmt.Errorf("报告异常请求,请刷新重试")
 			}
 		}
-		switch flag {
-		case marketScaleMain:
-			rData, err := mae.MarketTime()
-			if err != nil { //若无报告内容,删除报告记录
-				go mae.removeEmptyRecord()
-			}
+		// 1. 查mongo
+		var rData map[string]interface{}
+		var err error
+		rData, err = mae.GetMongoData(flag)
+		// 查到数据则直接返回
+		if err == nil && rData != nil {
+			return rData, nil
+		}
+		// 2.没有查询到数据 判断是不是离线的
+		//  正常情况下正在离线生的不会走到这里
+		// 离线的应该生成完报告之后才会调用获取结果接口 这里处理是防止直接调接口传正在离线生的报告
+		if mae.Offline == ValueOffline || mae.Offline == 0 {
+			return nil, err
+		}
+		// 3. 实时则查es
+		rData, err = mae.realTimeQuery(flag)
+		if err == nil && len(rData) > 0 {
+			// 4.存库
+			mae.SaveMongoReport(rData, flag)
 			return rData, err
-		case marketTopProject:
-			return mae.ProjectTop10()
-		case marketProjectAllData:
-			return mae.AllData()
-		case marketScaleRefine:
-			return mae.marketScaleRefineQuery()
-		case marketBuyerAndWinner:
-			return mae.BuyerWinnerAnalysis(), nil
 		}
-		return nil, fmt.Errorf("未知请求")
+		return nil, err
 	}()
 	if err == nil && rData != nil && len(rData) > 0 {
+		delete(rData, "s_m_id")
+		delete(rData, "_id")
 		redis.Put(ReportCacheDB, thisCacheKey, rData, ReportCacheTime)
 	}
 	return rData, err
 }
 
+// 实时查询
+func (mae *MarketAnalysisEntity) realTimeQuery(flag int) (map[string]interface{}, error) {
+	switch flag {
+	case marketScaleMain:
+		rData, err := mae.MarketTime()
+		if err != nil { //若无报告内容,删除报告记录
+			go mae.removeEmptyRecord()
+		}
+		return rData, err
+	case marketTopProject:
+		return mae.ProjectTop10()
+	case marketProjectAllData:
+		return mae.AllData()
+	case marketScaleRefine:
+		return mae.marketScaleRefineQuery()
+	case marketBuyerAndWinner:
+		return mae.BuyerWinnerAnalysis(), nil
+	}
+	return nil, fmt.Errorf("未知请求")
+}
+
+// GetMongoData 从mongo库查询数据
+func (mae *MarketAnalysisEntity) GetMongoData(flag int) (map[string]interface{}, error) {
+	collName, err := GetMongoColl(flag)
+	if err != nil || collName == "" {
+		return nil, fmt.Errorf("未知请求")
+	}
+	// 查询
+	query := map[string]interface{}{
+		"s_m_id": mae.MgoRecordId,
+	}
+	data, b := db.Mgo.FindOne(collName, query)
+	if !b || data == nil || len(*data) == 0 {
+		log.Println("没有查询到数据", b, query, collName)
+		return nil, nil
+	}
+	delete(*data, "s_m_id")
+	delete(*data, "_id")
+	return *data, nil
+}
+
+// GetMongoColl 获取mgo库对应的Coll
+func GetMongoColl(flag int) (string, error) {
+	switch flag {
+	case marketScaleMain:
+		return CollMarketScaleMain, nil
+	case marketTopProject:
+		return CollMarketTopProject, nil
+	case marketProjectAllData:
+		return CollMarketProjectAllData, nil
+	case marketScaleRefine:
+		return CollMarketScaleRefine, nil
+	case marketBuyerAndWinner:
+		return CollMarketBuyerAndWinner, nil
+	}
+	return "", fmt.Errorf("未知类型")
+}
+
+// SaveMongoReport 数据存mongo库
+func (mae *MarketAnalysisEntity) SaveMongoReport(rData map[string]interface{}, flag int) {
+	collName, _ := GetMongoColl(flag)
+	rData["s_m_id"] = mae.MgoRecordId
+	db.Mgo.Save(collName, rData)
+
+}
+
+// GetAnalyzingReport 是否有正在分析的离线报告
+func (mae *MarketAnalysisEntity) GetAnalyzingReport() string {
+	query := map[string]interface{}{
+		"s_userId": mae.UId,
+		"i_state":  map[string]interface{}{"$in": []int{ReportStateGenerating, ReportStateFailed}}, //状态:默认0:生成中;1:已生成;2:已取消;-1:生成失败
+		"i_del":    map[string]interface{}{"$ne": 1},
+	}
+	//log.Println("query:", query)
+	rs, b := db.Mgo.FindOne(ReportHistoryTable, query)
+	//log.Println("rs,b:", rs, b)
+	if b && rs != nil {
+		return mongodb.BsonIdToSId((*rs)["_id"])
+	}
+	return ""
+}
+
+// IsOffline 判断是否符合在线分析的条件
+func (mae *MarketAnalysisEntity) IsOffline() (offline bool) {
+	//离线生成:订阅词(关键词+排除词)超过300个(数量支持配置),或单次分析数据超过60万条,则离线生成;
+	keyCount := 0
+	for i := 0; i < len(mae.FormatParam.KeysItems); i++ {
+		items := mae.FormatParam.KeysItems[i]
+		for j := 0; j < len(items.A_Key); j++ {
+			AKey := items.A_Key[j]
+			for k := 0; k < len(AKey.Keyword); k++ {
+				keyCount++
+			}
+			for k := 0; k < len(AKey.Appended); k++ {
+				keyCount++
+			}
+			for k := 0; k < len(AKey.Exclude); k++ {
+				keyCount++
+			}
+		}
+	}
+	if keyCount > config.Config.MarketAnalysisPool.KeyWordsCount {
+		mae.Offline = ValueOffline
+		return true
+	}
+	//  查询数据量
+	countSql := fmt.Sprintf(mae.GetCommonQuerySql(), "")
+	log.Println("IsOffline count SQL:", countSql)
+	a := time.Now()
+	dataCount := elastic.Count("projectset", "projectset", countSql)
+	b := time.Since(a)
+	log.Println("IsOffline 统计数据量耗时:", b)
+	log.Println("IsOffline 数据量:", dataCount)
+	mae.OriginalTotal = dataCount
+	if int(dataCount) > config.Config.MarketAnalysisPool.ProjectNumLimit {
+		mae.Offline = ValueOffline
+		return true
+	}
+	mae.Offline = ValueRealTime
+	return false
+}
+func (mae *MarketAnalysisEntity) GetReportState() (generated bool, needUpdate bool, err error) {
+	// 查库
+	err = mae.GetAnalysisFromMgoDb()
+	if err != nil {
+		return
+	}
+	// 已经生成的可以直接返回
+	if mae.State == ReportStateGenerated {
+		generated = true
+		return
+	}
+	// 该字段没有值 说明是还没有被更新的历史数据 需要更新
+	// 且判断之后后续需要更新i_state和i_offline字段
+	if mae.Offline == 0 {
+		needUpdate = true
+	}
+	// 如果没有值的话 说明这是历史数据  需要接着走下面的流程进行判断
+	// 格式化数据 用于后面校验个数
+	if err = mae.ForMatData(); err != nil {
+		return
+	}
+	return
+
+}
+
+// Cancel 取消正在分析中的报告
+func (mae *MarketAnalysisEntity) Cancel() (bool, error) {
+	// 取消报告
+	queryMap := map[string]interface{}{
+		"_id":     mae.MgoRecordId,
+		"i_state": map[string]interface{}{"$in": []int{ReportStateGenerating, ReportStateFailed}}, //状态:默认0:生成中;1:已生成;2:已取消;-1:生成失败
+		"i_del":   map[string]interface{}{"$ne": 1},
+	}
+	if mae.UId == mae.Pid { //主账号
+		queryMap["s_parentId"] = mae.Pid
+	} else {
+		queryMap["s_userId"] = mae.UId
+	}
+	//验证
+	rs, b := db.Mgo.FindOne(ReportHistoryTable, queryMap)
+	//
+	if !b || rs == nil {
+		return false, fmt.Errorf("未查询到该记录")
+	}
+	update := db.Mgo.UpdateById(ReportHistoryTable, mae.MgoRecordId, map[string]interface{}{"$set": map[string]interface{}{
+		"l_updateTime": time.Now().Unix(),
+		"i_state":      ReportStateCanceled,
+	}})
+	if !update {
+		log.Println("分析报告取消失败:", mae.MgoRecordId)
+		return false, fmt.Errorf("取消失败")
+	}
+	// redis里面放取消标识
+	redis.Put(ReportCacheDB, fmt.Sprintf(ReportCanceledKey, mae.MgoRecordId), 1, ReportCanceledTime)
+	return update, nil
+}
+
+// UpdateOffline 更新报告是否是离线报告
+func (mae *MarketAnalysisEntity) UpdateOffline(offline bool) bool {
+	set := map[string]interface{}{
+		"i_state":      qutil.If(offline, ReportStateGenerating, ReportStateGenerated),
+		"i_offline":    qutil.If(offline, ValueOffline, ValueRealTime),
+		"l_updateTime": time.Now().Unix(),
+		"s_mgoUserId":  mae.MgoUserId, // 这里更新这些字段是因为这几个字段是p437 版本新加上的 历史数据没有这些字段
+		"s_positionId": mae.PositionId,
+		"s_phone":      mae.Phone,
+	}
+	if mae.OriginalTotal > 0 {
+		set["l_originalTotal"] = mae.OriginalTotal
+	}
+	data := map[string]interface{}{
+		"$set": set,
+	}
+	update := db.Mgo.UpdateById(ReportHistoryTable, mae.MgoRecordId, data)
+	if !update {
+		log.Println("UpdateOffline 更新报告状态失败:", data, mae.MgoRecordId)
+	}
+	return update
+}
+
+// UpdateState 更新报告生成状态
+func (mae *MarketAnalysisEntity) UpdateState(state int) bool {
+	data := map[string]interface{}{
+		"$set": map[string]interface{}{
+			"i_state":      state,
+			"l_updateTime": time.Now().Unix(),
+		},
+	}
+	update := db.Mgo.UpdateById(ReportHistoryTable, mae.MgoRecordId, data)
+	if !update {
+		log.Println("UpdateState 更新报告生成状态失败:", data, mae.MgoRecordId)
+	}
+	return update
+}
+
+// Delete 删除正在分析中的报告
+func (mae *MarketAnalysisEntity) Delete() (bool, error) {
+	// 删除报告
+	queryMap := map[string]interface{}{
+		"_id":   mae.MgoRecordId,
+		"i_del": map[string]interface{}{"$ne": 1},
+	}
+	if mae.UId == mae.Pid { //主账号
+		queryMap["s_parentId"] = mae.Pid
+	} else {
+		queryMap["s_userId"] = mae.UId
+	}
+	//验证
+	rs, b := db.Mgo.FindOne(ReportHistoryTable, queryMap)
+	//
+	if !b || rs == nil {
+		return false, fmt.Errorf("未查询到该记录")
+	}
+	update := db.Mgo.UpdateById(ReportHistoryTable, mae.MgoRecordId, map[string]interface{}{"$set": map[string]interface{}{
+		"l_updateTime": time.Now().Unix(),
+		"i_del":        1,
+	}})
+	if !update {
+		log.Println("分析报告删除失败:", mae.MgoRecordId)
+		return false, fmt.Errorf("删除失败")
+	}
+	// redis里面放取消标识
+	redis.Put(ReportCacheDB, fmt.Sprintf(ReportCanceledKey, mae.MgoRecordId), 1, ReportCanceledTime)
+	return update, nil
+}
 func GetEntNameByIds(ids []string) (returnMap map[string]string) {
 	returnMap = map[string]string{}
 	if len(ids) == 0 {
@@ -418,3 +721,22 @@ func GetEntNameByIds(ids []string) (returnMap map[string]string) {
 	}
 	return
 }
+
+// GetMsgOpen 获取用户服务通知开关是否开启
+func GetMsgOpen(mgoUserId string) bool {
+	pushSetMap, _ := db.Mgo.FindById("user", mgoUserId, `{"o_pushset":1,"s_m_openid":1}`)
+	//log.Println(mgoUserId, pushSetMap)
+	if pushSetMap != nil && len(*pushSetMap) > 0 {
+		pushset := qutil.ObjToMap((*pushSetMap)["o_pushset"])
+		if pushset == nil || len(*pushset) == 0 {
+			return false
+		}
+		msgServicePushSet := qutil.ObjToMap((*pushset)["o_msg_service"])
+		if msgServicePushSet != nil {
+			if qutil.IntAll((*msgServicePushSet)["i_apppush"]) == 1 || qutil.IntAll((*msgServicePushSet)["i_wxpush"]) == 1 {
+				return true
+			}
+		}
+	}
+	return false
+}

+ 21 - 5
src/jfw/modules/bigmember/src/entity/marketAnalysis/scaleRefineQuery.go

@@ -99,7 +99,7 @@ func (mae *MarketAnalysisEntity) marketScaleRefineQuery() (rMap map[string]inter
 		itemDataMap[group.ItemName] = group.UpdateTime
 	}
 	finalSql := fmt.Sprintf(mae.GetCommonQuerySqlWithAggs(), strings.Join(aggsGroup, ","))
-	//fmt.Println("finalSql-----4", finalSql)
+	fmt.Println("finalSql-----4", finalSql)
 	rMap = map[string]interface{}{}
 	res := util.GetAggs("projectset", "projectset", finalSql)
 	if res == nil || len(res) == 0 {
@@ -168,14 +168,30 @@ func (srd *scaleRefineData) doIdSwitch() {
 // formatData 计算百分比&获取企业对应名称
 func (srd *scaleRefineData) formatData() {
 	for _, v := range srd.Data {
-		v.TotalProp = float64(v.Total) / float64(srd.Total.Total)
-		v.AmountProp = v.Amount.Value / srd.Amount.Value
+		if srd.Total.Total == 0 {
+			v.TotalProp = 0
+		} else {
+			v.TotalProp = float64(v.Total) / float64(srd.Total.Total)
+		}
+		if srd.Amount.Value == 0 {
+			v.AmountProp = 0
+		} else {
+			v.AmountProp = v.Amount.Value / srd.Amount.Value
+		}
 
 		for _, vv := range v.WinnerTotalTop.Buckets {
-			vv.Prop = float64(vv.Total) / float64(v.Total)
+			if v.Total == 0 {
+				vv.Prop = 0
+			} else {
+				vv.Prop = float64(vv.Total) / float64(v.Total)
+			}
 		}
 		for _, vv := range v.WinnerAmountTop.Buckets {
-			vv.Prop = vv.Amount.Value / v.Amount.Value
+			if v.Amount.Value == 0 {
+				vv.Prop = 0
+			} else {
+				vv.Prop = vv.Amount.Value / v.Amount.Value
+			}
 		}
 	}
 	srd.ReturnData.Overall = srd.sortBy(totalAndAmount).getOverallData()

+ 41 - 7
src/jfw/modules/bigmember/src/entity/report.go

@@ -9,7 +9,9 @@ import (
 )
 
 const (
-	TableMemberReportProject = "member_report_project" // 大会员周报月报-项目明细表
+	TableMemberReportProject    = "member_report_project" // 大会员周报月报-项目明细表
+	From                        = "yyszsyy"
+	TableMemberReportProjectYys = "member_report_project_yys" // 大会员周报月报-项目明细表
 )
 
 type ReportProjectInfoParam struct {
@@ -24,6 +26,7 @@ type ReportProjectInfoParam struct {
 	End        int                 `json:"end"`        // 结束时间
 	PageSize   int                 `json:"pageSize"`   // 默认每页10条
 	PageNum    int                 `json:"pageNum"`    // 默认当前第一页
+	From       string              `json:"from"`       // 来源
 }
 
 // GetReportProjectInfo 周报/月报查询项目明细
@@ -103,7 +106,11 @@ func GetReportProjectInfo(param *ReportProjectInfoParam, positionId int64) (data
 	}
 	q += strings.Join(qstr, " and ")
 	// 查询数量
-	CountQuery := fmt.Sprintf("select count(1) from %s where position_id=? and start_time>=? and end_time<=? %s ", TableMemberReportProject, q)
+	table := TableMemberReportProject
+	if param.From == From {
+		table = TableMemberReportProjectYys
+	}
+	CountQuery := fmt.Sprintf("select count(1) from %s where position_id=? and start_time>=? and end_time<=? %s ", table, q)
 	total = db.Base.CountBySql(CountQuery, values...)
 	if total == 0 {
 		return data, 0
@@ -112,7 +119,7 @@ func GetReportProjectInfo(param *ReportProjectInfoParam, positionId int64) (data
 	if param.Sort == 1 {
 		orderby = " (if(bidamount >0,bidamount,budget)) desc,id desc"
 	}
-	query := fmt.Sprintf("select * from %s where position_id=? and start_time>=? and end_time<=? %s  order by  %s   limit %d,%d", TableMemberReportProject, q, orderby, (param.PageNum-1)*param.PageSize, param.PageSize)
+	query := fmt.Sprintf("select * from %s where position_id=? and start_time>=? and end_time<=? %s  order by  %s   limit %d,%d", table, q, orderby, (param.PageNum-1)*param.PageSize, param.PageSize)
 	rs := db.Base.SelectBySql(query, values...)
 	if rs != nil && len(*rs) > 0 {
 		data = *rs
@@ -143,8 +150,10 @@ func ReportProjectInfoFormat(data []map[string]interface{}) []map[string]interfa
 		tmp["area"] = data[i]["area"]
 		tmp["bidStatus"] = data[i]["bidstatus"]
 		tmp["buyerClass"] = data[i]["buyerclass"]
+		tmp["subscopeclass"] = data[i]["subscopeclass"]
 		tmp["bidAmount"] = data[i]["bidamount"]
 		tmp["budget"] = data[i]["budget"]
+		tmp["item"] = data[i]["items"]
 		tmp["buyer"] = data[i]["buyer"]
 		tmp["lastTime"] = data[i]["last_time"]
 		rs = append(rs, tmp)
@@ -153,8 +162,12 @@ func ReportProjectInfoFormat(data []map[string]interface{}) []map[string]interfa
 }
 
 // ReportGetArea 周报月报获取当前订阅地区
-func ReportGetArea(positionId, start, end int64) map[string]interface{} {
-	q := "SELECT area,group_concat(distinct(city)) as city from " + TableMemberReportProject + " where position_id =? and start_time >=? and end_time<=? and area!='全国' group by area;"
+func ReportGetArea(positionId, start, end int64, from string) map[string]interface{} {
+	table := TableMemberReportProject
+	if from == From {
+		table = TableMemberReportProjectYys
+	}
+	q := "SELECT area,group_concat(distinct(city)) as city from " + table + " where position_id =? and start_time >=? and end_time<=? and area!='全国' group by area;"
 	rs := db.Base.SelectBySql(q, positionId, start, end)
 	areaMap := map[string]interface{}{}
 	if rs != nil && len(*rs) > 0 {
@@ -177,9 +190,13 @@ func ReportGetArea(positionId, start, end int64) map[string]interface{} {
 }
 
 // ReportGetBuyerClass 周报月报获取当前采购单位类型
-func ReportGetBuyerClass(positionId, start, end int64) []string {
+func ReportGetBuyerClass(positionId, start, end int64, from string) []string {
 	buyerClass := []string{}
-	q := "select distinct(buyerclass) from " + TableMemberReportProject + " where position_id =? and start_time >=? and end_time<=?  order by buyerclass "
+	table := TableMemberReportProject
+	if from == From {
+		table = TableMemberReportProjectYys
+	}
+	q := "select distinct(buyerclass) from " + table + " where position_id =? and start_time >=? and end_time<=?  order by buyerclass "
 	rs := db.Base.SelectBySql(q, positionId, start, end)
 	if rs != nil && len(*rs) > 0 {
 		for i := 0; i < len(*rs); i++ {
@@ -189,6 +206,23 @@ func ReportGetBuyerClass(positionId, start, end int64) []string {
 	return buyerClass
 }
 
+// ReportGetIndustry 周报月报获取当前行业
+func ReportGetIndustry(positionId, start, end int64, from string) []string {
+	subscopeclass := []string{}
+	table := TableMemberReportProject
+	if from == From {
+		table = TableMemberReportProjectYys
+	}
+	q := "select distinct(subscopeclass) from " + table + " where position_id =? and start_time >=? and end_time<=?  order by subscopeclass "
+	rs := db.Base.SelectBySql(q, positionId, start, end)
+	if rs != nil && len(*rs) > 0 {
+		for i := 0; i < len(*rs); i++ {
+			subscopeclass = append(subscopeclass, common.ObjToString((*rs)[i]["subscopeclass"]))
+		}
+	}
+	return subscopeclass
+}
+
 // GetMainPositionId 根据用户id获取主账号的职位id
 func GetMainPositionId(mgoUserId string) (positionId int64) {
 	// 1. 查主账号的base_user_id

+ 159 - 7
src/jfw/modules/bigmember/src/service/report/marketAnalysis.go

@@ -23,6 +23,9 @@ type MarketAnalysis struct {
 	analysisHistory   xweb.Mapper `xweb:"/marketAnalysis/analysisHistory"`   //市场分析报告历史记录
 	analysisKeyWord   xweb.Mapper `xweb:"/marketAnalysis/analysisKeyWord"`   //市场分析报告订阅词
 	projectInfo       xweb.Mapper `xweb:"/marketAnalysis/projectInfo"`       //项目明细
+	cancel            xweb.Mapper `xweb:"/marketAnalysis/cancel"`            // 取消分析
+	isOffline         xweb.Mapper `xweb:"/marketAnalysis/isOffline"`         // 是否为离线查询
+	delete            xweb.Mapper `xweb:"/marketAnalysis/delete"`            // 删除报告
 }
 
 // 项目明细
@@ -111,9 +114,19 @@ func checkPower(session *httpsession.Session) (string, error) {
 	return bigMeg.Pid, nil
 }
 
+const (
+	AnalysisStatusRealTime  = 0 // 实时生成
+	AnalysisStatusOffline   = 1 // 离线生成
+	AnalysisStatusAnalyzing = 2 // 存在正在生成的报告
+)
+
 // DoAnalysis 开始分析报告
 func (this *MarketAnalysis) DoAnalysis() {
-	userId := qutil.ObjToString(this.GetSession("userId"))
+	sessVal := this.Session().GetMultiple()
+	mgoUserId := qutil.ObjToString(sessVal["mgoUserId"])
+	userId := qutil.ObjToString(sessVal["userId"])
+	positionId := qutil.IntAll(sessVal["positionId"])
+	phone := qutil.ObjToString(sessVal["phone"])
 	rData, errMsg := func() (interface{}, error) {
 		pid, powerErr := checkPower(this.Session())
 		if powerErr != nil {
@@ -129,15 +142,40 @@ func (this *MarketAnalysis) DoAnalysis() {
 			BuyerClass:     this.GetString("buyerclass"),     //采购单位类型【字符串】多个采购单位类型用逗号拼接
 			MatchingMode:   this.GetString("matchingMode"),   //匹配方式
 		}
-		mae := &marketAnalysis.MarketAnalysisEntity{BaseParam: bParam, UId: userId, Pid: pid}
+		mae := &marketAnalysis.MarketAnalysisEntity{BaseParam: bParam, UId: userId, Pid: pid, MgoUserId: mgoUserId, PositionId: positionId, Phone: phone}
 		if err := mae.ForMatData(); err != nil {
 			return nil, err
 		}
+		// 判断是否有正在分析中的报告
+		if analyzingId := mae.GetAnalyzingReport(); analyzingId != "" {
+			return map[string]interface{}{
+				"id":     util.EncodeId(analyzingId),
+				"status": AnalysisStatusAnalyzing,
+			}, nil
+		}
+		var status int
+		var msgOpen bool
+		// 判断是否为离线生成报告
+		offline := mae.IsOffline()
+		if offline {
+			// 查询消息开关
+			status = AnalysisStatusOffline
+			msgOpen = marketAnalysis.GetMsgOpen(mgoUserId)
+		} else {
+			status = AnalysisStatusRealTime
+		}
 		//存储分析记录
 		if err := mae.SaveAnalysisRecord(); err != nil {
 			return nil, err
 		}
-		return util.EncodeId(mae.MgoRecordId), nil
+		data := map[string]interface{}{
+			"id":     util.EncodeId(mae.MgoRecordId),
+			"status": status,
+		}
+		if offline {
+			data["msgOpen"] = msgOpen
+		}
+		return data, nil
 	}()
 	if errMsg != nil {
 		log.Printf("%s MarketAnalysis DoAnalysis Error:%s\n", userId, errMsg.Error())
@@ -147,13 +185,16 @@ func (this *MarketAnalysis) DoAnalysis() {
 
 // GetAnalysisResult 获取分析结果
 func (this *MarketAnalysis) GetAnalysisResult() {
-	userId := qutil.ObjToString(this.GetSession("userId"))
+	sessVal := this.Session().GetMultiple()
+	mgoUserId := qutil.ObjToString(sessVal["mgoUserId"])
+	userId := qutil.ObjToString(sessVal["userId"])
+	positionId := qutil.IntAll(sessVal["positionId"])
 	rData, errMsg := func() (interface{}, error) {
 		pid, powerErr := checkPower(this.Session())
 		if powerErr != nil {
 			return nil, powerErr
 		}
-		mae := &marketAnalysis.MarketAnalysisEntity{MgoRecordId: util.DecodeId(this.GetString("rid")), UId: userId, Pid: pid}
+		mae := &marketAnalysis.MarketAnalysisEntity{MgoRecordId: util.DecodeId(this.GetString("rid")), UId: userId, Pid: pid, MgoUserId: mgoUserId, PositionId: positionId}
 		if err := mae.GetAnalysisFromMgoDb(); err != nil {
 			return nil, err
 		}
@@ -175,9 +216,120 @@ func (this *MarketAnalysis) GetAnalysisResult() {
 	this.ServeJson(NewResult(rData, errMsg))
 }
 
+// Cancel 取消正在分析的报告
+func (this *MarketAnalysis) Cancel() {
+	sessVal := this.Session().GetMultiple()
+	mgoUserId := qutil.ObjToString(sessVal["mgoUserId"])
+	userId := qutil.ObjToString(sessVal["userId"])
+	positionId := qutil.IntAll(sessVal["positionId"])
+	rData, errMsg := func() (interface{}, error) {
+		pid, powerErr := checkPower(this.Session())
+		if powerErr != nil {
+			return nil, powerErr
+		}
+		mae := &marketAnalysis.MarketAnalysisEntity{MgoRecordId: util.DecodeId(this.GetString("rid")), UId: userId, Pid: pid, MgoUserId: mgoUserId, PositionId: positionId}
+		rData, err := mae.Cancel()
+		return rData, err
+	}()
+	if errMsg != nil {
+		log.Printf("%s MarketAnalysis Cancel Error:%s\n", userId, errMsg.Error())
+	}
+	this.ServeJson(NewResult(rData, errMsg))
+}
+
+// IsOffline 判断是否是离线报告
+func (this *MarketAnalysis) IsOffline() {
+	sessVal := this.Session().GetMultiple()
+	mgoUserId := qutil.ObjToString(sessVal["mgoUserId"])
+	userId := qutil.ObjToString(sessVal["userId"])
+	positionId := qutil.IntAll(sessVal["positionId"])
+	phone := qutil.ObjToString(sessVal["phone"])
+	rData, errMsg := func() (interface{}, error) {
+		pid, powerErr := checkPower(this.Session())
+		if powerErr != nil {
+			return nil, powerErr
+		}
+		mae := &marketAnalysis.MarketAnalysisEntity{MgoRecordId: util.DecodeId(this.GetString("rid")), UId: userId, Pid: pid, MgoUserId: mgoUserId, PositionId: positionId, Phone: phone}
+		// 判断是否已经生成成功
+		generated, needUpdate, err := mae.GetReportState()
+		if err != nil {
+			return nil, err
+		}
+		// 已经生成的 可以直接返回
+		if generated {
+			return map[string]interface{}{
+				"id":     util.EncodeId(mae.MgoRecordId),
+				"status": AnalysisStatusRealTime,
+			}, nil
+		}
+		// 如果查看的是还没有生成成功的 说明是历史数据
+		// 判断是否有正在分析中的报告
+		if analyzingId := mae.GetAnalyzingReport(); analyzingId != "" && analyzingId != mae.MgoRecordId {
+			//log.Println("有正在分析的:", analyzingId, mae.MgoRecordId)
+			return map[string]interface{}{
+				"id":     util.EncodeId(analyzingId),
+				"status": AnalysisStatusAnalyzing,
+			}, nil
+		}
+		var status int
+		var msgOpen bool
+		// 判断是实时的还是离线的
+		offline := mae.IsOffline()
+		if err != nil {
+			return nil, err
+		}
+		//  如果是历史报告处理完之后需要更新字段
+		if needUpdate {
+			mae.UpdateOffline(offline)
+		}
+		// 离线的
+		if offline {
+			// 查询消息开关
+			status = AnalysisStatusOffline
+			msgOpen = marketAnalysis.GetMsgOpen(mgoUserId)
+		} else {
+			status = AnalysisStatusRealTime
+		}
+		data := map[string]interface{}{
+			"id":      util.EncodeId(mae.MgoRecordId),
+			"status":  status,
+			"msgOpen": msgOpen,
+		}
+		return data, nil
+	}()
+	if errMsg != nil {
+		log.Printf("%s MarketAnalysis IsOffline Error:%s\n", userId, errMsg.Error())
+	}
+	this.ServeJson(NewResult(rData, errMsg))
+}
+
+// Delete 删除接口
+func (this *MarketAnalysis) Delete() {
+	sessVal := this.Session().GetMultiple()
+	mgoUserId := qutil.ObjToString(sessVal["mgoUserId"])
+	userId := qutil.ObjToString(sessVal["userId"])
+	positionId := qutil.IntAll(sessVal["positionId"])
+	rData, errMsg := func() (interface{}, error) {
+		pid, powerErr := checkPower(this.Session())
+		if powerErr != nil {
+			return nil, powerErr
+		}
+		mae := &marketAnalysis.MarketAnalysisEntity{MgoRecordId: util.DecodeId(this.GetString("rid")), UId: userId, Pid: pid, MgoUserId: mgoUserId, PositionId: positionId}
+		data, err := mae.Delete()
+		return data, err
+	}()
+	if errMsg != nil {
+		log.Printf("%s MarketAnalysis Delete Error:%s\n", userId, errMsg.Error())
+	}
+	this.ServeJson(NewResult(rData, errMsg))
+}
+
 // AnalysisHistory 分析报告历史
 func (this *MarketAnalysis) AnalysisHistory() {
-	userId := qutil.ObjToString(this.GetSession("userId"))
+	sessVal := this.Session().GetMultiple()
+	mgoUserId := qutil.ObjToString(sessVal["mgoUserId"])
+	userId := qutil.ObjToString(sessVal["userId"])
+	positionId := qutil.IntAll(sessVal["positionId"])
 	rData, errMsg := func() (interface{}, error) {
 		pid, powerErr := checkPower(this.Session())
 		if powerErr != nil {
@@ -192,7 +344,7 @@ func (this *MarketAnalysis) AnalysisHistory() {
 		if pageSizeErr != nil {
 			pageSize = 10
 		}
-		mae := &marketAnalysis.MarketAnalysisEntity{UId: userId, Pid: pid}
+		mae := &marketAnalysis.MarketAnalysisEntity{UId: userId, Pid: pid, MgoUserId: mgoUserId, PositionId: positionId}
 		total, list := mae.GetRecordList(pageNum, pageSize)
 		return map[string]interface{}{
 			"total": total,

+ 72 - 40
src/jfw/modules/bigmember/src/service/report/report.go

@@ -3,6 +3,7 @@ package report
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/gogf/gf/v2/util/gconv"
 	"jy/src/jfw/modules/bigmember/src/config"
 	. "jy/src/jfw/modules/bigmember/src/db"
 	"jy/src/jfw/modules/bigmember/src/entity"
@@ -57,8 +58,10 @@ func (s BarCharts) Swap(i, j int) {
 const (
 	oneday                                       = 86400
 	pushspace_experience_member_statistic        = "pushspace_experience_member_statistic"
-	pushspace_experience_member_statistic_winner = "pushspace_experience_member_statistic_winner"
+	From                                         = "yyszsyy"
 	pushspace_member_statistic                   = "pushspace_member_statistic"
+	pushspace_member_statistic_yys               = "pushspace_member_statistic_yys"
+	pushspace_experience_member_statistic_winner = "pushspace_experience_member_statistic_winner"
 	pushspace_member_statistic_winner            = "pushspace_member_statistic_winner"
 	C_User                                       = "user"
 	C_Entniche_Rule                              = "entniche_rule"
@@ -69,8 +72,9 @@ func (r *Report) Index() {
 		return
 	}
 	ym := r.GetString("ym")
+	from := r.GetString("from")
 	result := M{}
-	userId, qk, qv := getQuery(r.Session())
+	userId, qk, qv := getQuery(r.Session(), from)
 	if qk == "" || qv == nil {
 		return
 	}
@@ -95,7 +99,11 @@ func (r *Report) Index() {
 		R.InvalidReqParam(r.ResponseWriter, r.Request, "ym")
 		return
 	}
-	list, ok := Mgo.Find(pushspace_member_statistic, query, `{"startdate":-1}`, `{"startdate":1,"enddate":1,"pushcount":1,"pushtime":1,"unread":1}`, false, -1, -1)
+	table := pushspace_member_statistic
+	if from == From {
+		table = pushspace_member_statistic_yys
+	}
+	list, ok := Mgo.Find(table, query, `{"startdate":-1}`, `{"startdate":1,"enddate":1,"pushcount":1,"pushtime":1,"unread":1}`, false, -1, -1)
 	array := []map[string]interface{}{}
 	if ok && list != nil {
 		for _, v := range *list {
@@ -110,34 +118,38 @@ func (r *Report) Index() {
 	}
 	result["list"] = array
 	result["subscribe"] = false
-	o_mb := &map[string]interface{}{}
-	user := config.Compatible.Select(userId, `{"o_member_jy":1,"i_member_status":1,"s_member_mainid":1,"i_member_sub_status":1}`)
-	if user != nil {
-		if s_member_mainid := qutil.ObjToString((*user)["s_member_mainid"]); s_member_mainid != "" && qutil.IntAll((*user)["i_member_sub_status"]) == 1 && qutil.IntAll((*user)["i_member_status"]) > 0 {
-			user, ok = Mgo.FindById("user", s_member_mainid, `{"o_member_jy":1}`)
+	if from != From {
+		o_mb := &map[string]interface{}{}
+		user := config.Compatible.Select(userId, `{"o_member_jy":1,"i_member_status":1,"s_member_mainid":1,"i_member_sub_status":1}`)
+		if user != nil {
+			if s_member_mainid := qutil.ObjToString((*user)["s_member_mainid"]); s_member_mainid != "" && qutil.IntAll((*user)["i_member_sub_status"]) == 1 && qutil.IntAll((*user)["i_member_status"]) > 0 {
+				user, ok = Mgo.FindById("user", s_member_mainid, `{"o_member_jy":1}`)
+			}
 		}
-	}
-	if ok && o_mb != nil {
-		o_member_jy, _ := (*o_mb)["o_member_jy"].(map[string]interface{})
-		if o_member_jy["a_infotype"] != nil || o_member_jy["o_area"] != nil || o_member_jy["a_buyerclass"] != nil {
-			result["subscribe"] = true
-		} else {
-			a_items, _ := o_member_jy["a_items"].([]interface{})
-		L:
-			for _, a_item := range a_items {
-				itemMap, _ := a_item.(map[string]interface{})
-				a_keys, _ := itemMap["a_key"].([]interface{})
-				for _, a_key := range a_keys {
-					keyMap, _ := a_key.(map[string]interface{})
-					keys, _ := keyMap["key"].([]interface{})
-					for _, key := range keys {
-						if strings.TrimSpace(qutil.ObjToString(key)) != "" {
-							result["subscribe"] = true
-							break L
+		if ok && o_mb != nil {
+			o_member_jy, _ := (*o_mb)["o_member_jy"].(map[string]interface{})
+			if o_member_jy["a_infotype"] != nil || o_member_jy["o_area"] != nil || o_member_jy["a_buyerclass"] != nil {
+				result["subscribe"] = true
+			} else {
+				a_items, _ := o_member_jy["a_items"].([]interface{})
+			L:
+				for _, a_item := range a_items {
+					itemMap, _ := a_item.(map[string]interface{})
+					a_keys, _ := itemMap["a_key"].([]interface{})
+					for _, a_key := range a_keys {
+						keyMap, _ := a_key.(map[string]interface{})
+						keys, _ := keyMap["key"].([]interface{})
+						for _, key := range keys {
+							if strings.TrimSpace(qutil.ObjToString(key)) != "" {
+								result["subscribe"] = true
+								break L
+							}
 						}
 					}
 				}
 			}
+		} else {
+			result["subscribe"] = true
 		}
 	}
 	r.ServeJson(Result{
@@ -150,12 +162,13 @@ func (r *Report) Detail() {
 	}
 	start, _ := r.GetInt("start")
 	end, _ := r.GetInt("end")
+	from := r.GetString("from")
 	var m M
-	userId, qk, qv := getQuery(r.Session())
+	userId, qk, qv := getQuery(r.Session(), from)
 	if qk == "" || qv == nil {
 		return
 	}
-	coll, coll_winner := getColl(r.Header("referer"), r.GetString("type"))
+	coll, coll_winner := getColl(r.Header("referer"), r.GetString("type"), from)
 	// userId, _, _ = MainUserId(userId, "", 0)
 	if end-start == 518400 { //周
 		m = weekResult(qk, qv, start, end, coll, coll_winner)
@@ -184,10 +197,13 @@ func (r *Report) Detail() {
 		positionId = mainPositionId
 	}
 	// 省份
-	m["area"] = entity.ReportGetArea(positionId, start, end)
+	m["area"] = entity.ReportGetArea(positionId, start, end, from)
 	// 采购单位类型
-	m["buyerClass"] = entity.ReportGetBuyerClass(positionId, start, end)
+	m["buyerClass"] = entity.ReportGetBuyerClass(positionId, start, end, from)
 	m["isNewData"] = isNewData
+	if from == From {
+		m["industry"] = entity.ReportGetIndustry(positionId, start, end, from)
+	}
 	r.ServeJson(Result{
 		Data: m,
 	})
@@ -252,7 +268,7 @@ func weekResult(qk string, qv interface{}, start, end int64, coll, coll_winner s
 			"$lte": end,
 		},
 		"type": 2,
-	}, `{"startdate":1}`, `{"startdate":1,"type":1,"item":1,"bidamount":1,"budget":1,"zhao_matchitem":1,"zhong_matchitem":1,"follow_project":1,"follow_ent":1,"nextweek_bidopen":1,"project_amount":1}`, false, 0, 2)
+	}, `{"startdate":1}`, `{"startdate":1,"type":1,"item":1,"industry":1,bidamount":1,"budget":1,"zhao_matchitem":1,"zhong_matchitem":1,"follow_project":1,"follow_ent":1,"nextweek_bidopen":1,"project_amount":1}`, false, 0, 2)
 	result := M{}
 	prevWeek := map[string]interface{}{}
 	if ok && list != nil {
@@ -263,6 +279,8 @@ func weekResult(qk string, qv interface{}, start, end int64, coll, coll_winner s
 			}
 			//订阅关键词组
 			result["item"] = v["item"]
+			//行业
+			result["industry"] = v["industry"]
 			//本周新增招标项目数量
 			result["zhao_matchitem"] = v["zhao_matchitem"]
 			//本周新增开标项目数量
@@ -298,7 +316,7 @@ func monthResult(userId, qk string, qv interface{}, start, end int64, coll, coll
 		"enddate": map[string]interface{}{
 			"$lte": end,
 		},
-	}, `{"startdate":1}`, `{"startdate":1,"item":1,"project_matchitem_count":1,"project_matchitem_bidamount":1,"project_count":1,"project_bidamount_count":1,"project_area_count":1,"project_area_bidamount":1,"project_buyerclass_count":1,"project_buyerclass_bidamount":1,"project_buyerclass_average_bidamount":1,"winner_area":1,"buyer":1,"winner":1,"project_bidamount":1,"winner_capital_array":1,"winner_project":1}`, false, -1, -1)
+	}, `{"startdate":1}`, `{"startdate":1,"item":1,"industry":1,project_matchitem_count":1,"project_matchitem_bidamount":1,"project_count":1,"project_bidamount_count":1,"project_area_count":1,"project_area_bidamount":1,"project_buyerclass_count":1,"project_buyerclass_bidamount":1,"project_buyerclass_average_bidamount":1,"winner_area":1,"buyer":1,"winner":1,"project_bidamount":1,"winner_capital_array":1,"winner_project":1}`, false, -1, -1)
 	if ok && sixDatas != nil {
 		project_count := &BarCharts{
 			Keys: map[int64]bool{},
@@ -315,6 +333,8 @@ func monthResult(userId, qk string, qv interface{}, start, end int64, coll, coll
 			} else if startdate == start { //本月
 				//订阅关键词组
 				result["item"] = v["item"]
+				//行业
+				result["industry"] = v["industry"]
 				//本月项目规模
 				result["project_matchitem_count"] = v["project_matchitem_count"]
 				//本月项目规模
@@ -514,7 +534,7 @@ func getGrowthRate(v1, v2 float64) float64 {
 }
 
 func (r *Report) Tip() {
-	_, qk, qv := getQuery(r.Session())
+	_, qk, qv := getQuery(r.Session(), "")
 	if qk == "" || qv == nil {
 		return
 	}
@@ -535,7 +555,7 @@ func (r *Report) Tip() {
 	r.ServeJson(result)
 }
 func (r *Report) Tipover() {
-	_, qk, qv := getQuery(r.Session())
+	_, qk, qv := getQuery(r.Session(), "")
 	if qk == "" || qv == nil {
 		return
 	}
@@ -553,7 +573,7 @@ func tipover(qk string, qv interface{}, coll string) bool {
 	}, false, true)
 }
 func (r *Report) Openpushmsg() error {
-	userId, qk, qv := getQuery(r.Session())
+	userId, qk, qv := getQuery(r.Session(), "")
 	pushcount, _ := r.GetInteger("pushcount")
 	positionType := qutil.Int64All(r.GetSession("positionType"))
 	start, _ := r.GetInt("start")
@@ -582,12 +602,17 @@ func (r *Report) Openpushmsg() error {
 }
 
 func (r *Report) Starttime() {
-	_, qk, qv := getQuery(r.Session())
+	from := r.GetString("from")
+	_, qk, qv := getQuery(r.Session(), from)
 	if qk == "" || qv == nil {
 		return
 	}
+	table := pushspace_member_statistic
+	if from == From {
+		table = pushspace_member_statistic_yys
+	}
 	week_start, month_start := 0, 0
-	week_list, _ := Mgo.Find(pushspace_member_statistic, map[string]interface{}{
+	week_list, _ := Mgo.Find(table, map[string]interface{}{
 		qk:     qv,
 		"type": 2,
 	}, `{"enddate":1}`, `{"enddate":1,"dateym":1}`, false, 0, 1)
@@ -598,7 +623,7 @@ func (r *Report) Starttime() {
 		}
 	}
 	//
-	month_list, _ := Mgo.Find(pushspace_member_statistic, map[string]interface{}{
+	month_list, _ := Mgo.Find(table, map[string]interface{}{
 		qk:     qv,
 		"type": 3,
 	}, `{"enddate":1}`, `{"enddate":1,"dateym":1}`, false, 0, 1)
@@ -616,10 +641,14 @@ func (r *Report) Starttime() {
 	})
 }
 
-func getColl(referer, t string) (string, string) {
+func getColl(referer, t, from string) (string, string) {
 	if t == "free" || strings.Contains(referer, "t=wx_experience") || strings.Contains(referer, "t=app_experience") {
+
 		return pushspace_experience_member_statistic, pushspace_experience_member_statistic_winner
 	} else {
+		if from == From {
+			return pushspace_member_statistic_yys, pushspace_member_statistic_winner
+		}
 		return pushspace_member_statistic, pushspace_member_statistic_winner
 	}
 }
@@ -633,9 +662,12 @@ func isFree(userId string, positionType int64, session *httpsession.Session) boo
 	return false
 }
 
-func getQuery(sess *httpsession.Session) (string, string, interface{}) {
+func getQuery(sess *httpsession.Session, from string) (string, string, interface{}) {
 	sessMap := sess.GetMultiple()
 	userId := qutil.ObjToString(sessMap["userId"])
+	if from == From {
+		return userId, "userid", gconv.String(sessMap["positionId"])
+	}
 	if qutil.IntAll(sessMap["positionType"]) == 1 {
 		return userId, "ent_userid", qutil.IntAll(sessMap["entUserId"])
 	} else {

+ 2 - 2
src/jfw/modules/bigmember/src/util/email.go

@@ -1,13 +1,13 @@
 package util
 
 import (
+	"app.yhyue.com/moapp/jybase/mail"
 	"fmt"
 	"log"
-	"app.yhyue.com/moapp/jybase/mail"
 	"time"
 )
 
-//发送邮件
+// 发送邮件
 func SendRetryMail(retry int, user_mail, subject, content, fname string, bt []byte, auth []*mail.GmailAuth) bool {
 	for i := 1; i <= retry; i++ {
 		for _, v := range auth { //使用多个邮箱尝试发送

+ 6 - 5
src/jfw/modules/publicapply/src/userbase/entity/entity.go

@@ -446,8 +446,8 @@ func (o *OriginalPower) CountRecord() int {
 	// UseUserAccountTime 此时间之后使用新表统计数据
 	if initjson.UseUserAccountTime.Before(time.Now()) {
 		// 查询条数
-		qCount := fmt.Sprintf("select usage_count from %s where position_id=? %s", TableOriginalUserLedger, where)
-		countRs := db.Mysql.SelectBySql(qCount, o.PositionId)
+		qCount := fmt.Sprintf("select usage_count from %s where position_id=? and user_type=? %s", TableOriginalUserLedger, where)
+		countRs := db.Mysql.SelectBySql(qCount, o.PositionId, o.UserType)
 		// 已经有数据
 		if countRs != nil && len(*countRs) > 0 {
 			return util.IntAll((*countRs)[0]["usage_count"])
@@ -518,12 +518,12 @@ func (o *OriginalPower) IncOriginalUserAccount() {
 		where = fmt.Sprintf(`and create_time >="%s"`, timeStart)
 	}
 	// 查询条数
-	qCount := fmt.Sprintf("select id from %s where position_id=? %s", TableOriginalUserLedger, where)
-	countRs := db.Mysql.SelectBySql(qCount, o.PositionId)
+	qCount := fmt.Sprintf("select id from %s where position_id=? and user_type=? %s", TableOriginalUserLedger, where)
+	countRs := db.Mysql.SelectBySql(qCount, o.PositionId, o.UserType)
 	if countRs != nil && len(*countRs) > 0 {
 		id := (*countRs)[0]["id"]
 		// 存在则执行更新
-		_, err := db.Mysql.ExecBySql(fmt.Sprintf("update %s set usage_count=usage_count+1 where id=?", TableOriginalUserLedger), id)
+		_, err := db.Mysql.ExecBySql(fmt.Sprintf("update %s set usage_count=usage_count+1 where id=? ", TableOriginalUserLedger), id)
 		if err != nil {
 			log.Println("更新条数信息失败:", err, o.Phone, o.UserId, o.Client, o.PositionId)
 			return
@@ -537,6 +537,7 @@ func (o *OriginalPower) IncOriginalUserAccount() {
 			"usage_count": 1,
 			"create_time": date.NowFormat(date.Date_Full_Layout),
 			"update_time": date.NowFormat(date.Date_Full_Layout),
+			"user_type":   o.UserType,
 		})
 	}
 }

+ 1 - 1
src/web/staticres/common-module/collection/js/keyword-mobile.js

@@ -198,7 +198,7 @@ var keywordComponent = {
           if(_this.protype == 'bigmember') {
             if(res.error_code == 0) {
               let maxarr = []
-              if(res.data) {
+              if(res.data && res.data.member_jy) {
                 let data = res.data.member_jy.a_items
                 _this.keywordGroupList = data
                 data.forEach(function(item,index) {

+ 46 - 7
src/web/staticres/common-module/diy-report/css/report-list.css

@@ -1,18 +1,18 @@
 .info-card-group {
-    padding: .4rem;
+    padding: .16rem .24rem;
     box-sizing: border-box;
     background: #F4F5F7;
     font-family: -apple-system,BlinkMacSystemFont,Helvetica Neue,PingFang SC,Microsoft YaHei,Source Han Sans SC,Noto Sans CJK SC,WenQuanYi Micro Hei,sans-serif;
 
 }
 .info-card-group .info-card-item + .info-card-item {
-    margin-top: .32rem;
+    margin-top: .16rem;
 }
 .info-card-group .info-card-item {
     background: #FFFFFF;
     box-shadow: 0px 2px 8px 1px rgba(54, 147, 179, 0.05);
     border-radius: 8px;
-    padding: .32rem .24rem;
+    padding: 0 .32rem .32rem;
     box-sizing: border-box;
     font-size: .26rem;
     font-weight: 500;
@@ -20,10 +20,10 @@
     line-height: .4rem;
 }
 .info-card-item .text-title {
-    font-size: .3rem;
+    font-size: .26rem;
     font-weight: 700;
     color: #161826;
-    line-height: .44rem;
+    line-height: .4rem;
 }
 .info-flex-r-box {
     display: flex;
@@ -44,7 +44,7 @@
     align-items: flex-start;
     justify-content: flex-start;
 }
-.info-card-item button {
+/* .info-card-item button {
     display: flex;
     align-items: center;
     justify-content: center;
@@ -58,7 +58,7 @@
     font-weight: 400;
     color: #FFFFFF;
     border: none;
-}
+} */
 .info-card-item .toggle-row-box i {
     font-size: .32rem;
     margin-left: .04rem;
@@ -85,3 +85,42 @@
     font-size: .26rem;
     color: #161826;
 }
+.info-status-header{
+  display: flex;
+  justify-content: space-between;
+  padding: 0 0 .32rem 0;
+}
+.info-status-header .r-status{
+  flex-shrink: 0;
+  margin-left: -0.32rem;
+  padding: 0.04rem .24rem;
+  font-size: .26rem;
+  line-height: .4rem;
+  color: #fff;
+  border-radius: 0.16rem 0 .16rem 0;
+}
+.info-status-header .r-status.green{
+  background: #0BD991;
+}
+.info-status-header .r-status.yellow{
+  background: #FF9F40;
+}
+.info-status-header .r-status.red{
+  background: #FB483D;
+}
+.info-status-header .r-notes{
+  margin-left: .24rem;
+  padding-top: 0.12rem;
+  font-size: .26rem;
+  color: #5F5E64;
+  white-space: nowrap;
+  /* text-overflow: ellipsis; */
+  /* overflow: hidden; */
+}
+.j-button-group{
+  padding: .24rem 0 0;
+}
+.j-button-group button{
+  border-radius: 0.12rem;
+  height: .72rem;
+}

+ 52 - 2
src/web/staticres/common-module/diy-report/js/report-list.js

@@ -14,8 +14,18 @@ var reportListTemplate = `
             </div>
         </div>
         <div class="info-card-item" v-for="(item, i) in reportList" :key="item.id" :class="{ 'card-opened': item.open }">
+            <div class="info-status-header">
+              <div class="r-status" :class="getStatusBg(item.state)" v-if="item.state || item.state === 0">@@getStatusTitle(item.state)@@</div>
+              <div class="r-notes" v-if="item.state === 0">
+                <span v-if="item.msgOpen">请留意微信、APP报告生成成功消息提醒</span>
+                <div class="info-flex-r-box" @click.stop="goSetPush" v-else><span class="ellipsis">前往开启【消息-服务通知】提醒</span><van-icon name="arrow" style="margin-left:4px;"></van-icon></div>
+              </div>
+              <div class="r-notes" v-if="item.state >= 1 && item.updateTime">
+                于<span class="highlight-text">@@(item.updateTime * 1000)|formatTime('yyyy-MM-dd HH:mm')@@</span><span v-if="item.state === 1">生成成功</span><span v-else>取消</span>
+              </div>
+            </div>
             <div class="info-flex-r-box">
-                <div class="text-title">报告&nbsp;@@(item.createTime * 1000)|formatTime('yyyy.MM.dd HH:mm:ss')@@</div>
+                <div class="text-title">发起时间:@@(item.createTime * 1000)|formatTime('yyyy-MM-dd HH:mm')@@</div>
                     <div class="info-flex-r-box toggle-row-box" @click="item.open = !item.open">
                         <span>@@item.open ? '收起' : '展开'@@条件</span>
                         <transition name="van-fade">
@@ -63,7 +73,13 @@ var reportListTemplate = `
                         </div>
                     </div>
                 </div>
-                <button @click="goReport(item.id)">查看报告</button>
+                <div class="j-button-group" v-if="item.state === 1 || item.state === null">
+                  <button class="j-button-cancel" @click="goDelete(item.id)">删除</button>
+                  <button class="j-button-confirm" @click="goReport(item.id)">查看</button>
+                </div>
+                <div class="j-button-group" v-else>
+                  <button class="j-button-confirm" @click="goDelete(item.id)">删除</button>
+                </div>
             </div>
         </div>
     </van-list>
@@ -149,6 +165,9 @@ var reportListMobileComponent = {
         goReport: function (id) {
             this.$emit('go-report', id)
         },
+        goDelete: function (id) {
+            this.$emit('go-delete', id)
+        },
         getList: function () {
             $.ajax({
                 url: '/bigmember/marketAnalysis/analysisHistory',
@@ -229,6 +248,37 @@ var reportListMobileComponent = {
                 }
             } catch (e) {}
             return tempStr
+        },
+        getStatusTitle: function (state) {
+          switch (state) {
+            case 0:
+              return '报告生成中'
+            case 1:
+              return '报告生成成功'
+            case 2:
+              return '报告已取消'
+          }
+        },
+        getStatusBg: function (state) {
+          switch (state) {
+            case 0:
+              return 'yellow'
+            case 1:
+              return 'green'
+            case 2:
+              return 'red'
+          }
+        },
+        goSetPush: function () {
+          location.href = '/jy_mobile/push/pushsetting?active=1'
+        },
+        onRefresh: function () {
+          this.listState.pageNum = 1
+          this.listState.loaded = false
+          this.listState.loading = true
+          this.listState.finished = false
+          this.reportList = []
+          this.getList()
         }
     }
 }

+ 9 - 2
src/web/staticres/common-module/report-analysis/css/report_analysis.css

@@ -42,8 +42,8 @@
   color: #5F5E64;
 }
 .van-dialog .van-button {
-  font-size: 18px;
-  line-height: 26px;
+  font-size: .36rem;
+  line-height:.52rem;
 }
 
 .van-dialog  .van-button--default {
@@ -67,12 +67,19 @@
 }
 .j-confirm-dialog.text-center .van-dialog__message {
   text-align: center;
+  color: #5F5E64;
 }
 .j-confirm-dialog .van-button {
   font-size: .36rem;
   color: #171826;
   line-height: .52rem;
 }
+.j-confirm-dialog.text-justify .van-dialog__message {
+  font-size: .3rem!important;
+  color: #5F5E64!important;
+  line-height: .44rem!important;
+  text-align: justify!important;
+}
 
 /* j-tag */
 .j-tag {

+ 83 - 9
src/web/staticres/common-module/report-analysis/js/report_analysis.js

@@ -857,25 +857,78 @@ var vm = new Vue({
 
       this.analysis.loaded = false
       this.analysis.loading = true
-      this.showLoading()
-
+      var loading = this.showLoading()
+      /* 
+        1.如该用户当前身份下存在“生成中”的报告 则弹框提示报告生成中等等。。。
+        2.该用户不存在“生成中”的报告,且该报告需要离线生成
+         2-1:【消息-服务通知】APP、微信提醒均未开启 则弹框提示去开启
+         2-2:【消息-服务通知】APP或微信提醒已开启 则弹框提示我知道了
+      */
       $.ajax({
         type: 'POST',
         url: '/bigmember/marketAnalysis/doAnalysis',
         data: query,
         success: function (res) {
+          loading.clear()
           if (res && res.error_code === 0 && res.data) {
-            this.rid = res.data
-            this.analysis.loaded = true
-            // location.replace('./report_analysis?id=' + res.data)
-            this.rid = res.data
-            history.replaceState({}, null, '?id=' + this.rid)
-            this.getReportResult()
+            // id:报告id
+            // msgOpen:微信或APP提醒是否打开(离线生成时会返回该字段)
+            // status:0-在线生成 走原逻辑 1-离线生成 (判断msgOpen出提示文案) 2-存在正在生成的报告(需要出弹出提示,由用户确认,id为需要取消的报告id,如果用户确认,则传该id调用取消接口后再次调用分析接口)
+            if (res.data.status === 0) {
+              this.rid = res.data.id
+              this.analysis.loaded = true
+              // location.replace('./report_analysis?id=' + res.data)
+              this.rid = res.data.id
+              history.replaceState({}, null, '?id=' + this.rid)
+              this.getReportResult()
+            } else if (res.data.status === 1) {
+              return this.showDialog({
+                allowHtml: true,
+                title: '报告生成中',
+                message: res.data.msgOpen ? '由于您的分析内容较多,报告正在努力生成中,生成成功后将会通过微信、APP给您发送1消息通知,届时您再前往查看此报告。' : '由于您的分析内容较多,报告正在努力生成中。建议您前往开启“<span class="highlight-text">消息-服务通知</span>”提醒,报告生成成功后可通过微信或APP给您发送1消息通知,届时您可前往查看此报告。',
+                className: 'j-confirm-dialog text-justify',
+                showCancelButton: !res.data.msgOpen,
+                confirmButtonText: res.data.msgOpen ? '我知道了' : '去开启',
+                cancelButtonText: res.data.msgOpen ? '' : '暂不开启',
+                beforeClose: (action, done) => {
+                  if (action === 'confirm') {
+                    if (!res.data.msgOpen) {
+                      // 去开启  进到推送设置页面
+                      location.href = '/jy_mobile/push/pushsetting?active=1'
+                    } else {
+                      // 我知道了 回到历史报告页面
+                      this.goToAnalysisHistory()
+                    }
+                  } else {
+                    // 暂不开启  回到历史报告页面
+                    this.goToAnalysisHistory()
+                  }
+                  done()
+                }
+              })
+            } else if (res.data.status === 2) {
+              return this.showDialog({
+                title: '报告生成确认',
+                message: '您有1份报告正在“生成中”,请确定是否按照此条件重新生成,注:如确定则原状态为“生成中”的报告将被自动取消。',
+                className: 'j-confirm-dialog text-justify',
+                beforeClose: (action, done) => {
+                  if (action === 'confirm') {
+                    this.reportCancel(res.data.id)
+                    done()
+                  } else {
+                    done()
+                  }
+                }
+              })
+            }
+            
           } else {
+            loading.clear()
             this.$toast(res.error_msg)
           }
         }.bind(this),
         complete: function () {
+          loading.clear()
           this.analysis.loading = false
         }.bind(this)
       })
@@ -1006,7 +1059,7 @@ var vm = new Vue({
 
 
           } else {
-            this.$toast('请求失败')
+            // this.$toast('请求失败')
           }
         }.bind(this)
       })
@@ -2097,6 +2150,27 @@ var vm = new Vue({
           location.href = `./unit_portrayal?entName=${id}`
         }
       }
+    },
+    // 报告取消
+    reportCancel: function (id) {
+      var _this = this
+      $.ajax({
+        type: 'POST',
+        url: '/bigmember/marketAnalysis/cancel',
+        data: {
+          rid: id
+        },
+        success: function (res) {
+          if (res.data) {
+            _this.startAnalysis()
+          } else {
+            _this.showToast(res.error_msg)
+          }
+        },
+        error: function (error) {
+          console.error(error)
+        }
+      })
     }
   }
 })

+ 119 - 6
src/web/staticres/common-module/report-analysis/js/report_analysis_history.js

@@ -101,16 +101,107 @@ var vm = new Vue({
     beforeTabChange: function (name) {
       if (name !== this.tabActiveName) {
         this.saveState()
-        this.goToAnalysis()
+        // this.goToAnalysis()
+        var href = './report_analysis'
+        location.replace(href)
       }
       return false
     },
     goToAnalysis: function (id) {
-      var href = './report_analysis'
-      if (id) {
-        href += ('?id=' + id)
-      }
-      location.replace(href)
+      var loading = this.showLoading()
+      var _this = this
+      $.ajax({
+        type: 'POST',
+        url: '/bigmember/marketAnalysis/isOffline',
+        data: {
+          rid: id
+        },
+        success: function (res) {
+          loading.clear()
+          if (res.data) {
+            if (res.data.status === 0) {
+              // 原逻辑
+              var href = './report_analysis'
+              if (id) {
+                href += ('?id=' + id)
+              }
+              location.replace(href)
+            } else if (res.data.status === 1) {
+              // 离线生成 (判断msgOpen 出提示文案)
+              _this.showDialog({
+                allowHtml: true,
+                title: '报告生成中',
+                message: res.data.msgOpen ? '由于您的分析内容较多,报告正在努力生成中,生成成功后将会通过微信、APP给您发送1消息通知,届时您再前往查看此报告。' : '由于您的分析内容较多,报告正在努力生成中。建议您前往开启“<span class="highlight-text">消息-服务通知</span>”提醒,报告生成成功后可通过微信或APP给您发送1消息通知,届时您可前往查看此报告。',
+                className: 'j-confirm-dialog text-justify',
+                showCancelButton: !res.data.msgOpen,
+                confirmButtonText: res.data.msgOpen ? '我知道了' : '去开启',
+                cancelButtonText: res.data.msgOpen ? '' : '暂不开启'
+              }).then(function () {
+                  // 未开启-去开启
+                  if (!res.data.msgOpen) {
+                    location.href = '/jy_mobile/push/pushsetting?active=1'
+                  }
+              })
+              .catch(function(error) {
+                console.log(error)
+              })
+            } else if (res.data.status === 2) {
+              // 存在正在生成的报告 需要出弹出提示,由用户确认,id为需要取消的报告id,如果用户确认,则传该id调用取消接口后再次调用该接口)
+              _this.showDialog({
+                title: '报告生成确认',
+                message: '您有1份报告正在“生成中”,请确定是否按照此条件重新生成,注:如确定则原状态为“生成中”的报告将被自动取消。',
+                className: 'j-confirm-dialog text-justify',
+              }).then(function () {
+                // 调用取消
+                _this.reportCancel(res.data.id)
+              })
+              .catch(function() {
+                console.log('catch')
+              })
+            }
+          } else {
+            _this.showToast(res.data.error_msg)
+          }
+        },
+        error: function (error) {
+          loading.clear()
+          console.warn(error)
+        }
+      })
+      
+    },
+    goDelete: function (id) {
+      var _this = this
+      _this.showDialog({
+        title: '报告删除确认',
+        message: '确定删除此报告吗?',
+        className: 'j-confirm-dialog text-center',
+      }).then(function (res) {
+          var loading = _this.showLoading()
+          $.ajax({
+            type: 'POST',
+            url: '/bigmember/marketAnalysis/delete',
+            data: {
+              rid: id
+            },
+            success: function (res) {
+              loading.clear()
+              if (res.data) {
+                _this.showToast('删除成功')
+                _this.$refs.list.onRefresh()
+              } else {
+                _this.showToast(res.error_msg || '删除失败')
+              }
+            },
+            error: function (error) {
+              loading.clear()
+              console.warn(error)
+            }
+          })
+      })
+      .catch(function() {
+          console.log('取消删除报告')
+      })
     },
     // 重置列表数据
     resetHistoryState: function () {
@@ -217,6 +308,28 @@ var vm = new Vue({
       }
 
       return $data
+    },
+    // 报告取消
+    reportCancel: function (id) {
+      var _this = this
+      $.ajax({
+        type: 'POST',
+        url: '/bigmember/marketAnalysis/cancel',
+        data: {
+          rid: id
+        },
+        success: function (res) {
+          if (res.data) {
+            _this.$refs.list.onRefresh()
+            _this.goToAnalysis(id)
+          } else {
+            _this.showToast(res.error_msg)
+          }
+        },
+        error: function (error) {
+          console.error(error)
+        }
+      })
     }
   }
 })

+ 1 - 1
src/web/templates/big-member/wx/page_report_analysis_history.html

@@ -44,7 +44,7 @@
           </van-tabs>
           <section v-show="tabActiveName === 'analysis'" class="j-main analysis-content"></section>
           <section v-show="tabActiveName === 'history'" class="j-main history-content">
-              <report-list-mobile-component ref="list" @go-report="goToAnalysis"></report-list-mobile-component>
+              <report-list-mobile-component ref="list" @go-report="goToAnalysis" @go-delete="goDelete"></report-list-mobile-component>
           </section>
       </div>
   </div>