浏览代码

Merge branch 'master' into feature/v1.1.38

lianbingjie 1 年之前
父节点
当前提交
6826181d5b

+ 10 - 0
.gitignore

@@ -0,0 +1,10 @@
+/jyBXBuyer/rpc/jylog/
+/jyBXBuyer/api/jylog/
+*.exe
+*_linux
+/jyBXBase/.output/
+/jyBXBase/rpc/jylog/
+/jyBXBase/api/jylog/
+/jyBXCore/rpc/jylog/
+*.log
+.idea

+ 29 - 1
jyBXCore/api/bxcore.api

@@ -48,6 +48,12 @@ type (
 		Err_msg  string      `json:"error_msg"`
 		Data     interface{} `json:"data"`
 	}
+	commonPushResp {
+		Err_code   int64       `json:"error_code"`
+		Err_msg    string      `json:"error_msg"`
+		Data       interface{} `json:"data"`
+		SourceItem interface{} `json:"sourceItem"`
+	}
 	//
 	searchLimitReq {
 		AppId      string `header:"appId,optional"`    //appid
@@ -164,6 +170,9 @@ type (
 		EntUserIdArr []string `json:"entUserIdArr,optional"`
 		StartTime    int64    `json:"startTime,optional"`
 		EndTime      int64    `json:"endTime,optional"`
+		Source       []int64  `json:"source,optional"`   // 标讯项目来源 -1:全部  1:个人订阅 2:企业自动分发 3:企业手动分发
+		BidWay       int64    `json:"bidWay,optional"`   // 投标类型 -1:全部 1:直接投标 2:渠道投标
+		IsMobile     bool     `json:"isMobile,optional"` // 是否是移动端  因为移动端和pc要求展示的不一致
 	}
 	polymerizeSearchReq {
 		SearchCode   string `json:"searchCode"`
@@ -177,6 +186,23 @@ type (
 		EntAccountId int64  `header:"entAccountId,optional"` //企业账户id
 		EntUserId    int64  `header:"newUserId,optional"`
 	}
+	ProjectDetailReq {
+		PositionId      int64    `header:"positionId,optional"`   //职位id
+		PositionType    int64    `header:"positionType,optional"` //职位类型 0个人 1企业
+		EntId           int64    `header:"entId,optional"`
+		EntUserId       int64    `header:"entUserId,optional"`
+		DeptId          int64    `header:"deptId,optional"`
+		EntUserIdArr    []string `json:"entUserIdArr,optional"`
+		StartTime       int64    `json:"startTime,optional"`
+		EndTime         int64    `json:"endTime,optional"`
+		Source          []int64  `json:"source,optional"`          // 标讯项目来源 -1:全部  1:个人订阅 2:企业自动分发 3:企业手动分发
+		BidWay          int64    `json:"bidWay,optional"`          // 投标类型 -1:全部 1:直接投标 2:渠道投标
+		Isparticipate   int64    `json:"isparticipate,optional"`   // 参标状态;-1:全部 0:未参标 1:是
+		UpdateStartTime int64    `json:"updateStartTime,optional"` //
+		UpdateEndTime   int64    `json:"updateEndTime,optional"`   //
+		PageSize        int64    `json:"pageSize,optional"`        //
+		PageNum         int64    `json:"pageNum,optional"`         //
+	}
 )
 service bxcore-api {
 	@handler searchList
@@ -202,10 +228,12 @@ service bxcore-api {
 	@handler participateList //我的参标项目列表|企业参标项目列表
 	post /jybx/core/participate/:identity/list(participateListReq) returns (commonResp)
 	@handler pushStatistics //	订阅推送统计
-	post /jybx/core/statistics/pushStatistics(ptatisticsListReq) returns (commonResp)
+	post /jybx/core/statistics/pushStatistics(ptatisticsListReq) returns (commonPushResp)
 	@handler projectStatistics//参标项目统计
 	post /jybx/core/statistics/projectStatistics(ptatisticsListReq) returns (commonResp)
 	@handler polymerizeSearch//参标项目统计
 	post /jybx/core/polymerizeSearch(polymerizeSearchReq) returns (commonResp)
+	@handler statisticsProjectDetails//参标项目明细
+	post /jybx/core/statistics/projectDetails(ProjectDetailReq) returns (commonResp)
 	
 }

+ 5 - 0
jyBXCore/api/internal/handler/routes.go

@@ -82,6 +82,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
 				Path:    "/jybx/core/polymerizeSearch",
 				Handler: polymerizeSearchHandler(serverCtx),
 			},
+			{
+				Method:  http.MethodPost,
+				Path:    "/jybx/core/statistics/projectDetails",
+				Handler: statisticsProjectDetailsHandler(serverCtx),
+			},
 		},
 	)
 }

+ 28 - 0
jyBXCore/api/internal/handler/statisticsProjectDetailsHandler.go

@@ -0,0 +1,28 @@
+package handler
+
+import (
+	"net/http"
+
+	"github.com/zeromicro/go-zero/rest/httpx"
+	"jyBXCore/api/internal/logic"
+	"jyBXCore/api/internal/svc"
+	"jyBXCore/api/internal/types"
+)
+
+func statisticsProjectDetailsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		var req types.ProjectDetailReq
+		if err := httpx.Parse(r, &req); err != nil {
+			httpx.Error(w, err)
+			return
+		}
+
+		l := logic.NewStatisticsProjectDetailsLogic(r.Context(), svcCtx)
+		resp, err := l.StatisticsProjectDetails(&req)
+		if err != nil {
+			httpx.Error(w, err)
+		} else {
+			httpx.OkJson(w, resp)
+		}
+	}
+}

+ 1 - 0
jyBXCore/api/internal/logic/projectStatisticsLogic.go

@@ -38,6 +38,7 @@ func (l *ProjectStatisticsLogic) ProjectStatistics(req *types.PtatisticsListReq)
 		DeptId:       req.DeptId,
 		StartTime:    req.StartTime,
 		EndTime:      req.EndTime,
+		BidWay:       req.BidWay,
 	})
 	if err != nil {
 		return nil, err

+ 6 - 3
jyBXCore/api/internal/logic/pushStatisticsLogic.go

@@ -25,7 +25,7 @@ func NewPushStatisticsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Pu
 	}
 }
 
-func (l *PushStatisticsLogic) PushStatistics(req *types.PtatisticsListReq) (resp *types.CommonResp, err error) {
+func (l *PushStatisticsLogic) PushStatistics(req *types.PtatisticsListReq) (resp *types.CommonPushResp, err error) {
 	// todo: add your logic here and delete this line
 	for k, v := range req.EntUserIdArr {
 		req.EntUserIdArr[k] = encrypt.SE.Decode4Hex(v)
@@ -38,11 +38,14 @@ func (l *PushStatisticsLogic) PushStatistics(req *types.PtatisticsListReq) (resp
 		DeptId:       req.DeptId,
 		StartTime:    req.StartTime,
 		EndTime:      req.EndTime,
+		Source:       req.Source,
+		IsMobile:     req.IsMobile,
 	})
 	if err != nil {
 		return nil, err
 	}
-	return &types.CommonResp{
-		Data: res.Data,
+	return &types.CommonPushResp{
+		Data:       res.Data,
+		SourceItem: res.SourceItem,
 	}, nil
 }

+ 57 - 0
jyBXCore/api/internal/logic/statisticsProjectDetailsLogic.go

@@ -0,0 +1,57 @@
+package logic
+
+import (
+	"app.yhyue.com/moapp/jybase/encrypt"
+	"context"
+	"jyBXCore/rpc/type/bxcore"
+
+	"jyBXCore/api/internal/svc"
+	"jyBXCore/api/internal/types"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type StatisticsProjectDetailsLogic struct {
+	logx.Logger
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+}
+
+func NewStatisticsProjectDetailsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *StatisticsProjectDetailsLogic {
+	return &StatisticsProjectDetailsLogic{
+		Logger: logx.WithContext(ctx),
+		ctx:    ctx,
+		svcCtx: svcCtx,
+	}
+}
+
+func (l *StatisticsProjectDetailsLogic) StatisticsProjectDetails(req *types.ProjectDetailReq) (resp *types.CommonResp, err error) {
+	// todo: add your logic here and delete this line
+	for k, v := range req.EntUserIdArr {
+		req.EntUserIdArr[k] = encrypt.SE.Decode4Hex(v)
+	}
+	res, err := l.svcCtx.BxCore.ProjectDetails(l.ctx, &bxcore.ProjectDetailsReq{
+		EntId:              req.EntId,
+		EntUserId:          req.EntUserId,
+		PositionId:         req.PositionId,
+		EntUserIdArr:       req.EntUserIdArr,
+		DeptId:             req.DeptId,
+		StartTime:          req.StartTime,
+		EndTime:            req.EndTime,
+		BidWay:             req.BidWay,
+		Source:             req.Source,
+		IsParticipate:      req.Isparticipate,
+		BidUpdateEndTime:   req.UpdateEndTime,
+		BidUpdateStartTime: req.UpdateStartTime,
+		PageNum:            req.PageNum,
+		PageSize:           req.PageSize,
+		PositionType:       req.PositionType,
+	})
+	if err != nil {
+		return nil, err
+	}
+	return &types.CommonResp{
+		Data: res.Data,
+	}, nil
+	return
+}

+ 28 - 0
jyBXCore/api/internal/types/types.go

@@ -42,6 +42,13 @@ type CommonResp struct {
 	Data     interface{} `json:"data"`
 }
 
+type CommonPushResp struct {
+	Err_code   int64       `json:"error_code"`
+	Err_msg    string      `json:"error_msg"`
+	Data       interface{} `json:"data"`
+	SourceItem interface{} `json:"sourceItem"`
+}
+
 type SearchLimitReq struct {
 	AppId      string `header:"appId,optional"`    //appid
 	TimeOut    int64  `json:"timeOut,optional"`    //过滤过期时间
@@ -157,6 +164,9 @@ type PtatisticsListReq struct {
 	EntUserIdArr []string `json:"entUserIdArr,optional"`
 	StartTime    int64    `json:"startTime,optional"`
 	EndTime      int64    `json:"endTime,optional"`
+	Source       []int64  `json:"source,optional"`   // 标讯项目来源 -1:全部  1:个人订阅 2:企业自动分发 3:企业手动分发
+	BidWay       int64    `json:"bidWay,optional"`   // 投标类型 -1:全部 1:直接投标 2:渠道投标
+	IsMobile     bool     `json:"isMobile,optional"` // 是否是移动端  因为移动端和pc要求展示的不一致
 }
 
 type PolymerizeSearchReq struct {
@@ -171,3 +181,21 @@ type PolymerizeSearchReq struct {
 	EntAccountId int64  `header:"entAccountId,optional"` //企业账户id
 	EntUserId    int64  `header:"newUserId,optional"`
 }
+
+type ProjectDetailReq struct {
+	PositionId      int64    `header:"positionId,optional"`   //职位id
+	PositionType    int64    `header:"positionType,optional"` //职位类型 0个人 1企业
+	EntId           int64    `header:"entId,optional"`
+	EntUserId       int64    `header:"entUserId,optional"`
+	DeptId          int64    `header:"deptId,optional"`
+	EntUserIdArr    []string `json:"entUserIdArr,optional"`
+	StartTime       int64    `json:"startTime,optional"`
+	EndTime         int64    `json:"endTime,optional"`
+	Source          []int64  `json:"source,optional"`          // 标讯项目来源 -1:全部  1:个人订阅 2:企业自动分发 3:企业手动分发
+	BidWay          int64    `json:"bidWay,optional"`          // 投标类型 -1:全部 1:直接投标 2:渠道投标
+	Isparticipate   int64    `json:"isparticipate,optional"`   // 参标状态;-1:全部 0:未参标 1:是
+	UpdateStartTime int64    `json:"updateStartTime,optional"` //
+	UpdateEndTime   int64    `json:"updateEndTime,optional"`   //
+	PageSize        int64    `json:"pageSize,optional"`        //
+	PageNum         int64    `json:"pageNum,optional"`         //
+}

+ 60 - 1
jyBXCore/rpc/bxcore.proto

@@ -1,5 +1,5 @@
 syntax = "proto3";
-
+import    "google/protobuf/any.proto";
 package bxcore;
 option go_package = "./bxcore";
 
@@ -450,12 +450,41 @@ message StatisticsListReq{
   int64  deptId = 5; //部门id
   int64 startTime = 6;
   int64 endTime = 7;
+  repeated int64 source = 8; // source  0:全部  1:个人订阅 2:企业自动分发 3:企业手动分发
+  int64 bidWay = 9; //   -1:全部 1:直接投标 2:渠道投标
+  bool isMobile = 10; //  是否是移动端
+
+}
+// 企业项目参标明细
+message ProjectDetailsReq{
+  int64  entId = 1; //企业id
+  int64  entUserId = 2; // 企业下用户id
+  int64  positionId = 3; // 职位id
+  int64  positionType = 15; // 职位类型
+  repeated string  entUserIdArr = 4; //人员选择
+  int64  deptId = 5; //部门id
+  int64 startTime = 6;
+  int64 endTime = 7;
+  repeated int64 source = 8; // source  0:全部  1:个人订阅 2:企业自动分发 3:企业手动分发
+  int64 bidWay = 9; //   -1:全部 1:直接投标 2:渠道投标
+  int64 bidUpdateStartTime =10;// 参标状态跟新时间开始
+  int64 bidUpdateEndTime =11;// 参标状态跟新时间结束
+  int64 isParticipate = 12;// -1全部 1-已参标是 0-未参标否
+  int64 PageNum = 13;// 页码 从1开始
+  int64 PageSize =14;// 每页条数 默认50
+}
+// 筛选项
+message sourceItem{
+ string name =1;
+ int64  value =2;
 }
 message PushStatisticsDataRes{
   int64 err_code = 1;
   string err_msg = 2;
   repeated PushStatisticsData data = 3;
+  repeated sourceItem sourceItem = 4; // 来源的筛选项
 }
+
 message PushStatisticsData{
   string  personName = 1;
   string  departmentName = 2;
@@ -465,12 +494,38 @@ message PushStatisticsData{
   int64   winNumb = 6;//中标数量
   int64   browseNumb = 7;//浏览数量
   string  entUserId = 8;
+  string  source = 9;
 }
 message ProjectStatisticsDataRes{
   int64 err_code = 1;
   string err_msg = 2;
   repeated ProjectStatisticsData data = 3;
 }
+message StageValue{
+  string Name =1 ;
+  string Value =2 ;
+  string Date =3;
+}
+
+message ProjectDetailData {
+  string  projectName =1 ;// 项目名称
+  string  source = 2 ; // 标讯/项目来源 '来源;1:个人订阅 2:企业自动分发 3:企业手动分发
+  string  isDistribute = 3 ;// 手动分发状态  1已分发
+  string  disDate =4 ; // 分发时间
+  string  viewDate = 5 ;//最早浏览时间
+  string  id =7 ;// 项目id
+  repeated StageValue stage =6 ;// 阶段相关信息  <阶段名称,勾选时间>
+}
+message DetailData{
+  repeated ProjectDetailData list = 1;
+  int64    total  = 2;
+}
+message DetailDataRes{
+  int64 err_code = 1;
+  string err_msg = 2;
+  DetailData data =3;
+  }
+
 message ProjectStatisticsData{
   string  personName = 1;
   string  departmentName = 2;
@@ -483,6 +538,8 @@ message ProjectStatisticsData{
   int64   notBidNumber = 9; //未中标数量
   int64   endNumb = 10; //终止数量
   string  entUserId = 11;
+  int64   participateProjectNumb = 12;// 参标数量
+  repeated StageValue stage = 14;
 }
 message  PolymerizeSearchReq{
   int64  entId = 1; //企业id
@@ -569,4 +626,6 @@ service BxCore {
   rpc ProjectStatistics(StatisticsListReq) returns (ProjectStatisticsDataRes);
   //聚合搜索
   rpc PolymerizeSearch(PolymerizeSearchReq) returns (PolymerizeSearchResp);
+  rpc ProjectDetails(ProjectDetailsReq) returns (DetailDataRes);
+
 }

+ 12 - 0
jyBXCore/rpc/bxcore/bxcore.go

@@ -14,6 +14,8 @@ import (
 
 type (
 	BidTypeReq               = bxcore.BidTypeReq
+	DetailData               = bxcore.DetailData
+	DetailDataRes            = bxcore.DetailDataRes
 	MenuList                 = bxcore.MenuList
 	PInfo                    = bxcore.PInfo
 	ParticipateActionReq     = bxcore.ParticipateActionReq
@@ -42,6 +44,8 @@ type (
 	ParticipateShowRes       = bxcore.ParticipateShowRes
 	PolymerizeSearchReq      = bxcore.PolymerizeSearchReq
 	PolymerizeSearchResp     = bxcore.PolymerizeSearchResp
+	ProjectDetailData        = bxcore.ProjectDetailData
+	ProjectDetailsReq        = bxcore.ProjectDetailsReq
 	ProjectStatisticsData    = bxcore.ProjectStatisticsData
 	ProjectStatisticsDataRes = bxcore.ProjectStatisticsDataRes
 	PushStatisticsData       = bxcore.PushStatisticsData
@@ -57,6 +61,8 @@ type (
 	SearchResp               = bxcore.SearchResp
 	SearchReturn             = bxcore.SearchReturn
 	ShowInfo                 = bxcore.ShowInfo
+	SourceItem               = bxcore.SourceItem
+	StageValue               = bxcore.StageValue
 	StatisticsListReq        = bxcore.StatisticsListReq
 	TipInfo                  = bxcore.TipInfo
 	UpdateBidStatusReq       = bxcore.UpdateBidStatusReq
@@ -92,6 +98,7 @@ type (
 		ProjectStatistics(ctx context.Context, in *StatisticsListReq, opts ...grpc.CallOption) (*ProjectStatisticsDataRes, error)
 		// 聚合搜索
 		PolymerizeSearch(ctx context.Context, in *PolymerizeSearchReq, opts ...grpc.CallOption) (*PolymerizeSearchResp, error)
+		ProjectDetails(ctx context.Context, in *ProjectDetailsReq, opts ...grpc.CallOption) (*DetailDataRes, error)
 	}
 
 	defaultBxCore struct {
@@ -188,3 +195,8 @@ func (m *defaultBxCore) PolymerizeSearch(ctx context.Context, in *PolymerizeSear
 	client := bxcore.NewBxCoreClient(m.cli.Conn())
 	return client.PolymerizeSearch(ctx, in, opts...)
 }
+
+func (m *defaultBxCore) ProjectDetails(ctx context.Context, in *ProjectDetailsReq, opts ...grpc.CallOption) (*DetailDataRes, error) {
+	client := bxcore.NewBxCoreClient(m.cli.Conn())
+	return client.ProjectDetails(ctx, in, opts...)
+}

+ 12 - 1
jyBXCore/rpc/etc/bxcore.yaml

@@ -54,4 +54,15 @@ SearchConcurrency: 20
 NoLoginSearch: # 未登录搜索配置
   Switch: true # 是否打开并发限制
   ExecutionNum: 5 # 执行池
-  Wait: 2 # 等待池
+  Wait: 2 # 等待池
+Stages:
+    - "参标状态"
+    - "投标类型"
+    - "已报名##"
+    - "投标决策##"
+    - "编制投标文件##"
+    - "递交投标文件##"
+    - "中标公示##"
+    - "签合同##"
+    - "已结束"
+    - "终止参标"

+ 1 - 0
jyBXCore/rpc/internal/config/config.go

@@ -59,6 +59,7 @@ type Config struct {
 		ExecutionNum int
 		Wait         int
 	}
+	Stages []string
 }
 
 type Db struct {

+ 40 - 0
jyBXCore/rpc/internal/logic/projectdetailslogic.go

@@ -0,0 +1,40 @@
+package logic
+
+import (
+	"context"
+	"jyBXCore/rpc/internal/svc"
+	"jyBXCore/rpc/service"
+	"jyBXCore/rpc/type/bxcore"
+
+	"github.com/zeromicro/go-zero/core/logx"
+)
+
+type ProjectDetailsLogic struct {
+	ctx    context.Context
+	svcCtx *svc.ServiceContext
+	logx.Logger
+}
+
+func NewProjectDetailsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ProjectDetailsLogic {
+	return &ProjectDetailsLogic{
+		ctx:    ctx,
+		svcCtx: svcCtx,
+		Logger: logx.WithContext(ctx),
+	}
+}
+
+func (l *ProjectDetailsLogic) ProjectDetails(in *bxcore.ProjectDetailsReq) (*bxcore.DetailDataRes, error) {
+	participateService := service.ParticipateStatistics{
+		PositionId: in.PositionId,
+		EntId:      in.EntId,
+		DeptId:     in.EntId,
+		EntUserId:  in.EntUserId,
+	}
+
+	data := participateService.ProjectDetails(in.EntUserIdArr, in)
+	return &bxcore.DetailDataRes{
+		ErrCode: 0,
+		ErrMsg:  "",
+		Data:    &data,
+	}, nil
+}

+ 1 - 1
jyBXCore/rpc/internal/logic/projectstatisticslogic.go

@@ -34,7 +34,7 @@ func (l *ProjectStatisticsLogic) ProjectStatistics(in *bxcore.StatisticsListReq)
 		DeptId:     in.EntId,
 		EntUserId:  in.EntUserId,
 	}
-	data := participateService.ProjectStatistics(in.EntUserIdArr, in.StartTime, in.EndTime)
+	data := participateService.ProjectStatistics(in.EntUserIdArr, in.StartTime, in.EndTime, in.BidWay)
 	return &bxcore.ProjectStatisticsDataRes{
 		ErrCode: 0,
 		ErrMsg:  "",

+ 7 - 4
jyBXCore/rpc/internal/logic/pushstatisticslogic.go

@@ -33,10 +33,13 @@ func (l *PushStatisticsLogic) PushStatistics(in *bxcore.StatisticsListReq) (*bxc
 		DeptId:     in.EntId,
 		EntUserId:  in.EntUserId,
 	}
-	data := participateService.PushStatistics(in.EntUserIdArr, in.StartTime, in.EndTime)
+	// 推送参标数据统计
+	data := participateService.PushStatistics(in.EntUserIdArr, in.StartTime, in.EndTime, in.Source, in.IsMobile)
+	sourceItem := participateService.GetSourceItem(int(in.EntId), int(in.PositionId))
 	return &bxcore.PushStatisticsDataRes{
-		ErrCode: 0,
-		ErrMsg:  "",
-		Data:    data,
+		ErrCode:    0,
+		ErrMsg:     "",
+		Data:       data,
+		SourceItem: sourceItem,
 	}, nil
 }

+ 3 - 0
jyBXCore/rpc/internal/logic/updatebidstatuslogic.go

@@ -4,6 +4,7 @@ import (
 	"app.yhyue.com/moapp/jybase/common"
 	"context"
 	"jyBXCore/rpc/model/es"
+	"jyBXCore/rpc/model/mysql"
 	"jyBXCore/rpc/service"
 	"jyBXCore/rpc/util"
 	"time"
@@ -75,6 +76,8 @@ func (l *UpdateBidStatusLogic) UpdateBidStatus(in *bxcore.UpdateBidStatusReq) (*
 	if err := participateService.UpdateBidStatus(in, projectId); err != nil {
 		result.ErrMsg = err.Error()
 	} else {
+		// p408 增加
+		go mysql.NewParStage(in.EntId, in.EntUserId, in.PositionId, projectId).UpdateStage()
 		result.Data = true
 		result.ErrCode = 0
 	}

+ 5 - 0
jyBXCore/rpc/internal/server/bxcoreserver.go

@@ -105,3 +105,8 @@ func (s *BxCoreServer) PolymerizeSearch(ctx context.Context, in *bxcore.Polymeri
 	l := logic.NewPolymerizeSearchLogic(ctx, s.svcCtx)
 	return l.PolymerizeSearch(in)
 }
+
+func (s *BxCoreServer) ProjectDetails(ctx context.Context, in *bxcore.ProjectDetailsReq) (*bxcore.DetailDataRes, error) {
+	l := logic.NewProjectDetailsLogic(ctx, s.svcCtx)
+	return l.ProjectDetails(in)
+}

+ 26 - 0
jyBXCore/rpc/model/es/project.go

@@ -100,3 +100,29 @@ func GetBidInfoByPId(infoId string) *[]map[string]interface{} {
 	return projectResult
 
 }
+
+func GetProjectNameByProjectId(projectId []string) *[]map[string]interface{} {
+	projectQuery := `{
+  "query": {
+    "bool": {
+      "must": [
+        {
+          "terms": {
+            "_id": [
+              "` + strings.Join(projectId, "\",\"") + `"
+            ]
+          }
+        }
+      ]
+    }
+  },
+  "_source": [
+    "_id",
+    "projectname"
+  ],"size":%d
+}`
+	projectQuery = fmt.Sprintf(projectQuery, len(projectId))
+	projectResult := elastic.Get(IndexProjectSet, TypeProjectSet, projectQuery)
+	return projectResult
+
+}

+ 42 - 17
jyBXCore/rpc/model/mysql/participateBid.go

@@ -38,23 +38,29 @@ type RecordsContent struct {
 var (
 	PartTable                  = "participate"
 	ParticipateBidRecordsTable = "participate_bid_records"
-	ParticipateUserTable       = "participate_user" // 参标用户表
-	EntnicheUserTable          = "entniche_user"    // 企业用户表
+	ParticipateUserTable       = "participate_user"             // 参标用户表
+	EntnicheUserTable          = "entniche_user"                // 企业用户表
+	ParticipateStage           = "participate_stage_statistics" //阶段信息
 )
 
+type PostionIdEntUserId struct {
+	PositionId int64
+	EntUserId  int64
+}
+
 // 划转参标信息
 func TransferParticipateInfo(projectId string, in *bxcore.ParticipateActionReq) error {
 	defer MC.Catch()
 	//保存或更新新跟踪人
 	if !IC.BaseMysql.ExecTx("划转参标信息", func(tx *sql.Tx) bool {
 		var (
-			b1                                             = true
-			b2, b3                                         bool
-			now                                            = time.Now()
-			content                                        = "从%s名下划转给%s%s"
-			lastNotes                                      = ",保留原参标人"
-			fromEntUserNames, toEntUserNames, toEntUserIds []string
-			ids                                            []int
+			b1                                                             = true
+			b2, b3                                                         bool
+			now                                                            = time.Now()
+			content                                                        = "从%s名下划转给%s%s"
+			lastNotes                                                      = ",保留原参标人"
+			fromEntUserNames, fromEntUserIds, toEntUserNames, toEntUserIds []string
+			ids                                                            []int
 		)
 		partInfo := IC.BaseMysql.SelectBySqlByTx(tx, "SELECT id,position_id FROM "+ParticipateUserTable+" WHERE  project_id = ?  AND ent_id = ? AND state > -1", projectId, in.EntId)
 		if partInfo == nil || len(*partInfo) == 0 {
@@ -67,6 +73,7 @@ func TransferParticipateInfo(projectId string, in *bxcore.ParticipateActionReq)
 				userInfo := IC.Middleground.UserCenter.IdentityByPositionId(positionId)
 				if userInfo.EntUserName != "" {
 					fromEntUserNames = append(fromEntUserNames, userInfo.EntUserName)
+					fromEntUserIds = append(fromEntUserIds, strconv.FormatInt(userInfo.EntUserId, 10))
 				}
 			}
 		}
@@ -87,6 +94,9 @@ func TransferParticipateInfo(projectId string, in *bxcore.ParticipateActionReq)
 				"mark":        -2, //0:参标;1:被划入;-1:终止参标;-2:被划走
 				"update_date": date.FormatDate(&now, date.Date_Full_Layout),
 			})
+		} else {
+			//划转参标项目,如果不保留原参标人,原参标人 更新参标记录表
+			fromEntUserIds = []string{}
 		}
 		//移动端 划转对象是多选
 		//划转对象entuserid 解密
@@ -142,14 +152,15 @@ func TransferParticipateInfo(projectId string, in *bxcore.ParticipateActionReq)
 		//个人版 根据职位id 和项目id查询划转记录
 		//划转记录
 		b2 = IC.BaseMysql.InsertByTx(tx, ParticipateBidRecordsTable, map[string]interface{}{
-			"ent_id":               in.EntId,
-			"ent_user_id":          in.EntUserId,
-			"position_id":          in.PositionId,
-			"project_id":           projectId,
-			"record_type":          0,
-			"transfer_ent_user_id": strings.Join(toEntUserIds, ","),
-			"record_content":       fmt.Sprintf(content, strings.Join(fromEntUserNames, "、"), strings.Join(toEntUserNames, "、"), lastNotes),
-			"create_date":          date.FormatDate(&now, date.Date_Full_Layout),
+			"ent_id":                in.EntId,
+			"ent_user_id":           in.EntUserId,
+			"position_id":           in.PositionId,
+			"project_id":            projectId,
+			"record_type":           0,
+			"transfer_from_user_id": strings.Join(fromEntUserIds, ","),
+			"transfer_ent_user_id":  strings.Join(toEntUserIds, ","),
+			"record_content":        fmt.Sprintf(content, strings.Join(fromEntUserNames, "、"), strings.Join(toEntUserNames, "、"), lastNotes),
+			"create_date":           date.FormatDate(&now, date.Date_Full_Layout),
 		}) > 0
 		log.Println(b1, "--", b2, "--", b3)
 		return b1 && b2 && b3
@@ -178,6 +189,9 @@ func CancelParticipateInfo(in *bxcore.ParticipateActionReq, roleId int64) error
 		if roleId == 0 {
 			query["position_id"] = in.PositionId
 			tip = "终止参标"
+
+		} else {
+
 		}
 		insert := map[string]interface{}{
 			"state":       -1,
@@ -888,6 +902,17 @@ func InsertBidContent(recordData map[string]interface{}) (flag bool) {
 	return r2 > 0
 }
 
+// InsertBidContent 新增投标状态信息及操作记录
+func UpdateParticipateUserTable(projectId string, positionId int64) (flag bool) {
+	r2 := IC.BaseMysql.Update(ParticipateUserTable, map[string]interface{}{
+		"project_id":  projectId,
+		"position_id": positionId,
+	}, map[string]interface{}{
+		"update_date": date.NowFormat(date.Date_Full_Layout),
+	})
+	return r2
+}
+
 // GetBidRecordsEnt 获取操作记录列表企业
 func GetBidRecordsEnt(projectId string, entId, page, pageSize int64) (rs *[]map[string]interface{}, total int64) {
 	query := "SELECT * FROM " + ParticipateBidRecordsTable + " where project_id=? and ent_id=?   order by create_date desc limit ?,?"

+ 205 - 0
jyBXCore/rpc/model/mysql/participateStage.go

@@ -0,0 +1,205 @@
+package mysql
+
+import (
+	"app.yhyue.com/moapp/jybase/common"
+	"app.yhyue.com/moapp/jybase/date"
+	"encoding/json"
+	"fmt"
+	"github.com/gogf/gf/v2/util/gconv"
+	IC "jyBXCore/rpc/init"
+	"jyBXCore/rpc/util"
+	"log"
+	"strconv"
+	"strings"
+	"time"
+)
+
+type ParStage struct {
+	EntId      int64
+	EntUserId  int64
+	PositionId int64
+	ProjectId  string
+}
+
+func NewParStage(entId, entUserId, positionId int64, projectId string) *ParStage {
+	return &ParStage{
+		EntId:      entId,
+		EntUserId:  entUserId,
+		PositionId: positionId,
+		ProjectId:  projectId,
+	}
+}
+
+type StageInfo struct {
+	Name  string `json:"name"`
+	Value string `json:"value"`
+	Date  string `json:"date"`
+}
+
+func (p *ParStage) UpdateStage() {
+	var (
+		recordsMap      = map[string]*StageInfo{}
+		updateInsertMap = map[string]interface{}{
+			"ent_id":     p.EntId,
+			"project_id": p.ProjectId,
+		}
+		now                     = time.Now()
+		createDate              string
+		lastDate                int64 //最新一次更新时间
+		entUserIdBool           = map[string]bool{}
+		positionIdBool          = map[string]bool{}
+		entUserIds, positionIds []string
+		findSql                 = map[string]interface{}{
+			"project_id": p.ProjectId,
+			"ent_id":     p.EntId,
+		}
+		state         = "已完成"
+		isParticipate bool
+	)
+	//个人版
+	if p.EntId == 0 {
+		findSql["position_id"] = p.PositionId
+	}
+	recordsContentData := IC.BaseMysql.Find(ParticipateBidRecordsTable, findSql, "", "create_date", -1, -1)
+	//判断 当前企业或用户是否已参标
+	findSql["state"] = 0
+	if count := IC.BaseMysql.Count("participate_user", findSql); count > 0 {
+		isParticipate = true
+	}
+	if recordsContentData != nil && len(*recordsContentData) > 0 {
+		for _, rv := range *recordsContentData {
+			var (
+				recordContent = gconv.String(rv["record_content"])
+				content, ok   = util.ConvertJSONString(recordContent)
+				contentMap    = map[string]interface{}{}
+				contentStr    string
+				entUserId     = strconv.FormatInt(gconv.Int64(rv["ent_user_id"]), 10)
+				positionId    = strconv.FormatInt(gconv.Int64(rv["position_id"]), 10)
+				cdStr         = strings.Split(gconv.String(rv["create_date"]), " ")[0]
+				cdTime, _     = time.ParseInLocation(date.Date_Full_Layout, gconv.String(rv["create_date"]), time.Local)
+			)
+			lastDate = common.If(cdTime.Unix() > lastDate, cdTime.Unix(), lastDate).(int64)
+			createDate = common.If(cdStr != "", cdStr, createDate).(string)
+			if createDate != "" {
+				createDate = strings.ReplaceAll(createDate, "-", ".")
+			}
+			if ok {
+				contentMap = gconv.Map(content)
+				if contentMap["afterMap"] != nil {
+					afterMap, _ := util.ConvertJSONString(gconv.String(contentMap["afterMap"]))
+					afterM := gconv.Map(afterMap)
+					if afterM != nil {
+						if bidStage := gconv.Map(afterM["bidStage"]); bidStage != nil {
+							status := gconv.String(bidStage["status"])
+							values := gconv.SliceStr(bidStage["value"])
+							if len(values) > 0 {
+								for _, bv := range values {
+									if status == "调整" || recordsMap[bv].Date == "" {
+										recordsMap[bv] = &StageInfo{
+											Name:  fmt.Sprintf("%s(阶段)", bv),
+											Value: state,
+											Date:  createDate,
+										}
+									}
+								}
+							}
+						}
+						if bidType := gconv.Map(afterM["bidType"]); bidType != nil {
+							value := gconv.String(bidType["value"])
+							if value != "" {
+								recordsMap["投标类型"] = &StageInfo{
+									Name:  "投标类型",
+									Value: value,
+									Date:  createDate,
+								}
+							}
+						}
+					}
+				}
+				afterMap, _ := util.ConvertJSONString(gconv.String(contentMap["after"]))
+				afterM := gconv.Map(afterMap)
+				if afterM != nil {
+					updateInsertMap["bid_way"] = gconv.Int64(afterM["bidType"])
+				}
+			} else {
+				contentStr = gconv.String(content)
+				//参标:多人参标同一个项目,查看当前项目是否已被当前企业其他员工参标,如果已被其他员工参标,则参标时间不更新。
+				//参标后 未被 终止参标
+				if strings.TrimSpace(contentStr) == "参标" {
+					recordsMap["参标状态"] = &StageInfo{
+						Name:  "参标状态",
+						Value: "已参标",
+						Date:  createDate,
+					}
+				}
+				//终止参标--
+				if strings.Contains(contentStr, "终止参标") {
+					if !isParticipate {
+						recordsMap[contentStr] = &StageInfo{
+							Name:  "终止参标",
+							Value: "已终止",
+							Date:  createDate,
+						}
+					}
+				}
+			}
+			//entUserIds 操作和被操作企业用户id集合 408
+			if !entUserIdBool[entUserId] {
+				entUserIdBool[entUserId] = true
+				entUserIds = append(entUserIds, entUserId)
+			}
+			//职位id
+			if !positionIdBool[positionId] {
+				positionIdBool[positionId] = true
+				positionIds = append(positionIds, positionId)
+			}
+		}
+	}
+	if recordsMap != nil {
+		var stages []*StageInfo
+		for _, sv := range IC.C.Stages {
+			v := strings.Split(sv, "##")[0]
+			records := recordsMap[v]
+			if records == nil {
+				records = &StageInfo{
+					Name:  strings.ReplaceAll(sv, "##", "(阶段)"),
+					Value: "-",
+				}
+			}
+			stages = append(stages, &StageInfo{
+				Name:  records.Name,
+				Value: records.Value,
+				Date:  records.Date,
+			})
+		}
+		// 转换结构体数组为JSON字符串
+		jsonData, err := json.Marshal(stages)
+		if err != nil {
+			fmt.Println("Failed to marshal struct array to JSON:", err)
+			return
+		}
+		updateInsertMap["stage"] = string(jsonData)
+		updateInsertMap["ent_user_ids"] = strings.Join(entUserIds, ",")
+		updateInsertMap["is_participate"] = common.If(isParticipate, 1, 0).(int)
+		updateInsertMap["position_ids"] = strings.Join(positionIds, ",")
+		updateSelectSlq := fmt.Sprintf(`SELECT * FROM %s WHERE ent_id = ? and project_id = ? `, ParticipateStage)
+		if p.EntId == 0 {
+			add := fmt.Sprintf(" and  FIND_IN_SET('%s', position_ids) > 0", strconv.FormatInt(p.PositionId, 10))
+			updateSelectSlq = fmt.Sprintf("%s%s", updateSelectSlq, add)
+		}
+		if s := IC.BaseMysql.SelectBySql(updateSelectSlq, p.EntId, p.ProjectId); len(*s) > 0 {
+			updateInsertMap["update_date"] = date.FormatDateByInt64(&lastDate, date.Date_Full_Layout)
+			if ok := IC.BaseMysql.Update(ParticipateStage, map[string]interface{}{
+				"id": gconv.Int64((*s)[0]["id"]),
+			}, updateInsertMap); !ok {
+				log.Println("参标操作记录 更新异常:", gconv.Int64((*s)[0]["id"]))
+			}
+		} else {
+			updateInsertMap["create_date"] = date.FormatDate(&now, date.Date_Full_Layout)
+			updateInsertMap["update_date"] = date.FormatDateByInt64(&lastDate, date.Date_Full_Layout)
+			if i := IC.BaseMysql.Insert(ParticipateStage, updateInsertMap); i == 0 {
+				log.Println("参标操作记录 插入异常:", updateInsertMap)
+			}
+		}
+	}
+}

+ 6 - 0
jyBXCore/rpc/service/participate.go

@@ -94,6 +94,8 @@ func ParticipateDo(in *bxcore.ParticipateActionReq) error {
 		if err := mysql.SaveParticipateInfo(in); err != nil {
 			return err
 		}
+		// p408 增加
+		go mysql.NewParStage(in.EntId, in.EntUserId, in.PositionId, in.ProjectIds).UpdateStage()
 	case "out": //终止参标
 		if in.BidIds == "" && in.ProjectIds == "" {
 			return fmt.Errorf("缺少参数")
@@ -104,6 +106,9 @@ func ParticipateDo(in *bxcore.ParticipateActionReq) error {
 		if err := mysql.CancelParticipateInfo(in, entRoleId); err != nil {
 			return err
 		}
+		// p408 增加
+		go mysql.NewParStage(in.EntId, in.EntUserId, in.PositionId, in.ProjectIds).UpdateStage()
+
 	case "transfer":
 		//PC端 多个项目-》一个人
 		//移动端  一个项目-》多个人
@@ -140,6 +145,7 @@ func ParticipateDo(in *bxcore.ParticipateActionReq) error {
 				log.Println(fmt.Sprintf("是否允许多人参标:%v, 项目id:%s,企业id:%d,划转对象entuserid:%s,划转异常:", isAllow, projectId, in.EntId, in.ToEntUserId))
 				continue
 			}
+			go mysql.NewParStage(in.EntId, in.EntUserId, in.PositionId, projectId).UpdateStage()
 			projectNum += 1
 		}
 		if projectNum == 0 { //!= len(projectIds)

+ 1 - 0
jyBXCore/rpc/service/participateBid.go

@@ -305,6 +305,7 @@ func (p *ParticipateBid) UpdateBidStatus(in *bxcore.UpdateBidStatusReq, projectI
 	if flag := mysql.InsertBidContent(recordData); !flag {
 		return fmt.Errorf("更新失败")
 	}
+	mysql.UpdateParticipateUserTable(projectId, p.PositionId)
 	return nil
 
 }

+ 709 - 52
jyBXCore/rpc/service/participateStatistics.go

@@ -2,11 +2,16 @@ package service
 
 import (
 	"app.yhyue.com/moapp/jybase/common"
+	"app.yhyue.com/moapp/jybase/date"
 	"app.yhyue.com/moapp/jybase/encrypt"
+	"app.yhyue.com/moapp/jybase/redis"
 	"encoding/json"
 	"fmt"
 	"jyBXCore/rpc/bxcore"
 	IC "jyBXCore/rpc/init"
+	"jyBXCore/rpc/model/es"
+	"log"
+	"strconv"
 	"strings"
 	"time"
 )
@@ -24,34 +29,109 @@ type ParticipateStatistics struct {
 	EntUserId  int64
 }
 
-func (in *ParticipateStatistics) PushStatistics(entUserIdArr []string, startTime, endTime int64) (result []*bxcore.PushStatisticsData) {
+func (in *ParticipateStatistics) PushStatistics(entUserIdArr []string, startTime, endTime int64, source []int64, isMobile bool) (result []*bxcore.PushStatisticsData) {
 	//判断是企业、部门还是个人
-	isAdmin, personArrStr, users := in.PersonHandle(entUserIdArr)
+	isAdmin, personArrStr, users, userInfo := in.PersonHandle(entUserIdArr)
 	//时间处理
-	query := QueryHandle(isAdmin, startTime, endTime, personArrStr)
+	query := QueryHandle(isAdmin, startTime, endTime, personArrStr, source, 0)
 	//推送数据查询
 	dataList := IC.BaseMysql.SelectBySql(fmt.Sprintf("select  * from  participate_push_statistics where %s order  by ymd", strings.Join(query, " and ")))
+	isManage := false
 	if users != nil && len(*users) > 0 {
-		result = PushHandle(dataList, users, isAdmin)
+		if userInfo != nil {
+			isManage = userInfo.Role_admin_department || userInfo.Role_admin_system
+		}
+
+		result = PushHandle(dataList, users, isAdmin, isManage, isMobile)
 	}
 	return
 }
 
-func (in *ParticipateStatistics) ProjectStatistics(entUserIdArr []string, startTime, endTime int64) (result []*bxcore.ProjectStatisticsData) {
+func (in *ParticipateStatistics) ProjectStatistics(entUserIdArr []string, startTime, endTime, bidWay int64) (result []*bxcore.ProjectStatisticsData) {
 	//判断是企业、部门还是个人
-	isAdmin, personArrStr, users := in.PersonHandle(entUserIdArr)
-	query := QueryHandle(isAdmin, startTime, endTime, personArrStr)
+	isAdmin, personArrStr, users, _ := in.PersonHandle(entUserIdArr)
+	query := QueryHandle(isAdmin, startTime, endTime, personArrStr, []int64{}, bidWay)
 	//订阅推送数处理
 	//推送数据查询
-	dataList := IC.BaseMysql.SelectBySql(fmt.Sprintf("select  * from  participate_project_statistics where %s order  by ymd", strings.Join(query, "and ")))
+	dataList := IC.BaseMysql.SelectBySql(fmt.Sprintf("select  * from  participate_project_statistics where %s order  by ymd  ", strings.Join(query, "and ")))
 	if users != nil && len(*users) > 0 {
-		result = ProjectHandle(dataList, users, isAdmin)
+		result = ProjectHandle(dataList, users, isAdmin, bidWay)
 	}
 	return
 }
 
-func (in *ParticipateStatistics) PersonHandle(entUserIdArr []string) (isAdmin bool, idStr string, users *[]map[string]interface{}) {
-	userEnt := EntInfo(common.IntAll(in.EntId), common.IntAll(in.EntUserId))
+var (
+	SourceMap = map[string]string{
+		"1": "个人订阅",
+		"2": "企业自动分发",
+		"3": "企业手动分发",
+	}
+)
+
+// GetSourceItem 获取标讯项目来源筛选项
+func (in *ParticipateStatistics) GetSourceItem(entId, positionId int) (result []*bxcore.SourceItem) {
+	redisKey := "sourceItem%s_%d"
+	query := "select group_concat(distinct(source))  as `source` from participate_push_statistics where  "
+	// 个人用户
+	if entId == 0 {
+		// 查职位id
+		query += fmt.Sprintf(" position_id =%d", positionId)
+		redisKey = fmt.Sprintf(redisKey, "personal", positionId)
+	} else {
+		// 企业用户 用企业id查
+		query += fmt.Sprintf(" ent_id =%d", entId)
+		redisKey = fmt.Sprintf(redisKey, "ent", entId)
+	}
+	if data, err := redis.GetBytes("other", redisKey); err == nil && data != nil {
+		err := json.Unmarshal(*data, &result)
+		if err == nil {
+			return
+		} else {
+			log.Println("序列化失败", redisKey, err)
+		}
+	}
+	rs := IC.BaseMysql.SelectBySql(query)
+	if rs == nil || len(*rs) == 0 {
+		//emtpy := make([]*bxcore.SourceItem, 0)
+		return
+	}
+	existMap := map[string]struct{}{}
+	source_ := common.InterfaceToStr((*rs)[0]["source"])
+	splitSource := strings.Split(source_, ",")
+	for i := 0; i < len(splitSource); i++ {
+		value := splitSource[i]
+		if value == "" {
+			continue
+		}
+		if _, ok := existMap[value]; ok {
+			continue
+		} else {
+			existMap[value] = struct{}{}
+		}
+		if name, ok := SourceMap[value]; ok {
+			value_, err := strconv.Atoi(value)
+			if err == nil {
+				result = append(result, &bxcore.SourceItem{
+					Name:  name,
+					Value: int64(value_),
+				})
+			} else {
+				log.Println("转换失败:", err, value)
+			}
+		}
+
+	}
+	if len(result) > 0 {
+		//  存缓存
+		rsByte, _ := json.Marshal(result)
+		redis.PutBytes("other", redisKey, &rsByte, 60*60*2)
+	}
+	return
+}
+
+// 注意:该方法返回值中第一个虽然名字叫IsAdmin 但是实际是指是否是企业版 用于区分企业和个人版 不能用于是否是管理员
+func (in *ParticipateStatistics) PersonHandle(entUserIdArr []string) (isAdmin bool, idStr string, users *[]map[string]interface{}, userEnt *CurrentUser) {
+	userEnt = EntInfo(common.IntAll(in.EntId), common.IntAll(in.EntUserId))
 	if len(entUserIdArr) > 0 {
 		//查询所选人的部门以及人员信息
 		users = GetUser(entUserIdArr)
@@ -102,22 +182,34 @@ type PushData struct {
 	BidNumb         map[string]interface{}
 	WinNumb         map[string]interface{}
 	BrowseNumb      map[string]interface{}
+	SourceMap       map[string]sourceStruct
+}
+type sourceStruct struct {
+	PushNumb        map[string]interface{}
+	ParticipateNumb map[string]interface{}
+	BidNumb         map[string]interface{}
+	WinNumb         map[string]interface{}
+	BrowseNumb      map[string]interface{}
 }
 type ProjectData struct {
-	PersonName     string
-	DepartmentName string
-	entUserId      string
-	BidNumb        map[string]interface{} //投标数量
-	DirectBidNumb  map[string]interface{} //直接投标数
-	ChannelBidNumb map[string]interface{} //渠道投标数
-	WinNumb        map[string]interface{}
-	DirectWinNumb  map[string]interface{} //直接中标数
-	ChannelWinNumb map[string]interface{} //渠道中标数
-	NotBidNumber   map[string]interface{} //未中标数量
-	EndNumb        map[string]interface{} //终止数量
+	PersonName             string
+	DepartmentName         string
+	entUserId              string
+	BidNumb                map[string]interface{} //投标数量
+	DirectBidNumb          map[string]interface{} //直接投标数
+	ChannelBidNumb         map[string]interface{} //渠道投标数
+	WinNumb                map[string]interface{}
+	DirectWinNumb          map[string]interface{} //直接中标数
+	ChannelWinNumb         map[string]interface{} //渠道中标数
+	NotBidNumber           map[string]interface{} //未中标数量
+	EndNumb                map[string]interface{} //终止数量
+	participateProjectNumb map[string]interface{} // 参标数量
+	Stage                  map[string]map[string]interface{}
+	StageStr               map[string]string   // 字符串处理
+	EndProject             map[string]struct{} // 终止参标项目不参与统计
 }
 
-func PushHandle(data *[]map[string]interface{}, users *[]map[string]interface{}, isAdmin bool) []*bxcore.PushStatisticsData {
+func PushHandle(data *[]map[string]interface{}, users *[]map[string]interface{}, isAdmin bool, isManager bool, isMobile bool) []*bxcore.PushStatisticsData {
 	result := &map[int64]*PushData{}
 	for k, v := range *users {
 		(*result)[common.Int64All(v["id"])] = &PushData{
@@ -159,27 +251,92 @@ func PushHandle(data *[]map[string]interface{}, users *[]map[string]interface{},
 						delete((*result)[userId].WinNumb, project_id)
 					}
 				}
+				// 如果是个人处理  按照source分开统计
+				if !isManager && common.InterfaceToStr(common.InterfaceToStr(v["source"])) != "" && !isMobile {
+					splitSource := strings.Split(common.InterfaceToStr(common.InterfaceToStr(v["source"])), ",")
+					for i := 0; i < len(splitSource); i++ {
+						//
+						if (*result)[userId].SourceMap == nil {
+							(*result)[userId].SourceMap = map[string]sourceStruct{}
+						}
+						if _, ok := (*result)[userId].SourceMap[splitSource[i]]; !ok {
+							(*result)[userId].SourceMap[splitSource[i]] = sourceStruct{}
+						}
+						tmp := (*result)[userId].SourceMap[splitSource[i]]
+						//浏览总数处理处理
+						if common.Int64All(v["isvisit"]) > 0 {
+							tmp.BrowseNumb = DataHanle(tmp.BrowseNumb, project_id)
+						}
+						//推送总数处理
+						tmp.PushNumb = DataHanle(tmp.PushNumb, project_id)
+						//参标总数处理
+						if common.Int64All(v["isparticipate"]) > 0 {
+							tmp.ParticipateNumb = DataHanle(tmp.ParticipateNumb, project_id)
+						}
+						//投标总数处理
+						if common.Int64All(v["isbid"]) > 0 {
+							tmp.BidNumb = DataHanle(tmp.BidNumb, project_id)
+						}
+						//中标总数处理
+						if common.Int64All(v["win_status"]) != 0 {
+							win_status := common.Int64All(v["win_status"])
+							if win_status == 1 {
+								tmp.WinNumb = DataHanle(tmp.WinNumb, project_id)
+							} else {
+								delete(tmp.WinNumb, project_id)
+							}
+						}
+						(*result)[userId].SourceMap[splitSource[i]] = tmp
+					}
+
+				}
 			}
 		}
 	}
-	pushStatisticsList := make([]*bxcore.PushStatisticsData, len(*result))
-	for _, v := range *result {
-		personName := strings.Split(v.PersonName, "_")[0]
-		k := common.Int64All(strings.Split(v.PersonName, "_")[1])
-		pushStatisticsList[k] = &bxcore.PushStatisticsData{
-			PersonName:      personName,
-			DepartmentName:  v.DepartmentName,
-			EntUserId:       encrypt.SE.Encode2Hex(v.entUserId),
-			PushNumb:        common.Int64All(len(v.PushNumb)),
-			ParticipateNumb: common.Int64All(len(v.ParticipateNumb)),
-			BidNumb:         common.Int64All(len(v.BidNumb)),
-			WinNumb:         common.Int64All(len(v.WinNumb)),
-			BrowseNumb:      common.Int64All(len(v.BrowseNumb)),
+	pushStatisticsList := []*bxcore.PushStatisticsData{}
+	if !isManager && !isMobile {
+		for _, v := range *result {
+			for sourceK, sourceV := range v.SourceMap {
+				personName := strings.Split(v.PersonName, "_")[0]
+				pushStatisticsList = append(pushStatisticsList, &bxcore.PushStatisticsData{
+					PersonName:      personName,
+					DepartmentName:  v.DepartmentName,
+					EntUserId:       encrypt.SE.Encode2Hex(v.entUserId),
+					ParticipateNumb: int64(len(sourceV.ParticipateNumb)),
+					BrowseNumb:      int64(len(sourceV.BrowseNumb)),
+					BidNumb:         int64(len(sourceV.BidNumb)),
+					WinNumb:         int64(len(sourceV.WinNumb)),
+					PushNumb:        int64(len(sourceV.PushNumb)),
+					Source:          common.InterfaceToStr(SourceMap[sourceK]),
+				})
+			}
+		}
+	} else {
+		pushStatisticsList = make([]*bxcore.PushStatisticsData, len(*result))
+		for _, v := range *result {
+			personName := strings.Split(v.PersonName, "_")[0]
+			k := common.Int64All(strings.Split(v.PersonName, "_")[1])
+			pushStatisticsList[k] = &bxcore.PushStatisticsData{
+				PersonName:      personName,
+				DepartmentName:  v.DepartmentName,
+				EntUserId:       encrypt.SE.Encode2Hex(v.entUserId),
+				PushNumb:        common.Int64All(len(v.PushNumb)),
+				ParticipateNumb: common.Int64All(len(v.ParticipateNumb)),
+				BidNumb:         common.Int64All(len(v.BidNumb)),
+				WinNumb:         common.Int64All(len(v.WinNumb)),
+				BrowseNumb:      common.Int64All(len(v.BrowseNumb)),
+			}
 		}
 	}
 	return pushStatisticsList
 }
-func ProjectHandle(data *[]map[string]interface{}, users *[]map[string]interface{}, isAdmin bool) []*bxcore.ProjectStatisticsData {
+
+var stageNameArr = map[int64][]string{
+	1: []string{"已报名", "投标决策", "编制投标文件", "递交投标文件", "中标公示", "签合同", "已结束"},
+	2: []string{"已报名", "中标公示", "签合同", "已结束"},
+}
+
+func ProjectHandle(data *[]map[string]interface{}, users *[]map[string]interface{}, isAdmin bool, bidWay int64) []*bxcore.ProjectStatisticsData {
 	result := &map[int64]*ProjectData{}
 	for k, v := range *users {
 		(*result)[common.Int64All(v["id"])] = &ProjectData{
@@ -198,6 +355,42 @@ func ProjectHandle(data *[]map[string]interface{}, users *[]map[string]interface
 			}
 			project_id := common.InterfaceToStr(common.InterfaceToStr(v["project_id"]))
 			if (*result)[userId] != nil {
+				stage := common.ObjToString(v["bid_stage"])
+				if (*result)[userId].StageStr == nil {
+					(*result)[userId].StageStr = map[string]string{}
+				}
+				(*result)[userId].StageStr[project_id] = stage
+				isEnd_ := common.IntAll(v["isend"])
+				if isEnd_ == 1 {
+					if (*result)[userId].EndProject == nil {
+						(*result)[userId].EndProject = map[string]struct{}{}
+					}
+					// 标记成已结束
+					(*result)[userId].EndProject[project_id] = struct{}{}
+				}
+			}
+
+		}
+		existProject := map[string]struct{}{}
+		for _, v := range *data {
+			userId := int64(0)
+			if isAdmin {
+				userId = common.Int64All(v["ent_user_id"])
+			} else {
+				userId = common.Int64All(v["position_id"])
+			}
+			project_id := common.InterfaceToStr(common.InterfaceToStr(v["project_id"]))
+			if (*result)[userId] != nil {
+				//终止参保统计
+				if common.Int64All(v["isend"]) != 0 {
+					(*result)[userId].EndNumb = DataHanle((*result)[userId].EndNumb, project_id)
+				}
+				// 已经终止的不参与统计数量
+				if _, ok := (*result)[userId].EndProject[project_id]; ok {
+					continue
+				}
+				//  参标数量
+				(*result)[userId].participateProjectNumb = DataHanle((*result)[userId].participateProjectNumb, project_id)
 				//投标数量
 				if common.Int64All(v["isbid"]) > 0 {
 					(*result)[userId].BidNumb = DataHanle((*result)[userId].BidNumb, project_id)
@@ -236,29 +429,72 @@ func ProjectHandle(data *[]map[string]interface{}, users *[]map[string]interface
 						delete((*result)[userId].DirectWinNumb, project_id)
 					}
 				}
-				//终止参保统计
-				if common.Int64All(v["isend"]) != 0 {
-					(*result)[userId].EndNumb = DataHanle((*result)[userId].EndNumb, project_id)
+
+				if bidWay == 0 {
+					// 投标类型为全部时不需要统计勾选的数量
+					continue
+				}
+				existKey := fmt.Sprintf("%d_%s", userId, project_id)
+				if _, ok := existProject[existKey]; !ok {
+					existProject[existKey] = struct{}{}
+				} else {
+					continue
+				}
+				// 阶段勾选数量统计
+				stage := common.InterfaceToStr((*result)[userId].StageStr[project_id])
+				if stage != "" {
+					stageSplit := strings.Split(stage, ",")
+					if (*result)[userId].Stage == nil {
+						(*result)[userId].Stage = map[string]map[string]interface{}{}
+					}
+					for i := 0; i < len(stageSplit); i++ {
+						stageK := stageSplit[i]
+						(*result)[userId].Stage[stageK] = DataHanle((*result)[userId].Stage[stageSplit[i]], project_id)
+					}
 				}
 			}
 		}
 	}
 	projectStatisticsList := make([]*bxcore.ProjectStatisticsData, len(*result))
+
+	stageName := []string{}
+	if bidWay != 0 {
+		stageName = stageNameArr[bidWay]
+	}
 	for _, v := range *result {
 		personName := strings.Split(v.PersonName, "_")[0]
 		k := common.Int64All(strings.Split(v.PersonName, "_")[1])
+		tmpStage := []*bxcore.StageValue{}
+		if bidWay != 0 && len(stageName) > 0 {
+			for i := 0; i < len(stageName); i++ {
+				name_ := fmt.Sprintf("%s(阶段)", stageName[i])
+				if stageV, ok := v.Stage[stageName[i]]; ok {
+					tmpStage = append(tmpStage, &bxcore.StageValue{
+						Name:  name_,
+						Value: fmt.Sprintf("%d", len(stageV)),
+					})
+				} else {
+					tmpStage = append(tmpStage, &bxcore.StageValue{
+						Name:  name_,
+						Value: "-",
+					})
+				}
+			}
+		}
 		projectStatisticsList[k] = &bxcore.ProjectStatisticsData{
-			PersonName:     personName,
-			DepartmentName: v.DepartmentName,
-			EntUserId:      encrypt.SE.Encode2Hex(v.entUserId),
-			BidNumb:        common.Int64All(len(v.BidNumb)),
-			DirectBidNumb:  common.Int64All(len(v.DirectBidNumb)),
-			ChannelBidNumb: common.Int64All(len(v.ChannelBidNumb)),
-			WinNumb:        common.Int64All(len(v.WinNumb)),
-			DirectWinNumb:  common.Int64All(len(v.DirectWinNumb)),
-			ChannelWinNumb: common.Int64All(len(v.ChannelWinNumb)),
-			NotBidNumber:   common.Int64All(len(v.NotBidNumber)),
-			EndNumb:        common.Int64All(len(v.EndNumb)),
+			PersonName:             personName,
+			DepartmentName:         v.DepartmentName,
+			EntUserId:              encrypt.SE.Encode2Hex(v.entUserId),
+			BidNumb:                common.Int64All(len(v.BidNumb)),
+			DirectBidNumb:          common.Int64All(len(v.DirectBidNumb)),
+			ChannelBidNumb:         common.Int64All(len(v.ChannelBidNumb)),
+			WinNumb:                common.Int64All(len(v.WinNumb)),
+			DirectWinNumb:          common.Int64All(len(v.DirectWinNumb)),
+			ChannelWinNumb:         common.Int64All(len(v.ChannelWinNumb)),
+			NotBidNumber:           common.Int64All(len(v.NotBidNumber)),
+			EndNumb:                common.Int64All(len(v.EndNumb)),
+			ParticipateProjectNumb: common.Int64All(len(v.participateProjectNumb)),
+			Stage:                  tmpStage,
 		}
 	}
 	return projectStatisticsList
@@ -385,7 +621,7 @@ func DataHanle(data map[string]interface{}, project_id string) map[string]interf
 	}
 	return data
 }
-func QueryHandle(isAdmin bool, startTime, endTime int64, personArrStr string) []string {
+func QueryHandle(isAdmin bool, startTime, endTime int64, personArrStr string, source []int64, bidWay int64) []string {
 	//时间处理
 	query := []string{}
 	if isAdmin {
@@ -406,5 +642,426 @@ func QueryHandle(isAdmin bool, startTime, endTime int64, personArrStr string) []
 	if endTime != 0 {
 		query = append(query, fmt.Sprintf(" ymd <= %d ", endTime))
 	}
+	if len(source) > 0 {
+		sourceArr := []string{}
+		for i := 0; i < len(source); i++ {
+			sourceArr = append(sourceArr, fmt.Sprintf(" FIND_IN_SET('%d', source)", source[i]))
+		}
+		query = append(query, fmt.Sprintf(" (%s) ", strings.Join(sourceArr, " or ")))
+	}
+	if bidWay != 0 {
+		query = append(query, fmt.Sprintf("bid_way = %d", bidWay))
+	}
 	return query
 }
+func (in *ParticipateStatistics) ProjectDetails(entUserIdArr []string, detailReq *bxcore.ProjectDetailsReq) (result bxcore.DetailData) {
+	//判断是企业、还是个人
+	isEnt, personArrStr, _, _ := in.PersonHandle(entUserIdArr)
+	queryType := GetQueryType(detailReq)
+	query, countQuery := GetDetailQuery(isEnt, personArrStr, detailReq, queryType)
+	totalCount := IC.BaseMysql.CountBySql(countQuery)
+	if totalCount <= 0 {
+		return
+	}
+	// 处理数据  这里只是筛选出项目id和阶段信息
+	dataList := IC.BaseMysql.SelectBySql(query)
+	if dataList == nil || len(*dataList) == 0 {
+		return
+	}
+	// 处理数据 补充项目名称、推送表字段 、格式化数据
+	rs := ProjectDetailHandle(detailReq, *dataList)
+	result = bxcore.DetailData{
+		List:  rs,
+		Total: totalCount,
+	}
+	// 处理数据
+	return
+}
+
+// 这个查询只用查出符合筛选条件的项目id 以及该项目对应的stage
+// 查询类型   1查左边 连右表查stage 2只查右表 3内连接
+func GetDetailQuery(isEnt bool, personArrStr string, req *bxcore.ProjectDetailsReq, queryType int) (string, string) {
+	// 处理分页
+	if req.PageNum == 0 || req.PageSize == 0 {
+		req.PageNum = 1
+		req.PageSize = 50
+	}
+	// 这是因为数据库中 0是未参标 1是已参标, 接收参数时和其他默认参数保持一致 0-全部 1 是未参标 2 是已参标
+	req.IsParticipate -= 1
+	//if queryType == 0 {
+	//	// 空搜索调整
+	//	query1, query2 := []string{}, []string{}
+	//	query3 := ""
+	//	//没有传时间,默认时间处理
+	//	var start = time.Now().AddDate(0, 0, -10)
+	//	query1 = append(query1, fmt.Sprintf(" a.ymd >= %s ", start.Local().Format("20060102")))
+	//	query2 = append(query2, fmt.Sprintf(" b.update_date >= '%s' ", start.Local().Format(date.Date_Full_Layout)))
+	//	if isEnt {
+	//		//是企业版
+	//		query1 = append(query1, fmt.Sprintf(" a.ent_user_id in (%s) ", personArrStr))
+	//		personArr := []string{}
+	//		personArrStrSplit := strings.Split(personArrStr, ",")
+	//		for i := 0; i < len(personArrStrSplit); i++ {
+	//			personArr = append(personArr, fmt.Sprintf(" FIND_IN_SET('%s', b.ent_user_ids)", personArrStrSplit[i]))
+	//		}
+	//		if len(personArr) > 0 {
+	//			personStr := strings.Join(personArr, " or ")
+	//			query2 = append(query2, fmt.Sprintf(" (%s)", personStr))
+	//		}
+	//		query3 = fmt.Sprintf("b.ent_id='%d'", req.EntId) // 连接时右表的条件
+	//	} else {
+	//		//不是企业版
+	//		query1 = append(query1, fmt.Sprintf(" a.position_id = %s ", personArrStr))
+	//		personArr := []string{}
+	//		personArrStrSplit := strings.Split(personArrStr, ",")
+	//		for i := 0; i < len(personArrStrSplit); i++ {
+	//			personArr = append(personArr, fmt.Sprintf(" FIND_IN_SET('%s', b.position_ids)", personArrStrSplit[i]))
+	//		}
+	//		if len(personArr) > 0 {
+	//			personStr := strings.Join(personArr, " or ")
+	//			query2 = append(query2, fmt.Sprintf(" (%s)", personStr))
+	//		}
+	//		query3 = fmt.Sprintf("b.position_ids='%d'", req.PositionId) // 连接时右表的条件
+	//	}
+	//	q := `SELECT A.project_id, B.stage
+	//			FROM
+	//			(SELECT DISTINCT(project_id)
+	//			FROM
+	//				(SELECT project_id FROM participate_push_statistics a
+	//				WHERE %s UNION
+	//				SELECT project_id
+	//				FROM participate_stage_statistics b
+	//				WHERE %s) order by project_id
+	//			LIMIT %d , %d) A
+	//            LEFT JOIN
+	//			participate_stage_statistics B ON A.project_id = B.project_id and  %s` // and 连接前对右表过滤
+	//	q2 := "select count(project_id) from (select distinct(project_id) from participate_push_statistics a where %s union select project_id from participate_stage_statistics b where %s )"
+	//	q1Str := strings.Join(query1, " and ")
+	//	q2Str := strings.Join(query2, " and ")
+	//	return fmt.Sprintf(q, q1Str, q2Str, (req.PageNum-1)*req.PageSize, req.PageSize, query3), fmt.Sprintf(q2, q1Str, q2Str)
+	//}
+	var q, qCount string
+	query := []string{}
+	joinStr := ""
+	particiLeftJoin := ""
+	if queryType == 1 {
+		if isEnt {
+			//是企业版
+			query = append(query, fmt.Sprintf(" pps.ent_user_id in (%s) ", personArrStr))
+			joinStr = fmt.Sprintf("and pss.ent_id = %d", req.EntId)
+		} else {
+			query = append(query, fmt.Sprintf(" pps.position_id = %s ", personArrStr))
+			//不是企业版
+			joinStr = fmt.Sprintf("and pss.position_ids =%d", req.PositionId)
+		}
+		if req.StartTime != 0 {
+			query = append(query, fmt.Sprintf(" pps.ymd >= %d ", req.StartTime))
+		}
+		if req.EndTime != 0 {
+			query = append(query, fmt.Sprintf(" pps.ymd <= %d ", req.EndTime))
+		}
+		if req.StartTime == 0 && req.EndTime == 0 {
+			//没有传时间,默认时间处理
+			var start = time.Now().AddDate(0, 0, -30)
+			query = append(query, fmt.Sprintf(" pps.ymd >= %s ", start.Local().Format("20060102")))
+		}
+		// 标讯/项目来源
+		if len(req.Source) > 0 {
+			sourceArr := []string{}
+			for i := 0; i < len(req.Source); i++ {
+				if req.Source[i] == 0 {
+					sourceArr = append(sourceArr, "NOT FIND_IN_SET('3', pps.source)")
+				} else {
+					sourceArr = append(sourceArr, fmt.Sprintf(" FIND_IN_SET('%d', pps.source)", req.Source[i]))
+				}
+			}
+			query = append(query, fmt.Sprintf(" (%s) ", strings.Join(sourceArr, " or ")))
+		}
+		//参标状态:-1全部,0未参标、1已参标
+		if req.IsParticipate != -1 {
+			query = append(query, " (pss.project_id is null or pss.is_participate=0)")
+			particiLeftJoin = fmt.Sprintf(" left join participate_stage_statistics pss on  (pps.project_id=pss.project_id  %s)", joinStr)
+		}
+		q = `select A.project_id, pss.stage from (SELECT project_id, MAX(ymd) as max_ymd
+			FROM (
+				SELECT DISTINCT pps.project_id, pps.ymd
+				FROM participate_push_statistics pps %s
+				WHERE %s
+			) AS subquery
+			GROUP BY project_id
+			ORDER BY max_ymd ,project_id DESC limit %d,%d) A left join participate_stage_statistics pss on (A.project_id=pss.project_id  %s);`
+		qCount = `SELECT 
+					COUNT(DISTINCT (pps.project_id))
+				  FROM
+					participate_push_statistics pps
+					   %s where %s`
+		q = fmt.Sprintf(q, particiLeftJoin, strings.Join(query, " and "), (req.PageNum-1)*req.PageSize, req.PageSize, joinStr)
+		qCount = fmt.Sprintf(qCount, particiLeftJoin, strings.Join(query, " and "))
+		return q, qCount
+	} else if queryType == 2 {
+		if isEnt {
+			//是企业版
+			personArr := []string{}
+			personArrStrSplit := strings.Split(personArrStr, ",")
+			for i := 0; i < len(personArrStrSplit); i++ {
+				personArr = append(personArr, fmt.Sprintf(" FIND_IN_SET('%s', b.ent_user_ids)", personArrStrSplit[i]))
+			}
+			if len(personArr) > 0 {
+				personStr := strings.Join(personArr, " or ")
+				query = append(query, fmt.Sprintf("(%s)", personStr))
+			}
+		} else {
+			//不是企业版
+			query = append(query, fmt.Sprintf(" FIND_IN_SET('%s', b.position_ids)", personArrStr))
+		}
+		//// 无默认时间时
+		if req.BidUpdateStartTime == 0 && req.BidUpdateEndTime == 0 {
+			var start = time.Now().AddDate(0, 0, -30)
+			query = append(query, fmt.Sprintf(" b.update_date >= '%s' ", start.Local().Format(date.Date_Full_Layout)))
+		}
+		// 参标状态更新时间
+		if req.BidUpdateStartTime != 0 {
+			bidStart_, err := time.ParseInLocation(date.Date_yyyyMMdd, fmt.Sprintf("%d", req.BidUpdateStartTime), time.Local)
+			if err != nil {
+				bidStart_ = time.Now().AddDate(0, 0, -30)
+				log.Println("时间转换失败,使用默认值:", err)
+			}
+			bidStart := date.FormatDate(&bidStart_, date.Date_Full_Layout)
+			query = append(query, fmt.Sprintf("b.update_date >= '%s'", bidStart))
+		}
+		if req.BidUpdateEndTime != 0 {
+			bidEnd_, err := time.ParseInLocation("20060102 15:04:05", fmt.Sprintf("%d 23:59:59", req.BidUpdateEndTime), time.Local)
+			if err != nil {
+				bidEnd_ = time.Now().AddDate(0, 0, -30)
+				log.Println("时间转换失败,使用默认值:", err)
+			}
+			bidEnd := date.FormatDate(&bidEnd_, date.Date_Full_Layout)
+			query = append(query, fmt.Sprintf("b.update_date <= '%s'", bidEnd))
+		}
+		if req.BidWay != 0 {
+			query = append(query, fmt.Sprintf("b.bid_way = %d", req.BidWay))
+		}
+		//参标状态:-1全部,0未参标、1已参标
+		if req.IsParticipate == 1 {
+			query = append(query, fmt.Sprintf("b.is_participate = %d", req.IsParticipate))
+		}
+		q = "select b.project_id,b.stage    from  participate_stage_statistics b where %s %s order by b.update_date desc"
+		qCount = "select count(b.project_id)   from  participate_stage_statistics b where %s %s"
+	} else {
+		if isEnt {
+			//是企业版
+			query = append(query, fmt.Sprintf(" a.ent_user_id in (%s) ", personArrStr))
+			joinStr = "and a.ent_id = b.ent_id"
+		} else {
+			query = append(query, fmt.Sprintf(" a.position_id = %s ", personArrStr))
+			//不是企业版
+			// 个人版
+			joinStr = "and a.position_id = b.position_ids"
+		}
+		if req.StartTime != 0 && req.EndTime == 0 && req.BidUpdateStartTime == 0 && req.BidUpdateEndTime == 0 {
+			var start = time.Now().AddDate(0, 0, -30)
+			query = append(query, fmt.Sprintf(" a.ymd >= %s ", start.Local().Format("20060102")))
+		}
+		if req.StartTime != 0 {
+			query = append(query, fmt.Sprintf(" a.ymd >= %d ", req.StartTime))
+		}
+		if req.EndTime != 0 {
+			query = append(query, fmt.Sprintf(" a.ymd <= %d ", req.EndTime))
+		}
+		if len(req.Source) > 0 {
+			sourceArr := []string{}
+			for i := 0; i < len(req.Source); i++ {
+				if req.Source[i] == 0 {
+					sourceArr = append(sourceArr, "NOT FIND_IN_SET('3', a.source)")
+				} else {
+					sourceArr = append(sourceArr, fmt.Sprintf(" FIND_IN_SET('%d', a.source)", req.Source[i]))
+				}
+			}
+			query = append(query, fmt.Sprintf(" (%s) ", strings.Join(sourceArr, " or ")))
+		}
+		//参标状态:-1全部,0未参标、1已参标
+		if req.IsParticipate != -1 {
+			query = append(query, fmt.Sprintf("a.isparticipate = %d", req.IsParticipate))
+		}
+		// 投标类型 投标方式;1:直接投标 2:渠道投标',
+		if req.BidWay != 0 {
+			query = append(query, fmt.Sprintf("b.bid_way = %d", req.BidWay))
+		}
+		//  参标状态更新时间
+		if req.BidUpdateStartTime != 0 {
+			bidStart_, err := time.ParseInLocation(date.Date_yyyyMMdd, fmt.Sprintf("%d", req.BidUpdateStartTime), time.Local)
+			if err != nil {
+				bidStart_ = time.Now().AddDate(0, 0, -30)
+				log.Println("时间转换失败,使用默认值:", err)
+			}
+			bidStart := date.FormatDate(&bidStart_, date.Date_Full_Layout)
+			query = append(query, fmt.Sprintf("b.update_date >= '%s'", bidStart))
+		}
+		if req.BidUpdateEndTime != 0 {
+			bidEnd_, err := time.ParseInLocation("20060102 15:04:05", fmt.Sprintf("%d 23:59:59", req.BidUpdateEndTime), time.Local)
+			if err != nil {
+				bidEnd_ = time.Now().AddDate(0, 0, -30)
+				log.Println("时间转换失败,使用默认值:", err)
+			}
+			bidEnd := date.FormatDate(&bidEnd_, date.Date_Full_Layout)
+			query = append(query, fmt.Sprintf("b.update_date <= '%s'", bidEnd))
+		}
+		q = "select distinct(a.project_id),b.stage,b.update_date   from participate_push_statistics a inner join  participate_stage_statistics b on(b.project_id=a.project_id %s) where %s order by  b.update_date desc"
+		qCount = "select count(distinct(a.project_id))   from participate_push_statistics a inner join participate_stage_statistics b on(b.project_id=a.project_id %s) where %s"
+	}
+	if len(query) > 0 {
+		q = fmt.Sprintf(q, joinStr, strings.Join(query, " and "))
+		qCount = fmt.Sprintf(qCount, joinStr, strings.Join(query, " and "))
+	}
+	q = fmt.Sprintf("%s limit %d,%d", q, (req.PageNum-1)*req.PageSize, req.PageSize)
+	return q, qCount
+}
+
+type PushInfoStruct struct {
+	Source       string
+	VisitDate    string
+	DisDate      string
+	IsDistribute int
+}
+
+func ProjectDetailHandle(req *bxcore.ProjectDetailsReq, dataList []map[string]interface{}) []*bxcore.ProjectDetailData {
+	//dataList 里面包含 项目id、各阶段信息
+	// 处理项目名称
+	projectIdList := []string{}
+	for i := 0; i < len(dataList); i++ {
+		projectIdList = append(projectIdList, common.ObjToString(dataList[i]["project_id"]))
+	}
+	// 项目名称处理成map用于后续使用
+	projectNameMap := map[string]string{}
+	projectNameRs := es.GetProjectNameByProjectId(projectIdList)
+	if projectNameRs != nil && len(*projectNameRs) > 0 {
+		for i := 0; i < len(*projectNameRs); i++ {
+			projectId_ := common.ObjToString((*projectNameRs)[i]["_id"])
+			projectName_ := (*projectNameRs)[i]["projectname"]
+			projectNameMap[projectId_] = common.ObjToString(projectName_)
+		}
+	}
+	// 获取推送最新状态
+	newPushInfo := getNewPushInfo(projectIdList, req)
+	newPushInfoMap := map[string]PushInfoStruct{}
+	// 处理推送最新状态
+	if newPushInfo != nil && len(*newPushInfo) > 0 {
+		for i := 0; i < len(*newPushInfo); i++ {
+			project_ := common.ObjToString((*newPushInfo)[i]["project_id"])
+			info := PushInfoStruct{
+				Source:    common.ObjToString((*newPushInfo)[i]["source"]),
+				VisitDate: common.ObjToString((*newPushInfo)[i]["visit_date"]),
+				DisDate:   common.ObjToString((*newPushInfo)[i]["dis_date"]),
+			}
+			newPushInfoMap[project_] = info
+		}
+	}
+	results := []*bxcore.ProjectDetailData{}
+	// 处理成最后的数据
+	for i := 0; i < len(dataList); i++ {
+		projectId := common.ObjToString(dataList[i]["project_id"])
+		tmp := bxcore.ProjectDetailData{
+			ProjectName: projectNameMap[projectId],
+		}
+		isDistribute_ := ""
+		// 处理推送表相关字段
+		if pushInfo_, ok := newPushInfoMap[projectId]; ok {
+			sourceStr := []string{}
+			sourceExist := map[string]struct{}{}
+			if pushInfo_.Source != "" {
+				isDistribute_ = "未分发"
+				sourceSplit := strings.Split(pushInfo_.Source, ",")
+				for j := 0; j < len(sourceSplit); j++ {
+					if _, ok := sourceExist[sourceSplit[j]]; ok {
+						continue
+					}
+					sourceExist[sourceSplit[j]] = struct{}{}
+					if sourceSplit[j] == "3" {
+						isDistribute_ = "已分发"
+					}
+					if sourceName, ok := SourceMap[sourceSplit[j]]; ok {
+						sourceStr = append(sourceStr, sourceName)
+					}
+				}
+			}
+			tmp.Source = strings.Join(sourceStr, ",") //  处理成中文
+			tmp.ViewDate = pushInfo_.VisitDate
+			tmp.IsDistribute = isDistribute_
+			tmp.DisDate = pushInfo_.DisDate
+		} //
+		// 处理阶段勾选信息和参标、终止参标信息
+		s := common.ObjToString(dataList[i]["stage"])
+		if s == "" {
+			s = `[{"name":"参标状态","value":"-","date":""},{"name":"投标类型","value":"-","date":""},{"name":"已报名(阶段)","value":"-","date":""},{"name":"投标决策(阶段)","value":"-","date":""},{"name":"编制投标文件(阶段)","value":"-","date":""},{"name":"递交投标文件(阶段)","value":"-","date":""},{"name":"中标公示(阶段)","value":"-","date":""},{"name":"签合同(阶段)","value":"-","date":""},{"name":"已结束","value":"-","date":""},{"name":"终止参标","value":"-","date":""}]`
+		}
+		err := json.Unmarshal([]byte(strings.Replace(s, "'", "\"", -1)), &tmp.Stage)
+		if err != nil {
+			log.Println("stage 反序列失败", err)
+		}
+		tmp.Id = encrypt.EncodeArticleId2ByCheck(projectId)
+		results = append(results, &tmp)
+	}
+	return results
+}
+
+// 获取项目的最新推送状态信息
+func getNewPushInfo(projectIds []string, req *bxcore.ProjectDetailsReq) *[]map[string]interface{} {
+	q := []string{}
+	if req.PositionType == 0 {
+		q = append(q, fmt.Sprintf("position_id=%d", req.PositionId))
+	} else {
+		q = append(q, fmt.Sprintf("ent_id=%d", req.EntId))
+	}
+	if req.StartTime == 0 && req.EndTime == 0 {
+		//没有传时间,默认时间处理
+		var start = time.Now().AddDate(0, 0, -30)
+		q = append(q, fmt.Sprintf(" ymd >= %s ", start.Local().Format("20060102")))
+	}
+	if req.StartTime != 0 {
+		q = append(q, fmt.Sprintf(" ymd >= %d ", req.StartTime))
+
+	}
+	if req.EndTime != 0 {
+		q = append(q, fmt.Sprintf(" ymd <= %d ", req.EndTime))
+
+	}
+	s := strings.Join(q, " and ")
+	sql := `
+SELECT 
+     project_id,
+  GROUP_CONCAT(distinct(source))  as source,
+    DATE_FORMAT(min(visit_date), '%%Y.%%m.%%d') AS visit_date,
+  DATE_FORMAT( min(dis_date), '%%Y.%%m.%%d') AS dis_date
+FROM  participate_push_statistics 
+        WHERE
+            %s
+            AND project_id IN ( "` + strings.Join(projectIds, "\",\"") + `" )
+        GROUP BY project_id
+`
+
+	query := fmt.Sprintf(sql, s)
+	return IC.BaseMysql.SelectBySql(query)
+}
+
+// GetQueryType 查询类型    1查左边 连右表查stage 2只查右表 3内连接
+// IsParticipate 接收参数时 0 全部 1 未参标 2已参标  数据库中 未参标是0
+func GetQueryType(in *bxcore.ProjectDetailsReq) int {
+	// 空搜索  查左边  加时间范围
+	if in.BidUpdateStartTime == 0 && in.BidUpdateEndTime == 0 && in.StartTime == 0 && in.EndTime == 0 && in.IsParticipate == 0 && in.BidWay == 0 && len(in.Source) == 0 {
+		return 1
+	}
+	if in.IsParticipate == 1 {
+		// 未参标只筛选推送表 右表条件无效
+		in.BidWay = 0
+		in.BidUpdateStartTime = 0
+		in.BidUpdateEndTime = 0
+		return 1
+	}
+	if (in.StartTime != 0 || in.EndTime != 0 || len(in.Source) > 0) && (in.BidUpdateStartTime == 0 && in.BidUpdateEndTime == 0 && in.IsParticipate != 2 && in.BidWay == 0) {
+		return 1
+	}
+	if (in.StartTime == 0 && in.EndTime == 0 && len(in.Source) == 0) && (in.BidUpdateStartTime != 0 || in.BidUpdateEndTime == 0 || in.IsParticipate == 2 || in.BidWay != 0) {
+		return 2
+	}
+	return 3
+}

文件差异内容过多而无法显示
+ 850 - 267
jyBXCore/rpc/type/bxcore/bxcore.pb.go


+ 36 - 0
jyBXCore/rpc/type/bxcore/bxcore_grpc.pb.go

@@ -50,6 +50,7 @@ type BxCoreClient interface {
 	ProjectStatistics(ctx context.Context, in *StatisticsListReq, opts ...grpc.CallOption) (*ProjectStatisticsDataRes, error)
 	//聚合搜索
 	PolymerizeSearch(ctx context.Context, in *PolymerizeSearchReq, opts ...grpc.CallOption) (*PolymerizeSearchResp, error)
+	ProjectDetails(ctx context.Context, in *ProjectDetailsReq, opts ...grpc.CallOption) (*DetailDataRes, error)
 }
 
 type bxCoreClient struct {
@@ -186,6 +187,15 @@ func (c *bxCoreClient) PolymerizeSearch(ctx context.Context, in *PolymerizeSearc
 	return out, nil
 }
 
+func (c *bxCoreClient) ProjectDetails(ctx context.Context, in *ProjectDetailsReq, opts ...grpc.CallOption) (*DetailDataRes, error) {
+	out := new(DetailDataRes)
+	err := c.cc.Invoke(ctx, "/bxcore.BxCore/ProjectDetails", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
 // BxCoreServer is the server API for BxCore service.
 // All implementations must embed UnimplementedBxCoreServer
 // for forward compatibility
@@ -218,6 +228,7 @@ type BxCoreServer interface {
 	ProjectStatistics(context.Context, *StatisticsListReq) (*ProjectStatisticsDataRes, error)
 	//聚合搜索
 	PolymerizeSearch(context.Context, *PolymerizeSearchReq) (*PolymerizeSearchResp, error)
+	ProjectDetails(context.Context, *ProjectDetailsReq) (*DetailDataRes, error)
 	mustEmbedUnimplementedBxCoreServer()
 }
 
@@ -267,6 +278,9 @@ func (UnimplementedBxCoreServer) ProjectStatistics(context.Context, *StatisticsL
 func (UnimplementedBxCoreServer) PolymerizeSearch(context.Context, *PolymerizeSearchReq) (*PolymerizeSearchResp, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method PolymerizeSearch not implemented")
 }
+func (UnimplementedBxCoreServer) ProjectDetails(context.Context, *ProjectDetailsReq) (*DetailDataRes, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method ProjectDetails not implemented")
+}
 func (UnimplementedBxCoreServer) mustEmbedUnimplementedBxCoreServer() {}
 
 // UnsafeBxCoreServer may be embedded to opt out of forward compatibility for this service.
@@ -532,6 +546,24 @@ func _BxCore_PolymerizeSearch_Handler(srv interface{}, ctx context.Context, dec
 	return interceptor(ctx, in, info, handler)
 }
 
+func _BxCore_ProjectDetails_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(ProjectDetailsReq)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(BxCoreServer).ProjectDetails(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/bxcore.BxCore/ProjectDetails",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(BxCoreServer).ProjectDetails(ctx, req.(*ProjectDetailsReq))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
 // BxCore_ServiceDesc is the grpc.ServiceDesc for BxCore service.
 // It's only intended for direct use with grpc.RegisterService,
 // and not to be introspected or modified (even as a copy)
@@ -595,6 +627,10 @@ var BxCore_ServiceDesc = grpc.ServiceDesc{
 			MethodName: "PolymerizeSearch",
 			Handler:    _BxCore_PolymerizeSearch_Handler,
 		},
+		{
+			MethodName: "ProjectDetails",
+			Handler:    _BxCore_ProjectDetails_Handler,
+		},
 	},
 	Streams:  []grpc.StreamDesc{},
 	Metadata: "bxcore.proto",

+ 21 - 0
jyBXCore/rpc/util/participate.go

@@ -2,6 +2,7 @@ package util
 
 import (
 	"app.yhyue.com/moapp/jybase/redis"
+	"encoding/json"
 	"fmt"
 	IC "jyBXCore/rpc/init"
 	"sync"
@@ -61,3 +62,23 @@ func IsAllowedAccess(key string) string {
 	redis.Put("other", redisKey, key, 3)
 	return ""
 }
+
+// 判断字符串是否为合法的 JSON 字符串,如果是,则转换为 map;如果不是,则直接输出原始字符串
+// bool :true则是map 否则是string
+func ConvertJSONString(s string) (interface{}, bool) {
+	m, err := JSONStringToMap(s)
+	if err != nil {
+		return s, false
+	}
+	return m, true
+}
+
+// 判断字符串是否为合法的 JSON 字符串,并将其转换为 map
+func JSONStringToMap(s string) (map[string]interface{}, error) {
+	var m map[string]interface{}
+	err := json.Unmarshal([]byte(s), &m)
+	if err != nil {
+		return nil, err
+	}
+	return m, nil
+}

+ 14 - 0
jyBXCore/test.http

@@ -244,3 +244,17 @@ GRPC http://127.0.0.1:8003/bxcore.BxCore/ParticipateSetUpInfo
   "newUserId": 486050,
   "accountId": 360058
 }
+###
+GRPC http://127.0.0.1:8003/bxcore.BxCore/ParticipateSetUpInfo
+
+{
+"entId": 0,
+"entUserId": 0,
+"positionId": 1204781659,
+"positionType": 0,
+"mgoUserId": "641aa7bcac1c8e639bc15cdb",
+"appId": 10000,
+"userId": "641aa7bcac1c8e639bc15cdb",
+"newUserId": 486050,
+"accountId": 360058
+}

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