作者 唐旭辉

Merge branch 'test'

正在显示 65 个修改的文件 包含 4053 行增加145 行删除

要显示太多修改。

为保证性能只显示 65 of 65+ 个文件。

... ... @@ -78,6 +78,10 @@ spec:
value: "6DwjBO735"
- name: BUSINESS_ADMIN_HOST
value: "http://suplus-business-admin-dev.fjmaimaimai.com"
- name: KAFKA_HOST
value: "192.168.0.250:9092;192.168.0.251:9092;192.168.0.252:9092"
- name: KAFKA_CONSUMER_ID
value: "partnermg_dev"
volumes:
- name: accesslogs
emptyDir: {}
... ...
... ... @@ -60,7 +60,7 @@ spec:
- name: POSTGRESQL_PORT
value: "31544"
- name: LOG_LEVEL
value: "debug"
value: "info"
- name: ERROR_BASE_CODE
value: "1"
- name: ERROR_BASE_CODE_MULTIPLE
... ... @@ -75,6 +75,10 @@ spec:
value: "rsF0pL!6DwjBO735"
- name: BUSINESS_ADMIN_HOST
value: "http://suplus-business-admin-prd.fjmaimaimai.com"
- name: KAFKA_HOST
value: "192.168.0.250:9092;192.168.0.251:9092;192.168.0.252:9092"
- name: KAFKA_CONSUMER_ID
value: "partnermg_prd"
volumes:
- name: accesslogs
emptyDir: {}
\ No newline at end of file
emptyDir: {}
... ...
... ... @@ -75,6 +75,10 @@ spec:
value: "rsF0pL!6DwjBO735"
- name: BUSINESS_ADMIN_HOST
value: "http://suplus-business-admin-test.fjmaimaimai.com"
- name: KAFKA_HOST
value: "192.168.0.250:9092;192.168.0.251:9092;192.168.0.252:9092"
- name: KAFKA_CONSUMER_ID
value: "partnermg_test"
volumes:
- name: accesslogs
emptyDir: {}
... ...
... ... @@ -4,8 +4,10 @@ go 1.14
require (
github.com/GeeTeam/gt3-golang-sdk v0.0.0-20200116043922-446ca8a507d2
github.com/Shopify/sarama v1.23.1
github.com/ajg/form v1.5.1 // indirect
github.com/astaxie/beego v1.12.2
github.com/bsm/sarama-cluster v2.1.15+incompatible
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072 // indirect
github.com/fatih/structs v1.1.0 // indirect
... ...
package main
import (
"context"
"os"
"os/signal"
"syscall"
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
_ "gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/infrastructure/pg"
_ "gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/log"
_ "gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/port/beego"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/port/consumer"
)
func main() {
logs.Info("应用启动")
beego.Run()
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, os.Interrupt, os.Kill, syscall.SIGINT, syscall.SIGTERM)
ctx, cancel := context.WithCancel(context.Background())
go func() {
logs.Info("应用启动")
beego.Run()
}()
consumerRun := consumer.NewRuner()
if err := consumerRun.InitConsumer(); err != nil {
logs.Error("启动kafka消息消费者失败:%s", err)
}
go func() {
consumerRun.Start(ctx)
}()
go func() {
<-consumerRun.IsReady()
logs.Info("Sarama consumer up and running!...")
}()
for {
select {
case <-sigs:
cancel()
return
default:
}
}
}
... ...
package subscriber
import (
"fmt"
"time"
coreDomain "github.com/linmadan/egglib-go/core/domain"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/domain"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/domain/event"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/infrastructure/pg/transaction"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/infrastructure/repository"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/lib"
)
//订单数据修改触发的订阅事件
type OrderLogSubscriber struct {
TransactionContext *transaction.TransactionContext
}
var _ coreDomain.DomainEventSubscriber = (*OrderLogSubscriber)(nil)
func (subscriber *OrderLogSubscriber) HandleEvent(domainEvent coreDomain.DomainEvent) error {
var (
orderLogRepository domain.OrderLogRepository
userRepository domain.UsersRepository
adminUser domain.Users
err error
)
if orderLogRepository, err = repository.NewOrderLogRepository(subscriber.TransactionContext); err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
if userRepository, err = repository.NewUsersRepository(subscriber.TransactionContext); err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
switch domainEvent.EventType() {
//订单分红因为货品的数量变动而发送改变
case event.UPDATE_BONUS_BY_GOOD_NUMBER_EVENT:
currentEvent := domainEvent.(event.UpdateBonusByGoodNumber)
adminUser, err = userRepository.FindOne(domain.UsersFindOneQuery{Id: currentEvent.AdminId})
if err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
orderLog := domain.OrderLog{
OperatorType: domain.ORDER_LOG_OPERATOR_ADMIN,
OperatorId: currentEvent.AdminId,
Operator: adminUser.Name,
AlterTime: time.Now(),
DataFrom: domain.ORDER_LOG_FROM,
LogAction: "修改",
OrderId: currentEvent.OrderId,
GoodId: currentEvent.GoodId,
Descript: []domain.OrderLogDescript{
domain.OrderLogDescript{
Title: "调整商品数量",
Item: currentEvent.GoodName,
Action: []string{
fmt.Sprintf(`购买数量由"%s"调整为"%s"`, currentEvent.FormerNumber, currentEvent.NewNumber),
fmt.Sprintf(`商品总价由"¥%s"调整为"¥%s"`, currentEvent.FormerAmount, currentEvent.NewAmount),
},
},
},
}
err = orderLogRepository.Add(&orderLog)
break
//订单分红因为合伙人分红比例变动而发送改变
case event.UPDATE_BONUS_BY_PARTENT_BONUS_PERCENT_EVENT:
currentEvent := domainEvent.(event.UpdateBounsByPartnerBonusPercent)
adminUser, err = userRepository.FindOne(domain.UsersFindOneQuery{Id: currentEvent.AdminId})
if err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
orderLog := domain.OrderLog{
OperatorType: domain.ORDER_LOG_OPERATOR_ADMIN,
OperatorId: currentEvent.AdminId,
Operator: adminUser.Name,
AlterTime: time.Now(),
DataFrom: domain.ORDER_LOG_FROM,
LogAction: "修改",
OrderId: currentEvent.OrderId,
GoodId: currentEvent.GoodId,
Descript: []domain.OrderLogDescript{
domain.OrderLogDescript{
Title: "合伙人分红比例",
Item: currentEvent.GoodName,
Action: []string{
fmt.Sprintf(`分红比例由"%s"调整为"%s"`, currentEvent.FormerPartnerBonusPercent, currentEvent.NewPartnerBonusPercent),
fmt.Sprintf(`应收分红由"¥%s"调整为"¥%s"`, currentEvent.FormerPartnerBonus, currentEvent.NewPartnerBonus),
},
},
},
}
err = orderLogRepository.Add(&orderLog)
break
//更新订单的备注
case event.UPDATE_ORDER_REMARK:
currentEvent := domainEvent.(event.UpdateOrderRemark)
adminUser, err = userRepository.FindOne(domain.UsersFindOneQuery{Id: currentEvent.AdminId})
if err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
orderLog := domain.OrderLog{
OperatorType: domain.ORDER_LOG_OPERATOR_ADMIN,
OperatorId: currentEvent.AdminId,
Operator: adminUser.Name,
AlterTime: time.Now(),
DataFrom: domain.ORDER_LOG_FROM,
LogAction: "修改",
OrderId: currentEvent.OrderId,
GoodId: 0,
Descript: []domain.OrderLogDescript{
domain.OrderLogDescript{
Title: "编辑备注",
Item: currentEvent.NewRemark,
Action: []string{},
},
},
}
err = orderLogRepository.Add(&orderLog)
break
// 支付订单中货品的分红
case event.PAY_ORDER_GOOD_BONUS_EVENT:
currentEvent := domainEvent.(event.PayOrderGoodBonus)
adminUser, err = userRepository.FindOne(domain.UsersFindOneQuery{Id: currentEvent.AdminId})
if err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
orderLog := domain.OrderLog{
OperatorType: domain.ORDER_LOG_OPERATOR_ADMIN,
OperatorId: currentEvent.AdminId,
Operator: adminUser.Name,
AlterTime: time.Now(),
DataFrom: domain.ORDER_LOG_FROM,
LogAction: "支付",
OrderId: currentEvent.OrderId,
GoodId: currentEvent.GoodId,
Descript: []domain.OrderLogDescript{
domain.OrderLogDescript{
Title: "支付分红",
Item: currentEvent.GoodName,
Action: []string{
fmt.Sprintf(`支付分红"¥%.2f"`, currentEvent.PartnerBonus),
},
},
},
}
err = orderLogRepository.Add(&orderLog)
break
}
return err
}
func (subscriber *OrderLogSubscriber) SubscribedToEventTypes() []string {
return []string{
event.UPDATE_BONUS_BY_GOOD_NUMBER_EVENT,
event.UPDATE_BONUS_BY_PARTENT_BONUS_PERCENT_EVENT,
event.UPDATE_ORDER_REMARK,
event.PAY_ORDER_GOOD_BONUS_EVENT,
}
}
... ...
... ... @@ -21,6 +21,14 @@ func CreateOrderBaseDao(options map[string]interface{}) (*dao.OrderBaseDao, erro
return dao.NewOrderBaseDao(transactionContext)
}
func CreateOrderBestshopDao(options map[string]interface{}) (*dao.OrderBestshopDao, error) {
var transactionContext *transaction.TransactionContext
if value, ok := options["transactionContext"]; ok {
transactionContext = value.(*transaction.TransactionContext)
}
return dao.NewOrderBestshopDao(transactionContext)
}
func CreateUsersDao(options map[string]interface{}) (*dao.UsersDao, error) {
var transactionContext *transaction.TransactionContext
if value, ok := options["transactionContext"]; ok {
... ...
... ... @@ -13,3 +13,11 @@ func CreateBusinessBonusService(options map[string]interface{}) (service.Busines
}
return domainService.NewBusinessBonusService(transactionContext), nil
}
func CreateOrderBonusService(options map[string]interface{}) (service.OrderBonusService, error) {
var transactionContext *transaction.TransactionContext
if value, ok := options["transactionContext"]; ok {
transactionContext = value.(*transaction.TransactionContext)
}
return domainService.NewOrderBonusService(transactionContext), nil
}
... ...
... ... @@ -42,7 +42,7 @@ func CreateOrderGoodRepository(options map[string]interface{}) (domain.OrderGood
return repository.NewOrderGoodRepository(transactionContext)
}
//CreateOrderGoodRepository 订单信息
//CreateOrderGoodRepository 订单货品信息
func CreateUsersRepository(options map[string]interface{}) (domain.UsersRepository, error) {
var transactionContext *transaction.TransactionContext
if value, ok := options["transactionContext"]; ok {
... ... @@ -77,3 +77,30 @@ func CreateBusinessBonusRepository(options map[string]interface{}) (domain.Busin
}
return repository.NewBusinessBonusRepository(transactionContext)
}
//CreateOrderGoodBestshopRepository 小米(海鲜干货改)的订单商品信息
func CreateOrderGoodBestshopRepository(options map[string]interface{}) (domain.OrderGoodBestshopRepository, error) {
var transactionContext *transaction.TransactionContext
if value, ok := options["transactionContext"]; ok {
transactionContext = value.(*transaction.TransactionContext)
}
return repository.NewOrderGoodBestshopRepository(transactionContext)
}
//CreateOrderGoodBestshopRepository 小米(海鲜干货改)订单信息
func CreateOrderBestshopRepository(options map[string]interface{}) (domain.OrderBestshopRepository, error) {
var transactionContext *transaction.TransactionContext
if value, ok := options["transactionContext"]; ok {
transactionContext = value.(*transaction.TransactionContext)
}
return repository.NewOrderBestshopRepository(transactionContext)
}
//CreateOrderGoodBestshopRepository小米(海鲜干货改)订单信息
func CreateOrderLogRepository(options map[string]interface{}) (domain.OrderLogRepository, error) {
var transactionContext *transaction.TransactionContext
if value, ok := options["transactionContext"]; ok {
transactionContext = value.(*transaction.TransactionContext)
}
return repository.NewOrderLogRepository(transactionContext)
}
... ...
package command
//创建订单
type CreateOrderCommand struct {
//订单类型
OrderType int `json:"orderType"`
... ...
... ... @@ -2,6 +2,7 @@ package command
import "time"
//订单发货
type OrderDeliveryCommand struct {
OrderId int64 `json:"orderId"`
DeliveryTime time.Time `json:"deliveryTime"`
... ...
package command
//更新订单的商品数字并更新分红的数据
//UpdateGoodBouns 更新订单的商品数量,和支付状态 并更新分红的数据
type UpdateGoodBouns struct {
Id int64 `json:"id"` //订单id
GoodBouns []GoodBouns `json:"goodBouns"`
... ...
package command
//UpdateOrderPurposeCommand 更新意向
//UpdateOrderPurposeCommand 更新订单
type UpdateOrderCommand struct {
Id int64 `json:"id"`
//订单编号
... ...
... ... @@ -10,9 +10,11 @@ type ListOrderBaseQuery struct {
Offset int `json:"offset" `
// 查询限制
Limit int `json:"limit"`
//订单类型
OrderType int `json:"orderType"`
//发货单号
DeliveryCode string `json:"deliveryCode"`
CompanyId int64 `json:"companyId"`
//订单类型
OrderType int `json:"orderType"`
PartnerOrCode string `json:"partner_or_code"`
}
... ...
package query
type ListOrderBonusQuery struct {
// 查询偏离量
Offset int `json:"offset" `
// 查询限制
Limit int `json:"limit"`
CompanyId int64 `json:"companyId"`
//订单类型
OrderType int `json:"orderType"`
PartnerOrCode string `json:"partner_or_code"`
}
... ...
... ... @@ -2,19 +2,24 @@ package service
import (
"fmt"
"strings"
"time"
"github.com/astaxie/beego/logs"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/application/event/subscriber"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/application/factory"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/application/orderinfo/command"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/application/orderinfo/query"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/domain"
domainService "gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/domain/service"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/infrastructure/dao"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/infrastructure/pg/models"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/infrastructure/pg/transaction"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/lib"
)
//OrderService 管理员相关服务
//OrderService 自建订单,意向单,实发订单
type OrderInfoService struct {
}
... ... @@ -151,6 +156,7 @@ func (service OrderInfoService) GetOrderDetail(getOrderQuery query.GetOrderQuery
return order, nil
}
//CreateNewOrder 创建订单
func (service OrderInfoService) CreateNewOrder(cmd command.CreateOrderCommand) (*domain.OrderBase, error) {
var (
transactionContext, _ = factory.CreateTransactionContext(nil)
... ... @@ -436,9 +442,11 @@ func (service OrderInfoService) UpdateOrderData(cmd command.UpdateOrderCommand)
oldOrderData.Goods = newOrderGoods
//删不需要的订单总不需要的商品
delGoods = service.deleteOldOrderGoods(newOrderGoods, oldOrderGoods)
err = orderGoodRepository.Remove(oldOrderData.Id, cmd.CompanyId, delGoods...)
if err != nil {
return nil, lib.ThrowError(lib.INTERNAL_SERVER_ERROR, fmt.Sprintf("删除订单中的商品数据失败:%s", err))
if len(delGoods) > 0 {
err = orderGoodRepository.Remove(oldOrderData.Id, cmd.CompanyId, delGoods...)
if err != nil {
return nil, lib.ThrowError(lib.INTERNAL_SERVER_ERROR, fmt.Sprintf("删除订单中的商品数据失败:%s", err))
}
}
err = transactionContext.CommitTransaction()
if err != nil {
... ... @@ -612,7 +620,7 @@ func (service OrderInfoService) DisableOrEnable(cmd command.DisableOrderCommand)
return nil
}
//UpdateGoodBouns 更新货品的分红相关的数值
//UpdateGoodBouns 分红时,更新货品的分红相关的数值
func (service OrderInfoService) UpdateGoodBouns(cmd command.UpdateGoodBouns) error {
var (
transactionContext, _ = factory.CreateTransactionContext(nil)
... ... @@ -687,7 +695,7 @@ func (service OrderInfoService) UpdateGoodBouns(cmd command.UpdateGoodBouns) err
}
}
oldOrderData.Goods = oldOrderGoods
//变更订单类型
err = oldOrderData.Compute()
if err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, fmt.Sprintf("核算订单中合计的数值失败:%s", err))
... ... @@ -707,3 +715,466 @@ func (service OrderInfoService) UpdateGoodBouns(cmd command.UpdateGoodBouns) err
return nil
}
//PageListOrderBouns 获取订单的分红列表
func (service OrderInfoService) PageListOrderBonus(listOrderQuery query.ListOrderBonusQuery) ([]map[string]interface{}, int, error) {
transactionContext, err := factory.CreateTransactionContext(nil)
if err != nil {
return nil, 0, lib.ThrowError(lib.TRANSACTION_ERROR, err.Error())
}
if err = transactionContext.StartTransaction(); err != nil {
return nil, 0, err
}
defer func() {
transactionContext.RollbackTransaction()
}()
var (
ordersM []models.OrderBase
orders []domain.OrderBase
cnt int
orderBaseDao *dao.OrderBaseDao
)
if orderBaseDao, err = factory.CreateOrderBaseDao(map[string]interface{}{
"transactionContext": transactionContext,
}); err != nil {
return nil, cnt, lib.ThrowError(lib.TRANSACTION_ERROR, err.Error())
}
ordersM, cnt, err = orderBaseDao.OrderBonusListByCondition(
listOrderQuery.CompanyId,
listOrderQuery.OrderType,
listOrderQuery.PartnerOrCode,
listOrderQuery.Limit,
listOrderQuery.Offset,
)
for _, orderModel := range ordersM {
order := domain.OrderBase{
Id: orderModel.Id, OrderType: orderModel.OrderType, OrderCode: orderModel.OrderCode,
DeliveryCode: orderModel.DeliveryCode, Buyer: orderModel.Buyer, RegionInfo: orderModel.RegionInfo,
PartnerId: orderModel.PartnerId, SalesmanBonusPercent: orderModel.SalesmanBonusPercent,
CreateTime: orderModel.CreateTime, DeliveryTime: orderModel.DeliveryTime, UpdateTime: orderModel.UpdateTime,
IsDisable: orderModel.IsDisable,
OrderCompute: domain.OrderCompute{
PlanPartnerBonus: orderModel.PlanPartnerBonus, UsePartnerBonus: orderModel.UsePartnerBonus,
PartnerBonusHas: orderModel.PartnerBonusHas, PartnerBonusNot: orderModel.PartnerBonusNot,
PartnerBonusExpense: orderModel.PartnerBonusExpense, SalesmanBonus: orderModel.SalesmanBonus,
PlanOrderCount: orderModel.PlanOrderCount, PlanOrderAmount: orderModel.PlanOrderAmount,
UseOrderCount: orderModel.UseOrderCount, UseOrderAmount: orderModel.UseOrderAmount,
},
PartnerInfo: domain.Partner{
Id: orderModel.PartnerId,
},
BonusStatus: orderModel.BonusStatus,
CompanyId: orderModel.CompanyId,
}
orders = append(orders, order)
}
var (
PartnerInfoRepository domain.PartnerInfoRepository
orderGoodRepository domain.OrderGoodRepository
)
if PartnerInfoRepository, err = factory.CreatePartnerInfoRepository(map[string]interface{}{
"transactionContext": transactionContext,
}); err != nil {
return nil, 0, lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
if orderGoodRepository, err = factory.CreateOrderGoodRepository(map[string]interface{}{
"transactionContext": transactionContext,
}); err != nil {
return nil, 0, lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
var resp = []map[string]interface{}{}
for i := range orders {
partnerData := &domain.PartnerInfo{}
partnerData, err = PartnerInfoRepository.FindOne(domain.PartnerFindOneQuery{
UserId: orders[i].PartnerId,
})
if err != nil {
logs.Error("获取合伙(id=%d)失败%s", orders[i].PartnerId, err)
} else {
orders[i].PartnerInfo = partnerData.Partner
}
var (
goods []domain.OrderGood
hasBonusPercent bool
)
goods, _, err = orderGoodRepository.Find(domain.OrderGoodFindQuery{OrderId: orders[i].Id})
for ii := range goods {
if goods[ii].PartnerBonusPercent > 0 {
hasBonusPercent = true
}
}
listItem := map[string]interface{}{
"updateTime": orders[i].UpdateTime.Local().Format("2006-01-02 15:04:05"),
"id": orders[i].Id,
"shipmentsId": orders[i].DeliveryCode,
"partner": orders[i].PartnerInfo.PartnerName,
"dividendsReceivable": fmt.Sprint(orders[i].GetCurrentPartnerBonus()),
"dividendSpending": fmt.Sprint(orders[i].OrderCompute.PartnerBonusExpense),
"receiveDividends": fmt.Sprint(orders[i].OrderCompute.PartnerBonusHas),
"uncollectedDividends": fmt.Sprint(orders[i].OrderCompute.PartnerBonusNot),
"stateOfPayment": orders[i].BonusStatus,
"orderType": orders[i].OrderType,
"orderTypeName": domain.GetOrderBaseTypeName(orders[i].OrderType),
"orderNumber": orders[i].OrderCode,
}
if !hasBonusPercent {
listItem["receiveDividends"] = "-"
listItem["dividendsReceivable"] = "-"
listItem["dividendSpending"] = "-"
listItem["uncollectedDividends"] = "-"
}
resp = append(resp, listItem)
}
err = transactionContext.CommitTransaction()
if err != nil {
return resp, cnt, lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
return resp, cnt, nil
}
//GetOrderBestshopInfo 获取来源于xiangmi订单的详情以及分红数据
func (service OrderInfoService) GetOrderBestshopInfoWithBonus(orderBaseId int64, companyId int64) (interface{}, error) {
var (
transactionContext, _ = factory.CreateTransactionContext(nil)
err error
)
if err = transactionContext.StartTransaction(); err != nil {
return nil, lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
defer func() {
transactionContext.RollbackTransaction()
}()
var (
orderBaseRepository domain.OrderBaseRepository
orderGoodRepository domain.OrderGoodRepository
orderBestshopRepository domain.OrderBestshopRepository
orderGoodBestshopRepository domain.OrderGoodBestshopRepository
orderLogRepository domain.OrderLogRepository
partnerRepository domain.PartnerInfoRepository
)
if orderBaseRepository, err = factory.CreateOrderBaseRepository(map[string]interface{}{
"transactionContext": transactionContext,
}); err != nil {
return nil, lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
if orderGoodRepository, err = factory.CreateOrderGoodRepository(map[string]interface{}{
"transactionContext": transactionContext,
}); err != nil {
return nil, lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
if orderBestshopRepository, err = factory.CreateOrderBestshopRepository(map[string]interface{}{
"transactionContext": transactionContext,
}); err != nil {
return nil, lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
if orderGoodBestshopRepository, err = factory.CreateOrderGoodBestshopRepository(map[string]interface{}{
"transactionContext": transactionContext,
}); err != nil {
return nil, lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
if orderLogRepository, err = factory.CreateOrderLogRepository(map[string]interface{}{
"transactionContext": transactionContext,
}); err != nil {
return nil, lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
if partnerRepository, err = factory.CreatePartnerInfoRepository(map[string]interface{}{
"transactionContext": transactionContext,
}); err != nil {
return nil, lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
var (
orderData *domain.OrderBase
orderGoods []domain.OrderGood
orderBestshopData *domain.OrderBestShop
orderGoodBestshop []domain.OrderGoodBestShop
orderLogs []domain.OrderLog
partnerInfo *domain.PartnerInfo
)
orderData, err = orderBaseRepository.FindOne(domain.OrderBaseFindOneQuery{
OrderId: orderBaseId,
CompanyId: companyId,
})
if err != nil {
e := fmt.Sprintf("获取订单(order_base)数据失败,id=%d,company_id=%d,err=%s", orderBaseId, companyId, err)
return nil, lib.ThrowError(lib.INTERNAL_SERVER_ERROR, e)
}
if orderData.OrderType != domain.OrderTypeBestShop {
return nil, lib.ThrowError(lib.INTERNAL_SERVER_ERROR, "获取的订单数据失败,OrderType err")
}
orderGoods, _, err = orderGoodRepository.Find(domain.OrderGoodFindQuery{OrderId: orderData.Id})
if err != nil {
e := fmt.Sprintf("获取订单的商品(order_good)数据失败,order_id=%d,err=%s", orderData.Id, err)
return nil, lib.ThrowError(lib.INTERNAL_SERVER_ERROR, e)
}
orderData.Goods = orderGoods
partnerInfo, err = partnerRepository.FindOne(domain.PartnerFindOneQuery{UserId: orderData.PartnerId})
if err != nil {
e := fmt.Sprintf("获取订单中的合伙人(partner)数据失败,id=%d,order_id=%d,err=%s", orderData.PartnerId, orderData.Id, err)
logs.Error(e)
}
orderData.PartnerInfo = partnerInfo.Partner
orderBestshopData, err = orderBestshopRepository.FindOne(domain.OrderBestshopFindOneQuery{OrderId: orderData.DataFrom.DataId})
if err != nil {
e := fmt.Sprintf("获取xiangmi订单(order_bestshop)数据失败,id=%d,err=%s", orderData.DataFrom.DataId, err)
return nil, lib.ThrowError(lib.INTERNAL_SERVER_ERROR, e)
}
orderGoodBestshop, err = orderGoodBestshopRepository.Find(domain.OrderGoodBestshopFindQuery{OrderId: orderBestshopData.Id})
if err != nil {
e := fmt.Sprintf("获取xiangmi订单货品(order_good_bestshop)数据失败,order_id=%d,err=%s", orderBestshopData.Id, err)
return nil, lib.ThrowError(lib.INTERNAL_SERVER_ERROR, e)
}
orderBestshopData.Goods = orderGoodBestshop
orderLogs, err = orderLogRepository.Find(domain.OrderLogFindQuery{OrderId: orderData.Id})
if err != nil {
e := fmt.Sprintf("获取订单的修改记录(order_log)失败,err=%s", err)
logs.Error(e)
}
err = transactionContext.CommitTransaction()
if err != nil {
return nil, lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
result := service.buildOrderBestshopInfoData(orderData, orderBestshopData, orderLogs)
return result, nil
}
//BuildOrderBestshopInfoData 构建前端需要的数据结构
func (service OrderInfoService) buildOrderBestshopInfoData(orderBase *domain.OrderBase,
orderBestshop *domain.OrderBestShop, orderLogs []domain.OrderLog) interface{} {
orderGoodBestshopMap := map[int64]*domain.OrderGoodBestShop{}
for i := range orderBestshop.Goods {
goodid := orderBestshop.Goods[i].Id
orderGoodBestshopMap[goodid] = &orderBestshop.Goods[i]
}
//订单中的商品
productDetail := []map[string]interface{}{}
var hasPartnerBonusPercent bool
for i := range orderBase.Goods {
detail := map[string]interface{}{
"commodityName": orderBase.Goods[i].GoodName,
"productCodes": "",
"commodityCode": "",
"univalence": orderBase.Goods[i].Price,
"orderNum": orderBase.Goods[i].GetCurrentGoodNumber(),
"commodityPrice": orderBase.Goods[i].GetCurrentAmount(),
"partnerDividends": "",
"productId": orderBase.Goods[i].Id,
"paymentStatus": orderBase.Goods[i].BonusStatus,
"partnerRatio": orderBase.Goods[i].PartnerBonusPercent,
}
if orderBase.Goods[i].PartnerBonusPercent >= 0 {
hasPartnerBonusPercent = true
detail["partnerDividends"] = fmt.Sprint(orderBase.Goods[i].GetCurrentPartnerBonus())
}
goodBestshopId := orderBase.Goods[i].DataFrom.DataId
if v, ok := orderGoodBestshopMap[goodBestshopId]; ok {
detail["productCodes"] = v.Sn
detail["commodityCode"] = v.Bn
}
productDetail = append(productDetail, detail)
}
product := map[string]interface{}{
"orderNumCount": orderBase.GetCurrentOrderCount(),
"partnerDividendsCount": "",
"orderAmountAdjustmentCount": orderBase.GetCurrentOrderAmount(),
"detail": productDetail,
}
if hasPartnerBonusPercent {
product["partnerDividendsCount"] = fmt.Sprint(orderBase.GetCurrentPartnerBonus())
}
//订单描述
order := map[string]interface{}{
"orderId": orderBase.Id,
"orderState": orderBestshop.OrderState,
"customers": orderBestshop.BuyerName,
"address": orderBestshop.BuyerAddress,
"remarks": orderBestshop.BuyerRemark,
"partner": orderBase.PartnerInfo.PartnerName,
"phone": orderBestshop.BuyerPhone,
"orderTime": orderBestshop.OrderTime,
"shippingStatus": orderBestshop.DeliveryState,
"partnerDividends": "",
"receivedDividends": "",
"notReceivedDividend": "",
"dividendSpending": "",
"orderNumber": orderBase.OrderCode,
}
if hasPartnerBonusPercent {
order["partnerDividends"] = fmt.Sprint(orderBase.GetCurrentPartnerBonus())
order["receivedDividends"] = fmt.Sprint(orderBase.OrderCompute.PartnerBonusHas)
order["notReceivedDividend"] = fmt.Sprint(orderBase.OrderCompute.PartnerBonusNot)
order["dividendSpending"] = fmt.Sprint(orderBase.OrderCompute.PartnerBonusExpense)
}
modifyLog := []map[string]interface{}{}
for i := range orderLogs {
m := map[string]interface{}{
"title": orderLogs[i].LogAction,
"time": orderLogs[i].AlterTime.Local().Format("2006-01-02 15:04:05"),
"userName": orderLogs[i].Operator,
"id": orderLogs[i].Id,
}
detail := []map[string]string{}
for ii, vv := range orderLogs[i].Descript {
d := map[string]string{
"updateTitle": vv.Title,
"id": fmt.Sprint(ii),
"content": vv.Item,
}
if len(vv.Action) > 0 {
d["content"] = vv.Item + ":" + strings.Join(vv.Action, ";")
}
detail = append(detail, d)
}
m["updateList"] = detail
modifyLog = append(modifyLog, m)
}
result := map[string]interface{}{
"order": order,
"product": product,
"modify": modifyLog,
"remark": orderBase.Remark.RemarkBonus,
}
return result
}
//UpdateBounsWithGoodNumber 分红时,因修改订单中商品的数量发生分红变动
func (service OrderInfoService) UpdateBonusByGoodNumber(orderId int64, goodId int64, adminId int64, goodNumber int, reason string) error {
var (
transactionContext, _ = factory.CreateTransactionContext(nil)
err error
)
if err = transactionContext.StartTransaction(); err != nil {
return lib.ThrowError(lib.TRANSACTION_ERROR, err.Error())
}
defer func() {
transactionContext.RollbackTransaction()
}()
var (
orderBonuSrv domainService.OrderBonusService
)
orderBonuSrv, err = factory.CreateOrderBonusService(map[string]interface{}{
"transactionContext": transactionContext,
})
if err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
orderBonuSrv.Subscribe(&subscriber.OrderLogSubscriber{
TransactionContext: transactionContext.(*transaction.TransactionContext),
})
err = orderBonuSrv.UpdateBounsByGoodNumber(orderId, adminId, goodId, goodNumber, reason)
if err != nil {
return err
}
err = transactionContext.CommitTransaction()
if err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
return nil
}
//UpdateBounsByPartnerBonusPercent 分红时,因修改订单中商品的合伙人分行比例发生分红变动
func (service OrderInfoService) UpdateBonusByPartnerBonusPercent(orderId int64, goodId int64, adminId int64, percent float64, reason string) error {
var (
transactionContext, _ = factory.CreateTransactionContext(nil)
err error
)
if err = transactionContext.StartTransaction(); err != nil {
return lib.ThrowError(lib.TRANSACTION_ERROR, err.Error())
}
defer func() {
transactionContext.RollbackTransaction()
}()
var (
orderBonuSrv domainService.OrderBonusService
)
orderBonuSrv, err = factory.CreateOrderBonusService(map[string]interface{}{
"transactionContext": transactionContext,
})
if err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
orderBonuSrv.Subscribe(&subscriber.OrderLogSubscriber{
TransactionContext: transactionContext.(*transaction.TransactionContext),
})
err = orderBonuSrv.UpdateBounsByPartnerBonusPercent(orderId, adminId, goodId, percent, reason)
if err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
err = transactionContext.CommitTransaction()
if err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
return nil
}
//PayPartnerBonusWithOrderBestshop 支付分红,海鲜干货的订单 (orderType=domain.OrderTypeBestShop)
func (service OrderInfoService) PayPartnerBonusWithOrderBestshop(orderId int64, goodId int64, adminId int64) error {
var (
transactionContext, _ = factory.CreateTransactionContext(nil)
err error
)
if err = transactionContext.StartTransaction(); err != nil {
return lib.ThrowError(lib.TRANSACTION_ERROR, err.Error())
}
defer func() {
transactionContext.RollbackTransaction()
}()
var (
orderBonuSrv domainService.OrderBonusService
)
orderBonuSrv, err = factory.CreateOrderBonusService(map[string]interface{}{
"transactionContext": transactionContext,
})
if err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
orderBonuSrv.Subscribe(&subscriber.OrderLogSubscriber{
TransactionContext: transactionContext.(*transaction.TransactionContext),
})
err = orderBonuSrv.PayOrderGoodBonus(orderId, goodId, adminId)
if err != nil {
return err
}
err = transactionContext.CommitTransaction()
if err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
return nil
}
//PayPartnerBonusWithOrderBestshop 支付分红,海鲜干货的订单 (orderType=domain.OrderTypeBestShop)
func (service OrderInfoService) UpdateOrderRemarkBonus(orderId int64, adminId int64, remark string) error {
var (
transactionContext, _ = factory.CreateTransactionContext(nil)
err error
)
if err = transactionContext.StartTransaction(); err != nil {
return lib.ThrowError(lib.TRANSACTION_ERROR, err.Error())
}
defer func() {
transactionContext.RollbackTransaction()
}()
var (
orderBonuSrv domainService.OrderBonusService
)
orderBonuSrv, err = factory.CreateOrderBonusService(map[string]interface{}{
"transactionContext": transactionContext,
})
if err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
orderBonuSrv.Subscribe(&subscriber.OrderLogSubscriber{
TransactionContext: transactionContext.(*transaction.TransactionContext),
})
err = orderBonuSrv.UpdateOrderRemarkBonus(orderId, adminId, remark)
if err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
err = transactionContext.CommitTransaction()
if err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
return nil
}
... ...
... ... @@ -44,16 +44,16 @@ func (command CreatePartnerInfoCommand) ValidateCommand() error {
if command.RegionInfo == nil {
return lib.ThrowError(lib.ARG_ERROR, "区域必填")
}
if len(command.Salesman) == 0 {
return lib.ThrowError(lib.ARG_ERROR, "关联业务员必填")
}
for i := range command.Salesman {
if len(command.Salesman[i].Name) == 0 {
return lib.ThrowError(lib.ARG_ERROR, "关联业务员名称必填")
}
if len(command.Salesman[i].Telephone) == 0 {
return lib.ThrowError(lib.ARG_ERROR, "关联业务员电话必填")
}
}
// if len(command.Salesman) == 0 {
// return lib.ThrowError(lib.ARG_ERROR, "关联业务员必填")
// }
// for i := range command.Salesman {
// if len(command.Salesman[i].Name) == 0 {
// return lib.ThrowError(lib.ARG_ERROR, "关联业务员名称必填")
// }
// if len(command.Salesman[i].Telephone) == 0 {
// return lib.ThrowError(lib.ARG_ERROR, "关联业务员电话必填")
// }
// }
return nil
}
... ...
... ... @@ -31,19 +31,20 @@ func (command *UpdatePartnerInfoCommand) ValidateCommand() error {
if command.RegionInfo == nil {
return lib.ThrowError(lib.ARG_ERROR, "区域必填")
}
if len(command.Salesman) == 0 {
return lib.ThrowError(lib.ARG_ERROR, "关联业务员必填")
}
if command.Id == 0 {
return lib.ThrowError(lib.ARG_ERROR, "合伙人id错误")
}
for i := range command.Salesman {
if len(command.Salesman[i].Name) == 0 {
return lib.ThrowError(lib.ARG_ERROR, "关联业务员名称必填")
}
if len(command.Salesman[i].Telephone) == 0 {
return lib.ThrowError(lib.ARG_ERROR, "关联业务员电话必填")
}
}
// if len(command.Salesman) == 0 {
// return lib.ThrowError(lib.ARG_ERROR, "关联业务员必填")
// }
// for i := range command.Salesman {
// if len(command.Salesman[i].Name) == 0 {
// return lib.ThrowError(lib.ARG_ERROR, "关联业务员名称必填")
// }
// if len(command.Salesman[i].Telephone) == 0 {
// return lib.ThrowError(lib.ARG_ERROR, "关联业务员电话必填")
// }
// }
return nil
}
... ...
... ... @@ -222,6 +222,7 @@ func (PartnerInfoService *PartnerInfoService) UpdatePartnerInfo(cmd *command.Upd
}); err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
//TODO 修改为本地消息订阅
err = businessBonusSrv.EnableOrDisable(partnerInfo.Partner.Id)
if err != nil {
e := fmt.Sprintf("更新业务分红(partner_id=%d)数据失败:%s", partnerInfo.Partner.Id, err)
... ...
package command
//接收海鲜干货的订单
type CreateOrderFromBestshop struct {
//订单编号
OrderCode string `json:"orderCode"`
//下单时间
OrderTime string `json:"orderTime"`
//公司id
CompanyId int64 `json:"companyId"`
//订单状态
OrderState int8 `json:"orderState"`
//发货状态
DeliveryState int8 `json:"deliveryState"`
//买家名称
BuyerName string `json:"buyerName"`
BuyerId int64 `json:"buyerId"`
//买家电话
BuyerPhone string `json:"buyerPhone"`
//买家地址
BuyerAddress string `json:"buyerAddress"`
//买家备注
BuyerRemark string `json:"buyerRemark"`
//商品总数
OrderCount int `json:"orderCount"`
//d订单总额
OrderAmount float64 `json:"orderAmount"`
//发货时间
DeliveryTime string `json:"deliveryTime"`
PartnerId int64 `json:"partnerId"`
Goods []struct {
Id int64 `json:"id"`
//货品编号
Sn string `json:"sn"`
//商品编号
Bn string `json:"bn"`
//货品名称
Name string `json:"name"`
//单价
Price float64 `json:"price"`
//货品数量
Nums int `json:"nums"`
//订单总价
Amount float64 `json:"amount"`
} `json:"goods"`
}
... ...
package service
import (
"fmt"
"time"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/infrastructure/dao"
"github.com/astaxie/beego/logs"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/application/factory"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/application/syncOrder/command"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/domain"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/lib"
)
//从其他系统接收订单数据
const (
BEST_SHOP_UNIONID string = "gh_18eb644002fb" //海鲜干货小程序原始id
)
type SyncOrderService struct {
}
func NewOrderInfoService(option map[string]interface{}) *SyncOrderService {
newService := new(SyncOrderService)
return newService
}
//SyncOrderFromBestshop 接收来源于xiangmi小程序的订单数据
func (s SyncOrderService) SyncOrderFromBestshop(cmd command.CreateOrderFromBestshop) error {
var (
transactionContext, _ = factory.CreateTransactionContext(nil)
err error
)
if err = transactionContext.StartTransaction(); err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
defer func() {
transactionContext.RollbackTransaction()
}()
//检查账号是否存在
var (
orderBestshopDao *dao.OrderBestshopDao
orderExist bool
partnerRepository domain.PartnerInfoRepository
partnerData *domain.PartnerInfo
)
if orderBestshopDao, err = factory.CreateOrderBestshopDao(map[string]interface{}{
"transactionContext": transactionContext,
}); err != nil {
return lib.ThrowError(lib.TRANSACTION_ERROR, err.Error())
}
orderExist, err = orderBestshopDao.OrderExist(cmd.OrderCode)
if err != nil {
return lib.ThrowError(lib.TRANSACTION_ERROR, "orderBestshopDao.OrderExist err:"+err.Error())
}
//数据检查
if partnerRepository, err = factory.CreatePartnerInfoRepository(map[string]interface{}{
"transactionContext": transactionContext,
}); err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
partnerData, err = partnerRepository.FindOne(domain.PartnerFindOneQuery{UserId: cmd.PartnerId})
if err != nil {
e := fmt.Sprintf("未找到指定的合伙人(id=%d)数据,%s", cmd.PartnerId, err)
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, e)
}
if partnerData.CompanyId != cmd.CompanyId {
e := fmt.Sprintf("合伙人(partnerId)的公司(companyId=%d)和传递的参数中的companyId 不一致", partnerData.CompanyId)
logs.Warning(e)
}
err = transactionContext.CommitTransaction()
if err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
if orderExist {
//
logs.Warning("订单数据已存在:order_code=%s", cmd.OrderCode)
} else {
err = s.CreateOrderFromBestshop(cmd)
}
return err
}
func (s SyncOrderService) CreateOrderFromBestshop(cmd command.CreateOrderFromBestshop) error {
var (
transactionContext, _ = factory.CreateTransactionContext(nil)
err error
)
if err = transactionContext.StartTransaction(); err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
defer func() {
transactionContext.RollbackTransaction()
}()
var (
orderBestshopRepository domain.OrderBestshopRepository
orderGoodBestshopRepository domain.OrderGoodBestshopRepository
)
if orderBestshopRepository, err = factory.CreateOrderBestshopRepository(map[string]interface{}{
"transactionContext": transactionContext,
}); err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
if orderGoodBestshopRepository, err = factory.CreateOrderGoodBestshopRepository(map[string]interface{}{
"transactionContext": transactionContext,
}); err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
order := domain.OrderBestShop{
OrderCode: cmd.OrderCode,
OrderTime: cmd.OrderTime,
OrderState: cmd.OrderState,
OrderCount: cmd.OrderCount,
OrderAmount: cmd.OrderAmount,
CreateTime: time.Now(),
PartnerId: cmd.PartnerId,
BuyerName: cmd.BuyerName,
BuyerPhone: cmd.BuyerPhone,
BuyerAddress: cmd.BuyerAddress,
BuyerRemark: cmd.BuyerRemark,
BuyerId: cmd.BuyerId,
DeliveryState: cmd.DeliveryState,
DeliveryTime: cmd.DeliveryTime,
IsCopy: false,
CompanyId: cmd.CompanyId,
}
err = orderBestshopRepository.Add(&order)
if err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, "添加order_bestshop失败:"+err.Error())
}
goods := []domain.OrderGoodBestShop{}
for i := range cmd.Goods {
good := domain.OrderGoodBestShop{
Id: cmd.Goods[i].Id,
OrderId: order.Id,
Sn: cmd.Goods[i].Sn,
Bn: cmd.Goods[i].Bn,
Name: cmd.Goods[i].Name,
Price: cmd.Goods[i].Price,
Nums: cmd.Goods[i].Nums,
Amount: cmd.Goods[i].Amount,
}
err = orderGoodBestshopRepository.Add(&good)
if err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, "添加order_good失败:"+err.Error())
}
goods = append(goods, good)
}
order.Goods = goods
err = transactionContext.CommitTransaction()
if err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
if cmd.PartnerId <= 0 {
logs.Info("[SyncOrderFromBestshop] PartnerId<=0 ,不复制订单数据")
return nil
}
err = s.copyOrderBestshopToOrderBase(&order)
return err
}
//copyOrderBestshopToOrderBase 复制数据
func (s SyncOrderService) copyOrderBestshopToOrderBase(orderBestshop *domain.OrderBestShop) error {
var (
transactionContext, _ = factory.CreateTransactionContext(nil)
err error
)
if err = transactionContext.StartTransaction(); err != nil {
return lib.ThrowError(lib.TRANSACTION_ERROR, err.Error())
}
defer func() {
transactionContext.RollbackTransaction()
}()
var (
orderBaseRepository domain.OrderBaseRepository
orderGoodRepository domain.OrderGoodRepository
orderBestshopRepository domain.OrderBestshopRepository
partnerRepository domain.PartnerInfoRepository
companyRepository domain.CompanyRepository
)
if orderBaseRepository, err = factory.CreateOrderBaseRepository(map[string]interface{}{
"transactionContext": transactionContext,
}); err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
if orderGoodRepository, err = factory.CreateOrderGoodRepository(map[string]interface{}{
"transactionContext": transactionContext,
}); err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
if partnerRepository, err = factory.CreatePartnerInfoRepository(map[string]interface{}{
"transactionContext": transactionContext,
}); err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
if companyRepository, err = factory.CreateCompanyRepository(map[string]interface{}{
"transactionContext": transactionContext,
}); err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
if orderBestshopRepository, err = factory.CreateOrderBestshopRepository(map[string]interface{}{
"transactionContext": transactionContext,
}); err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
var (
partnerData *domain.PartnerInfo
companyData domain.Company
canCopyOrder bool
)
partnerData, err = partnerRepository.FindOne(domain.PartnerFindOneQuery{UserId: orderBestshop.PartnerId})
if err != nil {
e := fmt.Sprintf("未找到指定的合伙人(id=%d)数据,%s", orderBestshop.PartnerId, err)
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, e)
}
companyData, err = companyRepository.FindOne(domain.CompanyFindOneOptions{
Id: partnerData.CompanyId,
})
if err != nil {
e := fmt.Sprintf("未找到指定的合伙人的公司(partner_id=%d,company_id=%d)数据,%s", orderBestshop.PartnerId, partnerData.CompanyId, err)
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, e)
}
for _, v := range companyData.Applets {
if v.Id == BEST_SHOP_UNIONID {
canCopyOrder = true
}
}
if !canCopyOrder {
logs.Warning("公司未设置xiangmi小程序原始id; order_bestshop.id=%d", orderBestshop.Id)
return nil
}
var (
orderbase domain.OrderBase
ordergoods []domain.OrderGood
)
//TODO 添加orderBase
orderBestshop.CopyToOrderBase(&orderbase)
orderbase.CompanyId = companyData.Id
for i := range orderBestshop.Goods {
good := domain.NewOrderGood()
orderBestshop.Goods[i].CopyToOrderGood(&good)
good.CompanyId = partnerData.CompanyId
good.Compute()
good.CurrentBonusStatus.WartPayPartnerBonus(&good)
ordergoods = append(ordergoods, good)
}
orderbase.Goods = ordergoods
orderbase.PartnerId = partnerData.Partner.Id
orderbase.CompanyId = partnerData.CompanyId
orderbase.Compute()
err = orderBaseRepository.Save(&orderbase)
if err != nil {
e := fmt.Sprintf("添加order_base数据失败%s", err)
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, e)
}
for i := range ordergoods {
ordergoods[i].OrderId = orderbase.Id
}
// 添加goods
err = orderGoodRepository.Save(ordergoods)
if err != nil {
e := fmt.Sprintf("添加order_good数据失败%s", err)
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, e)
}
// 更新isCopy
orderBestshop.IsCopy = true
err = orderBestshopRepository.Edit(orderBestshop)
if err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
err = transactionContext.CommitTransaction()
if err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
return nil
}
func (s SyncOrderService) UpdateOrderFromBestshop(cmd command.CreateOrderFromBestshop) error {
var (
transactionContext, _ = factory.CreateTransactionContext(nil)
err error
)
if err = transactionContext.StartTransaction(); err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
defer func() {
transactionContext.RollbackTransaction()
}()
var (
orderBestshopRepository domain.OrderBestshopRepository
orderGoodBestshopRepository domain.OrderGoodBestshopRepository
)
if orderBestshopRepository, err = factory.CreateOrderBestshopRepository(map[string]interface{}{
"transactionContext": transactionContext,
}); err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
if orderGoodBestshopRepository, err = factory.CreateOrderGoodBestshopRepository(map[string]interface{}{
"transactionContext": transactionContext,
}); err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
var (
orderData *domain.OrderBestShop
orderGoods []domain.OrderGoodBestShop
)
orderData, err = orderBestshopRepository.FindOne(domain.OrderBestshopFindOneQuery{
OrderCode: cmd.OrderCode,
})
if err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, "获取orderBestshop(order_code=%s)数据失败,err=%s", cmd.OrderCode, err.Error())
}
orderData.OrderCode = cmd.OrderCode
orderData.OrderTime = cmd.OrderTime
orderData.OrderState = cmd.OrderState
orderData.OrderCount = cmd.OrderCount
orderData.OrderAmount = cmd.OrderAmount
orderData.PartnerId = cmd.PartnerId
orderData.BuyerName = cmd.BuyerName
orderData.BuyerPhone = cmd.BuyerPhone
orderData.BuyerAddress = cmd.BuyerAddress
orderData.BuyerRemark = cmd.BuyerRemark
orderData.BuyerId = cmd.BuyerId
orderData.DeliveryState = cmd.DeliveryState
orderData.DeliveryTime = cmd.DeliveryTime
orderData.CompanyId = cmd.CompanyId
err = orderBestshopRepository.Edit(orderData)
if err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, "编辑order_bestshop失败:"+err.Error())
}
orderGoods, err = orderGoodBestshopRepository.Find(domain.OrderGoodBestshopFindQuery{
OrderId: orderData.Id,
})
if err != nil {
e := fmt.Sprintf("获取orderGoodBestshop(order_id=%d)数据失败,err=%s", orderData.Id, err.Error())
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, e)
}
for i := range orderGoods {
var updated bool
for ii := range cmd.Goods {
if cmd.Goods[ii].Id != orderGoods[i].Id {
continue
}
orderGoods[i].Sn = cmd.Goods[ii].Sn
orderGoods[i].Bn = cmd.Goods[ii].Bn
orderGoods[i].Name = cmd.Goods[ii].Name
orderGoods[i].Price = cmd.Goods[ii].Price
orderGoods[i].Nums = cmd.Goods[ii].Nums
orderGoods[i].Amount = cmd.Goods[ii].Amount
updated = true
}
if updated {
err = orderGoodBestshopRepository.Edit(&orderGoods[i])
if err != nil {
logs.Error("更新order_good_bestshop失败:" + err.Error())
}
}
}
err = transactionContext.CommitTransaction()
if err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
return nil
}
... ...
... ... @@ -16,14 +16,21 @@ type SyncCompanyService struct{}
var _ SyncAction = (*SyncCompanyService)(nil)
type CompanyBaseApplet struct {
Name string `json:"name"`
URL string `json:"url"`
Id string `json:"id"`
}
//企业平台的公司基础数据
type CompanyBase struct {
Id int64 `json:"id"` //id
Name string `json:"name"` //公司名称名称
AdminCompanyId int `json:"admin_company_id"` //总后台的公司id
Logo string `json:"logo"` //公司图标
Remarks string `json:"remarks"` //备注
Abbreviation string `json:"abbreviation"`
Id int64 `json:"id"` //id
Name string `json:"name"` //公司名称名称
AdminCompanyId int `json:"admin_company_id"` //总后台的公司id
Logo string `json:"logo"` //公司图标
Remarks string `json:"remarks"` //备注
Abbreviation string `json:"abbreviation"` //公司简称
Applets []CompanyBaseApplet `json:"applets"` //公司对接的小程序
}
// CompanytData 企业平台发送过来的公司数据数据
... ... @@ -104,6 +111,15 @@ func (service SyncCompanyService) addCompany(data CompanytData) error {
}); err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
applets := []domain.CompanyApplets{}
for _, v := range data.Company.Applets {
app := domain.CompanyApplets{
Name: v.Name,
URL: v.URL,
Id: v.Id,
}
applets = append(applets, app)
}
comany := domain.Company{
Id: data.Company.Id,
Name: data.Company.Name,
... ... @@ -114,7 +130,9 @@ func (service SyncCompanyService) addCompany(data CompanytData) error {
//Status: data.Company.Status,
Enable: domain.CompanyEnableYes, //默认初始化值
Abbreviation: data.Company.Abbreviation,
Applets: applets,
}
err = companyRespository.Add(&comany)
if err != nil {
return fmt.Errorf("添加公司数据失败,%s", err)
... ... @@ -186,12 +204,22 @@ func (service SyncCompanyService) updateCompany(data CompanytData) error {
newUser, err = userRespository.FindOne(domain.UsersFindOneQuery{
Id: data.User.Id,
})
applets := []domain.CompanyApplets{}
for _, v := range data.Company.Applets {
app := domain.CompanyApplets{
Name: v.Name,
URL: v.URL,
Id: v.Id,
}
applets = append(applets, app)
}
oldCompany.Update(map[string]interface{}{
"Name": data.Company.Name,
"Logo": data.Company.Logo,
"Remarks": data.Company.Remarks,
"AdminCompanyId": data.Company.AdminCompanyId,
"Abbreviation": data.Company.Abbreviation,
"Applets": applets,
})
for i := range oldCompanyAdmins {
oldCompanyAdmins[i].Update(map[string]interface{}{
... ...
package constant
import (
"os"
"strings"
)
type KafkaConfig struct {
Servers []string `json:"servers"`
ConsumerId string `json:"consumerGroup"`
}
var KafkaCfg KafkaConfig
func init() {
KafkaCfg = KafkaConfig{
Servers: []string{"192.168.190.136:9092"},
ConsumerId: "partnermg_local",
}
if os.Getenv("KAFKA_HOST") != "" {
kafkaHost := os.Getenv("KAFKA_HOST")
KafkaCfg.Servers = strings.Split(kafkaHost, ";")
}
if os.Getenv("KAFKA_CONSUMER_ID") != "" {
consumerId := os.Getenv("KAFKA_CONSUMER_ID")
KafkaCfg.ConsumerId = consumerId
}
}
// "192.168.190.136:9092",
// "106.52.15.41:9092"
... ...
... ... @@ -17,6 +17,12 @@ const (
CompanyEnableNo int8 = 2
)
type CompanyApplets struct {
Name string `json:"name"`
URL string `json:"url"`
Id string `json:"id"`
}
// 公司信息
type Company struct {
// 唯一标识
... ... @@ -40,13 +46,10 @@ type Company struct {
// 更新时间
UpdateAt time.Time `json:"updateAt"`
// 删除时间
DeleteAt time.Time `json:"deleteAt"`
DeleteAt time.Time `json:"deleteAt"`
Applets []CompanyApplets `json:"applets"`
}
// func (c Company) StatusIsOk() bool {
// return c.Status == companyStatusUsable
// }
func (c Company) EnableIsOk() bool {
return c.Enable == CompanyEnableYes
}
... ... @@ -76,6 +79,9 @@ func (c *Company) Update(m map[string]interface{}) error {
if v, ok := m["Abbreviation"]; ok {
c.Abbreviation = v.(string)
}
if v, ok := m["Applets"]; ok {
c.Applets = v.([]CompanyApplets)
}
return nil
}
... ...
package event
const (
//订单分红因为货品的数量变动而发送改变
UPDATE_BONUS_BY_GOOD_NUMBER_EVENT string = "UpdateBonusByGoodNumber"
//订单分红因为合伙人分红比例变动而发送改变
UPDATE_BONUS_BY_PARTENT_BONUS_PERCENT_EVENT string = "UpdateBounsByPartnerBonusPercent"
//更新订单的备注
UPDATE_ORDER_REMARK string = "UpdateOrderRemark"
)
//UpdateBonusByGoodNumber
//订单分红因为货品的数量变动而发送改变
type UpdateBonusByGoodNumber struct {
//订单id
OrderId int64
//管理员id
AdminId int64
//货品id
GoodId int64
//货品名称
GoodName string
//旧的货品数量
FormerNumber string
//新的货品数量
NewNumber string
//旧的货品总额
FormerAmount string
//新的货品总额
NewAmount string
}
//EventType 事件名称
func (m UpdateBonusByGoodNumber) EventType() string {
return UPDATE_BONUS_BY_GOOD_NUMBER_EVENT
}
//UpdateBounsByPartnerBonusPercent
//订单分红因为合伙人分红比例变动而发送改变
type UpdateBounsByPartnerBonusPercent struct {
//订单id
OrderId int64
//管理员id
AdminId int64
//货品id
GoodId int64
//货品名称
GoodName string
//旧的合伙人分红比例
FormerPartnerBonusPercent string
//新的合伙人分红比例
NewPartnerBonusPercent string
//旧的合伙人分红
FormerPartnerBonus string
//新的合伙人分红
NewPartnerBonus string
}
//EventType 事件名称
func (m UpdateBounsByPartnerBonusPercent) EventType() string {
return UPDATE_BONUS_BY_PARTENT_BONUS_PERCENT_EVENT
}
//UpdateOrderRemark 更新订单的备注
type UpdateOrderRemark struct {
//订单id
OrderId int64
//管理员id
AdminId int64
//旧的备注
FormerRemark string
//新的备注
NewRemark string
}
//EventType 事件名称
func (m UpdateOrderRemark) EventType() string {
return UPDATE_ORDER_REMARK
}
... ...
package event
const (
//支付订单中货品的分红
PAY_ORDER_GOOD_BONUS_EVENT string = "PayOrderGoodBonus"
)
//PayOrderGoodBonus
//事件:支付订单中货品的分红
type PayOrderGoodBonus struct {
//订单id
OrderId int64
//货品名称
GoodName string
//订单中的货品id
GoodId int64
//管理员id
AdminId int64
//
PartnerBonus float64
}
func (p PayOrderGoodBonus) EventType() string {
return PAY_ORDER_GOOD_BONUS_EVENT
}
... ...
... ... @@ -7,10 +7,24 @@ import (
)
const (
OrderReal = iota + 1 //实发订单
OrderIntention //意向订单
OrderReal = iota + 1 //实发订单
OrderIntention //意向订单
OrderTypeBestShop //来自小程序海鲜干货的订单
)
func GetOrderBaseTypeName(orderType int) string {
var name string
switch orderType {
case OrderReal:
name = "自建订单"
case OrderTypeBestShop:
name = "小程序订单"
case OrderIntention:
name = "意向订单"
}
return name
}
const (
OrderDisableNot = iota //订单未关闭
OrderDisableYes //订单已关闭
... ... @@ -24,6 +38,41 @@ type Buyer struct {
ContactInfo string `json:"contactInfo"`
//收获地址
ShippingAddress string `json:"shippingAddress"`
//买家备注
Remark string `json:"remark"`
}
type OrderCompute struct {
//合伙人应收分红
PlanPartnerBonus float64 `json:"planPartnerBonus"`
//调整后的合伙人应收分红 (初始值=-1);
//业务判定时0是有效值,
//所以用空串表示无值,转换到数据库中为负值
UsePartnerBonus float64 `json:"usePartnerBonus"`
//合伙人已收分红
PartnerBonusHas float64 `json:"partnerBonusHas"`
//合伙人未收分红
PartnerBonusNot float64 `json:"partnerBonusNot"`
//合伙人分红支出
PartnerBonusExpense float64 `json:"partnerBonusExpense"`
//业务员抽成
SalesmanBonus float64 `json:"salesmanBonus"`
//预计的订单内货品总数
PlanOrderCount int `json:"planOrderCount"`
//预计的订单的总金额
PlanOrderAmount float64 `json:"planOrderAmount"`
//按需使用的订单内货品总数 (初始值=-1)
//业务判定时0是有效值,
//所以用空串表示无值,转换到数据库中为负值
UseOrderCount int `json:"useOrderCount"`
//按需使用的订单内货总金额 (初始值=-1)
//业务判定时0是有效值,
//所以用空串表示无值,转换到数据库中为负值
UseOrderAmount float64 `json:"useOrderAmount"`
}
type OrderBaseRemark struct {
RemarkBonus string `json:"remarkBonus"`
}
//OrderBase 订单基础
... ... @@ -61,35 +110,87 @@ type OrderBase struct {
BonusStatus int `json:"bonusStatus"`
//公司
CompanyId int64 `json:"companyId"`
//数据来源
DataFrom OrderDataFrom `json:"dataFrom"`
//备注
Remark OrderBaseRemark `json:"remark"`
}
type OrderCompute struct {
//合伙人应收分红
PlanPartnerBonus float64 `json:"planPartnerBonus"`
//调整后的合伙人应收分红 (初始值=-1);
//业务判定时0是有效值,
//所以用空串表示无值,转换到数据库中为负值
UsePartnerBonus float64 `json:"usePartnerBonus"`
//合伙人已收分红
PartnerBonusHas float64 `json:"partnerBonusHas"`
//合伙人未收分红
PartnerBonusNot float64 `json:"partnerBonusNot"`
//合伙人分红支出
PartnerBonusExpense float64 `json:"partnerBonusExpense"`
//业务员抽成
SalesmanBonus float64 `json:"salesmanBonus"`
//预计的订单内货品总数
PlanOrderCount int `json:"planOrderCount"`
//预计的订单的总金额
PlanOrderAmount float64 `json:"planOrderAmount"`
//按需使用的订单内货品总数 (初始值=-1)
//业务判定时0是有效值,
//所以用空串表示无值,转换到数据库中为负值
UseOrderCount int `json:"useOrderCount"`
//按需使用的订单内货总金额 (初始值=-1)
//业务判定时0是有效值,
//所以用空串表示无值,转换到数据库中为负值
UseOrderAmount float64 `json:"useOrderAmount"`
//GetCurrentPartnerBonus 获取当前合伙人应收分红
func (order *OrderBase) GetCurrentPartnerBonus() float64 {
if order.OrderCompute.UsePartnerBonus >= 0 {
return order.OrderCompute.UsePartnerBonus
}
return order.OrderCompute.PlanPartnerBonus
}
//GetCurrentOrderCount 获取当前订单商品总数
func (order *OrderBase) GetCurrentOrderCount() int {
if order.OrderCompute.UseOrderCount >= 0 {
return order.OrderCompute.UseOrderCount
}
return order.OrderCompute.PlanOrderCount
}
// GetCurrentOrderAmount 获取当前订单的总额
func (order *OrderBase) GetCurrentOrderAmount() float64 {
if order.OrderCompute.UseOrderAmount >= 0 {
return order.OrderCompute.UseOrderAmount
}
return order.OrderCompute.PlanOrderAmount
}
//Update 更新订单数据
//orderData 订单数据
//goodsMap 货品的数据,以货品的id做键,map[id]map[string]interface{}
func (order *OrderBase) Update(orderData map[string]interface{}, goodsMap map[int64]map[string]interface{}) error {
if v, ok := orderData["OrderCode"]; ok {
order.OrderCode = v.(string)
}
if v, ok := orderData["DeliveryCode"]; ok {
order.DeliveryCode = v.(string)
}
if v, ok := orderData["Buyer"]; ok {
order.Buyer = v.(Buyer)
}
if v, ok := orderData["SalesmanBonusPercent"]; ok {
order.SalesmanBonusPercent = v.(float64)
}
if v, ok := orderData["DeliveryTime"]; ok {
order.DeliveryTime = v.(time.Time)
}
if v, ok := orderData["IsDisable"]; ok {
order.IsDisable = v.(int)
}
for i := range order.Goods {
goodId := order.Goods[i].Id
if _, ok := goodsMap[goodId]; !ok {
continue
}
if err := order.Goods[i].Update(goodsMap[goodId]); err != nil {
return err
}
}
err := order.Compute()
return err
}
func (order *OrderBase) AddGoods(goods []OrderGood) {
order.Goods = append(order.Goods, goods...)
order.Compute()
}
func (order *OrderBase) DeleteGoods(goodIds []int64) {
var newGoods []OrderGood
for i := range order.Goods {
for _, goodId := range goodIds {
order.Goods[i].Id = goodId
continue
}
newGoods = append(newGoods, order.Goods[i])
}
order.Goods = newGoods
order.Compute()
}
//Compute 数据汇总核算
... ...
package domain
import "time"
//OrderBestShop 来源海鲜干货的订单
type OrderBestShop struct {
Id int64 `json:"id"`
//订单编号
OrderCode string `json:"orderCode"`
//下单时间
OrderTime string `json:"orderTime"`
//订单状态
OrderState int8 `json:"orderState"`
//发货状态
DeliveryState int8 `json:"deliveryState"`
//买家名称
BuyerName string `json:"buyerName"`
//买家电话
BuyerPhone string `json:"buyerPhone"`
//买家地址
BuyerAddress string `json:"buyerAddress"`
//买家备注
BuyerRemark string `json:"buyerRemark"`
//
BuyerId int64 `json:"buyerId"`
//商品总数
OrderCount int `json:"orderCount"`
//d订单总额
OrderAmount float64 `json:"orderAmount"`
//发货时间
DeliveryTime string `json:"deliveryTime"`
//创建时间
CreateTime time.Time `json:"createTime"`
PartnerId int64 `json:"partnerId"`
Goods []OrderGoodBestShop `json:"goods"`
//是否将数据同步到 order_base ,order_good
IsCopy bool `json:"isCopy"`
CompanyId int64 `json:"companyId"`
}
func (order OrderBestShop) CopyToOrderBase(o *OrderBase) {
o.Buyer = Buyer{
BuyerName: order.BuyerName,
ContactInfo: order.BuyerPhone,
ShippingAddress: order.BuyerAddress,
Remark: order.BuyerRemark,
}
o.DataFrom = OrderDataFrom{
Platform: OrderDataFromBestShop,
DataId: order.Id,
}
o.OrderType = OrderTypeBestShop
o.OrderCode = order.OrderCode
o.OrderCompute.PlanOrderAmount = order.OrderAmount
o.OrderCompute.PlanOrderCount = order.OrderCount
o.DeliveryTime, _ = time.Parse("2006-01-02 15:04:05", order.DeliveryTime)
return
}
type OrderBestshopFindOneQuery struct {
OrderId int64
OrderCode string
}
type OrderBestshopRepository interface {
Add(order *OrderBestShop) error
Edit(order *OrderBestShop) error
FindOne(qureyOptions OrderBestshopFindOneQuery) (*OrderBestShop, error)
}
//OrderGoodBestShop 订单明细
type OrderGoodBestShop struct {
Id int64 `json:"id"`
//订单id
OrderId int64 `json:"orderId"`
//货品编号
Sn string `json:"sn"`
//商品编号
Bn string `json:"bn"`
//货品名称
Name string `json:"name"`
//单价
Price float64 `json:"price"`
//货品数量
Nums int `json:"nums"`
//订单总价
Amount float64 `json:"amount"`
}
func (good OrderGoodBestShop) CopyToOrderGood(g *OrderGood) {
g.DataFrom = OrderDataFrom{
Platform: OrderDataFromBestShop,
DataId: good.Id,
}
g.GoodName = good.Name
g.Price = good.Price
g.PlanGoodNumber = good.Nums
g.GoodCompute.PlanAmount = good.Amount
g.BonusStatus = OrderGoodWaitPay
g.CurrentBonusStatus = OrderGoodBonusWaitPay{}
return
}
type OrderGoodBestshopFindQuery struct {
OrderId int64
}
type OrderGoodBestshopRepository interface {
Add(order *OrderGoodBestShop) error
Edit(good *OrderGoodBestShop) error
Find(qureyOptions OrderGoodBestshopFindQuery) ([]OrderGoodBestShop, error)
}
... ...
package domain
const (
//海鲜干货订单
OrderDataFromBestShop string = "bestshop"
)
//OrderDataFrom 订单数据来源
type OrderDataFrom struct {
Platform string `json:"platform"` //订单数据来源平台
DataId int64 `json:"dataId"` //订单数据id标识
}
... ...
... ... @@ -22,6 +22,32 @@ const (
OrderGoodHasPay int = 2
)
//GoodCompute 货品中计算值
type GoodCompute struct {
//预计的货品总额
PlanAmount float64 `json:"planAmount"`
//调整后的货品总额 (初始值=-1)
//业务判定时0是有效值,
//所以用空串表示无值,转换到数据库中为负值
UseAmount float64 `json:"useAmount"`
//预计的合伙人分红
PlanPartnerBonus float64 `json:"planPartnerBonus"`
//合伙人应收分红调整 (初始值=-1),
//业务判定时0是有效值,
//所以用空串表示无值,转换到数据库中为负值
UsePartnerBonus float64 `json:"usePartnerBonus"`
//合伙人已收分红
PartnerBonusHas float64 `json:"partnerBonusHas"`
//合伙人未收分红
PartnerBonusNot float64 `json:"partnerBonusNot"`
//合伙人分红支出
PartnerBonusExpense float64 `json:"partnerBonusExpense"`
}
type OrderGoodRemarkReason struct {
ModifyGoodNumber string `json:"modifyGoodNumber"` //货品数量变更的理由
ModifyPartnerBonusPercent string `json:"modifyPartnerBonusPercent"` //合伙人分红比例变更的理由
}
//OrderGood 订单中的货品
type OrderGood struct {
//货品id
... ... @@ -49,28 +75,61 @@ type OrderGood struct {
///核算订单相关数据
GoodCompute GoodCompute `json:"goodCompute"`
//公司
CompanyId int64
CompanyId int64 `json:"companyId"`
//原因备注
RemarkReason OrderGoodRemarkReason `json:"remarkReason"`
DataFrom OrderDataFrom `json:"data_from"`
}
type GoodCompute struct {
//预计的货品总额
PlanAmount float64 `json:"planAmount"`
//调整后的货品总额 (初始值=-1)
//业务判定时0是有效值,
//所以用空串表示无值,转换到数据库中为负值
UseAmount float64 `json:"useAmount"`
//预计的合伙人分红
PlanPartnerBonus float64 `json:"planPartnerBonus"`
//合伙人应收分红调整 (初始值=-1),
//业务判定时0是有效值,
//所以用空串表示无值,转换到数据库中为负值
UsePartnerBonus float64 `json:"usePartnerBonus"`
//合伙人已收分红
PartnerBonusHas float64 `json:"partnerBonusHas"`
//合伙人未收分红
PartnerBonusNot float64 `json:"partnerBonusNot"`
//合伙人分红支出
PartnerBonusExpense float64 `json:"partnerBonusExpense"`
//GetCurrentGoodNumber 获取当前的商品数量
func (good OrderGood) GetCurrentGoodNumber() int {
if good.UseGoodNumber >= 0 {
return good.UseGoodNumber
}
return good.PlanGoodNumber
}
//GetCurrentAmount 获取当前的商品总额
func (good OrderGood) GetCurrentAmount() float64 {
if good.GoodCompute.UseAmount >= 0 {
return good.GoodCompute.UseAmount
}
return good.GoodCompute.PlanAmount
}
//GetCurrentAmount 获取当前的商品合伙人分红
func (good OrderGood) GetCurrentPartnerBonus() float64 {
if good.GoodCompute.UsePartnerBonus >= 0 {
return good.GoodCompute.UsePartnerBonus
}
return good.GoodCompute.PlanPartnerBonus
}
//Update 更新商品相关的数据
func (good *OrderGood) Update(m map[string]interface{}) error {
if v, ok := m["GoodName"]; ok {
good.GoodName = v.(string)
}
if v, ok := m["PlanGoodNumber"]; ok {
good.PlanGoodNumber = v.(int)
}
if v, ok := m["UseGoodNumber"]; ok {
good.UseGoodNumber = v.(int)
}
if v, ok := m["Price"]; ok {
good.Price = v.(float64)
}
if v, ok := m["PartnerBonusPercent"]; ok {
good.PartnerBonusPercent = v.(float64)
}
if v, ok := m["Remark"]; ok {
good.Remark = v.(string)
}
if v, ok := m["RemarkReason"]; ok {
good.RemarkReason = v.(OrderGoodRemarkReason)
}
err := good.Compute()
return err
}
//OrderGoodBonusWaitPay 货品分红待支付
... ... @@ -95,6 +154,7 @@ func (waitPay OrderGoodBonusWaitPay) WartPayPartnerBonus(good *OrderGood) error
good.GoodCompute.PartnerBonusNot = good.GoodCompute.UsePartnerBonus
}
good.CurrentBonusStatus = OrderGoodBonusWaitPay{}
good.BonusStatus = OrderGoodWaitPay
return nil
}
... ... @@ -112,6 +172,7 @@ func (waitPay OrderGoodBonusWaitPay) PayPartnerBonus(good *OrderGood) error {
good.GoodCompute.PartnerBonusExpense = 0
good.GoodCompute.PartnerBonusNot = 0
good.CurrentBonusStatus = OrderGoodBonusHasPay{}
good.BonusStatus = OrderGoodHasPay
return nil
}
... ... @@ -125,6 +186,7 @@ func (hasPay OrderGoodBonusHasPay) PayPartnerBonus(good *OrderGood) error {
good.GoodCompute.PartnerBonusExpense = good.GoodCompute.PartnerBonusHas - good.GoodCompute.UsePartnerBonus
}
good.CurrentBonusStatus = OrderGoodBonusHasPay{}
good.BonusStatus = OrderGoodHasPay
return nil
}
... ... @@ -135,8 +197,9 @@ func (hasPay OrderGoodBonusHasPay) WartPayPartnerBonus(good *OrderGood) error {
//NewOrderGood 初始值设定
func NewOrderGood() OrderGood {
return OrderGood{
UseGoodNumber: -1,
BonusStatus: OrderGoodWaitPay,
UseGoodNumber: -1,
BonusStatus: OrderGoodWaitPay,
PartnerBonusPercent: -1,
GoodCompute: GoodCompute{
UsePartnerBonus: -1,
UseAmount: -1,
... ... @@ -150,9 +213,19 @@ func (good *OrderGood) Compute() error {
//计算预计货品总值
//计算预计合伙人分红
price := decimal.NewFromFloat(good.Price)
planamount := price.Mul(decimal.NewFromInt(int64(good.PlanGoodNumber))) //price*planGoodNumber
planGoodNumber := good.PlanGoodNumber
if good.PlanGoodNumber < 0 {
planGoodNumber = 0
}
planamount := price.Mul(decimal.NewFromInt(int64(planGoodNumber))) //price*planGoodNumber
var partnerBonusPercent float64
if good.PartnerBonusPercent < 0 {
partnerBonusPercent = 0
} else {
partnerBonusPercent = good.PartnerBonusPercent
}
//price*useGoodNumber
planPartnerBonus := planamount.Mul(decimal.NewFromFloat(good.PartnerBonusPercent)).Div(decimal.NewFromInt(100)) //price*planGoodNumber*PartnerBonusPercent
planPartnerBonus := planamount.Mul(decimal.NewFromFloat(partnerBonusPercent)).Div(decimal.NewFromInt(100)) //price*planGoodNumber*PartnerBonusPercent
good.GoodCompute.PlanAmount, _ = planamount.Round(2).BigFloat().Float64()
good.GoodCompute.PlanPartnerBonus, _ = planPartnerBonus.Round(2).BigFloat().Float64()
if good.UseGoodNumber < 0 {
... ... @@ -162,8 +235,8 @@ func (good *OrderGood) Compute() error {
} else {
//计算调整后的货品总值
//计算调整后的合伙人分红
useamount := price.Mul(decimal.NewFromInt(int64(good.UseGoodNumber))) //price*useGoodNumber/price*useGoodNumber
usePartnerBonus := useamount.Mul(decimal.NewFromFloat(good.PartnerBonusPercent)).Div(decimal.NewFromInt(100)) //price*useGoodNumber*PartnerBonusPercent
useamount := price.Mul(decimal.NewFromInt(int64(good.UseGoodNumber))) //price*useGoodNumber/price*useGoodNumber
usePartnerBonus := useamount.Mul(decimal.NewFromFloat(partnerBonusPercent)).Div(decimal.NewFromInt(100)) //price*useGoodNumber*PartnerBonusPercent
good.GoodCompute.UsePartnerBonus, _ = usePartnerBonus.Round(2).BigFloat().Float64()
good.GoodCompute.UseAmount, _ = useamount.Round(2).BigFloat().Float64()
}
... ... @@ -177,9 +250,12 @@ type OrderGoodFindQuery struct {
Limit int
CompanyId int64
}
type OrderGoodFindOneQuery struct {
GoodId int64
}
type OrderGoodRepository interface {
Save(order []OrderGood) error
Find(queryOptions OrderGoodFindQuery) ([]OrderGood, int, error)
FindOne(queryOptions OrderGoodFindOneQuery) (OrderGood, error)
Remove(orderid int64, companyId int64, ids ...int64) error
}
... ...
package domain
import "time"
// 日志数据来源
const (
//管理后台
ORDER_LOG_FROM string = "web_admin"
)
//操作人员的类型
const (
//操作人类型
ORDER_LOG_OPERATOR_ADMIN string = "admin"
)
//OrderLogDescript 描述日志内容
type OrderLogDescript struct {
Title string `json:"title"` //标题
Item string `json:"item"` //修改的项目
Action []string `json:"action"` //执行的操作
}
//OrderLog 订单修改记录
type OrderLog struct {
Id int64
OrderId int64 `json:"orderId"` //订单id
GoodId int64 `json:"goodId"`
AlterTime time.Time `json:"alterTime"` //时间
Operator string `json:"operator"` //操作人员
OperatorId int64 `json:"operatorId"` //操作人员Id
OperatorType string `json:"operatorType"` //操作人员的类型
LogAction string `json:"logAction"` //执行动作
Descript []OrderLogDescript `json:"descript"` //描述日志内容
DataFrom string `json:"dataFrom"` //修改操作的来源:"web_admin"
}
type OrderLogFindQuery struct {
OrderId int64 `json:"orderId"`
}
type OrderLogRepository interface {
Add(*OrderLog) error
Find(queryOptions OrderLogFindQuery) ([]OrderLog, error)
}
... ...
package domain
import (
"errors"
"fmt"
)
//类型为(orderType=OrderTypeBestShop) 海鲜干货的订单分红
//因页面上的对于该类型的订单分红状态处理方式 有别于原有的其他类型(OrderReal),所以单独提取出来
// OrderGoodWithBestshopBonusStatus 支付状态
type OrderGoodWithBestshopBonusStatus interface {
OrderGoodBonusStatus
UpdateOrderGoodNumber(good *OrderGood, number int) error
UpdatePertnerBonusPercent(good *OrderGood, percent float64) error
}
//OrderGoodWithBestshop 处理订单中商品的分红相关数据
type OrderGoodWithBestshop struct {
currentBonusStatus OrderGoodWithBestshopBonusStatus
}
func (o *OrderGoodWithBestshop) UpdateBonusByGoodNumber(good *OrderGood, number int) error {
o.reset(good)
if good.PlanGoodNumber < number {
return fmt.Errorf("修改商品数量的值不能大于初始值%d", good.PlanGoodNumber)
}
err := o.currentBonusStatus.UpdateOrderGoodNumber(good, number)
return err
}
func (o *OrderGoodWithBestshop) UpdateBonusByPertnerBonusPercent(good *OrderGood, percent float64) error {
o.reset(good)
err := o.currentBonusStatus.UpdatePertnerBonusPercent(good, percent)
return err
}
func (o *OrderGoodWithBestshop) PayPartnerBonus(good *OrderGood) error {
o.currentBonusStatus = OrderGoodBonusBestshopHasPay{}
err := good.Compute()
if err != nil {
return errors.New("核算商品数据失败" + err.Error())
}
err = good.CurrentBonusStatus.PayPartnerBonus(good)
return err
}
func (o *OrderGoodWithBestshop) reset(good *OrderGood) {
switch good.BonusStatus {
case OrderGoodWaitPay:
o.currentBonusStatus = OrderGoodBonusBestshopWaitPay{}
case OrderGoodHasPay:
o.currentBonusStatus = OrderGoodBonusBestshopHasPay{}
}
return
}
//OrderGoodBonusBestshopWaitPay 货品支付状态:待支付
type OrderGoodBonusBestshopWaitPay struct {
OrderGoodBonusWaitPay
}
var _ OrderGoodWithBestshopBonusStatus = (*OrderGoodBonusBestshopWaitPay)(nil)
func (waitPay OrderGoodBonusBestshopWaitPay) UpdateOrderGoodNumber(good *OrderGood, number int) error {
good.UseGoodNumber = number
//待支付状态计算
err := good.Compute()
if err != nil {
return errors.New("核算商品数据失败" + err.Error())
}
err = good.CurrentBonusStatus.WartPayPartnerBonus(good)
return err
}
func (waitPay OrderGoodBonusBestshopWaitPay) UpdatePertnerBonusPercent(good *OrderGood, percent float64) error {
good.PartnerBonusPercent = percent
//待支付状态计算
err := good.Compute()
if err != nil {
return errors.New("核算商品数据失败" + err.Error())
}
err = good.CurrentBonusStatus.WartPayPartnerBonus(good)
return err
}
//OrderGoodBonusBestshopHasPay 货品支付状态:已支付
type OrderGoodBonusBestshopHasPay struct {
OrderGoodBonusHasPay
}
var _ OrderGoodWithBestshopBonusStatus = (*OrderGoodBonusBestshopHasPay)(nil)
func (hasPay OrderGoodBonusBestshopHasPay) UpdateOrderGoodNumber(good *OrderGood, number int) error {
return errors.New("已支付分红的货品订单,不能修改货品数量")
}
func (hasPay OrderGoodBonusBestshopHasPay) UpdatePertnerBonusPercent(good *OrderGood, percent float64) error {
return errors.New("已支付分红的货品订单,不能修改合伙人分红比例")
}
... ...
package service
import (
coreDomain "github.com/linmadan/egglib-go/core/domain"
)
type OrderBonusService interface {
coreDomain.DomainEventPublisher
UpdateBounsByGoodNumber(orderId int64, adminId int64, goodid int64, goodWithNumber int, reason string) error
UpdateBounsByPartnerBonusPercent(orderId int64, adminId int64, goodId int64, partnerPercent float64, reason string) error
UpdateOrderRemarkBonus(orderId int64, adminId int64, remark string) error
PayOrderGoodBonus(orderId int64, goodId int64, adminId int64) error
}
... ...
... ... @@ -3,6 +3,9 @@ package dao
import (
"fmt"
"github.com/go-pg/pg/v10/orm"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/domain"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/infrastructure/pg/models"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/infrastructure/pg/transaction"
)
... ... @@ -22,7 +25,7 @@ func NewOrderBaseDao(transactionContext *transaction.TransactionContext) (*Order
}
func (dao OrderBaseDao) OrderCodeExist(code string, notId ...int64) (bool, error) {
tx := dao.transactionContext.PgDd
tx := dao.transactionContext.GetDB()
m := &models.OrderBase{}
query := tx.Model(m).Where("order_code=?", code)
if len(notId) > 0 {
... ... @@ -33,7 +36,7 @@ func (dao OrderBaseDao) OrderCodeExist(code string, notId ...int64) (bool, error
}
func (dao OrderBaseDao) DeliveryCodeExist(code string, companyId int64, notId ...int64) (bool, error) {
tx := dao.transactionContext.PgDd
tx := dao.transactionContext.GetDB()
m := &models.OrderBase{}
query := tx.Model(m).Where("delivery_code=?", code).Where("company_id=?", companyId)
if len(notId) > 0 {
... ... @@ -42,3 +45,32 @@ func (dao OrderBaseDao) DeliveryCodeExist(code string, companyId int64, notId ..
ok, err := query.Exists()
return ok, err
}
//OrderListByCondition 根据条件获取订单分红列表
//orderType 订单类型
//partnerOrCode 合伙人姓名或订单号或发货单号
func (dao OrderBaseDao) OrderBonusListByCondition(companyId int64, orderType int, partnerOrCode string, limit, offset int) ([]models.OrderBase, int, error) {
tx := dao.transactionContext.GetDB()
var orders []models.OrderBase
query := tx.Model(&orders).Where("order_base.company_id=?", companyId)
if orderType > 0 {
query = query.Where("order_base.order_type=?", orderType)
} else {
query = query.Where("order_base.order_type<>?", domain.OrderIntention)
}
if len(partnerOrCode) > 0 {
query = query.Join("LEFT JOIN partner_info as p ON order_base.partner_id=p.id").
WhereGroup(func(q *orm.Query) (*orm.Query, error) {
q = q.WhereOr("order_base.order_code like ? ", "%"+partnerOrCode+"%").
WhereOr("order_base.delivery_code like ? ", "%"+partnerOrCode+"%").
WhereOr("p.partner_name like ? ", "%"+partnerOrCode+"%")
return q, nil
})
}
query = query.Order("order_base.update_time DESC").
Offset(offset).
Limit(limit)
cnt, err := query.SelectAndCount()
return orders, cnt, err
}
... ...
package dao
import (
"fmt"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/infrastructure/pg/models"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/infrastructure/pg/transaction"
)
type OrderBestshopDao struct {
transactionContext *transaction.TransactionContext
}
func NewOrderBestshopDao(transactionContext *transaction.TransactionContext) (*OrderBestshopDao, error) {
if transactionContext == nil {
return nil, fmt.Errorf("transactionContext参数不能为nil")
} else {
return &OrderBestshopDao{
transactionContext: transactionContext,
}, nil
}
}
func (dao OrderBestshopDao) OrderExist(orderCode string) (bool, error) {
tx := dao.transactionContext.GetDB()
m := models.OrderBestshop{}
query := tx.Model(&m).Where("order_code=?", orderCode)
ok, err := query.Exists()
return ok, err
}
... ...
... ... @@ -16,6 +16,7 @@ func NewBusinessBonusService(tcx *transaction.TransactionContext) *BusinessBonus
transactionContext: tcx,
}
}
func (srv BusinessBonusService) EnableOrDisable(parntnerId int64) error {
var (
bonusDao, _ = dao.NewBusinessBonusDao(srv.transactionContext)
... ...
package domainService
import (
"errors"
"fmt"
coreDomain "github.com/linmadan/egglib-go/core/domain"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/domain"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/domain/event"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/domain/service"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/infrastructure/pg/transaction"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/infrastructure/repository"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/lib"
)
//OrderBonusServices 处理订单分红的相关操作
type OrderBonusService struct {
coreDomain.BaseEventPublisher
transactionContext *transaction.TransactionContext
}
var _ service.OrderBonusService = (*OrderBonusService)(nil)
func NewOrderBonusService(tcx *transaction.TransactionContext) *OrderBonusService {
return &OrderBonusService{
transactionContext: tcx,
}
}
//UpdateBounsWithGoodNumber 分红时,因修改订单中商品的数量发生分红变动
//目前只处理 xiangmi的订单 即 order_type = OrderTypeBestShop (3)
func (serve *OrderBonusService) UpdateBounsByGoodNumber(orderId int64, adminId int64, goodId int64, goodWithNumber int, reason string) error {
var (
userRepository domain.UsersRepository
orderBaseReponsitory domain.OrderBaseRepository
orderGoodRepository domain.OrderGoodRepository
oldOrder *domain.OrderBase
adminUser domain.Users
err error
)
if orderGoodRepository, err = repository.NewOrderGoodRepository(serve.transactionContext); err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
if orderBaseReponsitory, err = repository.NewOrderBaseRepository(serve.transactionContext); err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
if userRepository, err = repository.NewUsersRepository(serve.transactionContext); err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
oldOrder, err = orderBaseReponsitory.FindOne(domain.OrderBaseFindOneQuery{OrderId: orderId})
if err != nil {
e := fmt.Sprintf("获取订单(id=%d)数据失败,%s", orderId, err)
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, e)
}
if oldOrder.OrderType != domain.OrderTypeBestShop {
return lib.ThrowError(lib.BUSINESS_ERROR, "订单类型错误")
}
oldOrder.Goods, _, err = orderGoodRepository.Find(domain.OrderGoodFindQuery{OrderId: orderId})
if err != nil {
e := fmt.Sprintf("获取订单(id=%d)中的货品数据失败,%s", orderId, err)
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, e)
}
adminUser, err = userRepository.FindOne(domain.UsersFindOneQuery{Id: adminId})
if err != nil {
e := fmt.Sprintf("获取管理员用户(id=%d)数据失败,%s", adminId, err)
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, e)
}
if ok := adminUser.InCompany(oldOrder.CompanyId); !ok {
return lib.ThrowError(lib.BUSINESS_ERROR, "用户不能更新非自己公司的订单")
}
var (
updateGood domain.OrderGood
formerNumber int
formerAmount float64
newAmount float64
goodExist bool
)
for i := range oldOrder.Goods {
if oldOrder.Goods[i].Id != goodId {
continue
}
updateGood = oldOrder.Goods[i]
formerNumber = updateGood.GetCurrentGoodNumber()
formerAmount = updateGood.GetCurrentAmount()
err := new(domain.OrderGoodWithBestshop).
UpdateBonusByGoodNumber(&updateGood, goodWithNumber)
if err != nil {
return lib.ThrowError(lib.BUSINESS_ERROR, err.Error())
}
updateGood.RemarkReason.ModifyGoodNumber = reason
newAmount = updateGood.GetCurrentAmount()
goodExist = true
oldOrder.Goods[i] = updateGood
}
if !goodExist {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, "未找到指定的货品")
}
if err := oldOrder.Compute(); err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, "核算订单数据失败"+err.Error())
}
//更新商品数据
err = orderGoodRepository.Save([]domain.OrderGood{updateGood})
if err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, "更新商品数据失败,"+err.Error())
}
//更新订单数据
err = orderBaseReponsitory.Save(oldOrder)
if err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, "更新订单数据失败,"+err.Error())
}
if formerNumber != goodWithNumber {
// 事件发布
modifyEvent := event.UpdateBonusByGoodNumber{
OrderId: oldOrder.Id,
AdminId: adminId,
GoodId: oldOrder.Id,
GoodName: updateGood.GoodName,
FormerNumber: fmt.Sprint(formerNumber),
NewNumber: fmt.Sprint(goodWithNumber),
FormerAmount: fmt.Sprint(formerAmount),
NewAmount: fmt.Sprint(newAmount),
}
if err = serve.Publish(modifyEvent); err != nil {
return err
}
}
return nil
}
//UpdateBounsByPartnerBonusPercent 分红时,因修改订单中商品的合伙人分行比例发生分红变动
////目前只处理 xiangmi的订单 即 order_type = OrderTypeBestShop (3)
func (serve *OrderBonusService) UpdateBounsByPartnerBonusPercent(orderId int64, adminId int64, goodId int64, partnerPercent float64, reason string) error {
var (
userRepository domain.UsersRepository
orderBaseReponsitory domain.OrderBaseRepository
orderGoodRepository domain.OrderGoodRepository
oldOrder *domain.OrderBase
adminUser domain.Users
err error
)
if orderGoodRepository, err = repository.NewOrderGoodRepository(serve.transactionContext); err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
if orderBaseReponsitory, err = repository.NewOrderBaseRepository(serve.transactionContext); err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
if userRepository, err = repository.NewUsersRepository(serve.transactionContext); err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
oldOrder, err = orderBaseReponsitory.FindOne(domain.OrderBaseFindOneQuery{OrderId: orderId})
if err != nil {
e := fmt.Sprintf("获取订单(id=%d)数据失败,%s", orderId, err)
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, e)
}
if oldOrder.OrderType != domain.OrderTypeBestShop {
return lib.ThrowError(lib.BUSINESS_ERROR, "订单类型错误")
}
oldOrder.Goods, _, err = orderGoodRepository.Find(domain.OrderGoodFindQuery{OrderId: orderId})
if err != nil {
e := fmt.Sprintf("获取订单中(id=%d)的货品数据失败,%s", orderId, err)
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, e)
}
adminUser, err = userRepository.FindOne(domain.UsersFindOneQuery{Id: adminId})
if err != nil {
e := fmt.Sprintf("获取管理员用户(id=%d)数据失败,%s", adminId, err)
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, e)
}
if ok := adminUser.InCompany(oldOrder.CompanyId); !ok {
return lib.ThrowError(lib.BUSINESS_ERROR, "用户不能更新非自己公司的订单")
}
var (
updateGood domain.OrderGood
formerPartnerBonusPercent float64
formerPartnerBonus float64
newPartnerBonus float64
goodExist bool
)
for i := range oldOrder.Goods {
if oldOrder.Goods[i].Id != goodId {
continue
}
updateGood = oldOrder.Goods[i]
formerPartnerBonusPercent = updateGood.PartnerBonusPercent
formerPartnerBonus = updateGood.GetCurrentPartnerBonus()
err := new(domain.OrderGoodWithBestshop).
UpdateBonusByPertnerBonusPercent(&updateGood, partnerPercent)
if err != nil {
return lib.ThrowError(lib.BUSINESS_ERROR, err.Error())
}
updateGood.RemarkReason.ModifyPartnerBonusPercent = reason
newPartnerBonus = updateGood.GetCurrentPartnerBonus()
goodExist = true
oldOrder.Goods[i] = updateGood
}
if !goodExist {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, "未找到指定的货品")
}
if err := oldOrder.Compute(); err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, "核算订单数据失败"+err.Error())
}
//更新商品数据
err = orderGoodRepository.Save([]domain.OrderGood{updateGood})
if err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, "更新商品数据失败,"+err.Error())
}
//更新订单数据
err = orderBaseReponsitory.Save(oldOrder)
if err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, "更新订单数据失败,"+err.Error())
}
//事件发布
modifyEvent := event.UpdateBounsByPartnerBonusPercent{
OrderId: oldOrder.Id,
AdminId: adminUser.Id,
GoodId: updateGood.Id,
GoodName: updateGood.GoodName,
FormerPartnerBonusPercent: fmt.Sprint(formerPartnerBonusPercent),
NewPartnerBonusPercent: fmt.Sprint(partnerPercent),
FormerPartnerBonus: fmt.Sprint(formerPartnerBonus),
NewPartnerBonus: fmt.Sprint(newPartnerBonus),
}
if formerPartnerBonusPercent < 0 {
modifyEvent.FormerPartnerBonus = "-"
modifyEvent.FormerPartnerBonusPercent = "-"
}
if err = serve.Publish(modifyEvent); err != nil {
return err
}
return nil
}
//PayOrderGoodBonus 支付订单中货品的分红
//目前只处理 海鲜干货的订单 即 order_type = OrderTypeBestShop (3)
func (serve *OrderBonusService) PayOrderGoodBonus(orderId int64, goodId int64, adminId int64) error {
var (
userRepository domain.UsersRepository
orderBaseReponsitory domain.OrderBaseRepository
orderGoodRepository domain.OrderGoodRepository
oldOrder *domain.OrderBase
adminUser domain.Users
err error
)
if orderGoodRepository, err = repository.NewOrderGoodRepository(serve.transactionContext); err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
if orderBaseReponsitory, err = repository.NewOrderBaseRepository(serve.transactionContext); err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
if userRepository, err = repository.NewUsersRepository(serve.transactionContext); err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
oldOrder, err = orderBaseReponsitory.FindOne(domain.OrderBaseFindOneQuery{OrderId: orderId})
if err != nil {
e := fmt.Sprintf("获取订单(id=%d)数据失败,%s", orderId, err)
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, e)
}
if oldOrder.OrderType != domain.OrderTypeBestShop {
return lib.ThrowError(lib.BUSINESS_ERROR, "订单类型错误")
}
oldOrder.Goods, _, err = orderGoodRepository.Find(domain.OrderGoodFindQuery{OrderId: orderId})
if err != nil {
e := fmt.Sprintf("获取订单中(id=%d)的货品数据失败,%s", orderId, err)
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, e)
}
adminUser, err = userRepository.FindOne(domain.UsersFindOneQuery{Id: adminId})
if err != nil {
e := fmt.Sprintf("获取管理员用户(id=%d)数据失败,%s", adminId, err)
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, e)
}
if ok := adminUser.InCompany(oldOrder.CompanyId); !ok {
return lib.ThrowError(lib.BUSINESS_ERROR, "用户不能更新非自己公司的订单")
}
var (
updateGood domain.OrderGood
goodExist bool
)
for i := range oldOrder.Goods {
if oldOrder.Goods[i].Id != goodId {
continue
}
updateGood = oldOrder.Goods[i]
err := new(domain.OrderGoodWithBestshop).PayPartnerBonus(&updateGood)
if err != nil {
return lib.ThrowError(lib.BUSINESS_ERROR, err.Error())
}
goodExist = true
oldOrder.Goods[i] = updateGood
}
if !goodExist {
return errors.New("未找到指定的货品")
}
//计算订单的总数据
if err := oldOrder.Compute(); err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, "核算订单数据失败"+err.Error())
}
//更新商品数据
err = orderGoodRepository.Save([]domain.OrderGood{updateGood})
if err != nil {
return fmt.Errorf("更新商品数据失败,%s", err)
}
//更新订单数据
err = orderBaseReponsitory.Save(oldOrder)
if err != nil {
return fmt.Errorf("更新订单数据失败,%s", err)
}
//
payEvent := event.PayOrderGoodBonus{
OrderId: orderId,
GoodId: goodId,
AdminId: adminId,
GoodName: updateGood.GoodName,
PartnerBonus: updateGood.GetCurrentPartnerBonus(),
}
if err = serve.Publish(payEvent); err != nil {
return err
}
return nil
}
//UpdateOrderRemarkBonus 更新备注
//目前只处理 海鲜干货的订单 即 order_type = OrderTypeBestShop (3)
func (serve *OrderBonusService) UpdateOrderRemarkBonus(orderId int64, adminId int64, remark string) error {
var (
userRepository domain.UsersRepository
orderBaseReponsitory domain.OrderBaseRepository
oldOrder *domain.OrderBase
adminUser domain.Users
err error
)
if orderBaseReponsitory, err = repository.NewOrderBaseRepository(serve.transactionContext); err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
if userRepository, err = repository.NewUsersRepository(serve.transactionContext); err != nil {
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, err.Error())
}
oldOrder, err = orderBaseReponsitory.FindOne(domain.OrderBaseFindOneQuery{OrderId: orderId})
if err != nil {
e := fmt.Sprintf("获取订单(id=%d)数据失败,%s", orderId, err)
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, e)
}
if oldOrder.OrderType != domain.OrderTypeBestShop {
return lib.ThrowError(lib.BUSINESS_ERROR, "订单类型错误")
}
adminUser, err = userRepository.FindOne(domain.UsersFindOneQuery{Id: adminId})
if err != nil {
e := fmt.Sprintf("获取管理员用户(id=%d)数据失败,%s", adminId, err)
return lib.ThrowError(lib.INTERNAL_SERVER_ERROR, e)
}
if ok := adminUser.InCompany(oldOrder.CompanyId); !ok {
return lib.ThrowError(lib.BUSINESS_ERROR, "用户不能更新非自己公司的订单")
}
formerRemark := oldOrder.Remark.RemarkBonus
oldOrder.Remark.RemarkBonus = remark
//更新订单数据
err = orderBaseReponsitory.Save(oldOrder)
if err != nil {
return fmt.Errorf("更新订单数据失败,%s", err)
}
if formerRemark != remark {
//事件发布
modifyEvent := event.UpdateOrderRemark{
OrderId: oldOrder.Id,
AdminId: adminUser.Id,
FormerRemark: formerRemark,
NewRemark: remark,
}
if err = serve.Publish(modifyEvent); err != nil {
return err
}
}
return nil
}
... ...
... ... @@ -5,6 +5,7 @@ import (
"time"
"github.com/go-pg/pg/v10"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/domain"
)
// 公司信息
... ... @@ -34,6 +35,8 @@ type Company struct {
UpdateAt time.Time
// 删除时间
DeleteAt time.Time
Applets []domain.CompanyApplets
}
var _ pg.BeforeUpdateHook = (*Company)(nil)
... ...
... ... @@ -63,6 +63,10 @@ type OrderBase struct {
//分红支付状态
BonusStatus int
CompanyId int64
//数据来源
DataFrom domain.OrderDataFrom ``
//备注
Remark domain.OrderBaseRemark ``
}
var _ pg.BeforeUpdateHook = (*OrderBase)(nil)
... ...
package models
import "time"
type OrderBestshop struct {
tableName struct{} `pg:"order_bestshop"`
Id int64
//订单编号
OrderCode string
//下单时间
OrderTime string
//订单状态
OrderState int8
//发货状态
DeliveryState int8
//买家名称
BuyerName string
//买家电话
BuyerPhone string
//买家地址
BuyerAddress string
//买家备注
BuyerRemark string
//
BuyerId int64
//订单总数
OrderCount int
//d订单总额
OrderAmount float64
//发货时间
DeliveryTime string
//创建时间
CreateTime time.Time
PartnerId int64
//是否将数据同步到 order_base ,order_good
IsCopy bool `pg:",use_zero"`
CompanyId int64
}
... ...
package models
import "gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/domain"
//OrderGood 订单中的货品
type OrderGood struct {
tableName struct{} `pg:"order_good"`
... ... @@ -36,6 +38,11 @@ type OrderGood struct {
//分红状态
BonusStatus int
//备注信息
Remark string
Remark string
//公司id
CompanyId int64
//原因备注
RemarkReason domain.OrderGoodRemarkReason ``
DataFrom domain.OrderDataFrom ``
}
... ...
package models
type OrderGoodBestshop struct {
tableName struct{} `pg:"order_good_bestshop"`
Id int64
//订单id
OrderId int64
//货品编号
Sn string
//商品编号
Bn string
//货品名称
Name string
//单价
Price float64
//货品数量
Nums int
//订单总价
Amount float64
}
... ...
package models
import (
"context"
"time"
"github.com/go-pg/pg/v10"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/domain"
)
//OrderLog 订单修改记录
type OrderLog struct {
tableName struct{} `pg:"order_log"`
Id int64
OrderId int64 `` //订单id
AlterTime time.Time `` //时间
Operator string `` //操作人员
OperatorId int64 `` //操作人员Id
OperatorType string `` //操作人员的类型
LogAction string `` //执行动作
Descript []domain.OrderLogDescript `` //描述日志内容
DataFrom string `` //修改操作的来源:"web_admin"
}
var _ pg.BeforeInsertHook = (*OrderBase)(nil)
func (l *OrderLog) BeforeInsert(ctx context.Context) (context.Context, error) {
l.AlterTime = time.Now()
return ctx, nil
}
... ...
... ... @@ -36,6 +36,7 @@ func (repository CompanyRepository) transformPgModelToDomainModel(m *models.Comp
CreateAt: m.CreateAt,
UpdateAt: m.UpdateAt,
Abbreviation: m.Abbreviation,
Applets: m.Applets,
}, nil
}
... ... @@ -55,6 +56,7 @@ func (reponsitory CompanyRepository) Add(m *domain.Company) error {
CreateAt: m.CreateAt,
UpdateAt: m.UpdateAt,
Abbreviation: m.Abbreviation,
Applets: m.Applets,
}
_, err = tx.Model(&companyModel).Insert()
return err
... ... @@ -77,6 +79,7 @@ func (reponsitory CompanyRepository) Edit(m *domain.Company) error {
DeleteAt: m.DeleteAt,
UpdateAt: m.UpdateAt,
Abbreviation: m.Abbreviation,
Applets: m.Applets,
}
_, err = tx.Model(&companyModel).WherePK().Update()
return err
... ...
... ... @@ -42,6 +42,8 @@ func (reponsitory OrderBaseRepository) transformPgModelToDomainModel(orderModel
},
BonusStatus: orderModel.BonusStatus,
CompanyId: orderModel.CompanyId,
DataFrom: orderModel.DataFrom,
Remark: orderModel.Remark,
}
return order, nil
}
... ... @@ -62,7 +64,8 @@ func (repository OrderBaseRepository) Save(orderInfo *domain.OrderBase) error {
PartnerBonusHas: orderInfo.OrderCompute.PartnerBonusHas, PartnerBonusNot: orderInfo.OrderCompute.PartnerBonusNot,
PartnerBonusExpense: orderInfo.OrderCompute.PartnerBonusExpense, IsDisable: orderInfo.IsDisable,
CreateTime: orderInfo.CreateTime, BonusStatus: orderInfo.BonusStatus,
CompanyId: orderInfo.CompanyId,
CompanyId: orderInfo.CompanyId, DataFrom: orderInfo.DataFrom,
Remark: orderInfo.Remark,
}
if m.Id == 0 {
_, err = tx.Model(m).
... ... @@ -82,7 +85,7 @@ func (repository OrderBaseRepository) Save(orderInfo *domain.OrderBase) error {
}
func (repository OrderBaseRepository) Find(queryOption domain.OrderBaseFindQuery) ([]domain.OrderBase, int, error) {
db := repository.transactionContext.PgDd
db := repository.transactionContext.GetDB()
orderModels := []models.OrderBase{}
query := db.Model(&orderModels)
if queryOption.PartnerId > 0 {
... ...
package repository
import (
"fmt"
"time"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/domain"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/infrastructure/pg/models"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/infrastructure/pg/transaction"
)
type OrderBestshopRepository struct {
transactionContext *transaction.TransactionContext
}
var (
_ domain.OrderBestshopRepository = (*OrderBestshopRepository)(nil)
)
func NewOrderBestshopRepository(transactionContext *transaction.TransactionContext) (*OrderBestshopRepository, error) {
if transactionContext == nil {
return nil, fmt.Errorf("transactionContext参数不能为nil")
}
return &OrderBestshopRepository{transactionContext: transactionContext}, nil
}
func (respository OrderBestshopRepository) transformPgModelToDomainModel(orderModel *models.OrderBestshop) (order domain.OrderBestShop, err error) {
return domain.OrderBestShop{
Id: orderModel.Id,
OrderCode: orderModel.OrderCode,
OrderTime: orderModel.OrderTime,
OrderState: orderModel.OrderState,
DeliveryState: orderModel.DeliveryState,
BuyerName: orderModel.BuyerName,
BuyerPhone: orderModel.BuyerPhone,
BuyerAddress: orderModel.BuyerAddress,
BuyerRemark: orderModel.BuyerRemark,
BuyerId: orderModel.BuyerId,
OrderCount: orderModel.OrderCount,
OrderAmount: orderModel.OrderAmount,
DeliveryTime: orderModel.DeliveryTime,
CreateTime: orderModel.CreateTime,
PartnerId: orderModel.PartnerId,
IsCopy: orderModel.IsCopy,
CompanyId: orderModel.CompanyId,
}, nil
}
func (respository OrderBestshopRepository) Add(order *domain.OrderBestShop) error {
tx := respository.transactionContext.GetDB()
m := models.OrderBestshop{
OrderCode: order.OrderCode,
OrderTime: order.OrderTime,
OrderState: order.OrderState,
DeliveryState: order.DeliveryState,
BuyerName: order.BuyerName,
BuyerPhone: order.BuyerPhone,
BuyerAddress: order.BuyerAddress,
BuyerRemark: order.BuyerRemark,
BuyerId: order.BuyerId,
OrderCount: order.OrderCount,
OrderAmount: order.OrderAmount,
DeliveryTime: order.DeliveryTime,
CreateTime: time.Now(),
PartnerId: order.PartnerId,
IsCopy: order.IsCopy,
CompanyId: order.CompanyId,
}
_, err := tx.Model(&m).Insert()
order.Id = m.Id
return err
}
func (respository OrderBestshopRepository) Edit(order *domain.OrderBestShop) error {
tx := respository.transactionContext.GetDB()
m := models.OrderBestshop{
Id: order.Id,
OrderCode: order.OrderCode,
OrderTime: order.OrderTime,
OrderState: order.OrderState,
DeliveryState: order.DeliveryState,
BuyerName: order.BuyerName,
BuyerPhone: order.BuyerPhone,
BuyerAddress: order.BuyerAddress,
BuyerRemark: order.BuyerRemark,
BuyerId: order.BuyerId,
OrderCount: order.OrderCount,
OrderAmount: order.OrderAmount,
DeliveryTime: order.DeliveryTime,
CreateTime: order.CreateTime,
PartnerId: order.PartnerId,
IsCopy: order.IsCopy,
CompanyId: order.CompanyId,
}
_, err := tx.Model(&m).Where("id=?", order.Id).Update()
order.Id = m.Id
return err
}
func (respository OrderBestshopRepository) FindOne(queryOption domain.OrderBestshopFindOneQuery) (*domain.OrderBestShop, error) {
tx := respository.transactionContext.GetDB()
m := models.OrderBestshop{}
err := tx.Model(&m).
Where("id=?", queryOption.OrderId).
First()
if err != nil {
return nil, err
}
var order domain.OrderBestShop
order, err = respository.transformPgModelToDomainModel(&m)
if err != nil {
return nil, fmt.Errorf("OrderBestshop domain 数据结构转换失败:%s", err)
}
return &order, nil
}
... ...
package repository
import (
"fmt"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/domain"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/infrastructure/pg/models"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/infrastructure/pg/transaction"
)
type OrderGoodBestshopRepository struct {
transactionContext *transaction.TransactionContext
}
var (
_ domain.OrderGoodBestshopRepository = (*OrderGoodBestshopRepository)(nil)
)
func NewOrderGoodBestshopRepository(transactionContext *transaction.TransactionContext) (*OrderGoodBestshopRepository, error) {
if transactionContext == nil {
return nil, fmt.Errorf("transactionContext参数不能为nil")
}
return &OrderGoodBestshopRepository{transactionContext: transactionContext}, nil
}
func (respository OrderGoodBestshopRepository) transformPgModelToDomainModel(orderGoodModel *models.OrderGoodBestshop) (orderGood domain.OrderGoodBestShop, err error) {
return domain.OrderGoodBestShop{
Id: orderGoodModel.Id,
OrderId: orderGoodModel.OrderId,
Sn: orderGoodModel.Sn,
Bn: orderGoodModel.Bn,
Name: orderGoodModel.Name,
Price: orderGoodModel.Price,
Nums: orderGoodModel.Nums,
Amount: orderGoodModel.Amount,
}, nil
}
func (respository OrderGoodBestshopRepository) Add(good *domain.OrderGoodBestShop) error {
tx := respository.transactionContext.GetDB()
m := models.OrderGoodBestshop{
Id: good.Id,
OrderId: good.OrderId,
Sn: good.Sn,
Bn: good.Bn,
Name: good.Name,
Price: good.Price,
Nums: good.Nums,
Amount: good.Amount,
}
_, err := tx.Model(&m).Insert()
good.Id = m.Id
return err
}
func (respository OrderGoodBestshopRepository) Edit(good *domain.OrderGoodBestShop) error {
tx := respository.transactionContext.GetDB()
m := models.OrderGoodBestshop{
Id: good.Id,
OrderId: good.OrderId,
Sn: good.Sn,
Bn: good.Bn,
Name: good.Name,
Price: good.Price,
Nums: good.Nums,
Amount: good.Amount,
}
_, err := tx.Model(&m).Update()
return err
}
func (respository OrderGoodBestshopRepository) Find(queryOption domain.OrderGoodBestshopFindQuery) ([]domain.OrderGoodBestShop, error) {
tx := respository.transactionContext.GetDB()
goodModels := []models.OrderGoodBestshop{}
query := tx.Model(&goodModels)
if queryOption.OrderId > 0 {
query = query.Where("order_id=?", queryOption.OrderId)
}
query = query.Limit(1000)
err := query.Select()
if err != nil {
return nil, err
}
goods := []domain.OrderGoodBestShop{}
for i := range goodModels {
g, _ := respository.transformPgModelToDomainModel(&goodModels[i])
goods = append(goods, g)
}
return goods, nil
}
... ...
package repository
import (
"errors"
"fmt"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/domain"
... ... @@ -38,7 +39,9 @@ func (reponsitory OrderGoodRepository) transformPgModelToDomainModel(orderModel
PartnerBonusNot: orderModel.PartnerBonusNot,
PartnerBonusExpense: orderModel.PartnerBonusExpense,
},
CompanyId: orderModel.CompanyId,
CompanyId: orderModel.CompanyId,
RemarkReason: orderModel.RemarkReason,
DataFrom: orderModel.DataFrom,
}
switch orderModel.BonusStatus {
case domain.OrderGoodWaitPay:
... ... @@ -63,8 +66,9 @@ func (repository OrderGoodRepository) Save(data []domain.OrderGood) error {
PlanPartnerBonus: v.GoodCompute.PlanPartnerBonus, UsePartnerBonus: v.GoodCompute.UsePartnerBonus,
PartnerBonusHas: v.GoodCompute.PartnerBonusHas, PartnerBonusNot: v.GoodCompute.PartnerBonusNot,
PartnerBonusExpense: v.GoodCompute.PartnerBonusExpense, BonusStatus: v.BonusStatus,
Remark: v.Remark,
CompanyId: v.CompanyId,
Remark: v.Remark, CompanyId: v.CompanyId,
RemarkReason: v.RemarkReason,
DataFrom: v.DataFrom,
}
if v.Id == 0 {
_, err = tx.Model(m).
... ... @@ -138,3 +142,27 @@ func (repository OrderGoodRepository) Remove(orderId int64, companyId int64, goo
_, err = query.Delete()
return err
}
func (repository OrderGoodRepository) FindOne(queryOption domain.OrderGoodFindOneQuery) (domain.OrderGood, error) {
var (
good domain.OrderGood
goodModel models.OrderGood
err error
hasCondition bool
)
tx := repository.transactionContext.GetDB()
query := tx.Model(&goodModel)
if queryOption.GoodId > 0 {
query = query.Where("id=?", queryOption.GoodId)
hasCondition = true
}
if !hasCondition {
return good, errors.New("OrderGoodRepository.FindOne 必须使用查询条件")
}
err = query.First()
if err != nil {
return good, fmt.Errorf("获取订单的货品数据失败,%s", err)
}
good, err = repository.transformPgModelToDomainModel(&goodModel)
return good, err
}
... ...
package repository
import (
"fmt"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/domain"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/infrastructure/pg/models"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/infrastructure/pg/transaction"
)
//订单日志
type OrderLogRepository struct {
transactionContext *transaction.TransactionContext
}
var _ domain.OrderLogRepository = (*OrderLogRepository)(nil)
func NewOrderLogRepository(transactionContext *transaction.TransactionContext) (*OrderLogRepository, error) {
if transactionContext == nil {
return nil, fmt.Errorf("transactionContext参数不能为nil")
}
return &OrderLogRepository{transactionContext: transactionContext}, nil
}
func (repository OrderLogRepository) transformPgModelToDomainModel(m *models.OrderLog) (data domain.OrderLog, err error) {
return domain.OrderLog{
Id: m.Id,
OrderId: m.OrderId,
Operator: m.Operator,
OperatorId: m.OperatorId,
OperatorType: m.OperatorType,
AlterTime: m.AlterTime,
LogAction: m.LogAction,
Descript: m.Descript,
DataFrom: m.DataFrom,
}, nil
}
func (repository OrderLogRepository) Add(data *domain.OrderLog) error {
db := repository.transactionContext.GetDB()
m := models.OrderLog{
OrderId: data.OrderId,
Operator: data.Operator,
OperatorId: data.OperatorId,
OperatorType: data.OperatorType,
LogAction: data.LogAction,
Descript: data.Descript,
DataFrom: data.DataFrom,
}
_, err := db.Model(&m).Insert()
data.AlterTime = m.AlterTime
return err
}
func (repository OrderLogRepository) Find(queryOptions domain.OrderLogFindQuery) ([]domain.OrderLog, error) {
db := repository.transactionContext.GetDB()
logModels := []models.OrderLog{}
var err error
query := db.Model(&logModels)
if queryOptions.OrderId > 0 {
query = query.Where("order_id=?", queryOptions.OrderId)
}
query = query.Order("order_log.id")
query = query.Limit(1000)
err = query.Select()
if err != nil {
return nil, err
}
logReturn := []domain.OrderLog{}
for i := range logModels {
domainLog, err := repository.transformPgModelToDomainModel(&logModels[i])
if err != nil {
return logReturn, err
}
logReturn = append(logReturn, domainLog)
}
return logReturn, nil
}
... ...
... ... @@ -141,7 +141,7 @@ func (reponsitory UsersRepository) FindOne(queryOptions domain.UsersFindOneQuery
query = query.Where("open_id=?", queryOptions.OpenId)
}
if !hasCondition {
return domain.Users{}, errors.New("FindOne 必须要有查询条件")
return domain.Users{}, errors.New("UsersRepository.FindOne 必须要有查询条件")
}
err = query.First()
if err != nil {
... ...
... ... @@ -5,6 +5,7 @@ import (
categoryService "gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/application/partnerCategory/service"
partnerQuery "gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/application/partnerInfo/query"
partnerInfoService "gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/application/partnerInfo/service"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/domain"
)
type CommonController struct {
... ... @@ -64,3 +65,18 @@ func (c *CommonController) GetPartnerCategory() {
}
c.ResponseData(resp)
}
// GetOrderType 下拉选项数据通用接口,获取订单类型列表
func (c *CommonController) GetOrderType() {
resp := []map[string]interface{}{
map[string]interface{}{
"id": domain.OrderReal,
"name": domain.GetOrderBaseTypeName(domain.OrderReal),
},
map[string]interface{}{
"id": domain.OrderTypeBestShop,
"name": domain.GetOrderBaseTypeName(domain.OrderTypeBestShop),
},
}
c.ResponseData(resp)
}
... ...
... ... @@ -31,8 +31,8 @@ func (c *OrderDividendController) Prepare() {
//PageListOrderDividend 获取实发订单分红列表
func (c *OrderDividendController) PageListOrderDividend() {
type Parameter struct {
SearchText string `json:"searchText"`
Partner int64 `json:"partner"`
SearchWord string `json:"searchWord"`
OrderType int `json:"orderType"`
PageSize int `json:"pageSize"`
PageNumber int `json:"pageNumber"`
}
... ... @@ -45,6 +45,12 @@ func (c *OrderDividendController) PageListOrderDividend() {
c.ResponseError(errors.New("json数据解析失败"))
return
}
if !(param.OrderType == 0 ||
param.OrderType == domain.OrderReal ||
param.OrderType == domain.OrderTypeBestShop) {
c.ResponseError(errors.New("参数异常"))
return
}
if param.PageNumber == 0 {
param.PageNumber = 1
}
... ... @@ -53,38 +59,18 @@ func (c *OrderDividendController) PageListOrderDividend() {
}
companyId := c.GetUserCompany()
orderSrv := orderService.NewOrderInfoService(nil)
orderinfos, cnt, err := orderSrv.PageListOrderBase(orderQuery.ListOrderBaseQuery{
PartnerId: param.Partner,
DeliveryCode: param.SearchText,
OrderType: domain.OrderReal,
Limit: param.PageSize,
Offset: (param.PageNumber - 1) * param.PageSize,
CompanyId: companyId,
resp, cnt, err := orderSrv.PageListOrderBonus(orderQuery.ListOrderBonusQuery{
OrderType: param.OrderType,
PartnerOrCode: param.SearchWord,
Limit: param.PageSize,
Offset: (param.PageNumber - 1) * param.PageSize,
CompanyId: companyId,
})
if err != nil {
c.ResponseError(err)
return
}
rsp := []map[string]interface{}{}
for i := range orderinfos {
orderinfo := orderinfos[i]
m := map[string]interface{}{
"updateTime": orderinfo.UpdateTime.Local().Format("2006-01-02 15:04:05"),
"id": orderinfo.Id,
"shipmentsId": orderinfo.DeliveryCode,
"partner": orderinfo.PartnerInfo.PartnerName,
"dividendsReceivable": orderinfo.OrderCompute.PlanPartnerBonus,
"dividendSpending": orderinfo.OrderCompute.PartnerBonusExpense,
"receiveDividends": orderinfo.OrderCompute.PartnerBonusHas,
"uncollectedDividends": orderinfo.OrderCompute.PartnerBonusNot,
"stateOfPayment": orderinfo.BonusStatus,
}
if orderinfo.OrderCompute.UsePartnerBonus >= 0 {
m["dividendsReceivable"] = orderinfo.OrderCompute.UsePartnerBonus
}
rsp = append(rsp, m)
}
c.ResponsePageList(rsp, cnt, param.PageNumber)
c.ResponsePageList(resp, cnt, param.PageNumber)
return
}
... ... @@ -269,3 +255,147 @@ func (c *OrderDividendController) EditOrderDividend() {
c.ResponseData(nil)
return
}
//OrderDividendDetailForBestshop 海鲜干货的订单分红详情
func (c *OrderDividendController) OrderDividendDetailForBestshop() {
type Parameter struct {
Id string `json:"id"`
}
var (
param Parameter
err error
)
if err = c.BindJsonData(&param); err != nil {
logs.Error(err)
c.ResponseError(errors.New("json数据解析失败"))
return
}
orderid, _ := strconv.ParseInt(param.Id, 10, 64)
if orderid == 0 {
c.ResponseError(errors.New("参数错误"))
return
}
companyId := c.GetUserCompany()
orderSrv := orderService.NewOrderInfoService(nil)
respData, err := orderSrv.GetOrderBestshopInfoWithBonus(orderid, companyId)
if err != nil {
c.ResponseError(err)
return
}
c.ResponseData(respData)
return
}
//EditOrderForBestshop 编辑海鲜干货的订单 中的 货品数量和分红比例
func (c *OrderDividendController) EditOrderDividendForBestshop() {
type Parameter struct {
State int `json:"state"`
OrderId string `json:"orderId"`
ProductId string `json:"productId"`
Reason string `json:"reason"`
GoodNumber int `json:"goodNumber"`
PartnerBonusPercent float64 `json:"partnerBonusPercent"`
}
var (
param Parameter
err error
)
if err = c.BindJsonData(&param); err != nil {
logs.Error(err)
c.ResponseError(errors.New("json数据解析失败"))
return
}
orderid, _ := strconv.ParseInt(param.OrderId, 10, 64)
if orderid == 0 {
c.ResponseError(errors.New("参数错误"))
return
}
productId, _ := strconv.ParseInt(param.ProductId, 10, 64)
if productId == 0 {
c.ResponseError(errors.New("参数错误"))
return
}
adminId := c.GetUserId()
orderSrv := orderService.NewOrderInfoService(nil)
switch param.State {
case 1:
err = orderSrv.UpdateBonusByGoodNumber(orderid, productId, adminId, param.GoodNumber, param.Reason)
case 2:
err = orderSrv.UpdateBonusByPartnerBonusPercent(orderid, productId, adminId, param.PartnerBonusPercent, param.Reason)
}
if err != nil {
c.ResponseError(err)
return
}
c.ResponseData(nil)
return
}
//PayOrderGoodBonusForBestshop 支付海鲜干货订单中的分红
func (c *OrderDividendController) PayOrderGoodBonusForBestshop() {
type Parameter struct {
OrderId string `json:"orderId"`
ProductId string `json:"productId"`
}
var (
param Parameter
err error
)
if err = c.BindJsonData(&param); err != nil {
logs.Error(err)
c.ResponseError(errors.New("json数据解析失败"))
return
}
orderid, _ := strconv.ParseInt(param.OrderId, 10, 64)
if orderid == 0 {
c.ResponseError(errors.New("参数错误"))
return
}
productId, _ := strconv.ParseInt(param.ProductId, 10, 64)
if productId == 0 {
c.ResponseError(errors.New("参数错误"))
return
}
adminId := c.GetUserId()
orderSrv := orderService.NewOrderInfoService(nil)
err = orderSrv.PayPartnerBonusWithOrderBestshop(orderid, productId, adminId)
if err != nil {
c.ResponseError(err)
return
}
c.ResponseData(nil)
return
}
//EditOrderRemarkBonusForBestshop 编辑海鲜干货订单中的备注
func (c *OrderDividendController) EditOrderRemarkBonusForBestshop() {
type Parameter struct {
OrderId string `json:"orderId"`
Remark string `json:"remark"`
}
var (
param Parameter
err error
)
if err = c.BindJsonData(&param); err != nil {
logs.Error(err)
c.ResponseError(errors.New("json数据解析失败"))
return
}
orderid, _ := strconv.ParseInt(param.OrderId, 10, 64)
if orderid == 0 {
c.ResponseError(errors.New("参数错误"))
return
}
adminId := c.GetUserId()
orderSrv := orderService.NewOrderInfoService(nil)
err = orderSrv.UpdateOrderRemarkBonus(orderid, adminId, param.Remark)
if err != nil {
c.ResponseError(err)
return
}
c.ResponseData(nil)
return
}
... ...
... ... @@ -68,13 +68,15 @@ func (c *PartnerInfoController) CreatePartnerInfo() {
Status: param.State,
PartnerCategory: param.PartnerType,
CooperateTime: cooperateTime,
Salesman: []domain.Salesman{
CompanyId: companyId,
}
if len(param.SalesmanName) > 0 || len(param.Phone) > 0 {
cmd.Salesman = []domain.Salesman{
domain.Salesman{
Name: param.SalesmanName,
Telephone: param.Phone,
},
},
CompanyId: companyId,
}
}
if len(param.Area) > 0 {
cmd.RegionInfo = &domain.RegionInfo{
... ...
... ... @@ -29,6 +29,10 @@ func init() {
beego.NSRouter("/list", &controllers.OrderDividendController{}, "POST:PageListOrderDividend"),
beego.NSRouter("/edit", &controllers.OrderDividendController{}, "POST:EditOrderDividend"),
beego.NSRouter("/detail", &controllers.OrderDividendController{}, "POST:OrderDividendDetail"),
beego.NSRouter("/mini-program/detail", &controllers.OrderDividendController{}, "POST:OrderDividendDetailForBestshop"),
beego.NSRouter("/mini-program/modify", &controllers.OrderDividendController{}, "POST:EditOrderDividendForBestshop"),
beego.NSRouter("/mini-program/payDividends", &controllers.OrderDividendController{}, "POST:PayOrderGoodBonusForBestshop"),
beego.NSRouter("/mini-program/remarks", &controllers.OrderDividendController{}, "POST:EditOrderRemarkBonusForBestshop"),
beego.NSRouter("/business/detail", &controllers.BusinessBonusController{}, "POST:GetBusinessBonus"),
beego.NSRouter("/business/edit", &controllers.BusinessBonusController{}, "POST:UpdateBusinessBonus"),
... ... @@ -50,6 +54,7 @@ func init() {
beego.NSNamespace("/common",
beego.NSRouter("/partner", &controllers.CommonController{}, "POST:GetPartnerList"),
beego.NSRouter("/partnerType", &controllers.CommonController{}, "POST:GetPartnerCategory"),
beego.NSRouter("/orderType", &controllers.CommonController{}, "POST:GetOrderType"),
),
beego.NSNamespace("/enterprises",
... ...
package configs
import (
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/constant"
)
type MqConfig struct {
Servers []string `json:"servers"`
ConsumerId string `json:"consumerGroup"`
}
var Cfg = MqConfig{
Servers: constant.KafkaCfg.Servers,
ConsumerId: constant.KafkaCfg.ConsumerId,
}
// "192.168.190.136:9092",
// "106.52.15.41:9092"
... ...
package consumer
import (
"context"
"errors"
"fmt"
"time"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/port/consumer/configs"
"github.com/Shopify/sarama"
"github.com/astaxie/beego/logs"
cluster "github.com/bsm/sarama-cluster"
)
//MessageConsumer 消息消费者
type MessageConsumer struct {
ready chan struct{}
kafkaHosts []string
groupId string
topics []string
topicsHandles map[string]TopicHandle
// beforeHandles []TopicHandle
// afterHandles []TopicHandle
}
//实现对应的接口
var _ sarama.ConsumerGroupHandler = (*MessageConsumer)(nil)
func (c *MessageConsumer) Setup(groupSession sarama.ConsumerGroupSession) error {
close(c.ready)
return nil
}
func (c *MessageConsumer) Cleanup(groupSession sarama.ConsumerGroupSession) error {
return nil
}
func (c *MessageConsumer) ConsumeClaim(groupSession sarama.ConsumerGroupSession,
groupClaim sarama.ConsumerGroupClaim) error {
var (
topicHandle TopicHandle
err error
)
for message := range groupClaim.Messages() {
logs.Debug("Done Message claimed: timestamp = %v, topic = %s offset = %v value = %v \n",
message.Timestamp, message.Topic, message.Offset, string(message.Value))
if topicHandle, err = c.FindTopichandle(groupClaim.Topic()); err != nil {
logs.Error("FindTopichandle err:%s \n", err)
continue
}
if err = topicHandle(message); err != nil {
logs.Error("Message claimed: kafka消息处理错误 topic =", message.Topic, message.Offset, err)
}
groupSession.MarkMessage(message, "")
}
return nil
}
func (c *MessageConsumer) FindTopichandle(topic string) (TopicHandle, error) {
if v, ok := c.topicsHandles[topic]; ok {
return v, nil
}
return nil, errors.New("TopicHandle not found")
}
type Runer struct {
msgConsumer *MessageConsumer
consumerGroup sarama.ConsumerGroup
Consumer *cluster.Consumer
}
func NewRuner() *Runer {
topics := []string{}
for key := range TopicHandleRouters {
topics = append(topics, key)
}
r := &Runer{
msgConsumer: &MessageConsumer{
ready: make(chan struct{}),
kafkaHosts: configs.Cfg.Servers,
groupId: configs.Cfg.ConsumerId,
topicsHandles: TopicHandleRouters,
topics: topics,
},
}
logs.Debug("kafka_host=%v; topic=%v;groupid=%s ", r.msgConsumer.kafkaHosts,
r.msgConsumer.topics, r.msgConsumer.groupId)
return r
}
func (r *Runer) InitConsumer() error {
config := sarama.NewConfig()
//config.Consumer.Group.Rebalance.Strategy = sarama.BalanceStrategyRoundRobin
config.Consumer.Offsets.Initial = sarama.OffsetNewest
config.Version = sarama.V0_10_2_1
if err := config.Validate(); err != nil {
msg := fmt.Sprintf("Kafka producer config invalidate. config: %v. err: %v", configs.Cfg, err)
logs.Error(msg)
panic(msg)
}
consumerGroup, err := sarama.NewConsumerGroup(r.msgConsumer.kafkaHosts, r.msgConsumer.groupId, config)
if err != nil {
return err
}
r.consumerGroup = consumerGroup
return nil
}
// func (r *Runer) InitConsumer() error {
// clusterCfg := cluster.NewConfig()
// clusterCfg.Consumer.Return.Errors = true
// clusterCfg.Consumer.Offsets.Initial = sarama.OffsetOldest
// clusterCfg.Group.Return.Notifications = true
// clusterCfg.Version = sarama.V0_10_2_1
// // khosts := []string{"192.168.0.252:9092", "192.168.0.251:9092", "192.168.0.250:9092"}
// // groupid := "partnermg_dev"
// // topic := []string{"topic_test"}
// logs.Debug(r.msgConsumer.kafkaHosts, r.msgConsumer.groupId, r.msgConsumer.topics)
// consumer, err := cluster.NewConsumer(r.msgConsumer.kafkaHosts, r.msgConsumer.groupId, r.msgConsumer.topics, clusterCfg)
// // consumer, err := cluster.NewConsumer(khosts, groupid, topic, clusterCfg)
// if err != nil {
// msg := fmt.Sprintf("Create kafka consumer error: %v. config: %v", err, clusterCfg)
// logs.Error(msg)
// panic(msg)
// }
// r.Consumer = consumer
// return nil
// }
func (r *Runer) Start(ctx context.Context) {
defer func() {
if e := recover(); e != nil {
logs.Error(e)
}
}()
if len(r.msgConsumer.topics) == 0 {
logs.Error("there has no topics")
return
}
for {
select {
case <-ctx.Done():
logs.Warning("ctx cancel;consumerGroup.Close()")
r.consumerGroup.Close()
return
default:
if err := r.consumerGroup.Consume(ctx, r.msgConsumer.topics, r.msgConsumer); err != nil {
logs.Error("consumerGroup err:%s \n", err)
//等待重试
timer := time.NewTimer(5 * time.Second)
<-timer.C
}
r.msgConsumer.ready = make(chan struct{})
}
}
}
// func (r *Runer) Start(ctx context.Context) {
// for {
// select {
// case msg, more := <-r.Consumer.Messages():
// if more {
// logs.Info("Partition:%d, Offset:%d, Key:%s, Value:%s Timestamp:%s\n", msg.Partition, msg.Offset, string(msg.Key), string(msg.Value), msg.Timestamp)
// r.Consumer.MarkOffset(msg, "") // mark message as processed
// }
// case err, more := <-r.Consumer.Errors():
// if more {
// logs.Info("Kafka consumer error: %v", err.Error())
// }
// case ntf, more := <-r.Consumer.Notifications():
// if more {
// logs.Info("Kafka consumer rebalance: %v", ntf)
// }
// case <-ctx.Done():
// logs.Info("Stop consumer server...")
// r.Consumer.Close()
// return
// }
// }
// }
func (r *Runer) IsReady() <-chan struct{} {
return r.msgConsumer.ready
}
... ...
package handles
import "encoding/json"
type DataFromMessage struct {
Module string `json:"module"`
Action string `json:"action"`
Data json.RawMessage `json:"data"`
}
... ...
package handles
import (
"encoding/json"
"fmt"
"github.com/Shopify/sarama"
"github.com/astaxie/beego/logs"
syncOrderCmd "gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/application/syncOrder/command"
syncOrderSrv "gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/application/syncOrder/service"
)
func DataFromXiangMi(message *sarama.ConsumerMessage) error {
logs.Info("Done Message claimed: timestamp = %v, topic = %s offset = %v value = %v \n",
message.Timestamp, message.Topic, message.Offset, string(message.Value))
var (
msgData DataFromMessage
err error
)
err = json.Unmarshal(message.Value, &msgData)
if err != nil {
return fmt.Errorf("[Consumer][SyncBestshopOrder] 解析kafka数据失败;%s", err)
}
dataAction := msgData.Module + "/" + msgData.Action
switch dataAction {
case "xiangmi.order/ship":
err = syncBestshopOrder(msgData.Data)
if err != nil {
e := fmt.Errorf("[Consumer][SyncBestshopOrder] %s", err)
return e
}
default:
logs.Error("未找到执行动作:Module=%s,Action=%s", msgData.Module, msgData.Action)
}
return nil
}
//SyncBestshopOrder 同步
func syncBestshopOrder(data []byte) error {
var (
cmd syncOrderCmd.CreateOrderFromBestshop
err error
)
err = json.Unmarshal(data, &cmd)
if err != nil {
return fmt.Errorf("[Consumer][syncBestshopOrder] 解析kafka数据失败;%s", err)
}
if cmd.PartnerId <= 0 {
logs.Info("[Consumer][syncBestshopOrder] PartnerId<=0 ,不处理消息")
return nil
}
srv := syncOrderSrv.NewOrderInfoService(nil)
err = srv.SyncOrderFromBestshop(cmd)
if err != nil {
e := fmt.Errorf("[Consumer][syncBestshopOrder] %s", err)
return e
}
return err
}
... ...
package consumer
import (
"os"
"github.com/Shopify/sarama"
"gitlab.fjmaimaimai.com/mmm-go/partnermg/pkg/port/consumer/handles"
)
//TopicHandle 处理kafka中得消息
type TopicHandle func(*sarama.ConsumerMessage) error
//TopicHandleRouters 根据topic区分消息并进行处理
var TopicHandleRouters = map[string]TopicHandle{
// "topic_test": func(message *sarama.ConsumerMessage) error {
// logs.Info("Done Message claimed: timestamp = %v, topic = %s offset = %v value = %v \n",
// message.Timestamp, message.Topic, message.Offset, string(message.Value))
// return nil
// },
}
func init() {
var runEnv string
if os.Getenv("KAFKA_CONSUMER_ID") != "" {
runEnv = os.Getenv("KAFKA_CONSUMER_ID")
}
if runEnv == "partnermg_test" {
initHandleRoutersTest()
}
if runEnv == "partnermg_prd" {
initHandleRoutersProd()
}
}
func initHandleRoutersTest() {
TopicHandleRouters["xiangmi_project_test"] = handles.DataFromXiangMi
}
func initHandleRoutersProd() {
TopicHandleRouters["xiangmi_project"] = handles.DataFromXiangMi
}
... ...
dist: xenial
language: go
go:
- 1.10.x
- 1.11.x
- 1.12.x
os:
- linux
- osx
matrix:
include:
name: "Go 1.11.x CentOS 32bits"
language: go
go: 1.11.x
os: linux
services:
- docker
script:
# Please update Go version in travis_test_32 as needed
- "docker run -i -v \"${PWD}:/zstd\" toopher/centos-i386:centos6 /bin/bash -c \"linux32 --32bit i386 /zstd/travis_test_32.sh\""
install:
- "wget https://github.com/DataDog/zstd/files/2246767/mr.zip"
- "unzip mr.zip"
script:
- "go build"
- "PAYLOAD=`pwd`/mr go test -v"
- "PAYLOAD=`pwd`/mr go test -bench ."
... ...
Simplified BSD License
Copyright (c) 2016, Datadog <info@datadoghq.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
... ...
# Zstd Go Wrapper
[C Zstd Homepage](https://github.com/Cyan4973/zstd)
The current headers and C files are from *v1.3.8* (Commit
[470344d](https://github.com/facebook/zstd/releases/tag/v1.3.8)).
## Usage
There are two main APIs:
* simple Compress/Decompress
* streaming API (io.Reader/io.Writer)
The compress/decompress APIs mirror that of lz4, while the streaming API was
designed to be a drop-in replacement for zlib.
### Simple `Compress/Decompress`
```go
// Compress compresses the byte array given in src and writes it to dst.
// If you already have a buffer allocated, you can pass it to prevent allocation
// If not, you can pass nil as dst.
// If the buffer is too small, it will be reallocated, resized, and returned bu the function
// If dst is nil, this will allocate the worst case size (CompressBound(src))
Compress(dst, src []byte) ([]byte, error)
```
```go
// CompressLevel is the same as Compress but you can pass another compression level
CompressLevel(dst, src []byte, level int) ([]byte, error)
```
```go
// Decompress will decompress your payload into dst.
// If you already have a buffer allocated, you can pass it to prevent allocation
// If not, you can pass nil as dst (allocates a 4*src size as default).
// If the buffer is too small, it will retry 3 times by doubling the dst size
// After max retries, it will switch to the slower stream API to be sure to be able
// to decompress. Currently switches if compression ratio > 4*2**3=32.
Decompress(dst, src []byte) ([]byte, error)
```
### Stream API
```go
// NewWriter creates a new object that can optionally be initialized with
// a precomputed dictionary. If dict is nil, compress without a dictionary.
// The dictionary array should not be changed during the use of this object.
// You MUST CALL Close() to write the last bytes of a zstd stream and free C objects.
NewWriter(w io.Writer) *Writer
NewWriterLevel(w io.Writer, level int) *Writer
NewWriterLevelDict(w io.Writer, level int, dict []byte) *Writer
// Write compresses the input data and write it to the underlying writer
(w *Writer) Write(p []byte) (int, error)
// Close flushes the buffer and frees C zstd objects
(w *Writer) Close() error
```
```go
// NewReader returns a new io.ReadCloser that will decompress data from the
// underlying reader. If a dictionary is provided to NewReaderDict, it must
// not be modified until Close is called. It is the caller's responsibility
// to call Close, which frees up C objects.
NewReader(r io.Reader) io.ReadCloser
NewReaderDict(r io.Reader, dict []byte) io.ReadCloser
```
### Benchmarks (benchmarked with v0.5.0)
The author of Zstd also wrote lz4. Zstd is intended to occupy a speed/ratio
level similar to what zlib currently provides. In our tests, the can always
be made to be better than zlib by chosing an appropriate level while still
keeping compression and decompression time faster than zlib.
You can run the benchmarks against your own payloads by using the Go benchmarks tool.
Just export your payload filepath as the `PAYLOAD` environment variable and run the benchmarks:
```go
go test -bench .
```
Compression of a 7Mb pdf zstd (this wrapper) vs [czlib](https://github.com/DataDog/czlib):
```
BenchmarkCompression 5 221056624 ns/op 67.34 MB/s
BenchmarkDecompression 100 18370416 ns/op 810.32 MB/s
BenchmarkFzlibCompress 2 610156603 ns/op 24.40 MB/s
BenchmarkFzlibDecompress 20 81195246 ns/op 183.33 MB/s
```
Ratio is also better by a margin of ~20%.
Compression speed is always better than zlib on all the payloads we tested;
However, [czlib](https://github.com/DataDog/czlib) has optimisations that make it
faster at decompressiong small payloads:
```
Testing with size: 11... czlib: 8.97 MB/s, zstd: 3.26 MB/s
Testing with size: 27... czlib: 23.3 MB/s, zstd: 8.22 MB/s
Testing with size: 62... czlib: 31.6 MB/s, zstd: 19.49 MB/s
Testing with size: 141... czlib: 74.54 MB/s, zstd: 42.55 MB/s
Testing with size: 323... czlib: 155.14 MB/s, zstd: 99.39 MB/s
Testing with size: 739... czlib: 235.9 MB/s, zstd: 216.45 MB/s
Testing with size: 1689... czlib: 116.45 MB/s, zstd: 345.64 MB/s
Testing with size: 3858... czlib: 176.39 MB/s, zstd: 617.56 MB/s
Testing with size: 8811... czlib: 254.11 MB/s, zstd: 824.34 MB/s
Testing with size: 20121... czlib: 197.43 MB/s, zstd: 1339.11 MB/s
Testing with size: 45951... czlib: 201.62 MB/s, zstd: 1951.57 MB/s
```
zstd starts to shine with payloads > 1KB
### Stability - Current state: STABLE
The C library seems to be pretty stable and according to the author has been tested and fuzzed.
For the Go wrapper, the test cover most usual cases and we have succesfully tested it on all staging and prod data.
... ...
BSD License
For Zstandard software
Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name Facebook nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
... ...
/* ******************************************************************
bitstream
Part of FSE library
Copyright (C) 2013-present, Yann Collet.
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
You can contact the author at :
- Source repository : https://github.com/Cyan4973/FiniteStateEntropy
****************************************************************** */
#ifndef BITSTREAM_H_MODULE
#define BITSTREAM_H_MODULE
#if defined (__cplusplus)
extern "C" {
#endif
/*
* This API consists of small unitary functions, which must be inlined for best performance.
* Since link-time-optimization is not available for all compilers,
* these functions are defined into a .h to be included.
*/
/*-****************************************
* Dependencies
******************************************/
#include "mem.h" /* unaligned access routines */
#include "debug.h" /* assert(), DEBUGLOG(), RAWLOG() */
#include "error_private.h" /* error codes and messages */
/*=========================================
* Target specific
=========================================*/
#if defined(__BMI__) && defined(__GNUC__)
# include <immintrin.h> /* support for bextr (experimental) */
#endif
#define STREAM_ACCUMULATOR_MIN_32 25
#define STREAM_ACCUMULATOR_MIN_64 57
#define STREAM_ACCUMULATOR_MIN ((U32)(MEM_32bits() ? STREAM_ACCUMULATOR_MIN_32 : STREAM_ACCUMULATOR_MIN_64))
/*-******************************************
* bitStream encoding API (write forward)
********************************************/
/* bitStream can mix input from multiple sources.
* A critical property of these streams is that they encode and decode in **reverse** direction.
* So the first bit sequence you add will be the last to be read, like a LIFO stack.
*/
typedef struct {
size_t bitContainer;
unsigned bitPos;
char* startPtr;
char* ptr;
char* endPtr;
} BIT_CStream_t;
MEM_STATIC size_t BIT_initCStream(BIT_CStream_t* bitC, void* dstBuffer, size_t dstCapacity);
MEM_STATIC void BIT_addBits(BIT_CStream_t* bitC, size_t value, unsigned nbBits);
MEM_STATIC void BIT_flushBits(BIT_CStream_t* bitC);
MEM_STATIC size_t BIT_closeCStream(BIT_CStream_t* bitC);
/* Start with initCStream, providing the size of buffer to write into.
* bitStream will never write outside of this buffer.
* `dstCapacity` must be >= sizeof(bitD->bitContainer), otherwise @return will be an error code.
*
* bits are first added to a local register.
* Local register is size_t, hence 64-bits on 64-bits systems, or 32-bits on 32-bits systems.
* Writing data into memory is an explicit operation, performed by the flushBits function.
* Hence keep track how many bits are potentially stored into local register to avoid register overflow.
* After a flushBits, a maximum of 7 bits might still be stored into local register.
*
* Avoid storing elements of more than 24 bits if you want compatibility with 32-bits bitstream readers.
*
* Last operation is to close the bitStream.
* The function returns the final size of CStream in bytes.
* If data couldn't fit into `dstBuffer`, it will return a 0 ( == not storable)
*/
/*-********************************************
* bitStream decoding API (read backward)
**********************************************/
typedef struct {
size_t bitContainer;
unsigned bitsConsumed;
const char* ptr;
const char* start;
const char* limitPtr;
} BIT_DStream_t;
typedef enum { BIT_DStream_unfinished = 0,
BIT_DStream_endOfBuffer = 1,
BIT_DStream_completed = 2,
BIT_DStream_overflow = 3 } BIT_DStream_status; /* result of BIT_reloadDStream() */
/* 1,2,4,8 would be better for bitmap combinations, but slows down performance a bit ... :( */
MEM_STATIC size_t BIT_initDStream(BIT_DStream_t* bitD, const void* srcBuffer, size_t srcSize);
MEM_STATIC size_t BIT_readBits(BIT_DStream_t* bitD, unsigned nbBits);
MEM_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t* bitD);
MEM_STATIC unsigned BIT_endOfDStream(const BIT_DStream_t* bitD);
/* Start by invoking BIT_initDStream().
* A chunk of the bitStream is then stored into a local register.
* Local register size is 64-bits on 64-bits systems, 32-bits on 32-bits systems (size_t).
* You can then retrieve bitFields stored into the local register, **in reverse order**.
* Local register is explicitly reloaded from memory by the BIT_reloadDStream() method.
* A reload guarantee a minimum of ((8*sizeof(bitD->bitContainer))-7) bits when its result is BIT_DStream_unfinished.
* Otherwise, it can be less than that, so proceed accordingly.
* Checking if DStream has reached its end can be performed with BIT_endOfDStream().
*/
/*-****************************************
* unsafe API
******************************************/
MEM_STATIC void BIT_addBitsFast(BIT_CStream_t* bitC, size_t value, unsigned nbBits);
/* faster, but works only if value is "clean", meaning all high bits above nbBits are 0 */
MEM_STATIC void BIT_flushBitsFast(BIT_CStream_t* bitC);
/* unsafe version; does not check buffer overflow */
MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, unsigned nbBits);
/* faster, but works only if nbBits >= 1 */
/*-**************************************************************
* Internal functions
****************************************************************/
MEM_STATIC unsigned BIT_highbit32 (U32 val)
{
assert(val != 0);
{
# if defined(_MSC_VER) /* Visual */
unsigned long r=0;
_BitScanReverse ( &r, val );
return (unsigned) r;
# elif defined(__GNUC__) && (__GNUC__ >= 3) /* Use GCC Intrinsic */
return 31 - __builtin_clz (val);
# else /* Software version */
static const unsigned DeBruijnClz[32] = { 0, 9, 1, 10, 13, 21, 2, 29,
11, 14, 16, 18, 22, 25, 3, 30,
8, 12, 20, 28, 15, 17, 24, 7,
19, 27, 23, 6, 26, 5, 4, 31 };
U32 v = val;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
return DeBruijnClz[ (U32) (v * 0x07C4ACDDU) >> 27];
# endif
}
}
/*===== Local Constants =====*/
static const unsigned BIT_mask[] = {
0, 1, 3, 7, 0xF, 0x1F,
0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF,
0xFFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, 0x1FFFF,
0x3FFFF, 0x7FFFF, 0xFFFFF, 0x1FFFFF, 0x3FFFFF, 0x7FFFFF,
0xFFFFFF, 0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF, 0xFFFFFFF, 0x1FFFFFFF,
0x3FFFFFFF, 0x7FFFFFFF}; /* up to 31 bits */
#define BIT_MASK_SIZE (sizeof(BIT_mask) / sizeof(BIT_mask[0]))
/*-**************************************************************
* bitStream encoding
****************************************************************/
/*! BIT_initCStream() :
* `dstCapacity` must be > sizeof(size_t)
* @return : 0 if success,
* otherwise an error code (can be tested using ERR_isError()) */
MEM_STATIC size_t BIT_initCStream(BIT_CStream_t* bitC,
void* startPtr, size_t dstCapacity)
{
bitC->bitContainer = 0;
bitC->bitPos = 0;
bitC->startPtr = (char*)startPtr;
bitC->ptr = bitC->startPtr;
bitC->endPtr = bitC->startPtr + dstCapacity - sizeof(bitC->bitContainer);
if (dstCapacity <= sizeof(bitC->bitContainer)) return ERROR(dstSize_tooSmall);
return 0;
}
/*! BIT_addBits() :
* can add up to 31 bits into `bitC`.
* Note : does not check for register overflow ! */
MEM_STATIC void BIT_addBits(BIT_CStream_t* bitC,
size_t value, unsigned nbBits)
{
MEM_STATIC_ASSERT(BIT_MASK_SIZE == 32);
assert(nbBits < BIT_MASK_SIZE);
assert(nbBits + bitC->bitPos < sizeof(bitC->bitContainer) * 8);
bitC->bitContainer |= (value & BIT_mask[nbBits]) << bitC->bitPos;
bitC->bitPos += nbBits;
}
/*! BIT_addBitsFast() :
* works only if `value` is _clean_,
* meaning all high bits above nbBits are 0 */
MEM_STATIC void BIT_addBitsFast(BIT_CStream_t* bitC,
size_t value, unsigned nbBits)
{
assert((value>>nbBits) == 0);
assert(nbBits + bitC->bitPos < sizeof(bitC->bitContainer) * 8);
bitC->bitContainer |= value << bitC->bitPos;
bitC->bitPos += nbBits;
}
/*! BIT_flushBitsFast() :
* assumption : bitContainer has not overflowed
* unsafe version; does not check buffer overflow */
MEM_STATIC void BIT_flushBitsFast(BIT_CStream_t* bitC)
{
size_t const nbBytes = bitC->bitPos >> 3;
assert(bitC->bitPos < sizeof(bitC->bitContainer) * 8);
MEM_writeLEST(bitC->ptr, bitC->bitContainer);
bitC->ptr += nbBytes;
assert(bitC->ptr <= bitC->endPtr);
bitC->bitPos &= 7;
bitC->bitContainer >>= nbBytes*8;
}
/*! BIT_flushBits() :
* assumption : bitContainer has not overflowed
* safe version; check for buffer overflow, and prevents it.
* note : does not signal buffer overflow.
* overflow will be revealed later on using BIT_closeCStream() */
MEM_STATIC void BIT_flushBits(BIT_CStream_t* bitC)
{
size_t const nbBytes = bitC->bitPos >> 3;
assert(bitC->bitPos < sizeof(bitC->bitContainer) * 8);
MEM_writeLEST(bitC->ptr, bitC->bitContainer);
bitC->ptr += nbBytes;
if (bitC->ptr > bitC->endPtr) bitC->ptr = bitC->endPtr;
bitC->bitPos &= 7;
bitC->bitContainer >>= nbBytes*8;
}
/*! BIT_closeCStream() :
* @return : size of CStream, in bytes,
* or 0 if it could not fit into dstBuffer */
MEM_STATIC size_t BIT_closeCStream(BIT_CStream_t* bitC)
{
BIT_addBitsFast(bitC, 1, 1); /* endMark */
BIT_flushBits(bitC);
if (bitC->ptr >= bitC->endPtr) return 0; /* overflow detected */
return (bitC->ptr - bitC->startPtr) + (bitC->bitPos > 0);
}
/*-********************************************************
* bitStream decoding
**********************************************************/
/*! BIT_initDStream() :
* Initialize a BIT_DStream_t.
* `bitD` : a pointer to an already allocated BIT_DStream_t structure.
* `srcSize` must be the *exact* size of the bitStream, in bytes.
* @return : size of stream (== srcSize), or an errorCode if a problem is detected
*/
MEM_STATIC size_t BIT_initDStream(BIT_DStream_t* bitD, const void* srcBuffer, size_t srcSize)
{
if (srcSize < 1) { memset(bitD, 0, sizeof(*bitD)); return ERROR(srcSize_wrong); }
bitD->start = (const char*)srcBuffer;
bitD->limitPtr = bitD->start + sizeof(bitD->bitContainer);
if (srcSize >= sizeof(bitD->bitContainer)) { /* normal case */
bitD->ptr = (const char*)srcBuffer + srcSize - sizeof(bitD->bitContainer);
bitD->bitContainer = MEM_readLEST(bitD->ptr);
{ BYTE const lastByte = ((const BYTE*)srcBuffer)[srcSize-1];
bitD->bitsConsumed = lastByte ? 8 - BIT_highbit32(lastByte) : 0; /* ensures bitsConsumed is always set */
if (lastByte == 0) return ERROR(GENERIC); /* endMark not present */ }
} else {
bitD->ptr = bitD->start;
bitD->bitContainer = *(const BYTE*)(bitD->start);
switch(srcSize)
{
case 7: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[6]) << (sizeof(bitD->bitContainer)*8 - 16);
/* fall-through */
case 6: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[5]) << (sizeof(bitD->bitContainer)*8 - 24);
/* fall-through */
case 5: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[4]) << (sizeof(bitD->bitContainer)*8 - 32);
/* fall-through */
case 4: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[3]) << 24;
/* fall-through */
case 3: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[2]) << 16;
/* fall-through */
case 2: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[1]) << 8;
/* fall-through */
default: break;
}
{ BYTE const lastByte = ((const BYTE*)srcBuffer)[srcSize-1];
bitD->bitsConsumed = lastByte ? 8 - BIT_highbit32(lastByte) : 0;
if (lastByte == 0) return ERROR(corruption_detected); /* endMark not present */
}
bitD->bitsConsumed += (U32)(sizeof(bitD->bitContainer) - srcSize)*8;
}
return srcSize;
}
MEM_STATIC size_t BIT_getUpperBits(size_t bitContainer, U32 const start)
{
return bitContainer >> start;
}
MEM_STATIC size_t BIT_getMiddleBits(size_t bitContainer, U32 const start, U32 const nbBits)
{
U32 const regMask = sizeof(bitContainer)*8 - 1;
/* if start > regMask, bitstream is corrupted, and result is undefined */
assert(nbBits < BIT_MASK_SIZE);
return (bitContainer >> (start & regMask)) & BIT_mask[nbBits];
}
MEM_STATIC size_t BIT_getLowerBits(size_t bitContainer, U32 const nbBits)
{
assert(nbBits < BIT_MASK_SIZE);
return bitContainer & BIT_mask[nbBits];
}
/*! BIT_lookBits() :
* Provides next n bits from local register.
* local register is not modified.
* On 32-bits, maxNbBits==24.
* On 64-bits, maxNbBits==56.
* @return : value extracted */
MEM_STATIC size_t BIT_lookBits(const BIT_DStream_t* bitD, U32 nbBits)
{
/* arbitrate between double-shift and shift+mask */
#if 1
/* if bitD->bitsConsumed + nbBits > sizeof(bitD->bitContainer)*8,
* bitstream is likely corrupted, and result is undefined */
return BIT_getMiddleBits(bitD->bitContainer, (sizeof(bitD->bitContainer)*8) - bitD->bitsConsumed - nbBits, nbBits);
#else
/* this code path is slower on my os-x laptop */
U32 const regMask = sizeof(bitD->bitContainer)*8 - 1;
return ((bitD->bitContainer << (bitD->bitsConsumed & regMask)) >> 1) >> ((regMask-nbBits) & regMask);
#endif
}
/*! BIT_lookBitsFast() :
* unsafe version; only works if nbBits >= 1 */
MEM_STATIC size_t BIT_lookBitsFast(const BIT_DStream_t* bitD, U32 nbBits)
{
U32 const regMask = sizeof(bitD->bitContainer)*8 - 1;
assert(nbBits >= 1);
return (bitD->bitContainer << (bitD->bitsConsumed & regMask)) >> (((regMask+1)-nbBits) & regMask);
}
MEM_STATIC void BIT_skipBits(BIT_DStream_t* bitD, U32 nbBits)
{
bitD->bitsConsumed += nbBits;
}
/*! BIT_readBits() :
* Read (consume) next n bits from local register and update.
* Pay attention to not read more than nbBits contained into local register.
* @return : extracted value. */
MEM_STATIC size_t BIT_readBits(BIT_DStream_t* bitD, unsigned nbBits)
{
size_t const value = BIT_lookBits(bitD, nbBits);
BIT_skipBits(bitD, nbBits);
return value;
}
/*! BIT_readBitsFast() :
* unsafe version; only works only if nbBits >= 1 */
MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, unsigned nbBits)
{
size_t const value = BIT_lookBitsFast(bitD, nbBits);
assert(nbBits >= 1);
BIT_skipBits(bitD, nbBits);
return value;
}
/*! BIT_reloadDStream() :
* Refill `bitD` from buffer previously set in BIT_initDStream() .
* This function is safe, it guarantees it will not read beyond src buffer.
* @return : status of `BIT_DStream_t` internal register.
* when status == BIT_DStream_unfinished, internal register is filled with at least 25 or 57 bits */
MEM_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t* bitD)
{
if (bitD->bitsConsumed > (sizeof(bitD->bitContainer)*8)) /* overflow detected, like end of stream */
return BIT_DStream_overflow;
if (bitD->ptr >= bitD->limitPtr) {
bitD->ptr -= bitD->bitsConsumed >> 3;
bitD->bitsConsumed &= 7;
bitD->bitContainer = MEM_readLEST(bitD->ptr);
return BIT_DStream_unfinished;
}
if (bitD->ptr == bitD->start) {
if (bitD->bitsConsumed < sizeof(bitD->bitContainer)*8) return BIT_DStream_endOfBuffer;
return BIT_DStream_completed;
}
/* start < ptr < limitPtr */
{ U32 nbBytes = bitD->bitsConsumed >> 3;
BIT_DStream_status result = BIT_DStream_unfinished;
if (bitD->ptr - nbBytes < bitD->start) {
nbBytes = (U32)(bitD->ptr - bitD->start); /* ptr > start */
result = BIT_DStream_endOfBuffer;
}
bitD->ptr -= nbBytes;
bitD->bitsConsumed -= nbBytes*8;
bitD->bitContainer = MEM_readLEST(bitD->ptr); /* reminder : srcSize > sizeof(bitD->bitContainer), otherwise bitD->ptr == bitD->start */
return result;
}
}
/*! BIT_endOfDStream() :
* @return : 1 if DStream has _exactly_ reached its end (all bits consumed).
*/
MEM_STATIC unsigned BIT_endOfDStream(const BIT_DStream_t* DStream)
{
return ((DStream->ptr == DStream->start) && (DStream->bitsConsumed == sizeof(DStream->bitContainer)*8));
}
#if defined (__cplusplus)
}
#endif
#endif /* BITSTREAM_H_MODULE */
... ...
/*
* Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under both the BSD-style license (found in the
* LICENSE file in the root directory of this source tree) and the GPLv2 (found
* in the COPYING file in the root directory of this source tree).
* You may select, at your option, one of the above-listed licenses.
*/
#ifndef ZSTD_COMPILER_H
#define ZSTD_COMPILER_H
/*-*******************************************************
* Compiler specifics
*********************************************************/
/* force inlining */
#if !defined(ZSTD_NO_INLINE)
#if defined (__GNUC__) || defined(__cplusplus) || defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */
# define INLINE_KEYWORD inline
#else
# define INLINE_KEYWORD
#endif
#if defined(__GNUC__)
# define FORCE_INLINE_ATTR __attribute__((always_inline))
#elif defined(_MSC_VER)
# define FORCE_INLINE_ATTR __forceinline
#else
# define FORCE_INLINE_ATTR
#endif
#else
#define INLINE_KEYWORD
#define FORCE_INLINE_ATTR
#endif
/**
* FORCE_INLINE_TEMPLATE is used to define C "templates", which take constant
* parameters. They must be inlined for the compiler to elimininate the constant
* branches.
*/
#define FORCE_INLINE_TEMPLATE static INLINE_KEYWORD FORCE_INLINE_ATTR
/**
* HINT_INLINE is used to help the compiler generate better code. It is *not*
* used for "templates", so it can be tweaked based on the compilers
* performance.
*
* gcc-4.8 and gcc-4.9 have been shown to benefit from leaving off the
* always_inline attribute.
*
* clang up to 5.0.0 (trunk) benefit tremendously from the always_inline
* attribute.
*/
#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 8 && __GNUC__ < 5
# define HINT_INLINE static INLINE_KEYWORD
#else
# define HINT_INLINE static INLINE_KEYWORD FORCE_INLINE_ATTR
#endif
/* force no inlining */
#ifdef _MSC_VER
# define FORCE_NOINLINE static __declspec(noinline)
#else
# ifdef __GNUC__
# define FORCE_NOINLINE static __attribute__((__noinline__))
# else
# define FORCE_NOINLINE static
# endif
#endif
/* target attribute */
#ifndef __has_attribute
#define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
#endif
#if defined(__GNUC__)
# define TARGET_ATTRIBUTE(target) __attribute__((__target__(target)))
#else
# define TARGET_ATTRIBUTE(target)
#endif
/* Enable runtime BMI2 dispatch based on the CPU.
* Enabled for clang & gcc >=4.8 on x86 when BMI2 isn't enabled by default.
*/
#ifndef DYNAMIC_BMI2
#if ((defined(__clang__) && __has_attribute(__target__)) \
|| (defined(__GNUC__) \
&& (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))) \
&& (defined(__x86_64__) || defined(_M_X86)) \
&& !defined(__BMI2__)
# define DYNAMIC_BMI2 1
#else
# define DYNAMIC_BMI2 0
#endif
#endif
/* prefetch
* can be disabled, by declaring NO_PREFETCH build macro */
#if defined(NO_PREFETCH)
# define PREFETCH_L1(ptr) (void)(ptr) /* disabled */
# define PREFETCH_L2(ptr) (void)(ptr) /* disabled */
#else
# if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_I86)) /* _mm_prefetch() is not defined outside of x86/x64 */
# include <mmintrin.h> /* https://msdn.microsoft.com/fr-fr/library/84szxsww(v=vs.90).aspx */
# define PREFETCH_L1(ptr) _mm_prefetch((const char*)(ptr), _MM_HINT_T0)
# define PREFETCH_L2(ptr) _mm_prefetch((const char*)(ptr), _MM_HINT_T1)
# elif defined(__GNUC__) && ( (__GNUC__ >= 4) || ( (__GNUC__ == 3) && (__GNUC_MINOR__ >= 1) ) )
# define PREFETCH_L1(ptr) __builtin_prefetch((ptr), 0 /* rw==read */, 3 /* locality */)
# define PREFETCH_L2(ptr) __builtin_prefetch((ptr), 0 /* rw==read */, 2 /* locality */)
# else
# define PREFETCH_L1(ptr) (void)(ptr) /* disabled */
# define PREFETCH_L2(ptr) (void)(ptr) /* disabled */
# endif
#endif /* NO_PREFETCH */
#define CACHELINE_SIZE 64
#define PREFETCH_AREA(p, s) { \
const char* const _ptr = (const char*)(p); \
size_t const _size = (size_t)(s); \
size_t _pos; \
for (_pos=0; _pos<_size; _pos+=CACHELINE_SIZE) { \
PREFETCH_L2(_ptr + _pos); \
} \
}
/* disable warnings */
#ifdef _MSC_VER /* Visual Studio */
# include <intrin.h> /* For Visual 2005 */
# pragma warning(disable : 4100) /* disable: C4100: unreferenced formal parameter */
# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
# pragma warning(disable : 4204) /* disable: C4204: non-constant aggregate initializer */
# pragma warning(disable : 4214) /* disable: C4214: non-int bitfields */
# pragma warning(disable : 4324) /* disable: C4324: padded structure */
#endif
#endif /* ZSTD_COMPILER_H */
... ...