475 lines
17 KiB
Go
475 lines
17 KiB
Go
package report
|
||
|
||
import (
|
||
"errors"
|
||
"fmt"
|
||
"math"
|
||
"sort"
|
||
"strings"
|
||
"time"
|
||
|
||
"github.com/shopspring/decimal"
|
||
|
||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||
|
||
"git.rosy.net.cn/jx-callback/business/partner"
|
||
|
||
"git.rosy.net.cn/jx-callback/business/jxstore/permission"
|
||
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
|
||
|
||
"git.rosy.net.cn/baseapi/utils"
|
||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||
"git.rosy.net.cn/jx-callback/business/model"
|
||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||
)
|
||
|
||
type tStoreSkuBindAndSkuName struct {
|
||
CityCode int
|
||
StoreID int `orm:"column(store_id)"`
|
||
NameID int `orm:"column(name_id)"`
|
||
UnitPrice int
|
||
UnitPriceList []int
|
||
}
|
||
|
||
func GetStatisticsReportForOrders(ctx *jxcontext.Context, storeIDs []int, fromDate string, toDate string) (statisticsReportForOrdersList []*dao.StatisticsReportForOrdersList, err error) {
|
||
db := dao.GetDB()
|
||
fromDateParm := utils.Str2Time(fromDate)
|
||
toDateParm := utils.Str2Time(toDate)
|
||
//若时间间隔大于3个月则不允许查询
|
||
if math.Ceil(toDateParm.Sub(fromDateParm).Hours()/24) > 92 {
|
||
return nil, errors.New(fmt.Sprintf("查询间隔时间不允许大于3个月!: 时间范围:[%v] 至 [%v]", fromDate, toDate))
|
||
}
|
||
statisticsReportForOrdersList, err = dao.GetStatisticsReportForOrders(db, storeIDs, fromDateParm, toDateParm)
|
||
return statisticsReportForOrdersList, err
|
||
}
|
||
|
||
func GetStatisticsReportForAfsOrders(ctx *jxcontext.Context, storeIDs []int, fromDate string, toDate string) (statisticsReportForOrdersList []*dao.StatisticsReportForOrdersList, err error) {
|
||
db := dao.GetDB()
|
||
fromDateParm := utils.Str2Time(fromDate)
|
||
toDateParm := utils.Str2Time(toDate)
|
||
//若时间间隔大于3个月则不允许查询
|
||
if math.Ceil(toDateParm.Sub(fromDateParm).Hours()/24) > 92 {
|
||
return nil, errors.New(fmt.Sprintf("查询间隔时间不允许大于3个月!: 时间范围:[%v] 至 [%v]", fromDate, toDate))
|
||
}
|
||
statisticsReportForOrdersList, err = dao.GetGetStatisticsReportForAfsOrders(db, storeIDs, fromDateParm, toDateParm)
|
||
return statisticsReportForOrdersList, err
|
||
}
|
||
|
||
func StatisticsReportForStoreSkusPrice(ctx *jxcontext.Context, cityCodes, skuIDs []int, snapDate string, offset, pageSize int) (pagedInfo *model.PagedInfo, err error) {
|
||
var snapDateParam time.Time
|
||
db := dao.GetDB()
|
||
if snapDate != "" {
|
||
snapDateParam = utils.Str2Time(snapDate)
|
||
}
|
||
priceReferSnapshot, totalCount, err := dao.GetPriceReferSnapshot(db, cityCodes, skuIDs, 0, snapDateParam, offset, pageSize)
|
||
pagedInfo = &model.PagedInfo{
|
||
Data: priceReferSnapshot,
|
||
TotalCount: totalCount,
|
||
}
|
||
return
|
||
}
|
||
|
||
func BeginSavePriceRefer(ctx *jxcontext.Context, cityCodes, skuIDs []int, isAsync, isContinueWhenError bool) (hint string, err error) {
|
||
var priceReferSnapshotList []*model.PriceReferSnapshot
|
||
db := dao.GetDB()
|
||
snapshotAt := utils.Time2Date(time.Now().AddDate(0, 0, -1))
|
||
dao.DeletePriceReferHistory(db, utils.Time2Date(snapshotAt.AddDate(0, 0, -7)))
|
||
priceReferSnapshotDelete := &model.PriceReferSnapshot{SnapshotAt: snapshotAt}
|
||
dao.DeleteEntity(db, priceReferSnapshotDelete, "SnapshotAt")
|
||
taskSeqFunc := func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
|
||
switch step {
|
||
case 0:
|
||
priceReferSnapshot, err := dao.GetStatisticsReportForStoreSkusPrice(db, cityCodes, skuIDs)
|
||
if len(priceReferSnapshot) > 0 {
|
||
dao.Begin(db)
|
||
defer func() {
|
||
if r := recover(); r != nil || err != nil {
|
||
dao.Rollback(db)
|
||
if r != nil {
|
||
panic(r)
|
||
}
|
||
}
|
||
}()
|
||
for _, v := range priceReferSnapshot {
|
||
dao.WrapAddIDCULDEntity(v, ctx.GetUserName())
|
||
v.SnapshotAt = snapshotAt
|
||
}
|
||
dao.CreateMultiEntities(db, priceReferSnapshot)
|
||
dao.Commit(db)
|
||
}
|
||
case 1:
|
||
priceReferSnapshotList, err = dao.GetPriceReferSnapshotNoPage(db, nil, nil, nil, snapshotAt)
|
||
var (
|
||
citySkuMap = make(map[int]map[int][]int)
|
||
countryMap = make(map[int][]int)
|
||
resultMap = make(map[int]map[int]*model.PriceReferSnapshot)
|
||
resultCountryMap = make(map[int]*model.PriceReferSnapshot)
|
||
)
|
||
storeList, err := dao.GetStoreList(db, nil, nil, nil, nil, nil, "")
|
||
if err != nil {
|
||
return result, err
|
||
}
|
||
for _, v := range storeList {
|
||
if v.PayPercentage < 50 {
|
||
continue
|
||
}
|
||
var tList []*tStoreSkuBindAndSkuName
|
||
sql := `
|
||
SELECT DISTINCT b.city_code, a.store_id, Round(a.unit_price * IF(b.pay_percentage < 50 , 70, b.pay_percentage) / 100) AS unit_price, c.name_id
|
||
FROM store_sku_bind a
|
||
JOIN store b ON b.id = a.store_id AND b.deleted_at = ? AND b.status != ?
|
||
JOIN sku c ON c.id = a.sku_id
|
||
WHERE a.store_id = ?
|
||
AND c.name_id NOT IN(
|
||
SELECT b.name_id
|
||
FROM store_sku_bind a
|
||
JOIN sku b ON a.sku_id = b.id AND b.deleted_at = ?
|
||
WHERE a.deleted_at = ?
|
||
AND a.store_id = ?
|
||
AND b.name_id NOT IN(SELECT DISTINCT b.name_id
|
||
FROM store_sku_bind a
|
||
JOIN sku b ON a.sku_id = b.id AND b.deleted_at = ?
|
||
WHERE a.deleted_at = ?
|
||
AND a.store_id = ?
|
||
AND a.status = ?)
|
||
)
|
||
AND a.deleted_at = ?
|
||
`
|
||
sqlParams := []interface{}{
|
||
utils.DefaultTimeValue,
|
||
model.StoreStatusDisabled,
|
||
v.ID,
|
||
utils.DefaultTimeValue,
|
||
utils.DefaultTimeValue,
|
||
v.ID,
|
||
utils.DefaultTimeValue,
|
||
utils.DefaultTimeValue,
|
||
v.ID,
|
||
model.StoreSkuBindStatusNormal,
|
||
utils.DefaultTimeValue,
|
||
}
|
||
dao.GetRows(db, &tList, sql, sqlParams...)
|
||
skuNameMap := make(map[int][]int)
|
||
if len(tList) > 0 {
|
||
for _, vv := range tList {
|
||
skuNameMap[vv.NameID] = append(skuNameMap[vv.NameID], vv.UnitPrice)
|
||
countryMap[vv.NameID] = append(countryMap[vv.NameID], vv.UnitPrice)
|
||
}
|
||
if citySkuMap[v.CityCode] != nil {
|
||
for nameID, unitPriceList := range skuNameMap {
|
||
if citySkuMap[v.CityCode][nameID] != nil {
|
||
citySkuMap[v.CityCode][nameID] = append(citySkuMap[v.CityCode][nameID], unitPriceList...)
|
||
} else {
|
||
citySkuMap[v.CityCode][nameID] = unitPriceList
|
||
}
|
||
}
|
||
} else {
|
||
citySkuMap[v.CityCode] = skuNameMap
|
||
}
|
||
}
|
||
}
|
||
for k, v := range countryMap {
|
||
var midUnitPrice int
|
||
var avgUnitPrice int
|
||
sort.Ints(v)
|
||
if len(v)%2 == 0 {
|
||
midUnitPrice = v[len(v)/2-1]
|
||
} else {
|
||
midUnitPrice = v[len(v)/2]
|
||
}
|
||
for _, vv := range v {
|
||
avgUnitPrice += vv
|
||
}
|
||
priceRefer := &model.PriceReferSnapshot{
|
||
MidUnitPrice: midUnitPrice,
|
||
MaxUnitPrice: v[len(v)-1],
|
||
MinUnitPrice: v[0],
|
||
AvgUnitPrice: avgUnitPrice / len(v),
|
||
}
|
||
resultCountryMap[k] = priceRefer
|
||
}
|
||
for k1, v := range citySkuMap {
|
||
skuNameMap := make(map[int]*model.PriceReferSnapshot)
|
||
for k2, _ := range v {
|
||
var midUnitPrice int
|
||
var avgUnitPrice int
|
||
sort.Ints(v[k2])
|
||
if len(v[k2])%2 == 0 {
|
||
midUnitPrice = v[k2][len(v[k2])/2-1]
|
||
} else {
|
||
midUnitPrice = v[k2][len(v[k2])/2]
|
||
}
|
||
for _, vv := range v[k2] {
|
||
avgUnitPrice += vv
|
||
}
|
||
skuNameMap[k2] = &model.PriceReferSnapshot{
|
||
MidUnitPrice: midUnitPrice,
|
||
MaxUnitPrice: v[k2][len(v[k2])-1],
|
||
MinUnitPrice: v[k2][0],
|
||
AvgUnitPrice: avgUnitPrice / len(v[k2]),
|
||
}
|
||
}
|
||
resultMap[k1] = skuNameMap
|
||
}
|
||
dao.Begin(db)
|
||
defer func() {
|
||
if r := recover(); r != nil || err != nil {
|
||
dao.Rollback(db)
|
||
if r != nil {
|
||
panic(r)
|
||
}
|
||
}
|
||
}()
|
||
if len(priceReferSnapshotList) > 0 {
|
||
for _, v := range priceReferSnapshotList {
|
||
if v.CityCode == 0 {
|
||
if resultCountryMap[v.NameID] != nil {
|
||
v.MidUnitPrice = resultCountryMap[v.NameID].MidUnitPrice
|
||
v.MaxUnitPrice = resultCountryMap[v.NameID].MaxUnitPrice
|
||
v.AvgUnitPrice = resultCountryMap[v.NameID].AvgUnitPrice
|
||
v.MinUnitPrice = resultCountryMap[v.NameID].MinUnitPrice
|
||
dao.UpdateEntity(db, v, "MidUnitPrice", "MaxUnitPrice", "MinUnitPrice", "AvgUnitPrice")
|
||
}
|
||
continue
|
||
}
|
||
if resultMap[v.CityCode][v.NameID] != nil {
|
||
v.MidUnitPrice = resultMap[v.CityCode][v.NameID].MidUnitPrice
|
||
v.MaxUnitPrice = resultMap[v.CityCode][v.NameID].MaxUnitPrice
|
||
v.AvgUnitPrice = resultMap[v.CityCode][v.NameID].AvgUnitPrice
|
||
v.MinUnitPrice = resultMap[v.CityCode][v.NameID].MinUnitPrice
|
||
dao.UpdateEntity(db, v, "MidUnitPrice", "MaxUnitPrice", "MinUnitPrice", "AvgUnitPrice")
|
||
}
|
||
}
|
||
}
|
||
dao.Commit(db)
|
||
case 2:
|
||
dao.Begin(db)
|
||
defer func() {
|
||
if r := recover(); r != nil || err != nil {
|
||
dao.Rollback(db)
|
||
if r != nil {
|
||
panic(r)
|
||
}
|
||
}
|
||
}()
|
||
if len(priceReferSnapshotList) > 0 {
|
||
for _, v := range priceReferSnapshotList {
|
||
result, _ := dao.GetPriceReferPrice(db, v.CityCode, v.SkuID, snapshotAt)
|
||
v.MaxPrice = result.MaxPrice
|
||
v.MinPrice = result.MinPrice
|
||
v.AvgPrice = result.AvgPrice
|
||
v.MidPrice = result.MidPrice
|
||
dao.UpdateEntity(db, v, "MidPrice", "MaxPrice", "MinPrice", "AvgPrice")
|
||
}
|
||
}
|
||
dao.Commit(db)
|
||
//TODO 京东查询接口报错,暂时屏蔽了
|
||
// case 3:
|
||
// priceReferSnapshotList, err = dao.GetPriceReferSnapshotNoPage(db, []int{0}, nil, nil, snapshotAt)
|
||
// taskFunc := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||
// v := batchItemList[0].(*model.PriceReferSnapshot)
|
||
// for _, appOrg := range apimanager.CurAPIManager.GetAppOrgCodeList(model.VendorIDJD) {
|
||
// directPrice, _ := jd.GetAPI(appOrg).GetJdSkuDirectPrice(v.SkuID)
|
||
// v.JdDirectPrice = int(directPrice)
|
||
// dao.UpdateEntity(db, v, "JdDirectPrice")
|
||
// }
|
||
// return retVal, err
|
||
// }
|
||
// taskParallel := tasksch.NewParallelTask("获取并更新京东指导价格", tasksch.NewParallelConfig(), ctx, taskFunc, priceReferSnapshotList)
|
||
// tasksch.HandleTask(taskParallel, task, true).Run()
|
||
// _, err = taskParallel.GetResult(0)
|
||
}
|
||
return result, err
|
||
}
|
||
taskSeq := tasksch.NewSeqTask2("生成每日价格统计", ctx, isContinueWhenError, taskSeqFunc, 3)
|
||
tasksch.HandleTask(taskSeq, nil, true).Run()
|
||
if !isAsync {
|
||
_, err = taskSeq.GetResult(0)
|
||
hint = "1"
|
||
} else {
|
||
hint = taskSeq.GetID()
|
||
}
|
||
return hint, err
|
||
}
|
||
|
||
type GetManageStateResult struct {
|
||
OpenStoreCount int `json:"openStoreCount"` //营业店铺数
|
||
HaveRestStoreCount int `json:"haveRestStoreCount"` //临时休息店铺数
|
||
RestStoreCount int `json:"restStoreCount"` //休息店铺数
|
||
FinishOrderState *GetManageStateOrderInfo //完成订单情况
|
||
IngOrderState *GetManageStateOrderInfo //进行中订单情况
|
||
CancelOrderState *GetManageStateOrderInfo //取消订单情况
|
||
}
|
||
|
||
type GetManageStateOrderInfo struct {
|
||
ActualPayPrice int `json:"actualPayPrice"` //支付总额
|
||
TotalShopMoney int `json:"totalShopMoney"` //平台结算
|
||
DesiredFee int `json:"desiredFee"` //配送费
|
||
Yhld string `json:"yhld"` //优惠力度(支付总额-平台结算-配送支出)/支付总额*100%
|
||
}
|
||
|
||
func getStoreStatusCount(db *dao.DaoDB, cityCodes []int, vendorID, status int) (count int, err error) {
|
||
countType := &struct{ Count int }{}
|
||
sqlParams := []interface{}{}
|
||
sql := `
|
||
SELECT COUNT(DISTINCT a.id) count
|
||
FROM store a
|
||
LEFT JOIN store_map b ON a.id = b.store_id AND b.deleted_at = ?
|
||
WHERE a.deleted_at = ?
|
||
`
|
||
sqlParams = append(sqlParams, utils.DefaultTimeValue, utils.DefaultTimeValue)
|
||
if len(cityCodes) > 0 {
|
||
sql += " AND a.city_code IN (" + dao.GenQuestionMarks(len(cityCodes)) + ")"
|
||
sqlParams = append(sqlParams, cityCodes)
|
||
}
|
||
if vendorID != -1 {
|
||
sql += " AND b.vendor_id = ? AND b.status = ?"
|
||
sqlParams = append(sqlParams, vendorID, status)
|
||
} else {
|
||
sql += " AND a.status = ?"
|
||
sqlParams = append(sqlParams, status)
|
||
}
|
||
err = dao.GetRow(db, &countType, sql, sqlParams)
|
||
return countType.Count, err
|
||
}
|
||
|
||
func getOrderStateCount(db *dao.DaoDB, cityCodes []int, vendorID, status int) (getManageStateOrderInfo *GetManageStateOrderInfo, err error) {
|
||
sqlParams := []interface{}{}
|
||
getManageStateOrderInfo = &GetManageStateOrderInfo{}
|
||
endTime := time.Now().AddDate(0, -3, 0)
|
||
sql := `
|
||
SELECT SUM(b.actual_pay_price) actual_pay_price, SUM(b.total_shop_money) total_shop_money, SUM(IFNULL(c.desired_fee, 0)) desired_fee
|
||
FROM store a
|
||
LEFT JOIN goods_order b ON a.id = IF(b.jx_store_id = 0, b.store_id, b.jx_store_id)
|
||
LEFT JOIN waybill c ON IF(b.waybill_vendor_id = -1,b.vendor_order_id,b.vendor_waybill_id) = c.vendor_waybill_id
|
||
WHERE a.deleted_at = ? AND a.status <> ?
|
||
AND b.order_created_at < ? AND b.order_created_at > ?
|
||
`
|
||
sqlParams = append(sqlParams, utils.DefaultTimeValue, model.StoreStatusDisabled, time.Now(), endTime)
|
||
if len(cityCodes) > 0 {
|
||
sql += " AND a.city_code IN (" + dao.GenQuestionMarks(len(cityCodes)) + ")"
|
||
sqlParams = append(sqlParams, cityCodes)
|
||
}
|
||
if vendorID != -1 {
|
||
sql += " AND b.vendor_id = ?"
|
||
sqlParams = append(sqlParams, vendorID)
|
||
}
|
||
if status != model.OrderStatusDelivering {
|
||
sql += " AND b.status = ?"
|
||
sqlParams = append(sqlParams, status)
|
||
} else {
|
||
sql += " AND b.status < ? AND b.status >= ?"
|
||
sqlParams = append(sqlParams, model.OrderStatusEndBegin, model.OrderStatusWait4Pay)
|
||
}
|
||
err = dao.GetRow(db, &getManageStateOrderInfo, sql, sqlParams)
|
||
getManageStateOrderInfo.Yhld = utils.Float64ToStr(math.Round((float64(getManageStateOrderInfo.ActualPayPrice-getManageStateOrderInfo.TotalShopMoney-getManageStateOrderInfo.DesiredFee) / float64(getManageStateOrderInfo.ActualPayPrice) * 100))) + "%"
|
||
return getManageStateOrderInfo, err
|
||
}
|
||
|
||
func GetManageState(ctx *jxcontext.Context, cityCodes []int, vendorID int) (getManageStateResult *GetManageStateResult, err error) {
|
||
var (
|
||
db = dao.GetDB()
|
||
)
|
||
getManageStateResult = &GetManageStateResult{}
|
||
if openCount, err := getStoreStatusCount(db, cityCodes, vendorID, model.StoreStatusOpened); err == nil {
|
||
getManageStateResult.OpenStoreCount = openCount
|
||
}
|
||
if haveRestCount, err := getStoreStatusCount(db, cityCodes, vendorID, model.StoreStatusHaveRest); err == nil {
|
||
getManageStateResult.HaveRestStoreCount = haveRestCount
|
||
}
|
||
if restCount, err := getStoreStatusCount(db, cityCodes, vendorID, model.StoreStatusClosed); err == nil {
|
||
getManageStateResult.RestStoreCount = restCount
|
||
}
|
||
if finishCount, err := getOrderStateCount(db, cityCodes, vendorID, model.OrderStatusFinished); err == nil {
|
||
getManageStateResult.FinishOrderState = finishCount
|
||
}
|
||
if ingCount, err := getOrderStateCount(db, cityCodes, vendorID, model.OrderStatusDelivering); err == nil {
|
||
getManageStateResult.IngOrderState = ingCount
|
||
}
|
||
if cancelCount, err := getOrderStateCount(db, cityCodes, vendorID, model.OrderStatusCanceled); err == nil {
|
||
getManageStateResult.CancelOrderState = cancelCount
|
||
}
|
||
return getManageStateResult, err
|
||
}
|
||
|
||
type GetStoreManageStateResult struct {
|
||
StoreID int `json:"storeID"`
|
||
StoreName string `json:"storeName"`
|
||
CoverArea float64 `json:"coverArea"`
|
||
MarketScale int `json:"marketScale"` //市场规模
|
||
OpenTime int `json:"openTime"` //营业时长
|
||
SkuCount int `json:"skuCount"` //商品数
|
||
HighSkuCount int `json:"highSkuCount"` //虚高商品数
|
||
ActAmple int `json:"actAmple"` //活动丰富的
|
||
NullOrderCount int `json:"nullOrderCount"` //无效订单数
|
||
RefuseOrderCount int `json:"refuseOrderCount"` //拒绝订单数
|
||
RepurchaseRate int `json:"repurchaseRate"` //复购率(转化率)
|
||
}
|
||
|
||
func GetStoreManageState(ctx *jxcontext.Context, storeIDs []int, vendorID int, fromTime, toTime string) (getStoreManageStateResult []*GetStoreManageStateResult, err error) {
|
||
var (
|
||
db = dao.GetDB()
|
||
)
|
||
//权限
|
||
if permission.IsRoled(ctx) {
|
||
if storeIDsMap, err := permission.GetUserStoresResultMap(ctx.GetUserID()); err == nil {
|
||
var storeIDs2 []int
|
||
if len(storeIDs) > 0 {
|
||
for _, v := range storeIDs {
|
||
if storeIDsMap[v] != 0 {
|
||
storeIDs2 = append(storeIDs2, v)
|
||
}
|
||
}
|
||
} else {
|
||
for k, _ := range storeIDsMap {
|
||
storeIDs2 = append(storeIDs2, k)
|
||
}
|
||
}
|
||
storeIDs = nil
|
||
storeIDs = storeIDs2
|
||
}
|
||
}
|
||
for _, v := range storeIDs {
|
||
storeDetail, _ := dao.GetStoreDetail(db, v, vendorID, "")
|
||
result := &GetStoreManageStateResult{
|
||
StoreID: v,
|
||
StoreName: storeDetail.Name,
|
||
MarketScale: storeDetail.MarketScale,
|
||
CoverArea: storeDetail.CoverArea,
|
||
}
|
||
if result.CoverArea == 0 {
|
||
handler := partner.GetPurchasePlatformFromVendorID(vendorID)
|
||
store, _ := handler.ReadStore(ctx, storeDetail.VendorOrgCode, storeDetail.VendorStoreID)
|
||
if storeMaps, err := dao.GetStoresMapList(db, []int{vendorID}, []int{v}, nil, model.StoreStatusAll, model.StoreIsSyncAll, "", "", ""); len(storeMaps) > 0 && err == nil {
|
||
storeMaps[0].CoverArea = CalculateCoverArea(strings.Split(store.DeliveryRange, ";"))
|
||
dao.UpdateEntity(db, storeMaps[0], "CoverArea")
|
||
result.CoverArea = storeMaps[0].CoverArea
|
||
}
|
||
}
|
||
getStoreManageStateResult = append(getStoreManageStateResult, result)
|
||
}
|
||
return getStoreManageStateResult, err
|
||
}
|
||
|
||
func CalculateCoverArea(coordinate []string) (area float64) {
|
||
if len(coordinate) == 0 {
|
||
return 0
|
||
}
|
||
var xyList [][2]float64
|
||
for _, v := range coordinate {
|
||
cell := strings.Split(v, ",")
|
||
lat := utils.Str2Float64WithDefault(cell[0], 0)
|
||
lng := utils.Str2Float64WithDefault(cell[1], 0)
|
||
xys := jxutils.MillierConvertion(lat, lng)
|
||
xyList = append(xyList, xys)
|
||
}
|
||
var sum float64
|
||
for i := 0; i < len(xyList)-1; i++ {
|
||
sum += (xyList[i+1][0] - xyList[i][0]) * (xyList[i+1][1] + xyList[i+1][1])
|
||
}
|
||
sum += (xyList[0][0] - xyList[len(xyList)-1][0]) * (xyList[0][1] + xyList[len(xyList)-1][1])
|
||
sum /= float64(2)
|
||
area, _ = decimal.NewFromFloat(sum).Round(3).Float64()
|
||
return area
|
||
}
|