Browse Source

wip:销售业绩代码提交

wkyuer 5 months ago
parent
commit
f6c36be0e0

+ 3 - 1
internal/cmd/cmd.go

@@ -32,7 +32,9 @@ var (
 				)
 				//公共接口
 				s.Group("/common", func(group *ghttp.RouterGroup) {
-					group.GET("/getSelectItem", controller.GetSelectItemHandler) //获取公共选项
+					group.GET("/getSelectItem", controller.GetSelectItemHandler)              //获取公共选项
+					group.GET("/getSalesList", controller.GetSalesListHandler)                //获取销售人员列表
+					group.POST("/getAccountVipStatus", controller.GetAccountVipStatusHandler) //获取账户会员状态
 				})
 
 				//产品相关

+ 34 - 0
internal/controller/commonHandler.go

@@ -2,8 +2,10 @@ package controller
 
 import (
 	. "app.yhyue.com/moapp/jybase/api"
+	"fmt"
 	"github.com/gogf/gf/v2/frame/g"
 	"github.com/gogf/gf/v2/net/ghttp"
+	"github.com/pkg/errors"
 	"jyOrderManager/internal/jyutil"
 )
 
@@ -20,3 +22,35 @@ func GetSelectItemHandler(r *ghttp.Request) {
 	}
 	r.Response.WriteJson(NewResult(rData, err))
 }
+
+// GetSalesListHandler 获取销售人员列表
+func GetSalesListHandler(r *ghttp.Request) {
+	rData, err := func() (interface{}, error) {
+		return jyutil.JyDepartmentManager.GetShowDeptSales(r.Context()), nil
+	}()
+	if err != nil {
+		g.Log().Errorf(r.Context(), "GetSelectItemHandler 异常 %v", err)
+	}
+	r.Response.WriteJson(NewResult(rData, err))
+}
+
+func GetAccountVipStatusHandler(r *ghttp.Request) {
+	rData, err := func() (interface{}, error) {
+		rj, err := r.GetJson()
+		if err != nil {
+			return nil, errors.Wrap(err, "请求参数格式异常")
+		}
+		var (
+			buySet  = rj.Get("buyset").Int()     //购买主体
+			phone   = rj.Get("phone").String()   //手机号
+			company = rj.Get("company").String() //公司名称
+		)
+		return g.Map{
+			"xx": fmt.Sprintf("%d-%s-%s", buySet, phone, company),
+		}, nil
+	}()
+	if err != nil {
+		g.Log().Errorf(r.Context(), "GetAccountVipStatusHandler 异常 %v", err)
+	}
+	r.Response.WriteJson(NewResult(rData, err))
+}

+ 45 - 0
internal/jyutil/dept.go

@@ -173,3 +173,48 @@ func (mge *DeptManager) GetShowDeptTree(deptName ...string) (rData []interface{}
 	}
 	return rData
 }
+
+// GetShowDeptSales 获取销售人员列表
+func (mge *DeptManager) GetShowDeptSales(ctx context.Context) []g.Map {
+	gv, err := g.Redis().Get(ctx, "jyOrderManagerShowDeptSalesList")
+	if err == nil && !gv.IsEmpty() {
+		return gv.Maps()
+	}
+	res, err := g.DB().Query(ctx, `SELECT
+					d.name as deptName,
+					d.id as deptId,
+					u.id as entUserId,
+					u.name as userName,
+					u.phone as phone
+				FROM
+					jianyu.entniche_user u
+					INNER JOIN jianyu.entniche_department_user e
+					INNER JOIN jianyu.entniche_department d
+					on
+					( u.id = e.user_id AND e.dept_id=d.id  ) 
+				WHERE
+					 u.ent_id = ?`, g.Cfg("global").MustGet(ctx, "powerEntId", 25917).Int64())
+	if err != nil {
+		g.Log().Errorf(ctx, "获取企业人员异常")
+	}
+	var results []g.Map
+	for _, m := range res.List() {
+		var (
+			deptId = gconv.Int(m["deptId"])
+		)
+		results = append(results, g.Map{
+			"deptId":   deptId,
+			"deptName": mge.GetFullDeptName(deptId),
+			"name":     gconv.String(m["userName"]),
+			"userId":   gconv.String(m["entUserId"]),
+		})
+	}
+	if len(results) > 0 {
+		_ = g.Redis().SetEX(ctx, "jyOrderManagerShowDeptSalesList", results, 60*5)
+	}
+	return results
+}
+
+func (mge *DeptManager) GetDeptInfoByEntId(ctx context.Context, entId int) (deptId int, deptName, userName string, err error) {
+
+}

+ 430 - 0
internal/logic/order/saleRecord.go

@@ -0,0 +1,430 @@
+package order
+
+import (
+	"app.yhyue.com/moapp/jybase/common"
+	"context"
+	"fmt"
+	"github.com/gogf/gf/v2/frame/g"
+	"github.com/gogf/gf/v2/util/gconv"
+	uuid2 "github.com/google/uuid"
+	"github.com/pkg/errors"
+	"jyOrderManager/internal/jyutil"
+	"jyOrderManager/internal/model"
+	"log"
+	"sort"
+	"time"
+)
+
+// OrderSaleOperation 查询/更新销售业绩
+//func OrderSaleOperation(context *admin.Context) (interface{}, error) {
+//	param := new(struct {
+//		DoType     string `form:"doType" doc:"query|update"`
+//		OrderCode  string `form:"orderCode" doc:"订单号"`
+//		Reason     string `form:"reason" doc:"变更原因"`
+//		model.SalesMoney string `form:"salesMoney" doc:"新业绩划分"`
+//		NewValue   []*model.SalesMoney
+//	})
+//	err := context.Form(param)
+//	if err != nil || param == nil {
+//		return nil, fmt.Errorf("参数异常")
+//	}
+//	if param.OrderCode == "" {
+//		return nil, fmt.Errorf("缺少订单参数")
+//	}
+//
+//	switch param.DoType {
+//	case "query": // 查询当前业绩
+//		res := config.JysqlDB.SelectBySql("SELECT saler_name,saler_Id,saler_dept,money,distribution_channel FROM order_sale_record where ordercode=? and state=1", param.OrderCode)
+//		if res == nil || len(*res) == 0 {
+//			return nil, fmt.Errorf("未查询到业绩数据")
+//		}
+//		var rData []map[string]interface{}
+//		for _, m := range *res {
+//			rData = append(rData, map[string]interface{}{
+//				"name":                 m["saler_name"],
+//				"id":                   m["saler_Id"],
+//				"saler_dept":           m["saler_dept"],
+//				"money":                m["money"],
+//				"distribution_channel": m["distribution_channel"],
+//			})
+//		}
+//		return map[string]interface{}{
+//			"list":   rData,
+//			"reason": config.SysConfigs.SaleChangeReason,
+//		}, nil
+//	case "update": //更新业绩
+//		if err := gconv.Struct(param.model.SalesMoney, &param.NewValue); err != nil {
+//			return nil, fmt.Errorf("参数校验异常")
+//		}
+//		if err := OrderSalerChange(param.NewValue, param.OrderCode, context.User.Username, param.Reason); err != nil {
+//			return nil, err
+//		}
+//		return "success", nil
+//	default:
+//		return nil, fmt.Errorf("未知操作")
+//	}
+//}
+
+// saleSaveRecordTmp 销售业绩暂存
+var saleSaveRecordTmp = func(ctx context.Context, orderCode, createPerson string, record []*model.SalesMoney) error {
+	if len(record) == 0 {
+		return nil
+	}
+	var (
+		saveList []map[string]interface{}
+		uuid     = uuid2.New().String()
+		now      = time.Now().Local().Format(time.DateTime)
+	)
+	for _, s := range record {
+		deptId, deptName, userName, err := jyutil.JyDepartmentManager.GetDeptInfoByEntId(ctx, s.Id)
+		if err != nil {
+			return fmt.Errorf("获取用户%d部门异常", s.Id)
+		}
+		saveList = append(saveList, map[string]interface{}{
+			"state":                2,
+			"ordercode":            orderCode,
+			"saler_Id":             s.Id,
+			"saler_name":           userName,
+			"saler_dept":           deptName,
+			"saler_dept_id":        deptId,
+			"money":                s.Money,
+			"change_value":         s.Money,
+			"group_uuid":           uuid,
+			"operator":             createPerson,
+			"create_time":          now,
+			"distribution_channel": s.Channel,
+		})
+	}
+	res, err := g.DB().Save(ctx, "order_sale_record", saveList, 1)
+	if err != nil {
+		return fmt.Errorf("暂存销售业绩保存异常")
+	}
+	if num, _ := res.RowsAffected(); gconv.Int(num) != len(saveList) {
+		return fmt.Errorf("暂存销售业绩数量校验异常")
+	}
+	return nil
+}
+
+// SaveOrUpdateSaleMoneyTmp 插入更新暂存销售业绩(未完成订单可修改,管理员可在订单改为完成时修改)
+func SaveOrUpdateSaleMoneyTmp(ctx context.Context, orderCode, createPerson string, record []*model.SalesMoney) error {
+	var oldSalesMoney []*model.SalesMoney
+	for _, row := range record {
+		if row.Channel == "" {
+			return fmt.Errorf("请选择销售渠道")
+		}
+	}
+	//对比是否改变销售业绩
+	recordRes, err := g.DB().Query(ctx, "SELECT ent_userId,money,distribution_channel FROM order_sale_record WHERE ordercode=? and state=2 order by ent_userId asc", orderCode)
+	if err != nil {
+		return errors.Wrap(err, "查询业绩异常")
+	}
+	if !recordRes.IsEmpty() {
+		if recordRes.Len() == len(record) { //数量不对等
+			for _, m := range recordRes.List() {
+				oldSalesMoney = append(oldSalesMoney, &model.SalesMoney{
+					Id:      common.IntAll(m["ent_userId"]),
+					Money:   common.IntAll(m["money"]),
+					Channel: gconv.String(m["distribution_channel"]),
+				})
+			}
+			sort.Slice(record, func(i, j int) bool {
+				return record[i].Id < record[j].Id
+			})
+			//业绩无变更
+			if gconv.String(record) == gconv.String(oldSalesMoney) {
+				return nil
+			}
+		}
+		//更新变更后的业绩
+		_, err := g.DB().Update(ctx, "order_sale_record", g.Map{
+			"state": -2,
+		}, " state=2 and ordercode=? ", orderCode)
+		if err != nil {
+			return errors.Wrap(err, "暂存业绩修改异常")
+		}
+		if saleSaveRecordTmp(ctx, orderCode, createPerson, record) != nil {
+			return fmt.Errorf("暂存新业绩销售异常")
+		}
+		return nil
+	} else {
+		//新增销售业绩
+		return saleSaveRecordTmp(ctx, orderCode, createPerson, record)
+	}
+}
+
+// OrderMoneyMakeRed 订单红冲 销售业绩更改
+func OrderMoneyMakeRed(ctx context.Context, orderCode string, final int64) error {
+	var (
+		oldTotal                        int64 = 0
+		emptyMoneyRow, notEmptyMoneyRow []map[string]interface{}
+	)
+	oldRes, err := g.DB().Query(ctx, "SELECT ordercode,state,ent_userId,saler_name,saler_dept,saler_dept_id,money,change_value,group_uuid,last_group_uuid,change_reason,operator,statistics_time,create_time,distribution_channel FROM order_sale_record WHERE ordercode=? and state=1", orderCode)
+	if err != nil || oldRes.IsEmpty() {
+		return errors.Wrap(err, "业绩红冲未找到对应订单")
+	}
+
+	for _, m := range oldRes.List() {
+		money := gconv.Int64(m["money"])
+		oldTotal += money
+		if money == 0 {
+			emptyMoneyRow = append(emptyMoneyRow, m)
+		} else {
+			notEmptyMoneyRow = append(notEmptyMoneyRow, m)
+		}
+	}
+	if oldTotal == 0 {
+		log.Printf("OrderMoneyMakeRed 业绩总数为空")
+		return nil
+	}
+	// 当前业绩改为历史业绩
+	_, err = g.DB().Update(ctx, "order_sale_record", g.Map{
+		"state": -1,
+	}, "state=1 and ordercode=?", orderCode)
+	if err != nil {
+		return errors.Wrap(err, "")
+	}
+
+	var (
+		newUUid = uuid2.New().String()
+		nowTime = time.Now().Local().Format(time.DateTime)
+	)
+	for _, m := range emptyMoneyRow {
+		m["last_group_uuid"] = m["group_uuid"]
+		m["group_uuid"] = newUUid
+		m["create_time"] = nowTime
+		m["statistics_time"] = nowTime
+		m["change_value"] = 0
+		if _, err := g.DB().Save(ctx, "order_sale_record", m); err != nil {
+			return errors.Wrap(err, "业绩红冲 插入空业绩记录异常")
+		}
+	}
+	var surplus int64 = 0
+	for _, m := range notEmptyMoneyRow {
+		var (
+			money    = gconv.Int64(m["money"])
+			newMoney = ((money * final) + surplus) / oldTotal
+		)
+		if t := ((money * final) + surplus) % oldTotal; t != 0 {
+			surplus += t
+		}
+		m["last_group_uuid"] = m["group_uuid"]
+		m["group_uuid"] = newUUid
+		m["create_time"] = nowTime
+		m["statistics_time"] = nowTime
+		m["money"] = newMoney
+		m["change_reason"] = "订单红冲"
+		m["change_value"] = newMoney - money
+		if _, err := g.DB().Save(ctx, "order_sale_record", m); err != nil {
+			return errors.Wrap(err, "业绩红冲 插入非空业绩记录异常")
+		}
+	}
+	return nil
+}
+
+// OrderSalerChange 业绩变更
+func OrderSalerChange(ctx context.Context, newSales []*model.SalesMoney, orderCode, operator, reason string) error {
+	var (
+		newMoneyTotal                  int = 0
+		oldSallerMap, oldSallerChannel     = map[int]int{}, map[int]string{} //新业绩、旧业绩
+		lastUUid                       string
+	)
+
+	for _, row := range newSales {
+		newMoneyTotal += row.Money
+		if row.Channel == "" {
+			return fmt.Errorf("请选择销售渠道")
+		}
+	}
+	if newMoneyTotal == 0 {
+		return fmt.Errorf("请填写业绩信息")
+	}
+	if reason == "" {
+		return fmt.Errorf("请填写业绩业绩变更原因")
+	}
+	orderRes, err := g.DB().GetOne(ctx, "SELECT pay_money,order_status FROM dataexport_order WHERE ordercode=?", orderCode)
+	if err != nil || orderRes.IsEmpty() {
+		return fmt.Errorf("业绩变成 未查询到订单信息")
+	}
+	var (
+		orderMap     = orderRes.Map()
+		pay_money    = gconv.Int(orderMap["pay_money"])
+		order_status = gconv.Int(orderMap["order_status"])
+	)
+
+	if order_status != 1 {
+		return fmt.Errorf("该订单未支付")
+	}
+	if newMoneyTotal > pay_money {
+		return fmt.Errorf("销售业绩变更 合计需≤合同金额%.2f元", gconv.Float64(pay_money)/100)
+	}
+
+	var (
+		oldSalesMoney []*model.SalesMoney
+		oldMapping    = map[int]map[string]interface{}{}
+	)
+	dbRes, err := g.DB().Query(ctx, "SELECT ordercode,state,ent_userId,saler_name,saler_dept,saler_dept_id,money,change_value,group_uuid,last_group_uuid,change_reason,operator,statistics_time,distribution_channel,create_time FROM order_sale_record WHERE ordercode=? and state=1 order by ent_userId asc", orderCode)
+	if err != nil || dbRes.IsEmpty() {
+		return fmt.Errorf("销售业绩变更 查询旧业绩数据异常")
+	}
+
+	//校验业绩是否变更
+	for _, m := range dbRes.List() {
+		var (
+			money   = gconv.Int(m["money"])
+			salerId = gconv.Int(m["ent_userId"])
+			channel = gconv.String(m["distribution_channel"])
+		)
+		oldSallerMap[salerId] = money
+		oldSallerChannel[salerId] = channel
+		if lastUUid == "" {
+			lastUUid = gconv.String(m["group_uuid"])
+		}
+		oldMapping[salerId] = m
+		oldSalesMoney = append(oldSalesMoney, &model.SalesMoney{
+			Id:      salerId,
+			Money:   money,
+			Channel: channel,
+		})
+	}
+	sort.Slice(newSales, func(i, j int) bool {
+		return newSales[i].Id < newSales[j].Id
+	})
+	//业绩无变更
+	if gconv.String(newSales) == gconv.String(oldSalesMoney) {
+		return fmt.Errorf("业绩未变更")
+	}
+
+	//开始操作
+	//var operaErr error
+	//ok := config.JysqlDB.ExecTx("销售业绩红冲", func(tx *sql.Tx) bool {
+	//旧业绩改为历史业绩
+	if _, err := g.DB().Update(ctx, "order_sale_record", g.Map{
+		"state": -1,
+	}, "state=1 and ordercode=?", orderCode); err != nil {
+		return errors.Wrap(err, "旧业绩更改失败")
+	}
+	var (
+		saveList []map[string]interface{}
+		newUUid  = uuid2.New().String()
+		nowTime  = time.Now().Local().Format(time.DateTime)
+		insertEd = map[int]bool{}
+	)
+	for _, row := range newSales {
+		deptId, deptName, userName, err := jyutil.JyDepartmentManager.GetDeptInfoByEntId(ctx, row.Id)
+		if err != nil {
+			return errors.Wrap(err, fmt.Sprintf("%s 业绩变更获取 %d 部门异常\n", orderCode, row.Id))
+		}
+		changeValue := row.Money - oldSallerMap[row.Id]
+		if changeValue == 0 { //业绩未变动
+			m := oldMapping[row.Id]
+			m["last_group_uuid"] = lastUUid
+			m["group_uuid"] = newUUid
+			m["create_time"] = nowTime
+			m["statistics_time"] = nowTime
+			m["change_value"] = 0
+			if row.Channel != oldSallerChannel[row.Id] { //渠道更新
+				m["distribution_channel"] = row.Channel
+			}
+			saveList = append(saveList, m)
+		} else { //业绩变更
+			saveList = append(saveList, map[string]interface{}{
+				"state":                1,
+				"ordercode":            orderCode,
+				"ent_userId":           row.Id,
+				"saler_name":           userName,
+				"saler_dept":           deptName,
+				"saler_dept_id":        deptId,
+				"change_value":         changeValue,
+				"money":                row.Money,
+				"group_uuid":           newUUid,
+				"last_group_uuid":      lastUUid,
+				"operator":             operator,
+				"change_reason":        reason,
+				"create_time":          nowTime,
+				"statistics_time":      nowTime,
+				"distribution_channel": row.Channel,
+			})
+		}
+		insertEd[row.Id] = true
+	}
+	for salerId, m := range oldMapping {
+		if insertEd[salerId] {
+			continue
+		}
+		m["last_group_uuid"] = lastUUid
+		m["group_uuid"] = newUUid
+		m["create_time"] = nowTime
+		m["statistics_time"] = nowTime
+		m["change_reason"] = reason
+		m["operator"] = operator
+		if money := gconv.Int(m["money"]); money != 0 {
+			m["money"] = 0
+			m["change_value"] = -oldSallerMap[salerId]
+		} else {
+			m["change_value"] = 0
+		}
+		saveList = append(saveList, m)
+	}
+	if _, err := g.DB().Insert(ctx, "order_sale_record", saveList); err != nil {
+		return errors.Wrap(err, "业绩变更插入业绩异常")
+	}
+	return nil
+}
+
+// TakeEffectSale 管理后台销售业绩生效
+// effectTime 业绩统计时间
+// 1.非0元订单,订单状态由“未完成”变更为“已完成”,且协议归档状态为“已归档”,且回款状态为“未回款”,则系统自动生成“业绩归属变更”数据,变更原因为“协议归档”
+// 2.协议归档状态为未归档,订单状态为“已完成”,订单首次回款后,则系统自动生成1条“业绩变更”数据,变更原因为“回款成功”
+func TakeEffectSale(ctx context.Context, orderCode, reason string, effectTime string) error {
+	affect, err := g.DB().Update(ctx, "order_sale_record", g.Map{
+		"state":           1,
+		"change_reason":   reason,
+		"statistics_time": effectTime,
+		"operator":        "系统自动",
+	}, "state=2 and ordercode=?", orderCode)
+	if err != nil {
+		return errors.Wrap(err, fmt.Sprintf("TakeEffectSale %s 激活的销售业绩失败", orderCode))
+	}
+	rowCount, err := affect.RowsAffected()
+	if err != nil {
+		return errors.Wrap(err, "获取待激活业绩异常")
+	}
+	if rowCount == 0 {
+		return fmt.Errorf("TakeEffectSale %s 无待激活的销售业绩", orderCode)
+	}
+	return nil
+}
+
+// CommonChange 销售业绩生效+订单业绩统计时间更新
+// 仅【协议归档/回款(首次)】时
+// func CommonChange2(returnStatus, ContractStatus int, orderCode, contractArchiveTime string) {
+func CommonChange(ctx context.Context, orderCode, operationTime string, operationType int) error {
+	var (
+		reason     = "回款成功"
+		affectTime = operationTime
+	)
+	if operationType == ReturnProtocol { //未回款、已归档
+		//校验是否首次归档
+		count, err := g.DB().GetCount(ctx, "SELECT count(*) FROM contract WHERE contract_archive_status=1 AND order_code=?", orderCode)
+		if err != nil {
+			return errors.Wrap(err, "查询是否首次回款异常")
+		}
+		if count > 0 {
+			return nil
+		}
+		reason = "协议归档"
+	}
+	if affectTime == "" {
+		affectTime = time.Now().Local().Format(time.DateTime)
+	}
+	if err := TakeEffectSale(ctx, orderCode, reason, affectTime); err != nil {
+		log.Printf("TakeEffectSale %d %s失败 %v", operationType, orderCode, err)
+		return err
+	}
+	if _, err := g.DB().Update(ctx, "dataexport_order", g.Map{
+		"sale_time": affectTime,
+	}, "order_code=?", orderCode); err != nil {
+		log.Printf("TakeEffectSale  %d %s", operationType, err.Error())
+	}
+	return nil
+}

+ 1 - 1
internal/model/orderParams.go

@@ -55,7 +55,7 @@ type (
 	}
 
 	SalesMoney struct {
-		Id      int    `json:"id"`
+		Id      int    `json:"id"` //entUserId
 		Money   int    `json:"money"`
 		Channel string `json:"channel"`
 	}

+ 0 - 20
internal/model/user.go

@@ -1,20 +0,0 @@
-package model
-
-import "github.com/gogf/gf/v2/net/ghttp"
-
-type (
-	User struct {
-		MgoUserId  string //mgo库user表_id
-		PersonId   int64  //base_person表id,自然人id
-		UserId     string //base_user表id,用户id
-		PositionId int64  //base_position表id,职位id
-		AccountId  int64  //base_account表id,账户id
-		BaseUserId int64  `json:"base_user_id" doc:"部门id"`
-		DeptId     int64  `json:"deptId" doc:"部门id"`
-	}
-	Context struct {
-		Request *ghttp.Request // 一般情况下不需要使用此参数
-		Session *ghttp.Session // Session in context.
-		User    *User          // User in context.
-	}
-)