diff --git a/internal/db/models/accounts/user_account_daily_stat_dao.go b/internal/db/models/accounts/user_account_daily_stat_dao.go new file mode 100644 index 00000000..311cb9f7 --- /dev/null +++ b/internal/db/models/accounts/user_account_daily_stat_dao.go @@ -0,0 +1,80 @@ +package accounts + +import ( + "github.com/TeaOSLab/EdgeCommon/pkg/userconfigs" + _ "github.com/go-sql-driver/mysql" + "github.com/iwind/TeaGo/Tea" + "github.com/iwind/TeaGo/dbs" + "github.com/iwind/TeaGo/maps" + timeutil "github.com/iwind/TeaGo/utils/time" +) + +type UserAccountDailyStatDAO dbs.DAO + +func NewUserAccountDailyStatDAO() *UserAccountDailyStatDAO { + return dbs.NewDAO(&UserAccountDailyStatDAO{ + DAOObject: dbs.DAOObject{ + DB: Tea.Env, + Table: "edgeUserAccountDailyStats", + Model: new(UserAccountDailyStat), + PkName: "id", + }, + }).(*UserAccountDailyStatDAO) +} + +var SharedUserAccountDailyStatDAO *UserAccountDailyStatDAO + +func init() { + dbs.OnReady(func() { + SharedUserAccountDailyStatDAO = NewUserAccountDailyStatDAO() + }) +} + +// UpdateDailyStat 更新当天统计数据 +func (this *UserAccountDailyStatDAO) UpdateDailyStat(tx *dbs.Tx) error { + var day = timeutil.Format("Ymd") + var month = timeutil.Format("Ym") + income, err := SharedUserAccountLogDAO.SumDailyEventTypes(tx, day, userconfigs.AccountIncomeEventTypes) + if err != nil { + return err + } + + expense, err := SharedUserAccountLogDAO.SumDailyEventTypes(tx, day, userconfigs.AccountExpenseEventTypes) + if err != nil { + return err + } + if expense < 0 { + expense = -expense + } + + return this.Query(tx). + InsertOrUpdateQuickly(maps.Map{ + "day": day, + "month": month, + "income": income, + "expense": expense, + }, maps.Map{ + "income": income, + "expense": expense, + }) +} + +// FindDailyStats 查看按天统计 +func (this *UserAccountDailyStatDAO) FindDailyStats(tx *dbs.Tx, dayFrom string, dayTo string) (result []*UserAccountDailyStat, err error) { + _, err = this.Query(tx). + Between("day", dayFrom, dayTo). + Slice(&result). + FindAll() + return +} + +// FindMonthlyStats 查看某月统计 +func (this *UserAccountDailyStatDAO) FindMonthlyStats(tx *dbs.Tx, dayFrom string, dayTo string) (result []*UserAccountDailyStat, err error) { + _, err = this.Query(tx). + Result("SUM(income) AS income", "SUM(expense) AS expense", "month"). + Between("day", dayFrom, dayTo). + Group("month"). + Slice(&result). + FindAll() + return +} diff --git a/internal/db/models/accounts/user_account_daily_stat_dao_test.go b/internal/db/models/accounts/user_account_daily_stat_dao_test.go new file mode 100644 index 00000000..76141d0c --- /dev/null +++ b/internal/db/models/accounts/user_account_daily_stat_dao_test.go @@ -0,0 +1,6 @@ +package accounts + +import ( + _ "github.com/go-sql-driver/mysql" + _ "github.com/iwind/TeaGo/bootstrap" +) diff --git a/internal/db/models/accounts/user_account_daily_stat_model.go b/internal/db/models/accounts/user_account_daily_stat_model.go new file mode 100644 index 00000000..b720ab5d --- /dev/null +++ b/internal/db/models/accounts/user_account_daily_stat_model.go @@ -0,0 +1,22 @@ +package accounts + +// UserAccountDailyStat 账户每日统计 +type UserAccountDailyStat struct { + Id uint32 `field:"id"` // ID + Day string `field:"day"` // YYYYMMDD + Month string `field:"month"` // YYYYMM + Income float64 `field:"income"` // 收入 + Expense float64 `field:"expense"` // 支出 +} + +type UserAccountDailyStatOperator struct { + Id interface{} // ID + Day interface{} // YYYYMMDD + Month interface{} // YYYYMM + Income interface{} // 收入 + Expense interface{} // 支出 +} + +func NewUserAccountDailyStatOperator() *UserAccountDailyStatOperator { + return &UserAccountDailyStatOperator{} +} diff --git a/internal/db/models/accounts/user_account_daily_stat_model_ext.go b/internal/db/models/accounts/user_account_daily_stat_model_ext.go new file mode 100644 index 00000000..ec6c5a12 --- /dev/null +++ b/internal/db/models/accounts/user_account_daily_stat_model_ext.go @@ -0,0 +1 @@ +package accounts diff --git a/internal/db/models/accounts/user_account_dao.go b/internal/db/models/accounts/user_account_dao.go new file mode 100644 index 00000000..4adbc15f --- /dev/null +++ b/internal/db/models/accounts/user_account_dao.go @@ -0,0 +1,172 @@ +package accounts + +import ( + "github.com/TeaOSLab/EdgeAPI/internal/db/models" + "github.com/TeaOSLab/EdgeAPI/internal/errors" + "github.com/TeaOSLab/EdgeCommon/pkg/userconfigs" + _ "github.com/go-sql-driver/mysql" + "github.com/iwind/TeaGo/Tea" + "github.com/iwind/TeaGo/dbs" + "github.com/iwind/TeaGo/maps" + "github.com/iwind/TeaGo/types" +) + +type UserAccountDAO dbs.DAO + +func NewUserAccountDAO() *UserAccountDAO { + return dbs.NewDAO(&UserAccountDAO{ + DAOObject: dbs.DAOObject{ + DB: Tea.Env, + Table: "edgeUserAccounts", + Model: new(UserAccount), + PkName: "id", + }, + }).(*UserAccountDAO) +} + +var SharedUserAccountDAO *UserAccountDAO + +func init() { + dbs.OnReady(func() { + SharedUserAccountDAO = NewUserAccountDAO() + }) +} + +// FindUserAccountWithUserId 根据用户ID查找用户账户 +func (this *UserAccountDAO) FindUserAccountWithUserId(tx *dbs.Tx, userId int64) (*UserAccount, error) { + if userId <= 0 { + return nil, errors.New("invalid userId '" + types.String(userId) + "'") + } + + // 用户是否存在 + user, err := models.SharedUserDAO.FindEnabledUser(tx, userId, nil) + if err != nil { + return nil, err + } + if user == nil { + return nil, errors.New("invalid userId '" + types.String(userId) + "'") + } + + account, err := this.Query(tx). + Attr("userId", userId). + Find() + if err != nil { + return nil, err + } + if account != nil { + return account.(*UserAccount), nil + } + + var op = NewUserAccountOperator() + op.UserId = userId + _, err = this.SaveInt64(tx, op) + if err != nil { + return nil, err + } + return this.FindUserAccountWithUserId(tx, userId) +} + +// FindUserAccountWithAccountId 根据ID查找用户账户 +func (this *UserAccountDAO) FindUserAccountWithAccountId(tx *dbs.Tx, accountId int64) (*UserAccount, error) { + one, err := this.Query(tx). + Pk(accountId). + Find() + if one != nil { + return one.(*UserAccount), nil + } + return nil, err +} + +// UpdateUserAccount 操作用户账户 +func (this *UserAccountDAO) UpdateUserAccount(tx *dbs.Tx, accountId int64, delta float32, eventType userconfigs.AccountEventType, description string, params maps.Map) error { + account, err := this.FindUserAccountWithAccountId(tx, accountId) + if err != nil { + return err + } + if account == nil { + return errors.New("invalid account id '" + types.String(accountId) + "'") + } + var userId = int64(account.Id) + var deltaFloat64 = float64(delta) + if deltaFloat64 < 0 && account.Total < -deltaFloat64 { + return errors.New("not enough account quota to decrease") + } + + // 操作账户 + err = this.Query(tx). + Pk(account.Id). + Set("total", dbs.SQL("total+:delta")). + Param("delta", delta). + UpdateQuickly() + if err != nil { + return err + } + + // 生成日志 + err = SharedUserAccountLogDAO.CreateAccountLog(tx, userId, accountId, delta, 0, eventType, description, params) + if err != nil { + return err + } + + return nil +} + +// UpdateUserAccountFrozen 操作用户账户冻结余额 +func (this *UserAccountDAO) UpdateUserAccountFrozen(tx *dbs.Tx, userId int64, delta float32, eventType userconfigs.AccountEventType, description string, params maps.Map) error { + account, err := this.FindUserAccountWithUserId(tx, userId) + if err != nil { + return err + } + var deltaFloat64 = float64(delta) + if deltaFloat64 < 0 && account.TotalFrozen < -deltaFloat64 { + return errors.New("not enough account frozen quota to decrease") + } + + // 操作账户 + err = this.Query(tx). + Pk(account.Id). + Set("totalFrozen", dbs.SQL("total+:delta")). + Param("delta", delta). + UpdateQuickly() + if err != nil { + return err + } + + // 生成日志 + err = SharedUserAccountLogDAO.CreateAccountLog(tx, userId, int64(account.Id), 0, delta, eventType, description, params) + if err != nil { + return err + } + + return nil +} + +// CountAllAccounts 计算所有账户数量 +func (this *UserAccountDAO) CountAllAccounts(tx *dbs.Tx, keyword string) (int64, error) { + var query = this.Query(tx) + if len(keyword) > 0 { + query.Where("userId IN (SELECT id FROM " + models.SharedUserDAO.Table + " WHERE state=1 AND (username LIKE :keyword OR fullname LIKE :keyword))") + query.Param("keyword", keyword) + } else { + query.Where("userId IN (SELECT id FROM " + models.SharedUserDAO.Table + " WHERE state=1)") + } + return query.Count() +} + +// ListAccounts 列出单页账户 +func (this *UserAccountDAO) ListAccounts(tx *dbs.Tx, keyword string, offset int64, size int64) (result []*UserAccount, err error) { + var query = this.Query(tx) + if len(keyword) > 0 { + query.Where("userId IN (SELECT id FROM " + models.SharedUserDAO.Table + " WHERE state=1 AND (username LIKE :keyword OR fullname LIKE :keyword))") + query.Param("keyword", keyword) + } else { + query.Where("userId IN (SELECT id FROM " + models.SharedUserDAO.Table + " WHERE state=1)") + } + _, err = query. + DescPk(). + Offset(offset). + Limit(size). + Slice(&result). + FindAll() + return +} diff --git a/internal/db/models/accounts/user_account_dao_test.go b/internal/db/models/accounts/user_account_dao_test.go new file mode 100644 index 00000000..76141d0c --- /dev/null +++ b/internal/db/models/accounts/user_account_dao_test.go @@ -0,0 +1,6 @@ +package accounts + +import ( + _ "github.com/go-sql-driver/mysql" + _ "github.com/iwind/TeaGo/bootstrap" +) diff --git a/internal/db/models/accounts/user_account_log_dao.go b/internal/db/models/accounts/user_account_log_dao.go new file mode 100644 index 00000000..dac02154 --- /dev/null +++ b/internal/db/models/accounts/user_account_log_dao.go @@ -0,0 +1,128 @@ +package accounts + +import ( + "github.com/TeaOSLab/EdgeAPI/internal/db/models" + "github.com/TeaOSLab/EdgeAPI/internal/errors" + "github.com/TeaOSLab/EdgeCommon/pkg/userconfigs" + _ "github.com/go-sql-driver/mysql" + "github.com/iwind/TeaGo/Tea" + "github.com/iwind/TeaGo/dbs" + "github.com/iwind/TeaGo/maps" + "github.com/iwind/TeaGo/types" + timeutil "github.com/iwind/TeaGo/utils/time" +) + +type UserAccountLogDAO dbs.DAO + +func NewUserAccountLogDAO() *UserAccountLogDAO { + return dbs.NewDAO(&UserAccountLogDAO{ + DAOObject: dbs.DAOObject{ + DB: Tea.Env, + Table: "edgeUserAccountLogs", + Model: new(UserAccountLog), + PkName: "id", + }, + }).(*UserAccountLogDAO) +} + +var SharedUserAccountLogDAO *UserAccountLogDAO + +func init() { + dbs.OnReady(func() { + SharedUserAccountLogDAO = NewUserAccountLogDAO() + }) +} + +// CreateAccountLog 生成用户账户日志 +func (this *UserAccountLogDAO) CreateAccountLog(tx *dbs.Tx, userId int64, accountId int64, delta float32, deltaFrozen float32, eventType userconfigs.AccountEventType, description string, params maps.Map) error { + var op = NewUserAccountLogOperator() + op.UserId = userId + op.AccountId = accountId + op.Delta = delta + op.DeltaFrozen = deltaFrozen + + account, err := SharedUserAccountDAO.FindUserAccountWithAccountId(tx, accountId) + if err != nil { + return err + } + if account == nil { + return errors.New("invalid account id '" + types.String(accountId) + "'") + } + op.Total = account.Total + op.TotalFrozen = account.TotalFrozen + + op.EventType = eventType + op.Description = description + + if params == nil { + params = maps.Map{} + } + op.Params = params.AsJSON() + + op.Day = timeutil.Format("Ymd") + err = this.Save(tx, op) + if err != nil { + return err + } + + return SharedUserAccountDailyStatDAO.UpdateDailyStat(tx) +} + +// CountAccountLogs 计算日志数量 +func (this *UserAccountLogDAO) CountAccountLogs(tx *dbs.Tx, userId int64, accountId int64, keyword string, eventType string) (int64, error) { + var query = this.Query(tx) + if userId > 0 { + query.Attr("userId", userId) + } + if accountId > 0 { + query.Attr("accountId", accountId) + } + if len(keyword) > 0 { + query.Where("(userId IN (SELECT id FROM " + models.SharedUserDAO.Table + " WHERE state=1 AND (username LIKE :keyword OR fullname LIKE :keyword)) OR description LIKE :keyword)") + query.Param("keyword", "%"+keyword+"%") + } + if len(eventType) > 0 { + query.Attr("eventType", eventType) + } + return query.Count() +} + +// ListAccountLogs 列出单页日志 +func (this *UserAccountLogDAO) ListAccountLogs(tx *dbs.Tx, userId int64, accountId int64, keyword string, eventType string, offset int64, size int64) (result []*UserAccountLog, err error) { + var query = this.Query(tx) + if userId > 0 { + query.Attr("userId", userId) + } + if accountId > 0 { + query.Attr("accountId", accountId) + } + if len(keyword) > 0 { + query.Where("(userId IN (SELECT id FROM " + models.SharedUserDAO.Table + " WHERE state=1 AND (username LIKE :keyword OR fullname LIKE :keyword)) OR description LIKE :keyword)") + query.Param("keyword", "%"+keyword+"%") + } + if len(eventType) > 0 { + query.Attr("eventType", eventType) + } + _, err = query. + DescPk(). + Offset(offset). + Limit(size). + Slice(&result). + FindAll() + return +} + +// SumDailyEventTypes 统计某天数据总和 +func (this *UserAccountLogDAO) SumDailyEventTypes(tx *dbs.Tx, day string, eventTypes []userconfigs.AccountEventType) (float32, error) { + if len(eventTypes) == 0 { + return 0, nil + } + result, err := this.Query(tx). + Attr("day", day). + Attr("eventType", eventTypes). + Sum("delta", 0) + if err != nil { + return 0, err + } + return types.Float32(result), nil +} diff --git a/internal/db/models/accounts/user_account_log_dao_test.go b/internal/db/models/accounts/user_account_log_dao_test.go new file mode 100644 index 00000000..76141d0c --- /dev/null +++ b/internal/db/models/accounts/user_account_log_dao_test.go @@ -0,0 +1,6 @@ +package accounts + +import ( + _ "github.com/go-sql-driver/mysql" + _ "github.com/iwind/TeaGo/bootstrap" +) diff --git a/internal/db/models/accounts/user_account_log_model.go b/internal/db/models/accounts/user_account_log_model.go new file mode 100644 index 00000000..8546d679 --- /dev/null +++ b/internal/db/models/accounts/user_account_log_model.go @@ -0,0 +1,36 @@ +package accounts + +// UserAccountLog 用户账户日志 +type UserAccountLog struct { + Id uint64 `field:"id"` // ID + UserId uint64 `field:"userId"` // 用户ID + AccountId uint64 `field:"accountId"` // 账户ID + Delta float64 `field:"delta"` // 操作余额的数量(可为负) + DeltaFrozen float64 `field:"deltaFrozen"` // 操作冻结的数量(可为负) + Total float64 `field:"total"` // 操作后余额 + TotalFrozen float64 `field:"totalFrozen"` // 操作后冻结余额 + EventType string `field:"eventType"` // 类型 + Description string `field:"description"` // 描述文字 + Day string `field:"day"` // YYYYMMDD + CreatedAt uint64 `field:"createdAt"` // 时间 + Params string `field:"params"` // 参数 +} + +type UserAccountLogOperator struct { + Id interface{} // ID + UserId interface{} // 用户ID + AccountId interface{} // 账户ID + Delta interface{} // 操作余额的数量(可为负) + DeltaFrozen interface{} // 操作冻结的数量(可为负) + Total interface{} // 操作后余额 + TotalFrozen interface{} // 操作后冻结余额 + EventType interface{} // 类型 + Description interface{} // 描述文字 + Day interface{} // YYYYMMDD + CreatedAt interface{} // 时间 + Params interface{} // 参数 +} + +func NewUserAccountLogOperator() *UserAccountLogOperator { + return &UserAccountLogOperator{} +} diff --git a/internal/db/models/accounts/user_account_log_model_ext.go b/internal/db/models/accounts/user_account_log_model_ext.go new file mode 100644 index 00000000..ec6c5a12 --- /dev/null +++ b/internal/db/models/accounts/user_account_log_model_ext.go @@ -0,0 +1 @@ +package accounts diff --git a/internal/db/models/accounts/user_account_model.go b/internal/db/models/accounts/user_account_model.go new file mode 100644 index 00000000..dc92b297 --- /dev/null +++ b/internal/db/models/accounts/user_account_model.go @@ -0,0 +1,20 @@ +package accounts + +// UserAccount 用户账号 +type UserAccount struct { + Id uint64 `field:"id"` // ID + UserId uint64 `field:"userId"` // 用户ID + Total float64 `field:"total"` // 可用总余额 + TotalFrozen float64 `field:"totalFrozen"` // 冻结余额 +} + +type UserAccountOperator struct { + Id interface{} // ID + UserId interface{} // 用户ID + Total interface{} // 可用总余额 + TotalFrozen interface{} // 冻结余额 +} + +func NewUserAccountOperator() *UserAccountOperator { + return &UserAccountOperator{} +} diff --git a/internal/db/models/accounts/user_account_model_ext.go b/internal/db/models/accounts/user_account_model_ext.go new file mode 100644 index 00000000..ec6c5a12 --- /dev/null +++ b/internal/db/models/accounts/user_account_model_ext.go @@ -0,0 +1 @@ +package accounts diff --git a/internal/db/models/client_browser_dao.go b/internal/db/models/client_browser_dao.go index 031ac0f9..63e1d1c2 100644 --- a/internal/db/models/client_browser_dao.go +++ b/internal/db/models/client_browser_dao.go @@ -5,6 +5,7 @@ import ( _ "github.com/go-sql-driver/mysql" "github.com/iwind/TeaGo/Tea" "github.com/iwind/TeaGo/dbs" + "strconv" ) const ( @@ -85,7 +86,7 @@ func (this *ClientBrowserDAO) FindBrowserIdWithNameCacheable(tx *dbs.Tx, browser browserId, err := this.Query(tx). Where("JSON_CONTAINS(codes, :browserName)"). - Param("browserName", "\""+browserName+"\""). // 查询的需要是个JSON字符串,所以这里加双引号 + Param("browserName", strconv.Quote(browserName)). // 查询的需要是个JSON字符串,所以这里加双引号 ResultPk(). FindInt64Col(0) if err != nil { diff --git a/internal/db/models/client_system_dao.go b/internal/db/models/client_system_dao.go index 7d86f577..a7a10700 100644 --- a/internal/db/models/client_system_dao.go +++ b/internal/db/models/client_system_dao.go @@ -5,6 +5,7 @@ import ( _ "github.com/go-sql-driver/mysql" "github.com/iwind/TeaGo/Tea" "github.com/iwind/TeaGo/dbs" + "strconv" ) const ( @@ -85,7 +86,7 @@ func (this *ClientSystemDAO) FindSystemIdWithNameCacheable(tx *dbs.Tx, systemNam systemId, err := this.Query(tx). Where("JSON_CONTAINS(codes, :systemName)"). - Param("systemName", "\""+systemName+"\""). // 查询的需要是个JSON字符串,所以这里加双引号 + Param("systemName", strconv.Quote(systemName)). // 查询的需要是个JSON字符串,所以这里加双引号 ResultPk(). FindInt64Col(0) if err != nil { diff --git a/internal/db/models/regions/region_city_dao.go b/internal/db/models/regions/region_city_dao.go index 83315c6e..576f7e45 100644 --- a/internal/db/models/regions/region_city_dao.go +++ b/internal/db/models/regions/region_city_dao.go @@ -7,6 +7,7 @@ import ( "github.com/iwind/TeaGo/Tea" "github.com/iwind/TeaGo/dbs" "github.com/iwind/TeaGo/types" + "strconv" ) const ( @@ -119,7 +120,7 @@ func (this *RegionCityDAO) FindCityIdWithNameCacheable(tx *dbs.Tx, provinceId in cityId, err := this.Query(tx). Attr("provinceId", provinceId). Where("JSON_CONTAINS(codes, :cityName)"). - Param("cityName", "\""+cityName+"\""). // 查询的需要是个JSON字符串,所以这里加双引号 + Param("cityName", strconv.Quote(cityName)). // 查询的需要是个JSON字符串,所以这里加双引号 ResultPk(). FindInt64Col(0) if err != nil { diff --git a/internal/db/models/regions/region_country_dao.go b/internal/db/models/regions/region_country_dao.go index e21e9861..f43d7954 100644 --- a/internal/db/models/regions/region_country_dao.go +++ b/internal/db/models/regions/region_country_dao.go @@ -7,6 +7,7 @@ import ( "github.com/iwind/TeaGo/dbs" "github.com/iwind/TeaGo/types" "github.com/mozillazg/go-pinyin" + "strconv" "strings" ) @@ -88,7 +89,7 @@ func (this *RegionCountryDAO) FindCountryIdWithDataId(tx *dbs.Tx, dataId string) func (this *RegionCountryDAO) FindCountryIdWithName(tx *dbs.Tx, countryName string) (int64, error) { return this.Query(tx). Where("JSON_CONTAINS(codes, :countryName)"). - Param("countryName", "\""+countryName+"\""). // 查询的需要是个JSON字符串,所以这里加双引号 + Param("countryName", strconv.Quote(countryName)). // 查询的需要是个JSON字符串,所以这里加双引号 ResultPk(). FindInt64Col(0) } diff --git a/internal/db/models/regions/region_provider_dao.go b/internal/db/models/regions/region_provider_dao.go index 6496d267..5c404ee8 100644 --- a/internal/db/models/regions/region_provider_dao.go +++ b/internal/db/models/regions/region_provider_dao.go @@ -5,6 +5,7 @@ import ( _ "github.com/go-sql-driver/mysql" "github.com/iwind/TeaGo/Tea" "github.com/iwind/TeaGo/dbs" + "strconv" ) const ( @@ -85,7 +86,7 @@ func (this *RegionProviderDAO) FindProviderIdWithNameCacheable(tx *dbs.Tx, provi providerId, err := this.Query(tx). Where("JSON_CONTAINS(codes, :providerName)"). - Param("providerName", "\""+providerName+"\""). // 查询的需要是个JSON字符串,所以这里加双引号 + Param("providerName", strconv.Quote(providerName)). // 查询的需要是个JSON字符串,所以这里加双引号 ResultPk(). FindInt64Col(0) if err != nil { diff --git a/internal/db/models/regions/region_province_dao.go b/internal/db/models/regions/region_province_dao.go index 51811fc2..8ecc2eaf 100644 --- a/internal/db/models/regions/region_province_dao.go +++ b/internal/db/models/regions/region_province_dao.go @@ -7,6 +7,7 @@ import ( "github.com/iwind/TeaGo/Tea" "github.com/iwind/TeaGo/dbs" "github.com/iwind/TeaGo/types" + "strconv" ) const ( @@ -88,7 +89,7 @@ func (this *RegionProvinceDAO) FindProvinceIdWithName(tx *dbs.Tx, countryId int6 return this.Query(tx). Attr("countryId", countryId). Where("JSON_CONTAINS(codes, :provinceName)"). - Param("provinceName", "\""+provinceName+"\""). // 查询的需要是个JSON字符串,所以这里加双引号 + Param("provinceName", strconv.Quote(provinceName)). // 查询的需要是个JSON字符串,所以这里加双引号 ResultPk(). FindInt64Col(0) } diff --git a/internal/db/models/user_dao.go b/internal/db/models/user_dao.go index 62d99931..3dadbde7 100644 --- a/internal/db/models/user_dao.go +++ b/internal/db/models/user_dao.go @@ -8,6 +8,7 @@ import ( _ "github.com/go-sql-driver/mysql" "github.com/iwind/TeaGo/Tea" "github.com/iwind/TeaGo/dbs" + "github.com/iwind/TeaGo/maps" "github.com/iwind/TeaGo/types" stringutil "github.com/iwind/TeaGo/utils/string" timeutil "github.com/iwind/TeaGo/utils/time" @@ -55,15 +56,27 @@ func (this *UserDAO) DisableUser(tx *dbs.Tx, id int64) (rowsAffected int64, err Update() } -// FindEnabledUser 查找启用中的条目 -func (this *UserDAO) FindEnabledUser(tx *dbs.Tx, id int64) (*User, error) { +// FindEnabledUser 查找启用的用户 +func (this *UserDAO) FindEnabledUser(tx *dbs.Tx, userId int64, cacheMap maps.Map) (*User, error) { + if cacheMap == nil { + cacheMap = maps.Map{} + } + var cacheKey = this.Table + ":FindEnabledUser:" + types.String(userId) + cache, ok := cacheMap[cacheKey] + if ok { + return cache.(*User), nil + } + result, err := this.Query(tx). - Pk(id). + Pk(userId). Attr("state", UserStateEnabled). Find() if result == nil { return nil, err } + + cacheMap[cacheKey] = result + return result.(*User), err } diff --git a/internal/db/models/user_plan_dao.go b/internal/db/models/user_plan_dao.go index f2b585f8..6ef1b056 100644 --- a/internal/db/models/user_plan_dao.go +++ b/internal/db/models/user_plan_dao.go @@ -136,3 +136,14 @@ func (this *UserPlanDAO) UpdateUserPlan(tx *dbs.Tx, userPlanId int64, planId int op.IsOn = isOn return this.Save(tx, op) } + +// UpdateUserPlanDayTo 修改套餐日期 +func (this *UserPlanDAO) UpdateUserPlanDayTo(tx *dbs.Tx, userPlanId int64, dayTo string) error { + if userPlanId <= 0 { + return errors.New("invalid userPlanId") + } + var op = NewUserPlanOperator() + op.Id = userPlanId + op.DayTo = dayTo + return this.Save(tx, op) +} diff --git a/internal/rpc/services/nameservers/service_ns_domain.go b/internal/rpc/services/nameservers/service_ns_domain.go index c86cba1c..b406f467 100644 --- a/internal/rpc/services/nameservers/service_ns_domain.go +++ b/internal/rpc/services/nameservers/service_ns_domain.go @@ -9,6 +9,7 @@ import ( "github.com/TeaOSLab/EdgeAPI/internal/rpc/services" rpcutils "github.com/TeaOSLab/EdgeAPI/internal/rpc/utils" "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" + "github.com/iwind/TeaGo/maps" ) // NSDomainService 域名相关服务 @@ -89,7 +90,7 @@ func (this *NSDomainService) FindEnabledNSDomain(ctx context.Context, req *pb.Fi // 用户 var pbUser *pb.User if domain.UserId > 0 { - user, err := models.SharedUserDAO.FindEnabledUser(tx, int64(domain.UserId)) + user, err := models.SharedUserDAO.FindEnabledUser(tx, int64(domain.UserId), nil) if err != nil { return nil, err } @@ -148,6 +149,7 @@ func (this *NSDomainService) ListEnabledNSDomains(ctx context.Context, req *pb.L return nil, err } pbDomains := []*pb.NSDomain{} + var cacheMap = maps.Map{} for _, domain := range domains { // 集群 cluster, err := models.SharedNSClusterDAO.FindEnabledNSCluster(tx, int64(domain.ClusterId)) @@ -161,7 +163,7 @@ func (this *NSDomainService) ListEnabledNSDomains(ctx context.Context, req *pb.L // 用户 var pbUser *pb.User if domain.UserId > 0 { - user, err := models.SharedUserDAO.FindEnabledUser(tx, int64(domain.UserId)) + user, err := models.SharedUserDAO.FindEnabledUser(tx, int64(domain.UserId), cacheMap) if err != nil { return nil, err } diff --git a/internal/rpc/services/service_user.go b/internal/rpc/services/service_user.go index 26a52589..397954d8 100644 --- a/internal/rpc/services/service_user.go +++ b/internal/rpc/services/service_user.go @@ -163,7 +163,7 @@ func (this *UserService) FindEnabledUser(ctx context.Context, req *pb.FindEnable tx := this.NullTx() - user, err := models.SharedUserDAO.FindEnabledUser(tx, req.UserId) + user, err := models.SharedUserDAO.FindEnabledUser(tx, req.UserId, nil) if err != nil { return nil, err } diff --git a/internal/utils/time.go b/internal/utils/time.go index 234edb0e..106585b6 100644 --- a/internal/utils/time.go +++ b/internal/utils/time.go @@ -2,13 +2,14 @@ package utils import ( "github.com/TeaOSLab/EdgeAPI/internal/errors" + "github.com/iwind/TeaGo/lists" "github.com/iwind/TeaGo/types" timeutil "github.com/iwind/TeaGo/utils/time" "regexp" "time" ) -// 计算日期之间的所有日期,格式为YYYYMMDD +// RangeDays 计算日期之间的所有日期,格式为YYYYMMDD func RangeDays(dayFrom string, dayTo string) ([]string, error) { ok, err := regexp.MatchString(`^\d{8}$`, dayFrom) if err != nil { @@ -56,7 +57,56 @@ func RangeDays(dayFrom string, dayTo string) ([]string, error) { return result, nil } -// 计算小时之间的所有小时,格式为YYYYMMDDHH +// RangeMonths 计算日期之间的所有月份,格式为YYYYMM +func RangeMonths(dayFrom string, dayTo string) ([]string, error) { + ok, err := regexp.MatchString(`^\d{8}$`, dayFrom) + if err != nil { + return nil, err + } + if !ok { + return nil, errors.New("invalid 'dayFrom'") + } + + ok, err = regexp.MatchString(`^\d{8}$`, dayTo) + if err != nil { + return nil, err + } + if !ok { + return nil, errors.New("invalid 'dayTo'") + } + + if dayFrom > dayTo { + dayFrom, dayTo = dayTo, dayFrom + } + + result := []string{dayFrom[:6]} + + year := types.Int(dayFrom[:4]) + month := types.Int(dayFrom[4:6]) + day := types.Int(dayFrom[6:]) + t := time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.Local) + for { + t = t.AddDate(0, 0, 20) + newDay := timeutil.Format("Ymd", t) + if newDay <= dayTo { + var monthString = newDay[:6] + if !lists.ContainsString(result, monthString) { + result = append(result, monthString) + } + } else { + break + } + } + + var endMonth = dayTo[:6] + if !lists.ContainsString(result, endMonth) { + result = append(result, endMonth) + } + + return result, nil +} + +// RangeHours 计算小时之间的所有小时,格式为YYYYMMDDHH func RangeHours(hourFrom string, hourTo string) ([]string, error) { ok, err := regexp.MatchString(`^\d{10}$`, hourFrom) if err != nil { diff --git a/internal/utils/time_test.go b/internal/utils/time_test.go index 0b763d17..66b84b12 100644 --- a/internal/utils/time_test.go +++ b/internal/utils/time_test.go @@ -10,6 +10,13 @@ func TestRangeDays(t *testing.T) { t.Log(days) } +func TestRangeMonth(t *testing.T) { + days, err := RangeMonths("20200101", "20210115") + if err != nil { + t.Fatal(err) + } + t.Log(days) +} func TestRangeHours(t *testing.T) { {