mirror of
https://github.com/TeaOSLab/EdgeAPI.git
synced 2025-11-08 03:00:26 +08:00
自动生成账单,自动支付账单
This commit is contained in:
@@ -3,14 +3,36 @@ package accounts
|
|||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||||
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
||||||
|
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
|
||||||
_ "github.com/go-sql-driver/mysql"
|
_ "github.com/go-sql-driver/mysql"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
"github.com/iwind/TeaGo/dbs"
|
"github.com/iwind/TeaGo/dbs"
|
||||||
|
"github.com/iwind/TeaGo/lists"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
dbs.OnReadyDone(func() {
|
||||||
|
go func() {
|
||||||
|
// 自动支付账单任务
|
||||||
|
var ticker = time.NewTicker(12 * time.Hour)
|
||||||
|
for range ticker.C {
|
||||||
|
if SharedUserAccountDAO.Instance != nil {
|
||||||
|
err := SharedUserAccountDAO.Instance.RunTx(func(tx *dbs.Tx) error {
|
||||||
|
return SharedUserAccountDAO.PayBills(tx)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
remotelogs.Error("USER_ACCOUNT_DAO", "pay bills task failed: "+err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
type UserAccountDAO dbs.DAO
|
type UserAccountDAO dbs.DAO
|
||||||
|
|
||||||
func NewUserAccountDAO() *UserAccountDAO {
|
func NewUserAccountDAO() *UserAccountDAO {
|
||||||
@@ -170,3 +192,46 @@ func (this *UserAccountDAO) ListAccounts(tx *dbs.Tx, keyword string, offset int6
|
|||||||
FindAll()
|
FindAll()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PayBills 尝试自动支付账单
|
||||||
|
func (this *UserAccountDAO) PayBills(tx *dbs.Tx) error {
|
||||||
|
bills, err := models.SharedUserBillDAO.FindUnpaidBills(tx, 10000)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 先支付久远的
|
||||||
|
lists.Reverse(bills)
|
||||||
|
|
||||||
|
for _, bill := range bills {
|
||||||
|
if bill.Amount <= 0 {
|
||||||
|
err = models.SharedUserBillDAO.UpdateUserBillIsPaid(tx, int64(bill.Id), true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
account, err := SharedUserAccountDAO.FindUserAccountWithUserId(tx, int64(bill.UserId))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if account == nil || account.Total < bill.Amount {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 扣款
|
||||||
|
err = SharedUserAccountDAO.UpdateUserAccount(tx, int64(account.Id), -float32(bill.Amount), userconfigs.AccountEventTypePayBill, "支付账单"+bill.Code, maps.Map{"billId": bill.Id})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 改为已支付
|
||||||
|
err = models.SharedUserBillDAO.UpdateUserBillIsPaid(tx, int64(bill.Id), true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,4 +3,16 @@ package accounts
|
|||||||
import (
|
import (
|
||||||
_ "github.com/go-sql-driver/mysql"
|
_ "github.com/go-sql-driver/mysql"
|
||||||
_ "github.com/iwind/TeaGo/bootstrap"
|
_ "github.com/iwind/TeaGo/bootstrap"
|
||||||
|
"github.com/iwind/TeaGo/dbs"
|
||||||
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestUserAccountDAO_PayBills(t *testing.T) {
|
||||||
|
dbs.NotifyReady()
|
||||||
|
|
||||||
|
err := NewUserAccountDAO().PayBills(nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log("ok")
|
||||||
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ func init() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 启用条目
|
// EnableNodePriceItem 启用条目
|
||||||
func (this *NodePriceItemDAO) EnableNodePriceItem(tx *dbs.Tx, id int64) error {
|
func (this *NodePriceItemDAO) EnableNodePriceItem(tx *dbs.Tx, id int64) error {
|
||||||
_, err := this.Query(tx).
|
_, err := this.Query(tx).
|
||||||
Pk(id).
|
Pk(id).
|
||||||
@@ -44,7 +44,7 @@ func (this *NodePriceItemDAO) EnableNodePriceItem(tx *dbs.Tx, id int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 禁用条目
|
// DisableNodePriceItem 禁用条目
|
||||||
func (this *NodePriceItemDAO) DisableNodePriceItem(tx *dbs.Tx, id int64) error {
|
func (this *NodePriceItemDAO) DisableNodePriceItem(tx *dbs.Tx, id int64) error {
|
||||||
_, err := this.Query(tx).
|
_, err := this.Query(tx).
|
||||||
Pk(id).
|
Pk(id).
|
||||||
@@ -53,7 +53,7 @@ func (this *NodePriceItemDAO) DisableNodePriceItem(tx *dbs.Tx, id int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查找启用中的条目
|
// FindEnabledNodePriceItem 查找启用中的条目
|
||||||
func (this *NodePriceItemDAO) FindEnabledNodePriceItem(tx *dbs.Tx, id int64) (*NodePriceItem, error) {
|
func (this *NodePriceItemDAO) FindEnabledNodePriceItem(tx *dbs.Tx, id int64) (*NodePriceItem, error) {
|
||||||
result, err := this.Query(tx).
|
result, err := this.Query(tx).
|
||||||
Pk(id).
|
Pk(id).
|
||||||
@@ -65,7 +65,7 @@ func (this *NodePriceItemDAO) FindEnabledNodePriceItem(tx *dbs.Tx, id int64) (*N
|
|||||||
return result.(*NodePriceItem), err
|
return result.(*NodePriceItem), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据主键查找名称
|
// FindNodePriceItemName 根据主键查找名称
|
||||||
func (this *NodePriceItemDAO) FindNodePriceItemName(tx *dbs.Tx, id int64) (string, error) {
|
func (this *NodePriceItemDAO) FindNodePriceItemName(tx *dbs.Tx, id int64) (string, error) {
|
||||||
return this.Query(tx).
|
return this.Query(tx).
|
||||||
Pk(id).
|
Pk(id).
|
||||||
@@ -73,7 +73,7 @@ func (this *NodePriceItemDAO) FindNodePriceItemName(tx *dbs.Tx, id int64) (strin
|
|||||||
FindStringCol("")
|
FindStringCol("")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建价格
|
// CreateItem 创建价格
|
||||||
func (this *NodePriceItemDAO) CreateItem(tx *dbs.Tx, name string, itemType string, bitsFrom, bitsTo int64) (int64, error) {
|
func (this *NodePriceItemDAO) CreateItem(tx *dbs.Tx, name string, itemType string, bitsFrom, bitsTo int64) (int64, error) {
|
||||||
op := NewNodePriceItemOperator()
|
op := NewNodePriceItemOperator()
|
||||||
op.Name = name
|
op.Name = name
|
||||||
@@ -85,7 +85,7 @@ func (this *NodePriceItemDAO) CreateItem(tx *dbs.Tx, name string, itemType strin
|
|||||||
return this.SaveInt64(tx, op)
|
return this.SaveInt64(tx, op)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改价格
|
// UpdateItem 修改价格
|
||||||
func (this *NodePriceItemDAO) UpdateItem(tx *dbs.Tx, itemId int64, name string, bitsFrom, bitsTo int64) error {
|
func (this *NodePriceItemDAO) UpdateItem(tx *dbs.Tx, itemId int64, name string, bitsFrom, bitsTo int64) error {
|
||||||
if itemId <= 0 {
|
if itemId <= 0 {
|
||||||
return errors.New("invalid itemId")
|
return errors.New("invalid itemId")
|
||||||
@@ -98,7 +98,7 @@ func (this *NodePriceItemDAO) UpdateItem(tx *dbs.Tx, itemId int64, name string,
|
|||||||
return this.Save(tx, op)
|
return this.Save(tx, op)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 列出某个区域的所有价格
|
// FindAllEnabledRegionPrices 列出某个区域的所有价格
|
||||||
func (this *NodePriceItemDAO) FindAllEnabledRegionPrices(tx *dbs.Tx, priceType string) (result []*NodePriceItem, err error) {
|
func (this *NodePriceItemDAO) FindAllEnabledRegionPrices(tx *dbs.Tx, priceType string) (result []*NodePriceItem, err error) {
|
||||||
_, err = this.Query(tx).
|
_, err = this.Query(tx).
|
||||||
Attr("type", priceType).
|
Attr("type", priceType).
|
||||||
@@ -109,7 +109,7 @@ func (this *NodePriceItemDAO) FindAllEnabledRegionPrices(tx *dbs.Tx, priceType s
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 列出某个区域的所有启用的价格
|
// FindAllEnabledAndOnRegionPrices 列出某个区域的所有启用的价格
|
||||||
func (this *NodePriceItemDAO) FindAllEnabledAndOnRegionPrices(tx *dbs.Tx, priceType string) (result []*NodePriceItem, err error) {
|
func (this *NodePriceItemDAO) FindAllEnabledAndOnRegionPrices(tx *dbs.Tx, priceType string) (result []*NodePriceItem, err error) {
|
||||||
_, err = this.Query(tx).
|
_, err = this.Query(tx).
|
||||||
Attr("type", priceType).
|
Attr("type", priceType).
|
||||||
@@ -121,7 +121,7 @@ func (this *NodePriceItemDAO) FindAllEnabledAndOnRegionPrices(tx *dbs.Tx, priceT
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据字节查找付费项目
|
// SearchItemsWithBytes 根据字节查找付费项目
|
||||||
func (this *NodePriceItemDAO) SearchItemsWithBytes(items []*NodePriceItem, bytes int64) int64 {
|
func (this *NodePriceItemDAO) SearchItemsWithBytes(items []*NodePriceItem, bytes int64) int64 {
|
||||||
bytes *= 8
|
bytes *= 8
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ func init() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 启用条目
|
// EnableNodeRegion 启用条目
|
||||||
func (this *NodeRegionDAO) EnableNodeRegion(tx *dbs.Tx, id int64) error {
|
func (this *NodeRegionDAO) EnableNodeRegion(tx *dbs.Tx, id int64) error {
|
||||||
_, err := this.Query(tx).
|
_, err := this.Query(tx).
|
||||||
Pk(id).
|
Pk(id).
|
||||||
@@ -44,7 +44,7 @@ func (this *NodeRegionDAO) EnableNodeRegion(tx *dbs.Tx, id int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 禁用条目
|
// DisableNodeRegion 禁用条目
|
||||||
func (this *NodeRegionDAO) DisableNodeRegion(tx *dbs.Tx, id int64) error {
|
func (this *NodeRegionDAO) DisableNodeRegion(tx *dbs.Tx, id int64) error {
|
||||||
_, err := this.Query(tx).
|
_, err := this.Query(tx).
|
||||||
Pk(id).
|
Pk(id).
|
||||||
@@ -53,7 +53,7 @@ func (this *NodeRegionDAO) DisableNodeRegion(tx *dbs.Tx, id int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查找启用中的条目
|
// FindEnabledNodeRegion 查找启用中的条目
|
||||||
func (this *NodeRegionDAO) FindEnabledNodeRegion(tx *dbs.Tx, id int64) (*NodeRegion, error) {
|
func (this *NodeRegionDAO) FindEnabledNodeRegion(tx *dbs.Tx, id int64) (*NodeRegion, error) {
|
||||||
result, err := this.Query(tx).
|
result, err := this.Query(tx).
|
||||||
Pk(id).
|
Pk(id).
|
||||||
@@ -65,7 +65,7 @@ func (this *NodeRegionDAO) FindEnabledNodeRegion(tx *dbs.Tx, id int64) (*NodeReg
|
|||||||
return result.(*NodeRegion), err
|
return result.(*NodeRegion), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据主键查找名称
|
// FindNodeRegionName 根据主键查找名称
|
||||||
func (this *NodeRegionDAO) FindNodeRegionName(tx *dbs.Tx, id int64) (string, error) {
|
func (this *NodeRegionDAO) FindNodeRegionName(tx *dbs.Tx, id int64) (string, error) {
|
||||||
return this.Query(tx).
|
return this.Query(tx).
|
||||||
Pk(id).
|
Pk(id).
|
||||||
@@ -73,7 +73,7 @@ func (this *NodeRegionDAO) FindNodeRegionName(tx *dbs.Tx, id int64) (string, err
|
|||||||
FindStringCol("")
|
FindStringCol("")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建区域
|
// CreateRegion 创建区域
|
||||||
func (this *NodeRegionDAO) CreateRegion(tx *dbs.Tx, adminId int64, name string, description string) (int64, error) {
|
func (this *NodeRegionDAO) CreateRegion(tx *dbs.Tx, adminId int64, name string, description string) (int64, error) {
|
||||||
op := NewNodeRegionOperator()
|
op := NewNodeRegionOperator()
|
||||||
op.AdminId = adminId
|
op.AdminId = adminId
|
||||||
@@ -84,7 +84,7 @@ func (this *NodeRegionDAO) CreateRegion(tx *dbs.Tx, adminId int64, name string,
|
|||||||
return this.SaveInt64(tx, op)
|
return this.SaveInt64(tx, op)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改区域
|
// UpdateRegion 修改区域
|
||||||
func (this *NodeRegionDAO) UpdateRegion(tx *dbs.Tx, regionId int64, name string, description string, isOn bool) error {
|
func (this *NodeRegionDAO) UpdateRegion(tx *dbs.Tx, regionId int64, name string, description string, isOn bool) error {
|
||||||
if regionId <= 0 {
|
if regionId <= 0 {
|
||||||
return errors.New("invalid regionId")
|
return errors.New("invalid regionId")
|
||||||
@@ -97,7 +97,7 @@ func (this *NodeRegionDAO) UpdateRegion(tx *dbs.Tx, regionId int64, name string,
|
|||||||
return this.Save(tx, op)
|
return this.Save(tx, op)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 列出所有区域
|
// FindAllEnabledRegions 列出所有区域
|
||||||
func (this *NodeRegionDAO) FindAllEnabledRegions(tx *dbs.Tx) (result []*NodeRegion, err error) {
|
func (this *NodeRegionDAO) FindAllEnabledRegions(tx *dbs.Tx) (result []*NodeRegion, err error) {
|
||||||
_, err = this.Query(tx).
|
_, err = this.Query(tx).
|
||||||
State(NodeRegionStateEnabled).
|
State(NodeRegionStateEnabled).
|
||||||
@@ -108,7 +108,7 @@ func (this *NodeRegionDAO) FindAllEnabledRegions(tx *dbs.Tx) (result []*NodeRegi
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 列出所有价格
|
// FindAllEnabledRegionPrices 列出所有价格
|
||||||
func (this *NodeRegionDAO) FindAllEnabledRegionPrices(tx *dbs.Tx) (result []*NodeRegion, err error) {
|
func (this *NodeRegionDAO) FindAllEnabledRegionPrices(tx *dbs.Tx) (result []*NodeRegion, err error) {
|
||||||
_, err = this.Query(tx).
|
_, err = this.Query(tx).
|
||||||
State(NodeRegionStateEnabled).
|
State(NodeRegionStateEnabled).
|
||||||
@@ -120,7 +120,7 @@ func (this *NodeRegionDAO) FindAllEnabledRegionPrices(tx *dbs.Tx) (result []*Nod
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 列出所有启用的区域
|
// FindAllEnabledAndOnRegions 列出所有启用的区域
|
||||||
func (this *NodeRegionDAO) FindAllEnabledAndOnRegions(tx *dbs.Tx) (result []*NodeRegion, err error) {
|
func (this *NodeRegionDAO) FindAllEnabledAndOnRegions(tx *dbs.Tx) (result []*NodeRegion, err error) {
|
||||||
_, err = this.Query(tx).
|
_, err = this.Query(tx).
|
||||||
State(NodeRegionStateEnabled).
|
State(NodeRegionStateEnabled).
|
||||||
@@ -132,7 +132,7 @@ func (this *NodeRegionDAO) FindAllEnabledAndOnRegions(tx *dbs.Tx) (result []*Nod
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 排序
|
// UpdateRegionOrders 排序
|
||||||
func (this *NodeRegionDAO) UpdateRegionOrders(tx *dbs.Tx, regionIds []int64) error {
|
func (this *NodeRegionDAO) UpdateRegionOrders(tx *dbs.Tx, regionIds []int64) error {
|
||||||
order := len(regionIds)
|
order := len(regionIds)
|
||||||
for _, regionId := range regionIds {
|
for _, regionId := range regionIds {
|
||||||
@@ -148,7 +148,7 @@ func (this *NodeRegionDAO) UpdateRegionOrders(tx *dbs.Tx, regionIds []int64) err
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改价格项价格
|
// UpdateRegionItemPrice 修改价格项价格
|
||||||
func (this *NodeRegionDAO) UpdateRegionItemPrice(tx *dbs.Tx, regionId int64, itemId int64, price float32) error {
|
func (this *NodeRegionDAO) UpdateRegionItemPrice(tx *dbs.Tx, regionId int64, itemId int64, price float32) error {
|
||||||
one, err := this.Query(tx).
|
one, err := this.Query(tx).
|
||||||
Pk(regionId).
|
Pk(regionId).
|
||||||
|
|||||||
@@ -194,6 +194,15 @@ func (this *PlanDAO) ListEnabledPlans(tx *dbs.Tx, offset int64, size int64) (res
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FindAllEnabledPlans 查找所有可用套餐
|
||||||
|
func (this *PlanDAO) FindAllEnabledPlans(tx *dbs.Tx) (result []*Plan, err error) {
|
||||||
|
_, err = this.Query(tx).
|
||||||
|
State(PlanStateEnabled).
|
||||||
|
Slice(&result).
|
||||||
|
FindAll()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// SortPlans 增加排序
|
// SortPlans 增加排序
|
||||||
func (this *PlanDAO) SortPlans(tx *dbs.Tx, planIds []int64) error {
|
func (this *PlanDAO) SortPlans(tx *dbs.Tx, planIds []int64) error {
|
||||||
if len(planIds) == 0 {
|
if len(planIds) == 0 {
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ func (this *ServerDailyStatDAO) SaveStats(tx *dbs.Tx, stats []*pb.ServerDailySta
|
|||||||
"countCachedRequests": stat.CountCachedRequests,
|
"countCachedRequests": stat.CountCachedRequests,
|
||||||
"countAttackRequests": stat.CountAttackRequests,
|
"countAttackRequests": stat.CountAttackRequests,
|
||||||
"attackBytes": stat.AttackBytes,
|
"attackBytes": stat.AttackBytes,
|
||||||
|
"planId": stat.PlanId,
|
||||||
"day": day,
|
"day": day,
|
||||||
"hour": hour,
|
"hour": hour,
|
||||||
"timeFrom": timeFrom,
|
"timeFrom": timeFrom,
|
||||||
@@ -99,6 +100,7 @@ func (this *ServerDailyStatDAO) SaveStats(tx *dbs.Tx, stats []*pb.ServerDailySta
|
|||||||
"countCachedRequests": dbs.SQL("countCachedRequests+:countCachedRequests"),
|
"countCachedRequests": dbs.SQL("countCachedRequests+:countCachedRequests"),
|
||||||
"countAttackRequests": dbs.SQL("countAttackRequests+:countAttackRequests"),
|
"countAttackRequests": dbs.SQL("countAttackRequests+:countAttackRequests"),
|
||||||
"attackBytes": dbs.SQL("attackBytes+:attackBytes"),
|
"attackBytes": dbs.SQL("attackBytes+:attackBytes"),
|
||||||
|
"planId": stat.PlanId,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -139,6 +141,30 @@ func (this *ServerDailyStatDAO) SumUserMonthly(tx *dbs.Tx, userId int64, regionI
|
|||||||
SumInt64("bytes", 0)
|
SumInt64("bytes", 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SumUserMonthlyWithoutPlan 根据用户计算某月合计并排除套餐
|
||||||
|
// month 格式为YYYYMM
|
||||||
|
func (this *ServerDailyStatDAO) SumUserMonthlyWithoutPlan(tx *dbs.Tx, userId int64, regionId int64, month string) (int64, error) {
|
||||||
|
query := this.Query(tx)
|
||||||
|
if regionId > 0 {
|
||||||
|
query.Attr("regionId", regionId)
|
||||||
|
}
|
||||||
|
return query.
|
||||||
|
Attr("planId", 0).
|
||||||
|
Between("day", month+"01", month+"32").
|
||||||
|
Attr("userId", userId).
|
||||||
|
SumInt64("bytes", 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SumUserMonthlyFee 计算用户某个月费用
|
||||||
|
// month 格式为YYYYMM
|
||||||
|
func (this *ServerDailyStatDAO) SumUserMonthlyFee(tx *dbs.Tx, userId int64, month string) (float64, error) {
|
||||||
|
return this.Query(tx).
|
||||||
|
Attr("userId", userId).
|
||||||
|
Between("day", month+"01", month+"32").
|
||||||
|
Gt("fee", 0).
|
||||||
|
Sum("fee", 0)
|
||||||
|
}
|
||||||
|
|
||||||
// SumUserMonthlyPeek 获取某月带宽峰值
|
// SumUserMonthlyPeek 获取某月带宽峰值
|
||||||
// month 格式为YYYYMM
|
// month 格式为YYYYMM
|
||||||
func (this *ServerDailyStatDAO) SumUserMonthlyPeek(tx *dbs.Tx, userId int64, regionId int64, month string) (int64, error) {
|
func (this *ServerDailyStatDAO) SumUserMonthlyPeek(tx *dbs.Tx, userId int64, regionId int64, month string) (int64, error) {
|
||||||
@@ -345,6 +371,17 @@ func (this *ServerDailyStatDAO) FindDailyStats(tx *dbs.Tx, serverId int64, dayFr
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FindMonthlyStatsWithPlan 查找某月有套餐的流量
|
||||||
|
// month YYYYMM
|
||||||
|
func (this *ServerDailyStatDAO) FindMonthlyStatsWithPlan(tx *dbs.Tx, month string) (result []*ServerDailyStat, err error) {
|
||||||
|
_, err = this.Query(tx).
|
||||||
|
Between("day", month+"01", month+"32").
|
||||||
|
Gt("planId", 0).
|
||||||
|
Slice(&result).
|
||||||
|
FindAll()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// FindHourlyStats 按小时统计
|
// FindHourlyStats 按小时统计
|
||||||
func (this *ServerDailyStatDAO) FindHourlyStats(tx *dbs.Tx, serverId int64, hourFrom string, hourTo string) (result []*ServerDailyStat, err error) {
|
func (this *ServerDailyStatDAO) FindHourlyStats(tx *dbs.Tx, serverId int64, hourFrom string, hourTo string) (result []*ServerDailyStat, err error) {
|
||||||
ones, err := this.Query(tx).
|
ones, err := this.Query(tx).
|
||||||
@@ -390,6 +427,14 @@ func (this *ServerDailyStatDAO) FindTopUserStats(tx *dbs.Tx, hourFrom string, ho
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateStatFee 设置费用
|
||||||
|
func (this *ServerDailyStatDAO) UpdateStatFee(tx *dbs.Tx, statId int64, fee float32) error {
|
||||||
|
return this.Query(tx).
|
||||||
|
Pk(statId).
|
||||||
|
Set("fee", fee).
|
||||||
|
UpdateQuickly()
|
||||||
|
}
|
||||||
|
|
||||||
// Clean 清理历史数据
|
// Clean 清理历史数据
|
||||||
func (this *ServerDailyStatDAO) Clean(tx *dbs.Tx, days int) error {
|
func (this *ServerDailyStatDAO) Clean(tx *dbs.Tx, days int) error {
|
||||||
var day = timeutil.Format("Ymd", time.Now().AddDate(0, 0, -days))
|
var day = timeutil.Format("Ymd", time.Now().AddDate(0, 0, -days))
|
||||||
|
|||||||
@@ -2,21 +2,23 @@ package models
|
|||||||
|
|
||||||
// ServerDailyStat 计费流量统计
|
// ServerDailyStat 计费流量统计
|
||||||
type ServerDailyStat struct {
|
type ServerDailyStat struct {
|
||||||
Id uint64 `field:"id"` // ID
|
Id uint64 `field:"id"` // ID
|
||||||
UserId uint32 `field:"userId"` // 用户ID
|
UserId uint32 `field:"userId"` // 用户ID
|
||||||
ServerId uint32 `field:"serverId"` // 服务ID
|
ServerId uint32 `field:"serverId"` // 服务ID
|
||||||
RegionId uint32 `field:"regionId"` // 区域ID
|
RegionId uint32 `field:"regionId"` // 区域ID
|
||||||
Bytes uint64 `field:"bytes"` // 流量
|
Bytes uint64 `field:"bytes"` // 流量
|
||||||
CachedBytes uint64 `field:"cachedBytes"` // 缓存的流量
|
CachedBytes uint64 `field:"cachedBytes"` // 缓存的流量
|
||||||
CountRequests uint64 `field:"countRequests"` // 请求数
|
CountRequests uint64 `field:"countRequests"` // 请求数
|
||||||
CountCachedRequests uint64 `field:"countCachedRequests"` // 缓存的请求数
|
CountCachedRequests uint64 `field:"countCachedRequests"` // 缓存的请求数
|
||||||
CountAttackRequests uint64 `field:"countAttackRequests"` // 攻击请求数
|
CountAttackRequests uint64 `field:"countAttackRequests"` // 攻击请求数
|
||||||
AttackBytes uint64 `field:"attackBytes"` // 攻击流量
|
AttackBytes uint64 `field:"attackBytes"` // 攻击流量
|
||||||
Day string `field:"day"` // 日期YYYYMMDD
|
Day string `field:"day"` // 日期YYYYMMDD
|
||||||
Hour string `field:"hour"` // YYYYMMDDHH
|
Hour string `field:"hour"` // YYYYMMDDHH
|
||||||
TimeFrom string `field:"timeFrom"` // 开始时间HHMMSS
|
TimeFrom string `field:"timeFrom"` // 开始时间HHMMSS
|
||||||
TimeTo string `field:"timeTo"` // 结束时间
|
TimeTo string `field:"timeTo"` // 结束时间
|
||||||
IsCharged uint8 `field:"isCharged"` // 是否已计算费用
|
IsCharged uint8 `field:"isCharged"` // 是否已计算费用
|
||||||
|
PlanId uint64 `field:"planId"` // 套餐ID
|
||||||
|
Fee float64 `field:"fee"` // 费用
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServerDailyStatOperator struct {
|
type ServerDailyStatOperator struct {
|
||||||
@@ -35,6 +37,8 @@ type ServerDailyStatOperator struct {
|
|||||||
TimeFrom interface{} // 开始时间HHMMSS
|
TimeFrom interface{} // 开始时间HHMMSS
|
||||||
TimeTo interface{} // 结束时间
|
TimeTo interface{} // 结束时间
|
||||||
IsCharged interface{} // 是否已计算费用
|
IsCharged interface{} // 是否已计算费用
|
||||||
|
PlanId interface{} // 套餐ID
|
||||||
|
Fee interface{} // 费用
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServerDailyStatOperator() *ServerDailyStatOperator {
|
func NewServerDailyStatOperator() *ServerDailyStatOperator {
|
||||||
|
|||||||
@@ -1060,6 +1060,9 @@ func (this *ServerDAO) ComposeServerConfig(tx *dbs.Tx, server *Server, cacheMap
|
|||||||
if plan != nil {
|
if plan != nil {
|
||||||
config.UserPlan = &serverconfigs.UserPlanConfig{
|
config.UserPlan = &serverconfigs.UserPlanConfig{
|
||||||
DayTo: userPlan.DayTo,
|
DayTo: userPlan.DayTo,
|
||||||
|
Plan: &serverconfigs.PlanConfig{
|
||||||
|
Id: int64(plan.Id),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(plan.TrafficLimit) > 0 && (config.TrafficLimit == nil || !config.TrafficLimit.IsOn) {
|
if len(plan.TrafficLimit) > 0 && (config.TrafficLimit == nil || !config.TrafficLimit.IsOn) {
|
||||||
|
|||||||
@@ -2,12 +2,45 @@ package models
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeAPI/internal/utils/numberutils"
|
"github.com/TeaOSLab/EdgeAPI/internal/utils/numberutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
_ "github.com/go-sql-driver/mysql"
|
_ "github.com/go-sql-driver/mysql"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
"github.com/iwind/TeaGo/dbs"
|
"github.com/iwind/TeaGo/dbs"
|
||||||
|
"github.com/iwind/TeaGo/lists"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
"github.com/iwind/TeaGo/rands"
|
||||||
|
"github.com/iwind/TeaGo/types"
|
||||||
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
dbs.OnReadyDone(func() {
|
||||||
|
var generatedMonths = []string{}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
// 自动生成账单任务
|
||||||
|
var ticker = time.NewTicker(1 * time.Minute)
|
||||||
|
for range ticker.C {
|
||||||
|
// 是否已经生成了,如果已经生成了就跳过
|
||||||
|
var lastMonth = timeutil.Format("Ym", time.Now().AddDate(0, -1, 0))
|
||||||
|
if lists.ContainsString(generatedMonths, lastMonth) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err := SharedUserBillDAO.GenerateBills(nil, lastMonth)
|
||||||
|
if err != nil {
|
||||||
|
remotelogs.Error("UserBillDAO", "generate bills failed: "+err.Error())
|
||||||
|
} else {
|
||||||
|
generatedMonths = append(generatedMonths, lastMonth)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
type BillType = string
|
type BillType = string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -35,7 +68,7 @@ func init() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算账单数量
|
// CountAllUserBills 计算账单数量
|
||||||
func (this *UserBillDAO) CountAllUserBills(tx *dbs.Tx, isPaid int32, userId int64, month string) (int64, error) {
|
func (this *UserBillDAO) CountAllUserBills(tx *dbs.Tx, isPaid int32, userId int64, month string) (int64, error) {
|
||||||
query := this.Query(tx)
|
query := this.Query(tx)
|
||||||
if isPaid == 0 {
|
if isPaid == 0 {
|
||||||
@@ -52,7 +85,7 @@ func (this *UserBillDAO) CountAllUserBills(tx *dbs.Tx, isPaid int32, userId int6
|
|||||||
return query.Count()
|
return query.Count()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 列出单页账单
|
// ListUserBills 列出单页账单
|
||||||
func (this *UserBillDAO) ListUserBills(tx *dbs.Tx, isPaid int32, userId int64, month string, offset, size int64) (result []*UserBill, err error) {
|
func (this *UserBillDAO) ListUserBills(tx *dbs.Tx, isPaid int32, userId int64, month string, offset, size int64) (result []*UserBill, err error) {
|
||||||
query := this.Query(tx)
|
query := this.Query(tx)
|
||||||
if isPaid == 0 {
|
if isPaid == 0 {
|
||||||
@@ -75,19 +108,42 @@ func (this *UserBillDAO) ListUserBills(tx *dbs.Tx, isPaid int32, userId int64, m
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建账单
|
// FindUnpaidBills 查找未支付订单
|
||||||
func (this *UserBillDAO) CreateBill(tx *dbs.Tx, userId int64, billType BillType, description string, amount float32, month string) (int64, error) {
|
func (this *UserBillDAO) FindUnpaidBills(tx *dbs.Tx, size int64) (result []*UserBill, err error) {
|
||||||
op := NewUserBillOperator()
|
if size <= 0 {
|
||||||
op.UserId = userId
|
size = 10000
|
||||||
op.Type = billType
|
}
|
||||||
op.Description = description
|
_, err = this.Query(tx).
|
||||||
op.Amount = amount
|
Attr("isPaid", false).
|
||||||
op.Month = month
|
Lt("month", timeutil.Format("Ym")). //当月的不能支付,因为当月还没过完
|
||||||
op.IsPaid = false
|
Limit(size).
|
||||||
return this.SaveInt64(tx, op)
|
DescPk().
|
||||||
|
Slice(&result).
|
||||||
|
FindAll()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否有当月账单
|
// CreateBill 创建账单
|
||||||
|
func (this *UserBillDAO) CreateBill(tx *dbs.Tx, userId int64, billType BillType, description string, amount float32, month string) error {
|
||||||
|
code, err := this.GenerateBillCode(tx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return this.Query(tx).
|
||||||
|
InsertOrUpdateQuickly(maps.Map{
|
||||||
|
"userId": userId,
|
||||||
|
"type": billType,
|
||||||
|
"description": description,
|
||||||
|
"amount": amount,
|
||||||
|
"month": month,
|
||||||
|
"code": code,
|
||||||
|
"isPaid": false,
|
||||||
|
}, maps.Map{
|
||||||
|
"amount": amount,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExistBill 检查是否有当月账单
|
||||||
func (this *UserBillDAO) ExistBill(tx *dbs.Tx, userId int64, billType BillType, month string) (bool, error) {
|
func (this *UserBillDAO) ExistBill(tx *dbs.Tx, userId int64, billType BillType, month string) (bool, error) {
|
||||||
return this.Query(tx).
|
return this.Query(tx).
|
||||||
Attr("userId", userId).
|
Attr("userId", userId).
|
||||||
@@ -96,47 +152,10 @@ func (this *UserBillDAO) ExistBill(tx *dbs.Tx, userId int64, billType BillType,
|
|||||||
Exist()
|
Exist()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 生成账单
|
// GenerateBills 生成账单
|
||||||
// month 格式YYYYMM
|
// month 格式YYYYMM
|
||||||
func (this *UserBillDAO) GenerateBills(tx *dbs.Tx, month string) error {
|
func (this *UserBillDAO) GenerateBills(tx *dbs.Tx, month string) error {
|
||||||
// 用户
|
// 区域价格
|
||||||
offset := int64(0)
|
|
||||||
size := int64(100) // 每次只查询N次,防止由于执行时间过长而锁表
|
|
||||||
for {
|
|
||||||
userIds, err := SharedUserDAO.ListEnabledUserIds(tx, offset, size)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
offset += size
|
|
||||||
if len(userIds) == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, userId := range userIds {
|
|
||||||
// CDN流量账单
|
|
||||||
err := this.generateTrafficBill(tx, userId, month)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 生成CDN流量账单
|
|
||||||
// month 格式YYYYMM
|
|
||||||
func (this *UserBillDAO) generateTrafficBill(tx *dbs.Tx, userId int64, month string) error {
|
|
||||||
// 检查是否已经有账单了
|
|
||||||
b, err := this.ExistBill(tx, userId, BillTypeTraffic, month)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if b {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO 优化使用缓存
|
|
||||||
regions, err := SharedNodeRegionDAO.FindAllEnabledRegionPrices(tx)
|
regions, err := SharedNodeRegionDAO.FindAllEnabledRegionPrices(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -153,18 +172,104 @@ func (this *UserBillDAO) generateTrafficBill(tx *dbs.Tx, userId int64, month str
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cost := float32(0)
|
// 计算套餐费用
|
||||||
|
plans, err := SharedPlanDAO.FindAllEnabledPlans(tx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var planMap = map[int64]*Plan{}
|
||||||
|
for _, plan := range plans {
|
||||||
|
planMap[int64(plan.Id)] = plan
|
||||||
|
}
|
||||||
|
|
||||||
|
stats, err := SharedServerDailyStatDAO.FindMonthlyStatsWithPlan(tx, month)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, stat := range stats {
|
||||||
|
plan, ok := planMap[int64(stat.PlanId)]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if plan.PriceType != serverconfigs.PlanPriceTypeTraffic {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(plan.TrafficPrice) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var priceConfig = &serverconfigs.PlanTrafficPrice{}
|
||||||
|
err = json.Unmarshal([]byte(plan.TrafficPrice), priceConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if priceConfig.Base > 0 {
|
||||||
|
var fee = priceConfig.Base * (float32(stat.Bytes) / 1024 / 1024 / 1024)
|
||||||
|
err = SharedServerDailyStatDAO.UpdateStatFee(tx, int64(stat.Id), fee)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用户
|
||||||
|
offset := int64(0)
|
||||||
|
size := int64(100) // 每次只查询N次,防止由于执行时间过长而锁表
|
||||||
|
for {
|
||||||
|
userIds, err := SharedUserDAO.ListEnabledUserIds(tx, offset, size)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
offset += size
|
||||||
|
if len(userIds) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, userId := range userIds {
|
||||||
|
// CDN流量账单
|
||||||
|
err := this.generateTrafficBill(tx, userId, month, regions, priceItems)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateUserBillIsPaid 设置账单已支付
|
||||||
|
func (this *UserBillDAO) UpdateUserBillIsPaid(tx *dbs.Tx, billId int64, isPaid bool) error {
|
||||||
|
return this.Query(tx).
|
||||||
|
Pk(billId).
|
||||||
|
Set("isPaid", isPaid).
|
||||||
|
UpdateQuickly()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成CDN流量账单
|
||||||
|
// month 格式YYYYMM
|
||||||
|
func (this *UserBillDAO) generateTrafficBill(tx *dbs.Tx, userId int64, month string, regions []*NodeRegion, priceItems []*NodePriceItem) error {
|
||||||
|
// 检查是否已经有账单了
|
||||||
|
if month < timeutil.Format("Ym") {
|
||||||
|
b, err := this.ExistBill(tx, userId, BillTypeTraffic, month)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if b {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var cost = float32(0)
|
||||||
for _, region := range regions {
|
for _, region := range regions {
|
||||||
if len(region.Prices) == 0 || region.Prices == "null" {
|
if len(region.Prices) == 0 || region.Prices == "null" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
priceMap := map[string]float32{}
|
priceMap := map[string]float32{}
|
||||||
err = json.Unmarshal([]byte(region.Prices), &priceMap)
|
err := json.Unmarshal([]byte(region.Prices), &priceMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
trafficBytes, err := SharedServerDailyStatDAO.SumUserMonthly(tx, userId, int64(region.Id), month)
|
trafficBytes, err := SharedServerDailyStatDAO.SumUserMonthlyWithoutPlan(tx, userId, int64(region.Id), month)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -187,16 +292,22 @@ func (this *UserBillDAO) generateTrafficBill(tx *dbs.Tx, userId int64, month str
|
|||||||
cost += (float32(trafficBytes*8) / 1_000_000_000) * price
|
cost += (float32(trafficBytes*8) / 1_000_000_000) * price
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 套餐费用
|
||||||
|
planFee, err := SharedServerDailyStatDAO.SumUserMonthlyFee(tx, userId, month)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cost += float32(planFee)
|
||||||
|
|
||||||
if cost == 0 {
|
if cost == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建账单
|
// 创建账单
|
||||||
_, err = this.CreateBill(tx, userId, BillTypeTraffic, "按流量计费", cost, month)
|
return this.CreateBill(tx, userId, BillTypeTraffic, "按流量计费", cost, month)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取账单类型名称
|
// BillTypeName 获取账单类型名称
|
||||||
func (this *UserBillDAO) BillTypeName(billType BillType) string {
|
func (this *UserBillDAO) BillTypeName(billType BillType) string {
|
||||||
switch billType {
|
switch billType {
|
||||||
case BillTypeTraffic:
|
case BillTypeTraffic:
|
||||||
@@ -204,3 +315,18 @@ func (this *UserBillDAO) BillTypeName(billType BillType) string {
|
|||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GenerateBillCode 生成账单编号
|
||||||
|
func (this *UserBillDAO) GenerateBillCode(tx *dbs.Tx) (string, error) {
|
||||||
|
var code = timeutil.Format("YmdHis") + types.String(rands.Int(100000, 999999))
|
||||||
|
exists, err := this.Query(tx).
|
||||||
|
Attr("code", code).
|
||||||
|
Exist()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
return code, nil
|
||||||
|
}
|
||||||
|
return this.GenerateBillCode(tx)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
// 用户账单
|
// UserBill 用户账单
|
||||||
type UserBill struct {
|
type UserBill struct {
|
||||||
Id uint64 `field:"id"` // ID
|
Id uint64 `field:"id"` // ID
|
||||||
UserId uint32 `field:"userId"` // 用户ID
|
UserId uint32 `field:"userId"` // 用户ID
|
||||||
@@ -10,6 +10,7 @@ type UserBill struct {
|
|||||||
Month string `field:"month"` // 帐期YYYYMM
|
Month string `field:"month"` // 帐期YYYYMM
|
||||||
IsPaid uint8 `field:"isPaid"` // 是否已支付
|
IsPaid uint8 `field:"isPaid"` // 是否已支付
|
||||||
PaidAt uint64 `field:"paidAt"` // 支付时间
|
PaidAt uint64 `field:"paidAt"` // 支付时间
|
||||||
|
Code string `field:"code"` // 账单编号
|
||||||
CreatedAt uint64 `field:"createdAt"` // 创建时间
|
CreatedAt uint64 `field:"createdAt"` // 创建时间
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,6 +23,7 @@ type UserBillOperator struct {
|
|||||||
Month interface{} // 帐期YYYYMM
|
Month interface{} // 帐期YYYYMM
|
||||||
IsPaid interface{} // 是否已支付
|
IsPaid interface{} // 是否已支付
|
||||||
PaidAt interface{} // 支付时间
|
PaidAt interface{} // 支付时间
|
||||||
|
Code interface{} // 账单编号
|
||||||
CreatedAt interface{} // 创建时间
|
CreatedAt interface{} // 创建时间
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 账单相关服务
|
// UserBillService 账单相关服务
|
||||||
type UserBillService struct {
|
type UserBillService struct {
|
||||||
BaseService
|
BaseService
|
||||||
}
|
}
|
||||||
|
|
||||||
// 手工生成订单
|
// GenerateAllUserBills 手工生成订单
|
||||||
func (this *UserBillService) GenerateAllUserBills(ctx context.Context, req *pb.GenerateAllUserBillsRequest) (*pb.RPCSuccess, error) {
|
func (this *UserBillService) GenerateAllUserBills(ctx context.Context, req *pb.GenerateAllUserBillsRequest) (*pb.RPCSuccess, error) {
|
||||||
_, err := this.ValidateAdmin(ctx, 0)
|
_, err := this.ValidateAdmin(ctx, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -39,7 +39,7 @@ func (this *UserBillService) GenerateAllUserBills(ctx context.Context, req *pb.G
|
|||||||
return this.Success()
|
return this.Success()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算所有账单数量
|
// CountAllUserBills 计算所有账单数量
|
||||||
func (this *UserBillService) CountAllUserBills(ctx context.Context, req *pb.CountAllUserBillsRequest) (*pb.RPCCountResponse, error) {
|
func (this *UserBillService) CountAllUserBills(ctx context.Context, req *pb.CountAllUserBillsRequest) (*pb.RPCCountResponse, error) {
|
||||||
_, _, err := this.ValidateAdminAndUser(ctx, 0, req.UserId)
|
_, _, err := this.ValidateAdminAndUser(ctx, 0, req.UserId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -55,7 +55,7 @@ func (this *UserBillService) CountAllUserBills(ctx context.Context, req *pb.Coun
|
|||||||
return this.SuccessCount(count)
|
return this.SuccessCount(count)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 列出单页账单
|
// ListUserBills 列出单页账单
|
||||||
func (this *UserBillService) ListUserBills(ctx context.Context, req *pb.ListUserBillsRequest) (*pb.ListUserBillsResponse, error) {
|
func (this *UserBillService) ListUserBills(ctx context.Context, req *pb.ListUserBillsRequest) (*pb.ListUserBillsResponse, error) {
|
||||||
_, _, err := this.ValidateAdminAndUser(ctx, 0, req.UserId)
|
_, _, err := this.ValidateAdminAndUser(ctx, 0, req.UserId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -70,16 +70,20 @@ func (this *UserBillService) ListUserBills(ctx context.Context, req *pb.ListUser
|
|||||||
}
|
}
|
||||||
result := []*pb.UserBill{}
|
result := []*pb.UserBill{}
|
||||||
for _, bill := range bills {
|
for _, bill := range bills {
|
||||||
userFullname, err := models.SharedUserDAO.FindUserFullname(tx, int64(bill.UserId))
|
user, err := models.SharedUserDAO.FindEnabledBasicUser(tx, int64(bill.UserId))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if user == nil {
|
||||||
|
user = &models.User{Id: bill.UserId}
|
||||||
|
}
|
||||||
|
|
||||||
result = append(result, &pb.UserBill{
|
result = append(result, &pb.UserBill{
|
||||||
Id: int64(bill.Id),
|
Id: int64(bill.Id),
|
||||||
User: &pb.User{
|
User: &pb.User{
|
||||||
Id: int64(bill.UserId),
|
Id: int64(bill.UserId),
|
||||||
Fullname: userFullname,
|
Fullname: user.Fullname,
|
||||||
|
Username: user.Username,
|
||||||
},
|
},
|
||||||
Type: bill.Type,
|
Type: bill.Type,
|
||||||
TypeName: models.SharedUserBillDAO.BillTypeName(bill.Type),
|
TypeName: models.SharedUserBillDAO.BillTypeName(bill.Type),
|
||||||
@@ -88,6 +92,7 @@ func (this *UserBillService) ListUserBills(ctx context.Context, req *pb.ListUser
|
|||||||
Month: bill.Month,
|
Month: bill.Month,
|
||||||
IsPaid: bill.IsPaid == 1,
|
IsPaid: bill.IsPaid == 1,
|
||||||
PaidAt: int64(bill.PaidAt),
|
PaidAt: int64(bill.PaidAt),
|
||||||
|
Code: bill.Code,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return &pb.ListUserBillsResponse{UserBills: result}, nil
|
return &pb.ListUserBillsResponse{UserBills: result}, nil
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user