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.

422 lines
17 KiB

package model
import (
"fmt"
"moredoc/util/captcha"
"strconv"
"strings"
"time"
jsoniter "github.com/json-iterator/go"
"go.uber.org/zap"
"gorm.io/gorm"
)
var json = jsoniter.ConfigCompatibleWithStandardLibrary
const (
// ConfigCategorySystem 系统配置系统名称、logo、版权信息、是否闭站等
ConfigCategorySystem = "system"
// ConfigCategoryUser 用户配置:是否启用注册、是否需要审核等
ConfigCategoryUser = "user"
// ConfigCategoryEmail 邮箱配置smtp服务器、端口、用户名、密码、发件人
ConfigCategoryEmail = "email"
// ConfigCategoryCaptcha 验证码配置:是否启用验证码、验证码有效期、验证码长度、验证码类型等
ConfigCategoryCaptcha = "captcha"
// ConfigCategoryJWT JWT配置JWT有效期、JWT加密密钥等
ConfigCategoryJWT = "jwt"
// ConfigCategorySecurity 安全配置项
ConfigCategorySecurity = "security"
)
type Config struct {
2 years ago
Id int64 `form:"id" json:"id,omitempty" gorm:"primaryKey;autoIncrement;column:id;comment:;"`
2 years ago
Label string `form:"label" json:"label,omitempty" gorm:"column:label;type:varchar(64);size:64;comment:标签名称;"`
Name string `form:"name" json:"name,omitempty" gorm:"column:name;type:varchar(64);size:64;index:name_category,unique;comment:表单字段名称;"`
Value string `form:"value" json:"value,omitempty" gorm:"column:value;type:text;comment:值;"`
Placeholder string `form:"placeholder" json:"placeholder,omitempty" gorm:"column:placeholder;type:varchar(255);size:255;comment:提示信息;"`
InputType string `form:"input_type" json:"input_type,omitempty" gorm:"column:input_type;type:varchar(32);size:32;default:text;comment:输入类型;"`
Category string `form:"category" json:"category,omitempty" gorm:"column:category;type:varchar(32);size:32;index:name_category,unique;index:category;comment:所属类别;"`
Sort int `form:"sort" json:"sort,omitempty" gorm:"column:sort;type:int(11);size:11;default:0;comment:同一category下的排序;"`
2 years ago
Options string `form:"options" json:"options,omitempty" gorm:"column:options;type:text;comment:针对checkbox等的枚举值;"`
CreatedAt time.Time `form:"created_at" json:"created_at,omitempty" gorm:"column:created_at;type:datetime;comment:创建时间;"`
UpdatedAt time.Time `form:"updated_at" json:"updated_at,omitempty" gorm:"column:updated_at;type:datetime;comment:更新时间;"`
}
2 years ago
// 这里是proto文件中的结构体可以根据需要删除或者调整
//message Config {
// int64 id = 1;
// string label = 2;
// string name = 3;
// string value = 4;
// int32 placeholder = 5;
// int32 input_type = 6;
// string category = 7;
// int32 sort = 8;
// string options = 9;
// google.protobuf.Timestamp created_at = 10 [ (gogoproto.stdtime) = true ];
// google.protobuf.Timestamp updated_at = 11 [ (gogoproto.stdtime) = true ];
//}
func (Config) TableName() string {
return tablePrefix + "config"
}
// CreateConfig 创建Config
// TODO: 创建成功之后,注意相关表统计字段数值的增减
func (m *DBModel) CreateConfig(config *Config) (err error) {
err = m.db.Create(config).Error
if err != nil {
m.logger.Error("CreateConfig", zap.Error(err))
return
}
return
}
// UpdateConfig 更新Config如果需要更新指定字段则请指定updateFields参数
func (m *DBModel) UpdateConfig(config *Config, updateFields ...string) (err error) {
db := m.db.Model(config)
updateFields = m.FilterValidFields(Config{}.TableName(), updateFields...)
if len(updateFields) > 0 { // 更新指定字段
db = db.Select(updateFields)
}
err = db.Where("id = ?", config.Id).Updates(config).Error
if err != nil {
m.logger.Error("UpdateConfig", zap.Error(err))
}
return
}
// GetConfig 根据id获取Config
func (m *DBModel) GetConfig(id interface{}, fields ...string) (config Config, err error) {
db := m.db
fields = m.FilterValidFields(Config{}.TableName(), fields...)
if len(fields) > 0 {
db = db.Select(fields)
}
err = db.Where("id = ?", id).First(&config).Error
return
}
// GetConfigByNameCategory(name string, category string, fields ...string) 根据唯一索引获取Config
func (m *DBModel) GetConfigByNameCategory(name string, category string, fields ...string) (config Config, err error) {
db := m.db
fields = m.FilterValidFields(Config{}.TableName(), fields...)
if len(fields) > 0 {
db = db.Select(fields)
}
db = db.Where("name = ?", name)
db = db.Where("category = ?", category)
err = db.First(&config).Error
if err != nil && err != gorm.ErrRecordNotFound {
m.logger.Error("GetConfigByNameCategory", zap.Error(err))
return
}
return
}
type OptionGetConfigList struct {
Page int
Size int
WithCount bool // 是否返回总数
Ids []interface{} // id列表
SelectFields []string // 查询字段
QueryRange map[string][2]interface{} // map[field][]{min,max}
QueryIn map[string][]interface{} // map[field][]{value1,value2,...}
QueryLike map[string][]interface{} // map[field][]{value1,value2,...}
Sort []string
}
// GetConfigList 获取Config列表
func (m *DBModel) GetConfigList(opt OptionGetConfigList) (configList []Config, total int64, err error) {
db := m.db.Model(&Config{})
for field, rangeValue := range opt.QueryRange {
fields := m.FilterValidFields(Config{}.TableName(), field)
if len(fields) == 0 {
continue
}
if rangeValue[0] != nil {
db = db.Where(fmt.Sprintf("%s >= ?", field), rangeValue[0])
}
if rangeValue[1] != nil {
db = db.Where(fmt.Sprintf("%s <= ?", field), rangeValue[1])
}
}
for field, values := range opt.QueryIn {
fields := m.FilterValidFields(Config{}.TableName(), field)
if len(fields) == 0 {
continue
}
db = db.Where(fmt.Sprintf("%s in (?)", field), values)
}
for field, values := range opt.QueryLike {
fields := m.FilterValidFields(Config{}.TableName(), field)
if len(fields) == 0 {
continue
}
db = db.Where(strings.TrimSuffix(fmt.Sprintf(strings.Join(make([]string, len(values)+1), "%s like ? or"), field), "or"), values...)
}
if len(opt.Ids) > 0 {
db = db.Where("id in (?)", opt.Ids)
}
if opt.WithCount {
err = db.Count(&total).Error
if err != nil {
m.logger.Error("GetConfigList", zap.Error(err))
return
}
}
opt.SelectFields = m.FilterValidFields(Config{}.TableName(), opt.SelectFields...)
if len(opt.SelectFields) > 0 {
db = db.Select(opt.SelectFields)
}
if len(opt.Sort) > 0 {
var sorts []string
for _, sort := range opt.Sort {
slice := strings.Split(sort, " ")
if len(m.FilterValidFields(Config{}.TableName(), slice[0])) == 0 {
continue
}
if len(slice) == 2 {
sorts = append(sorts, fmt.Sprintf("%s %s", slice[0], slice[1]))
} else {
sorts = append(sorts, fmt.Sprintf("%s desc", slice[0]))
}
}
if len(sorts) > 0 {
db = db.Order(strings.Join(sorts, ","))
}
}
db = db.Offset((opt.Page - 1) * opt.Size).Limit(opt.Size)
err = db.Find(&configList).Error
if err != nil && err != gorm.ErrRecordNotFound {
m.logger.Error("GetConfigList", zap.Error(err))
}
return
}
// DeleteConfig 删除数据
// TODO: 删除数据之后,存在 config_id 的关联表,需要删除对应数据,同时相关表的统计数值,也要随着减少
func (m *DBModel) DeleteConfig(ids []interface{}) (err error) {
err = m.db.Where("id in (?)", ids).Delete(&Config{}).Error
if err != nil {
m.logger.Error("DeleteConfig", zap.Error(err))
}
return
}
2 years ago
const (
ConfigCaptchaLength = "length"
ConfigCaptchaWidth = "width"
ConfigCaptchaHeight = "height"
ConfigCaptchaType = "type"
)
type ConfigCaptcha struct {
Length int `json:"length"` // 验证码长度
Width int `json:"width"`
Height int `json:"height"`
Type string `json:"type"` // 验证码类型
}
// GetConfigOfCaptcha 获取验证码配置
func (m *DBModel) GetConfigOfCaptcha() (config ConfigCaptcha) {
var configs []Config
err := m.db.Where("category = ?", ConfigCategoryCaptcha).Find(&configs).Error
if err != nil && err != gorm.ErrRecordNotFound {
m.logger.Error("GetConfigOfCaptcha", zap.Error(err))
}
for _, cfg := range configs {
switch cfg.Name {
case "length":
config.Length, _ = strconv.Atoi(cfg.Value)
if config.Length <= 0 {
config.Length = 6
}
case "width":
config.Width, _ = strconv.Atoi(cfg.Value)
if config.Width <= 0 {
config.Width = 240
}
case "height":
config.Height, _ = strconv.Atoi(cfg.Value)
if config.Height <= 0 {
config.Height = 60
}
case "type":
// 验证码类型
config.Type = cfg.Value
}
}
return
}
2 years ago
const (
ConfigSystemDomain = "domain"
ConfigSystemTitle = "title"
ConfigSystemDescription = "description"
ConfigSystemKeywords = "keywords"
ConfigSystemLogo = "logo"
ConfigSystemFavicon = "favicon"
ConfigSystemIcp = "icp"
ConfigSystemAnalytics = "analytics"
ConfigSystemTheme = "theme"
ConfigSystemCopyright = "copyright"
)
type ConfigSystem struct {
Domain string `json:"domain"` // 站点域名,不带 HTTPS:// 和 HTTP://
Title string `json:"title"` // 系统名称
Keywords string `json:"keywords"` // 系统关键字
Description string `json:"description"` // 系统描述
Logo string `json:"logo"` // logo
Favicon string `json:"favicon"` // logo
Theme string `json:"theme"` // 网站主题
Copyright string `json:"copyright"` // 版权信息
ICP string `json:"icp"` // 网站备案
Analytics string `json:"analytics"` // 统计代码
}
// GetConfigOfSystem 获取系统配置
func (m *DBModel) GetConfigOfSystem() (config ConfigSystem) {
var confgis []Config
err := m.db.Where("category = ?", ConfigCategorySystem).Find(&confgis).Error
if err != nil && err != gorm.ErrRecordNotFound {
m.logger.Error("GetConfigOfSystem", zap.Error(err))
}
var data = make(map[string]interface{})
for _, cfg := range confgis {
switch cfg.Name {
// 字符串类型的配置项
case "title", "description", "keywords", "logo", "favicon", "icp", "domain", "analytics", "theme", "copyright":
data[cfg.Name] = cfg.Value
}
}
bytes, _ := json.Marshal(data)
json.Unmarshal(bytes, &config)
return
}
2 years ago
const (
ConfigSecurityIsClose = "is_close" // 是否关闭注册
ConfigSecurityEnableRegister = "enable_register" // 是否允许注册
ConfigSecurityEnableCaptchaLogin = "enable_captcha_login" // 是否开启登录验证码
ConfigSecurityEnableCaptchaRegister = "enable_captcha_register" // 是否开启注册验证码
ConfigSecurityEnableCaptchaComment = "enable_captcha_comment" // 是否开启注册验证码
ConfigSecurityEnableCaptchaFindPassword = "enable_captcha_find_password" // 是否开启注册验证码
ConfigSecurityEnableCaptchaUpload = "enable_captcha_upload" // 是否开启注册验证码
)
type ConfigSecurity struct {
IsClose bool `json:"is_close"` // 是否闭站
EnableRegister bool `json:"enable_register"` // 是否启用注册
EnableCaptchaLogin bool `json:"enable_captcha_login"` // 是否启用登录验证码
EnableCaptchaRegister bool `json:"enable_captcha_register"` // 是否启用注册验证码
EnableCaptchaComment bool `json:"enable_captcha_comment"` // 是否启用评论验证码
EnableCaptchaFindPassword bool `json:"enable_captcha_find_password"` // 找回密码是否需要验证码
EnableCaptchaUpload bool `json:"enable_captcha_upload"` // 上传文档是否需要验证码
}
// GetConfigOfSecurity 获取安全配置
func (m *DBModel) GetConfigOfSecurity(name ...string) (config ConfigSecurity) {
var configs []Config
db := m.db.Where("category = ?", ConfigCategorySecurity)
if len(name) > 0 {
db = db.Where("name in (?)", name)
}
err := db.Find(&configs).Error
if err != nil && err != gorm.ErrRecordNotFound {
m.logger.Error("GetConfigOfSecurity", zap.Error(err))
}
var data = make(map[string]interface{})
for _, cfg := range configs {
switch cfg.Name {
case "is_close", "enable_register", "enable_captcha_login", "enable_captcha_register", "enable_captcha_comment", "enable_captcha_find_password", "enable_captcha_upload":
value, _ := strconv.ParseBool(cfg.Value)
data[cfg.Name] = value
}
}
bytes, _ := json.Marshal(data)
json.Unmarshal(bytes, &config)
return
}
func (m *DBModel) initConfig() (err error) {
// 初始化配置项
cfgs := []Config{
// 系统配置项
{Category: ConfigCategorySystem, Name: ConfigSystemTitle, Label: "网站名称", Value: "MOREDOC · 魔刀文库", Placeholder: "请输入您网站的名称", InputType: "text", Sort: 1, Options: ""},
{Category: ConfigCategorySystem, Name: ConfigSystemDescription, Label: "网站描述", Value: "MOREDOC · 魔刀文库", Placeholder: "请输入您网站的描述", InputType: "text", Sort: 2, Options: ""},
{Category: ConfigCategorySystem, Name: ConfigSystemKeywords, Label: "网站关键字", Value: "MOREDOC · 魔刀文库", Placeholder: "请输入您网站的关键字", InputType: "text", Sort: 3, Options: ""},
{Category: ConfigCategorySystem, Name: ConfigSystemLogo, Label: "网站Logo", Value: "", Placeholder: "请输入您网站的Logo", InputType: "text", Sort: 4, Options: ""},
{Category: ConfigCategorySystem, Name: ConfigSystemFavicon, Label: "网站Favicon", Value: "", Placeholder: "请输入您网站的Favicon", InputType: "text", Sort: 5, Options: ""},
{Category: ConfigCategorySystem, Name: ConfigSystemIcp, Label: "网站备案号", Value: "", Placeholder: "请输入您网站的备案号", InputType: "text", Sort: 6, Options: ""},
{Category: ConfigCategorySystem, Name: ConfigSystemDomain, Label: "网站域名", Value: "", Placeholder: "请输入您网站的域名", InputType: "textarea", Sort: 7, Options: ""},
{Category: ConfigCategorySystem, Name: ConfigSystemAnalytics, Label: "网站统计代码", Value: "", Placeholder: "请输入您网站的统计代码", InputType: "text", Sort: 8, Options: ""},
{Category: ConfigCategorySystem, Name: ConfigSystemTheme, Label: "网站主题", Value: "default", Placeholder: "请输入您网站的主题", InputType: "text", Sort: 9, Options: ""},
{Category: ConfigCategorySystem, Name: ConfigSystemCopyright, Label: "网站版权信息", Value: "", Placeholder: "请输入您网站的版权信息", InputType: "text", Sort: 10, Options: ""},
// 验证码配置项
{Category: ConfigCategoryCaptcha, Name: ConfigCaptchaHeight, Label: "验证码高度", Value: "60", Placeholder: "请输入验证码高度默认为60", InputType: "number", Sort: 13, Options: ""},
{Category: ConfigCategoryCaptcha, Name: ConfigCaptchaWidth, Label: "验证码宽度", Value: "240", Placeholder: "请输入验证码宽度默认为240", InputType: "number", Sort: 14, Options: ""},
{Category: ConfigCategoryCaptcha, Name: ConfigCaptchaLength, Label: "验证码长度", Value: "5", Placeholder: "请输入验证码长度默认为6", InputType: "number", Sort: 15, Options: ""},
{Category: ConfigCategoryCaptcha, Name: ConfigCaptchaType, Label: "验证码类型", Value: "digit", Placeholder: "请选择验证码类型,默认为数字", InputType: "select", Sort: 16, Options: captcha.CaptchaTypeOptions},
// 安全配置项
{Category: ConfigCategorySecurity, Name: ConfigSecurityIsClose, Label: "是否关闭网站", Value: "false", Placeholder: "请选择是否关闭网站", InputType: "swith", Sort: 17, Options: ""},
{Category: ConfigCategorySecurity, Name: ConfigSecurityEnableRegister, Label: "是否允许注册", Value: "true", Placeholder: "请选择是否允许用户注册", InputType: "swith", Sort: 18, Options: ""},
{Category: ConfigCategorySecurity, Name: ConfigSecurityEnableCaptchaLogin, Label: "是否开启登录验证码", Value: "true", Placeholder: "请选择是否开启登录验证码", InputType: "swith", Sort: 19, Options: ""},
{Category: ConfigCategorySecurity, Name: ConfigSecurityEnableCaptchaRegister, Label: "是否开启注册验证码", Value: "true", Placeholder: "请选择是否开启注册验证码", InputType: "swith", Sort: 20, Options: ""},
{Category: ConfigCategorySecurity, Name: ConfigSecurityEnableCaptchaComment, Label: "是否开启评论验证码", Value: "true", Placeholder: "请选择是否开启评论验证码", InputType: "swith", Sort: 21, Options: ""},
{Category: ConfigCategorySecurity, Name: ConfigSecurityEnableCaptchaFindPassword, Label: "是否开启找回密码验证码", Value: "true", Placeholder: "请选择是否开启找回密码验证码", InputType: "swith", Sort: 22, Options: ""},
{Category: ConfigCategorySecurity, Name: ConfigSecurityEnableCaptchaUpload, Label: "是否开启文档上传验证码", Value: "true", Placeholder: "请选择是否开启文档上传验证码", InputType: "swith", Sort: 23, Options: ""},
}
for _, cfg := range cfgs {
existConfig, _ := m.GetConfigByNameCategory(cfg.Name, cfg.Category, "id")
if existConfig.Id > 0 {
// 更新除了值之外的所有字段
cfg.Id = existConfig.Id
err = m.db.Omit("value").Updates(&cfg).Error
if err != nil {
m.logger.Error("initConfig", zap.Error(err))
return
}
continue
}
err = m.CreateConfig(&cfg)
if err != nil {
m.logger.Error("initConfig", zap.Error(err))
return
}
}
return
}