You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
135 lines
6.2 KiB
135 lines
6.2 KiB
package console
|
|
|
|
import (
|
|
"yunyan/comm"
|
|
"yunyan/lego/sys/postgres"
|
|
"yunyan/pb"
|
|
|
|
"golang.org/x/crypto/bcrypt"
|
|
)
|
|
|
|
// Account 后台账号,存 console 主库(postgres,console_account 表)。
|
|
// 角色 Identity:1 超管 / 2 管理员 / 3 代理商 / 4 运营(语义见 pb.Identity)。
|
|
// 超管引导账号(yaml AdminAccount/AdminPassword)不入表,account_id=0;表里存的是后台新建的账号。
|
|
type Account struct {
|
|
Id uint32 `gorm:"primaryKey;autoIncrement;column:id" json:"id"`
|
|
Username string `gorm:"column:username;size:64;uniqueIndex" json:"username"`
|
|
Password string `gorm:"column:password;size:128" json:"-"` // bcrypt 哈希,绝不回传前端
|
|
Identity pb.Identity `gorm:"column:identity" json:"identity"` // 角色
|
|
Enabled bool `gorm:"column:enabled" json:"enabled"` // 停用的不能登录
|
|
Remark string `gorm:"column:remark;size:255" json:"remark"`
|
|
// 作用域绑定(仅代理/运营有意义;超管/管理员忽略,拥有全部权限)。均为 CSV,登录时回传前端做限制。
|
|
Access string `gorm:"column:access;type:text" json:"access"` // 可访问页面 id 列表,如 "dashboard,product"
|
|
Apps string `gorm:"column:apps;type:text" json:"apps"` // 绑定应用名称列表(与 stats_global_day.app_id 一致),如 "Voitrans,Echomeet"
|
|
Regions string `gorm:"column:regions;type:text" json:"regions"` // 绑定区域代码列表,如 "cn,us"
|
|
Products string `gorm:"column:products;type:text" json:"products"` // 绑定产品 id 列表,如 "45058,45059"
|
|
|
|
// 代理资源点余额(仅代理 identity=3 有意义):管理员充值入账,代理给终端用户赠送时按类原子扣减。
|
|
// 单位与赠送表单/公码奖励一致:VIP=天、翻译/会议=分钟。
|
|
BalanceVipday int64 `gorm:"column:balance_vipday" json:"balance_vipday"` // VIP天数余额
|
|
BalanceTrademin int64 `gorm:"column:balance_trademin" json:"balance_trademin"` // 翻译分钟余额
|
|
BalanceMeetmin int64 `gorm:"column:balance_meetmin" json:"balance_meetmin"` // 会议分钟余额
|
|
|
|
Createtime int64 `gorm:"column:createtime" json:"createtime"`
|
|
Updatetime int64 `gorm:"column:updatetime" json:"updatetime"`
|
|
}
|
|
|
|
func (Account) TableName() string { return comm.TableConsoleAccount }
|
|
|
|
// hashPassword 生成 bcrypt 哈希。
|
|
func hashPassword(plain string) (string, error) {
|
|
b, err := bcrypt.GenerateFromPassword([]byte(plain), bcrypt.DefaultCost)
|
|
return string(b), err
|
|
}
|
|
|
|
// checkPassword 校验明文与 bcrypt 哈希是否匹配。
|
|
func checkPassword(hash, plain string) bool {
|
|
return bcrypt.CompareHashAndPassword([]byte(hash), []byte(plain)) == nil
|
|
}
|
|
|
|
// ---- 账号表 CRUD(走 console 主库 postgres)----
|
|
|
|
func (this *modelComp) ensureAccountTable() error {
|
|
if err := postgres.CreateTable(comm.TableConsoleAccount, &Account{}); err != nil {
|
|
return err
|
|
}
|
|
// postgres CreateTable 对已存在的表跳过 AutoMigrate,故新增的绑定列需显式补(幂等)。
|
|
for _, col := range []string{"access", "apps", "regions", "products"} {
|
|
sql := "ALTER TABLE " + comm.TableConsoleAccount + " ADD COLUMN IF NOT EXISTS " + col + " text DEFAULT ''"
|
|
if res := postgres.Exec(sql); res.Error != nil {
|
|
return res.Error
|
|
}
|
|
}
|
|
// 代理资源点余额列(bigint),同样幂等补列。
|
|
for _, col := range []string{"balance_vipday", "balance_trademin", "balance_meetmin"} {
|
|
sql := "ALTER TABLE " + comm.TableConsoleAccount + " ADD COLUMN IF NOT EXISTS " + col + " bigint DEFAULT 0"
|
|
if res := postgres.Exec(sql); res.Error != nil {
|
|
return res.Error
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// agentDeductBalance 代理赠送时按类原子扣减资源点余额(CAS):任一类不足则一律不扣、返回 ok=false。
|
|
func (this *modelComp) agentDeductBalance(id uint32, vipday, trademin, meetmin, ts int64) (bool, error) {
|
|
res := postgres.Exec(
|
|
"UPDATE "+comm.TableConsoleAccount+" SET balance_vipday=balance_vipday-?, balance_trademin=balance_trademin-?, balance_meetmin=balance_meetmin-?, updatetime=? "+
|
|
"WHERE id=? AND balance_vipday>=? AND balance_trademin>=? AND balance_meetmin>=?",
|
|
vipday, trademin, meetmin, ts, id, vipday, trademin, meetmin)
|
|
if res.Error != nil {
|
|
return false, res.Error
|
|
}
|
|
return res.RowsAffected == 1, nil
|
|
}
|
|
|
|
// agentAddBalance 给代理账号余额按类加 delta(充值正数 / 回滚或下调负数);结果任一类为负则一律不改、返回 ok=false。
|
|
// WHERE 限定 identity=代理,避免误给非代理账号建立余额语义。
|
|
func (this *modelComp) agentAddBalance(id uint32, vipday, trademin, meetmin, ts int64) (bool, error) {
|
|
res := postgres.Exec(
|
|
"UPDATE "+comm.TableConsoleAccount+" SET balance_vipday=balance_vipday+?, balance_trademin=balance_trademin+?, balance_meetmin=balance_meetmin+?, updatetime=? "+
|
|
"WHERE id=? AND identity=? AND balance_vipday+?>=0 AND balance_trademin+?>=0 AND balance_meetmin+?>=0",
|
|
vipday, trademin, meetmin, ts, id, int32(pb.Identity_Agent), vipday, trademin, meetmin)
|
|
if res.Error != nil {
|
|
return false, res.Error
|
|
}
|
|
return res.RowsAffected == 1, nil
|
|
}
|
|
|
|
// listAccounts 返回全部账号(按 id 升序)。Password 字段 json:"-" 不会外泄。
|
|
func (this *modelComp) listAccounts() (models []*Account, err error) {
|
|
models = make([]*Account, 0)
|
|
err = postgres.Find(comm.TableConsoleAccount, &models, "")
|
|
return
|
|
}
|
|
|
|
// getAccountByName 按用户名取账号(登录用)。
|
|
func (this *modelComp) getAccountByName(username string) (model *Account, err error) {
|
|
model = &Account{}
|
|
err = postgres.FindOne(comm.TableConsoleAccount, model, "username=?", username)
|
|
return
|
|
}
|
|
|
|
// getAccount 按 id 取账号。
|
|
func (this *modelComp) getAccount(id uint32) (model *Account, err error) {
|
|
model = &Account{}
|
|
err = postgres.FindOne(comm.TableConsoleAccount, model, "id=?", id)
|
|
return
|
|
}
|
|
|
|
// addAccount 新增账号(Insert 后 model.Id 被回填)。
|
|
func (this *modelComp) addAccount(model *Account) (err error) {
|
|
err = postgres.Insert(comm.TableConsoleAccount, model)
|
|
return
|
|
}
|
|
|
|
// saveAccount 更新账号(整行保存)。
|
|
func (this *modelComp) saveAccount(model *Account) (err error) {
|
|
err = postgres.Save(comm.TableConsoleAccount, model)
|
|
return
|
|
}
|
|
|
|
// delAccount 删除账号。
|
|
func (this *modelComp) delAccount(id uint32) (err error) {
|
|
err = postgres.Delete(comm.TableConsoleAccount, "id=?", id)
|
|
return
|
|
}
|
|
|