mirror of
https://gitee.com/johng/gf.git
synced 2024-12-02 04:07:47 +08:00
完成gmvc基本开发及测试
This commit is contained in:
parent
03109055c1
commit
aacb6abb9a
81
README.MD
81
README.MD
@ -20,7 +20,7 @@ import "gitee.com/johng/gf/g/xxx/xxx"
|
|||||||
│ │ └── gset 并发安全的集合
|
│ │ └── gset 并发安全的集合
|
||||||
│ │
|
│ │
|
||||||
│ ├── database 【数据库操作】
|
│ ├── database 【数据库操作】
|
||||||
│ │ └── gdb MySQL、PostgreSQL的操作封装
|
│ │ └── gdb 通用关系型数据库操作封装(目前仅支持MySQL、PostgreSQL)
|
||||||
│ │
|
│ │
|
||||||
│ ├── encoding 【数据编码】
|
│ ├── encoding 【数据编码】
|
||||||
│ │ ├── gbase64 BASE64
|
│ │ ├── gbase64 BASE64
|
||||||
@ -34,10 +34,16 @@ import "gitee.com/johng/gf/g/xxx/xxx"
|
|||||||
│ │ ├── gsha1 SHA1
|
│ │ ├── gsha1 SHA1
|
||||||
│ │ └── gurl URL
|
│ │ └── gurl URL
|
||||||
│ │
|
│ │
|
||||||
|
│ ├── frame 【开发框架】
|
||||||
|
│ │ ├── gconfig 通用配置管理
|
||||||
|
│ │ ├── ginstance 单例对象管理
|
||||||
|
│ │ └── gmvc MVC模式封装基类
|
||||||
|
│ │
|
||||||
│ ├── net 【网络通信】
|
│ ├── net 【网络通信】
|
||||||
│ │ ├── ghttp HTTP客户端及服务端
|
│ │ ├── ghttp HTTP客户端及服务端
|
||||||
│ │ ├── gip IP操作
|
│ │ ├── gipv4 IP操作
|
||||||
│ │ ├── gscanner 端口扫描
|
│ │ ├── gscanner 端口扫描
|
||||||
|
│ │ ├── gsession SESSION会话管理
|
||||||
│ │ ├── gsmtp SMTP邮件管理
|
│ │ ├── gsmtp SMTP邮件管理
|
||||||
│ │ ├── gtcp TCP服务端
|
│ │ ├── gtcp TCP服务端
|
||||||
│ │ └── gudp UDP服务端
|
│ │ └── gudp UDP服务端
|
||||||
@ -45,12 +51,14 @@ import "gitee.com/johng/gf/g/xxx/xxx"
|
|||||||
│ ├── os 【系统管理】
|
│ ├── os 【系统管理】
|
||||||
│ │ ├── gcache 本地缓存管理
|
│ │ ├── gcache 本地缓存管理
|
||||||
│ │ ├── gconsole 命令行控制
|
│ │ ├── gconsole 命令行控制
|
||||||
|
│ │ ├── genv 环境变量管理
|
||||||
│ │ ├── gfile 文件管理
|
│ │ ├── gfile 文件管理
|
||||||
│ │ ├── gfilepool 文件指针池
|
│ │ ├── gfilepool 文件指针池
|
||||||
│ │ ├── gfilespace 文件碎片管理
|
│ │ ├── gfilespace 文件碎片管理
|
||||||
│ │ ├── glog 日志管理
|
│ │ ├── glog 日志管理
|
||||||
│ │ ├── gmmap MMAP文件内存映射
|
│ │ ├── gmmap MMAP文件内存映射
|
||||||
│ │ └── gtime 时间管理
|
│ │ ├── gtime 时间管理
|
||||||
|
│ │ └── gview 视图管理(通用的模板管理对象,独立于MVC模块)
|
||||||
│ │
|
│ │
|
||||||
│ └── util 【常用工具】
|
│ └── util 【常用工具】
|
||||||
│ ├── gpage 分页
|
│ ├── gpage 分页
|
||||||
@ -65,3 +73,70 @@ import "gitee.com/johng/gf/g/xxx/xxx"
|
|||||||
## 配置
|
## 配置
|
||||||
1. 默认读取文件运行目录的config.json文件;
|
1. 默认读取文件运行目录的config.json文件;
|
||||||
1. 支持多配置文件管理,默认配置文件目录为当前运行目录;
|
1. 支持多配置文件管理,默认配置文件目录为当前运行目录;
|
||||||
|
1. config.json配置文件说明:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"viewpath" : "视图文件存放路径",
|
||||||
|
"database" : {
|
||||||
|
"数据库分组名称" : [
|
||||||
|
{
|
||||||
|
"host" : "地址",
|
||||||
|
"port" : "端口",
|
||||||
|
"user" : "账号",
|
||||||
|
"pass" : "密码",
|
||||||
|
"name" : "数据库名称",
|
||||||
|
"type" : "数据库类型(mysql|pgsql)",
|
||||||
|
"role" : "数据库角色(master|slave)",
|
||||||
|
"charset" : "字符编码",
|
||||||
|
"priority" : "数据库优先级(负载均衡处理)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
1. config.json示例文件:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"viewpath" : "/home/john/Workspace/Go/gitee.com/johng/gf/geg/frame/mvc/view",
|
||||||
|
"database" : {
|
||||||
|
"default" : [
|
||||||
|
{
|
||||||
|
"host" : "127.0.0.1",
|
||||||
|
"port" : "3306",
|
||||||
|
"user" : "root",
|
||||||
|
"pass" : "123456",
|
||||||
|
"name" : "test",
|
||||||
|
"type" : "mysql",
|
||||||
|
"role" : "master",
|
||||||
|
"charset" : "utf8",
|
||||||
|
"priority" : "100"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"host" : "127.0.0.1",
|
||||||
|
"port" : "3306",
|
||||||
|
"user" : "root",
|
||||||
|
"pass" : "123456",
|
||||||
|
"name" : "test",
|
||||||
|
"type" : "mysql",
|
||||||
|
"role" : "slave",
|
||||||
|
"charset" : "utf8",
|
||||||
|
"priority" : "100"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ucenter" : [
|
||||||
|
{
|
||||||
|
"host" : "127.0.0.1",
|
||||||
|
"port" : "3306",
|
||||||
|
"user" : "root",
|
||||||
|
"pass" : "123456",
|
||||||
|
"name" : "ucenter",
|
||||||
|
"type" : "mysql",
|
||||||
|
"role" : "master",
|
||||||
|
"charset" : "utf8",
|
||||||
|
"priority" : "100"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
@ -27,8 +27,8 @@ type Link interface {
|
|||||||
Exec(q string, args ...interface{}) (sql.Result, error)
|
Exec(q string, args ...interface{}) (sql.Result, error)
|
||||||
Prepare(q string) (*sql.Stmt, error)
|
Prepare(q string) (*sql.Stmt, error)
|
||||||
|
|
||||||
GetAll(q string, args ...interface{}) (*List, error)
|
GetAll(q string, args ...interface{}) (List, error)
|
||||||
GetOne(q string, args ...interface{}) (*Map, error)
|
GetOne(q string, args ...interface{}) (Map, error)
|
||||||
GetValue(q string, args ...interface{}) (interface{}, error)
|
GetValue(q string, args ...interface{}) (interface{}, error)
|
||||||
|
|
||||||
PingMaster() error
|
PingMaster() error
|
||||||
@ -49,15 +49,15 @@ type Link interface {
|
|||||||
Commit() error
|
Commit() error
|
||||||
Rollback() error
|
Rollback() error
|
||||||
|
|
||||||
insert(table string, data *Map, option uint8) (sql.Result, error)
|
insert(table string, data Map, option uint8) (sql.Result, error)
|
||||||
Insert(table string, data *Map) (sql.Result, error)
|
Insert(table string, data Map) (sql.Result, error)
|
||||||
Replace(table string, data *Map) (sql.Result, error)
|
Replace(table string, data Map) (sql.Result, error)
|
||||||
Save(table string, data *Map) (sql.Result, error)
|
Save(table string, data Map) (sql.Result, error)
|
||||||
|
|
||||||
batchInsert(table string, list *List, batch int, option uint8) error
|
batchInsert(table string, list List, batch int, option uint8) error
|
||||||
BatchInsert(table string, list *List, batch int) error
|
BatchInsert(table string, list List, batch int) error
|
||||||
BatchReplace(table string, list *List, batch int) error
|
BatchReplace(table string, list List, batch int) error
|
||||||
BatchSave(table string, list *List, batch int) error
|
BatchSave(table string, list List, batch int) error
|
||||||
|
|
||||||
Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error)
|
Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error)
|
||||||
Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error)
|
Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error)
|
||||||
@ -136,7 +136,6 @@ func NewByGroup(groupName string) (Link, error) {
|
|||||||
if len(masterList) < 1 {
|
if len(masterList) < 1 {
|
||||||
return nil, errors.New("at least one master node configuration's need to make sense")
|
return nil, errors.New("at least one master node configuration's need to make sense")
|
||||||
}
|
}
|
||||||
fmt.Println(masterList)
|
|
||||||
masterNode := getConfigNodeByPriority(&masterList)
|
masterNode := getConfigNodeByPriority(&masterList)
|
||||||
var slaveNode *ConfigNode
|
var slaveNode *ConfigNode
|
||||||
if len(slaveList) > 0 {
|
if len(slaveList) > 0 {
|
||||||
|
@ -67,7 +67,7 @@ func (l *dbLink) formatError(err error, q *string, args ...interface{}) error {
|
|||||||
|
|
||||||
|
|
||||||
// 数据库查询,获取查询结果集,以列表结构返回
|
// 数据库查询,获取查询结果集,以列表结构返回
|
||||||
func (l *dbLink) GetAll(q string, args ...interface{}) (*List, error) {
|
func (l *dbLink) GetAll(q string, args ...interface{}) (List, error) {
|
||||||
// 执行sql
|
// 执行sql
|
||||||
rows, err := l.Query(q, args ...)
|
rows, err := l.Query(q, args ...)
|
||||||
if err != nil || rows == nil {
|
if err != nil || rows == nil {
|
||||||
@ -88,7 +88,7 @@ func (l *dbLink) GetAll(q string, args ...interface{}) (*List, error) {
|
|||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
err = rows.Scan(scanArgs...)
|
err = rows.Scan(scanArgs...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &list, err
|
return list, err
|
||||||
}
|
}
|
||||||
row := make(Map)
|
row := make(Map)
|
||||||
for i, col := range values {
|
for i, col := range values {
|
||||||
@ -96,16 +96,16 @@ func (l *dbLink) GetAll(q string, args ...interface{}) (*List, error) {
|
|||||||
}
|
}
|
||||||
list = append(list, row)
|
list = append(list, row)
|
||||||
}
|
}
|
||||||
return &list, nil
|
return list, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 数据库查询,获取查询结果集,以关联数组结构返回
|
// 数据库查询,获取查询结果集,以关联数组结构返回
|
||||||
func (l *dbLink) GetOne(q string, args ...interface{}) (*Map, error) {
|
func (l *dbLink) GetOne(q string, args ...interface{}) (Map, error) {
|
||||||
list, err := l.GetAll(q, args ...)
|
list, err := l.GetAll(q, args ...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &(*list)[0], nil
|
return list[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 数据库查询,获取查询字段值
|
// 数据库查询,获取查询字段值
|
||||||
@ -114,7 +114,7 @@ func (l *dbLink) GetValue(q string, args ...interface{}) (interface{}, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
for _, v := range *one {
|
for _, v := range one {
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
return "", nil
|
return "", nil
|
||||||
@ -194,11 +194,11 @@ func (l *dbLink) getInsertOperationByOption(option uint8) string {
|
|||||||
// 1: replace: 如果数据存在(主键或者唯一索引),那么删除后重新写入一条
|
// 1: replace: 如果数据存在(主键或者唯一索引),那么删除后重新写入一条
|
||||||
// 2: save: 如果数据存在(主键或者唯一索引),那么更新,否则写入一条新数据
|
// 2: save: 如果数据存在(主键或者唯一索引),那么更新,否则写入一条新数据
|
||||||
// 3: ignore: 如果数据存在(主键或者唯一索引),那么什么也不做
|
// 3: ignore: 如果数据存在(主键或者唯一索引),那么什么也不做
|
||||||
func (l *dbLink) insert(table string, data *Map, option uint8) (sql.Result, error) {
|
func (l *dbLink) insert(table string, data Map, option uint8) (sql.Result, error) {
|
||||||
var keys []string
|
var keys []string
|
||||||
var values []string
|
var values []string
|
||||||
var params []interface{}
|
var params []interface{}
|
||||||
for k, v := range *data {
|
for k, v := range data {
|
||||||
keys = append(keys, l.charl + k + l.charr)
|
keys = append(keys, l.charl + k + l.charr)
|
||||||
values = append(values, "?")
|
values = append(values, "?")
|
||||||
params = append(params, v)
|
params = append(params, v)
|
||||||
@ -207,7 +207,7 @@ func (l *dbLink) insert(table string, data *Map, option uint8) (sql.Result, erro
|
|||||||
updatestr := ""
|
updatestr := ""
|
||||||
if option == OPTION_SAVE {
|
if option == OPTION_SAVE {
|
||||||
var updates []string
|
var updates []string
|
||||||
for k, _ := range *data {
|
for k, _ := range data {
|
||||||
updates = append(updates, fmt.Sprintf("%s%s%s=VALUES(%s)", l.charl, k, l.charr, k))
|
updates = append(updates, fmt.Sprintf("%s%s%s=VALUES(%s)", l.charl, k, l.charr, k))
|
||||||
}
|
}
|
||||||
updatestr = fmt.Sprintf(" ON DUPLICATE KEY UPDATE %s", strings.Join(updates, ","))
|
updatestr = fmt.Sprintf(" ON DUPLICATE KEY UPDATE %s", strings.Join(updates, ","))
|
||||||
@ -219,33 +219,33 @@ func (l *dbLink) insert(table string, data *Map, option uint8) (sql.Result, erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CURD操作:单条数据写入, 仅仅执行写入操作,如果存在冲突的主键或者唯一索引,那么报错返回
|
// CURD操作:单条数据写入, 仅仅执行写入操作,如果存在冲突的主键或者唯一索引,那么报错返回
|
||||||
func (l *dbLink) Insert(table string, data *Map) (sql.Result, error) {
|
func (l *dbLink) Insert(table string, data Map) (sql.Result, error) {
|
||||||
return l.link.insert(table, data, OPTION_INSERT)
|
return l.link.insert(table, data, OPTION_INSERT)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CURD操作:单条数据写入, 如果数据存在(主键或者唯一索引),那么删除后重新写入一条
|
// CURD操作:单条数据写入, 如果数据存在(主键或者唯一索引),那么删除后重新写入一条
|
||||||
func (l *dbLink) Replace(table string, data *Map) (sql.Result, error) {
|
func (l *dbLink) Replace(table string, data Map) (sql.Result, error) {
|
||||||
return l.link.insert(table, data, OPTION_REPLACE)
|
return l.link.insert(table, data, OPTION_REPLACE)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CURD操作:单条数据写入, 如果数据存在(主键或者唯一索引),那么更新,否则写入一条新数据
|
// CURD操作:单条数据写入, 如果数据存在(主键或者唯一索引),那么更新,否则写入一条新数据
|
||||||
func (l *dbLink) Save(table string, data *Map) (sql.Result, error) {
|
func (l *dbLink) Save(table string, data Map) (sql.Result, error) {
|
||||||
return l.link.insert(table, data, OPTION_SAVE)
|
return l.link.insert(table, data, OPTION_SAVE)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 批量写入数据
|
// 批量写入数据
|
||||||
func (l *dbLink) batchInsert(table string, list *List, batch int, option uint8) error {
|
func (l *dbLink) batchInsert(table string, list List, batch int, option uint8) error {
|
||||||
var keys []string
|
var keys []string
|
||||||
var values []string
|
var values []string
|
||||||
var bvalues []string
|
var bvalues []string
|
||||||
var params []interface{}
|
var params []interface{}
|
||||||
var size int = len(*list)
|
var size int = len(list)
|
||||||
// 判断长度
|
// 判断长度
|
||||||
if size < 1 {
|
if size < 1 {
|
||||||
return errors.New("empty data list")
|
return errors.New("empty data list")
|
||||||
}
|
}
|
||||||
// 首先获取字段名称及记录长度
|
// 首先获取字段名称及记录长度
|
||||||
for k, _ := range (*list)[0] {
|
for k, _ := range list[0] {
|
||||||
keys = append(keys, k)
|
keys = append(keys, k)
|
||||||
values = append(values, "?")
|
values = append(values, "?")
|
||||||
}
|
}
|
||||||
@ -263,7 +263,7 @@ func (l *dbLink) batchInsert(table string, list *List, batch int, option uint8)
|
|||||||
// 构造批量写入数据格式(注意map的遍历是无序的)
|
// 构造批量写入数据格式(注意map的遍历是无序的)
|
||||||
for i := 0; i < size; i++ {
|
for i := 0; i < size; i++ {
|
||||||
for _, k := range keys {
|
for _, k := range keys {
|
||||||
params = append(params, (*list)[i][k])
|
params = append(params, list[i][k])
|
||||||
}
|
}
|
||||||
bvalues = append(bvalues, "(" + strings.Join(values, ",") + ")")
|
bvalues = append(bvalues, "(" + strings.Join(values, ",") + ")")
|
||||||
if len(bvalues) == batch {
|
if len(bvalues) == batch {
|
||||||
@ -285,17 +285,17 @@ func (l *dbLink) batchInsert(table string, list *List, batch int, option uint8)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CURD操作:批量数据指定批次量写入
|
// CURD操作:批量数据指定批次量写入
|
||||||
func (l *dbLink) BatchInsert(table string, list *List, batch int) error {
|
func (l *dbLink) BatchInsert(table string, list List, batch int) error {
|
||||||
return l.link.batchInsert(table, list, batch, OPTION_INSERT)
|
return l.link.batchInsert(table, list, batch, OPTION_INSERT)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CURD操作:批量数据指定批次量写入, 如果数据存在(主键或者唯一索引),那么删除后重新写入一条
|
// CURD操作:批量数据指定批次量写入, 如果数据存在(主键或者唯一索引),那么删除后重新写入一条
|
||||||
func (l *dbLink) BatchReplace(table string, list *List, batch int) error {
|
func (l *dbLink) BatchReplace(table string, list List, batch int) error {
|
||||||
return l.link.batchInsert(table, list, batch, OPTION_REPLACE)
|
return l.link.batchInsert(table, list, batch, OPTION_REPLACE)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CURD操作:批量数据指定批次量写入, 如果数据存在(主键或者唯一索引),那么更新,否则写入一条新数据
|
// CURD操作:批量数据指定批次量写入, 如果数据存在(主键或者唯一索引),那么更新,否则写入一条新数据
|
||||||
func (l *dbLink) BatchSave(table string, list *List, batch int) error {
|
func (l *dbLink) BatchSave(table string, list List, batch int) error {
|
||||||
return l.link.batchInsert(table, list, batch, OPTION_SAVE)
|
return l.link.batchInsert(table, list, batch, OPTION_SAVE)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,16 +307,16 @@ func (l *dbLink) Update(table string, data interface{}, condition interface{}, a
|
|||||||
switch data.(type) {
|
switch data.(type) {
|
||||||
case string:
|
case string:
|
||||||
updates = data.(string)
|
updates = data.(string)
|
||||||
case *Map:
|
case Map:
|
||||||
var keys []string
|
var keys []string
|
||||||
for k, v := range *data.(*Map) {
|
for k, v := range data.(Map) {
|
||||||
keys = append(keys, fmt.Sprintf("%s%s%s=?", l.charl, k, l.charr))
|
keys = append(keys, fmt.Sprintf("%s%s%s=?", l.charl, k, l.charr))
|
||||||
params = append(params, v)
|
params = append(params, v)
|
||||||
}
|
}
|
||||||
updates = strings.Join(keys, ",")
|
updates = strings.Join(keys, ",")
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("invalid data type for 'data' field, string or *Map expected")
|
return nil, errors.New("invalid data type for 'data' field, string or Map expected")
|
||||||
}
|
}
|
||||||
for _, v := range args {
|
for _, v := range args {
|
||||||
if r, ok := v.(string); ok {
|
if r, ok := v.(string); ok {
|
||||||
|
@ -3,9 +3,6 @@ package gdb
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"strconv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -99,42 +96,42 @@ func AddConfigNode (group string, node ConfigNode) {
|
|||||||
|
|
||||||
// 添加一台数据库服务器配置,通过解析规范的字符串配置实现
|
// 添加一台数据库服务器配置,通过解析规范的字符串配置实现
|
||||||
// 配置格式:账号@地址:端口,密码,数据库名称,数据库类型[,集群角色(master|slave),字符编码,负载均衡优先级,自定义链接]
|
// 配置格式:账号@地址:端口,密码,数据库名称,数据库类型[,集群角色(master|slave),字符编码,负载均衡优先级,自定义链接]
|
||||||
func AddConfigNodeByString (group string, nodestr string) {
|
//func AddConfigNodeByString (group string, nodestr string) {
|
||||||
reg, _ := regexp.Compile(`(.+)@(.+):([^,]+),([^,]+),([^,]+),([^,]+)`)
|
// reg, _ := regexp.Compile(`(.+)@(.+):([^,]+),([^,]+),([^,]+),([^,]+)`)
|
||||||
match := reg.FindStringSubmatch(nodestr)
|
// match := reg.FindStringSubmatch(nodestr)
|
||||||
if match != nil {
|
// if match != nil {
|
||||||
node := ConfigNode{
|
// node := ConfigNode{
|
||||||
User : strings.TrimSpace(match[1]),
|
// User : strings.TrimSpace(match[1]),
|
||||||
Host : strings.TrimSpace(match[2]),
|
// Host : strings.TrimSpace(match[2]),
|
||||||
Port : strings.TrimSpace(match[3]),
|
// Port : strings.TrimSpace(match[3]),
|
||||||
Pass : strings.TrimSpace(match[4]),
|
// Pass : strings.TrimSpace(match[4]),
|
||||||
Name : strings.TrimSpace(match[5]),
|
// Name : strings.TrimSpace(match[5]),
|
||||||
Type : strings.TrimSpace(match[6]),
|
// Type : strings.TrimSpace(match[6]),
|
||||||
}
|
// }
|
||||||
if len(match[0]) + 1 < len(nodestr) {
|
// if len(match[0]) + 1 < len(nodestr) {
|
||||||
extra := strings.Split(nodestr[len(match[0]) + 1:], ",")
|
// extra := strings.Split(nodestr[len(match[0]) + 1:], ",")
|
||||||
if len(extra) > 0 {
|
// if len(extra) > 0 {
|
||||||
node.Role = strings.TrimSpace(extra[0])
|
// node.Role = strings.TrimSpace(extra[0])
|
||||||
}
|
// }
|
||||||
if len(extra) > 1 {
|
// if len(extra) > 1 {
|
||||||
node.Charset = strings.TrimSpace(extra[1])
|
// node.Charset = strings.TrimSpace(extra[1])
|
||||||
}
|
// }
|
||||||
if len(extra) > 2 {
|
// if len(extra) > 2 {
|
||||||
node.Priority, _ = strconv.Atoi(strings.TrimSpace(extra[2]))
|
// node.Priority, _ = strconv.Atoi(strings.TrimSpace(extra[2]))
|
||||||
}
|
// }
|
||||||
if len(extra) > 3 {
|
// if len(extra) > 3 {
|
||||||
index := len(extra[0]) + len(extra[1]) + len(extra[2]) + 3
|
// index := len(extra[0]) + len(extra[1]) + len(extra[2]) + 3
|
||||||
node.Linkinfo = strings.TrimSpace(nodestr[len(match[0]) + 1 + index:])
|
// node.Linkinfo = strings.TrimSpace(nodestr[len(match[0]) + 1 + index:])
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
AddConfigNode(group, node)
|
// AddConfigNode(group, node)
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
// 添加默认链接的一台数据库服务器配置,通过解析规范的字符串配置实现
|
// 添加默认链接的一台数据库服务器配置,通过解析规范的字符串配置实现
|
||||||
func AddDefaultConfigNodeByString (nodestr string) {
|
//func AddDefaultConfigNodeByString (nodestr string) {
|
||||||
AddConfigNodeByString(gDEFAULT_CONFIG_GROUP_NAME, nodestr)
|
// AddConfigNodeByString(gDEFAULT_CONFIG_GROUP_NAME, nodestr)
|
||||||
}
|
//}
|
||||||
|
|
||||||
// 添加默认链接的一台数据库服务器配置
|
// 添加默认链接的一台数据库服务器配置
|
||||||
func AddDefaultConfigNode (node ConfigNode) {
|
func AddDefaultConfigNode (node ConfigNode) {
|
||||||
|
@ -19,7 +19,7 @@ type gLinkOp struct {
|
|||||||
start int
|
start int
|
||||||
limit int
|
limit int
|
||||||
data interface{}
|
data interface{}
|
||||||
dataList *List
|
dataList List
|
||||||
batch int
|
batch int
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +88,7 @@ func (op *gLinkOp) Data(data interface{}) (*gLinkOp) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 链式操作,操作数据记录项列表
|
// 链式操作,操作数据记录项列表
|
||||||
func (op *gLinkOp) List(list *List) (*gLinkOp) {
|
func (op *gLinkOp) List(list List) (*gLinkOp) {
|
||||||
op.dataList = list
|
op.dataList = list
|
||||||
return op
|
return op
|
||||||
}
|
}
|
||||||
@ -98,7 +98,7 @@ func (op *gLinkOp) Insert() (sql.Result, error) {
|
|||||||
if op.data == nil {
|
if op.data == nil {
|
||||||
return nil, errors.New("inserting into table with empty data")
|
return nil, errors.New("inserting into table with empty data")
|
||||||
}
|
}
|
||||||
if d, ok := op.data.(*Map); ok {
|
if d, ok := op.data.(Map); ok {
|
||||||
return op.link.Insert(op.tables, d)
|
return op.link.Insert(op.tables, d)
|
||||||
}
|
}
|
||||||
return nil, errors.New("inserting into table with invalid data type")
|
return nil, errors.New("inserting into table with invalid data type")
|
||||||
@ -109,7 +109,7 @@ func (op *gLinkOp) Replace() (sql.Result, error) {
|
|||||||
if op.data == nil {
|
if op.data == nil {
|
||||||
return nil, errors.New("replacing into table with empty data")
|
return nil, errors.New("replacing into table with empty data")
|
||||||
}
|
}
|
||||||
if d, ok := op.data.(*Map); ok {
|
if d, ok := op.data.(Map); ok {
|
||||||
return op.link.Insert(op.tables, d)
|
return op.link.Insert(op.tables, d)
|
||||||
}
|
}
|
||||||
return nil, errors.New("replacing into table with invalid data type")
|
return nil, errors.New("replacing into table with invalid data type")
|
||||||
@ -120,7 +120,7 @@ func (op *gLinkOp) Save() (sql.Result, error) {
|
|||||||
if op.data == nil {
|
if op.data == nil {
|
||||||
return nil, errors.New("saving into table with empty data")
|
return nil, errors.New("saving into table with empty data")
|
||||||
}
|
}
|
||||||
if d, ok := op.data.(*Map); ok {
|
if d, ok := op.data.(Map); ok {
|
||||||
return op.link.Insert(op.tables, d)
|
return op.link.Insert(op.tables, d)
|
||||||
}
|
}
|
||||||
return nil, errors.New("saving into table with invalid data type")
|
return nil, errors.New("saving into table with invalid data type")
|
||||||
@ -134,7 +134,7 @@ func (op *gLinkOp) Batch(batch int) *gLinkOp {
|
|||||||
|
|
||||||
// 链式操作, CURD - BatchInsert
|
// 链式操作, CURD - BatchInsert
|
||||||
func (op *gLinkOp) BatchInsert() error {
|
func (op *gLinkOp) BatchInsert() error {
|
||||||
if op.dataList == nil || len(*op.dataList) < 1 {
|
if op.dataList == nil || len(op.dataList) < 1 {
|
||||||
return errors.New("batch inserting into table with empty data list")
|
return errors.New("batch inserting into table with empty data list")
|
||||||
}
|
}
|
||||||
batch := 10
|
batch := 10
|
||||||
@ -146,7 +146,7 @@ func (op *gLinkOp) BatchInsert() error {
|
|||||||
|
|
||||||
// 链式操作, CURD - BatchReplace
|
// 链式操作, CURD - BatchReplace
|
||||||
func (op *gLinkOp) BatchReplace() error {
|
func (op *gLinkOp) BatchReplace() error {
|
||||||
if op.dataList == nil || len(*op.dataList) < 1 {
|
if op.dataList == nil || len(op.dataList) < 1 {
|
||||||
return errors.New("batch replacing into table with empty data list")
|
return errors.New("batch replacing into table with empty data list")
|
||||||
}
|
}
|
||||||
batch := 10
|
batch := 10
|
||||||
@ -158,7 +158,7 @@ func (op *gLinkOp) BatchReplace() error {
|
|||||||
|
|
||||||
// 链式操作, CURD - BatchSave
|
// 链式操作, CURD - BatchSave
|
||||||
func (op *gLinkOp) BatchSave() error {
|
func (op *gLinkOp) BatchSave() error {
|
||||||
if op.dataList == nil || len(*op.dataList) < 1 {
|
if op.dataList == nil || len(op.dataList) < 1 {
|
||||||
return errors.New("batch saving into table with empty data list")
|
return errors.New("batch saving into table with empty data list")
|
||||||
}
|
}
|
||||||
batch := 10
|
batch := 10
|
||||||
@ -185,7 +185,7 @@ func (op *gLinkOp) Delete() (sql.Result, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 链式操作,select
|
// 链式操作,select
|
||||||
func (op *gLinkOp) Select() (*List, error) {
|
func (op *gLinkOp) Select() (List, error) {
|
||||||
if op.fields == "" {
|
if op.fields == "" {
|
||||||
op.fields = "*"
|
op.fields = "*"
|
||||||
}
|
}
|
||||||
@ -206,17 +206,17 @@ func (op *gLinkOp) Select() (*List, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 链式操作,查询所有记录
|
// 链式操作,查询所有记录
|
||||||
func (op *gLinkOp) All() (*List, error) {
|
func (op *gLinkOp) All() (List, error) {
|
||||||
return op.Select()
|
return op.Select()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 链式操作,查询单条记录
|
// 链式操作,查询单条记录
|
||||||
func (op *gLinkOp) One() (*Map, error) {
|
func (op *gLinkOp) One() (Map, error) {
|
||||||
list, err := op.All()
|
list, err := op.All()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &(*list)[0], nil
|
return list[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 链式操作,查询字段值
|
// 链式操作,查询字段值
|
||||||
@ -225,7 +225,7 @@ func (op *gLinkOp) Value() (interface{}, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
for _, v := range *one {
|
for _, v := range one {
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
return "", nil
|
return "", nil
|
||||||
|
@ -1,58 +0,0 @@
|
|||||||
// 基类
|
|
||||||
package gbase
|
|
||||||
|
|
||||||
import (
|
|
||||||
"gitee.com/johng/gf/g/os/gfile"
|
|
||||||
"gitee.com/johng/gf/g/os/gconsole"
|
|
||||||
"gitee.com/johng/gf/g/database/gdb"
|
|
||||||
"gitee.com/johng/gf/g/frame/gconfig"
|
|
||||||
"gitee.com/johng/gf/g/frame/ginstance"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 框架基类,所有的基于gf框架的类对象都继承于此,以便使用框架的一些封装的核心组件
|
|
||||||
type Base struct {
|
|
||||||
Db gdb.Link // 数据库操作对象
|
|
||||||
Config *gconfig.Config // 配置管理对象
|
|
||||||
}
|
|
||||||
|
|
||||||
// 基类初始化,如若需要自定义初始化内置核心对象组件,可在继承子类中覆盖此方法
|
|
||||||
func (b *Base) Init() {
|
|
||||||
// 配置文件目录查找依次为:启动参数cfgpath、当前程序运行目录
|
|
||||||
if b.Config == nil {
|
|
||||||
path := gconsole.Option.Get("cfgpath")
|
|
||||||
if path == "" {
|
|
||||||
path = gfile.SelfDir()
|
|
||||||
}
|
|
||||||
ckey := "gf_config_with_path_" + path
|
|
||||||
result := ginstance.Get(ckey)
|
|
||||||
if result != nil {
|
|
||||||
b.Config = result.(*gconfig.Config)
|
|
||||||
} else {
|
|
||||||
b.Config = gconfig.New(path)
|
|
||||||
ginstance.Set(ckey, b.Config)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 数据库操作对象初始化
|
|
||||||
// 全局只有一个数据库单例对象,可以配置不同分组的配置进行使用
|
|
||||||
if b.Db == nil {
|
|
||||||
ckey := "gf_database"
|
|
||||||
result := ginstance.Get(ckey)
|
|
||||||
if result != nil {
|
|
||||||
b.Db = result.(gdb.Link)
|
|
||||||
} else {
|
|
||||||
if m := b.Config.GetMap("database"); m != nil {
|
|
||||||
for group, v := range m {
|
|
||||||
if list, ok := v.([]interface{}); ok {
|
|
||||||
for _, nodei := range list {
|
|
||||||
gdb.AddConfigNodeByString(group, nodei.(string))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if link, err := gdb.Instance(); err != nil {
|
|
||||||
b.Db = link
|
|
||||||
ginstance.Set(ckey, b.Db)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,21 @@
|
|||||||
// 单例对象管理工具
|
// 单例对象管理工具
|
||||||
|
// 框架内置了一些核心对象,并且可以通过Set和Get方法实现IoC以及对内置核心对象的自定义替换
|
||||||
package ginstance
|
package ginstance
|
||||||
|
|
||||||
import "gitee.com/johng/gf/g/container/gmap"
|
import (
|
||||||
|
"strconv"
|
||||||
|
"gitee.com/johng/gf/g/os/gfile"
|
||||||
|
"gitee.com/johng/gf/g/os/gconsole"
|
||||||
|
"gitee.com/johng/gf/g/database/gdb"
|
||||||
|
"gitee.com/johng/gf/g/frame/gconfig"
|
||||||
|
"gitee.com/johng/gf/g/container/gmap"
|
||||||
|
"gitee.com/johng/gf/g/os/glog"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
FRAME_CORE_COMPONENT_NAME_CONFIG = "gf.component.config"
|
||||||
|
FRAME_CORE_COMPONENT_NAME_DATABASE = "gf.component.database"
|
||||||
|
)
|
||||||
|
|
||||||
// 单例对象存储器
|
// 单例对象存储器
|
||||||
var instances = gmap.NewStringInterfaceMap()
|
var instances = gmap.NewStringInterfaceMap()
|
||||||
@ -14,4 +28,93 @@ func Get(k string) interface{} {
|
|||||||
// 设置单例对象
|
// 设置单例对象
|
||||||
func Set(k string, v interface{}) {
|
func Set(k string, v interface{}) {
|
||||||
instances.Set(k, v)
|
instances.Set(k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 核心对象:Config
|
||||||
|
// 配置文件目录查找依次为:启动参数cfgpath、当前程序运行目录
|
||||||
|
func Config() *gconfig.Config {
|
||||||
|
result := Get(FRAME_CORE_COMPONENT_NAME_CONFIG)
|
||||||
|
if result != nil {
|
||||||
|
return result.(*gconfig.Config)
|
||||||
|
} else {
|
||||||
|
path := gconsole.Option.Get("cfgpath")
|
||||||
|
if path == "" {
|
||||||
|
path = gfile.SelfDir()
|
||||||
|
}
|
||||||
|
config := gconfig.New(path)
|
||||||
|
Set(FRAME_CORE_COMPONENT_NAME_CONFIG, config)
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 核心对象:Database
|
||||||
|
func Database(names...string) gdb.Link {
|
||||||
|
result := Get(FRAME_CORE_COMPONENT_NAME_DATABASE)
|
||||||
|
if result != nil {
|
||||||
|
return result.(gdb.Link)
|
||||||
|
} else {
|
||||||
|
config := Config()
|
||||||
|
if config == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if m := config.GetMap("database"); m != nil {
|
||||||
|
for group, v := range m {
|
||||||
|
if list, ok := v.([]interface{}); ok {
|
||||||
|
for _, nodev := range list {
|
||||||
|
node := gdb.ConfigNode{}
|
||||||
|
nodem := nodev.(map[string]interface{})
|
||||||
|
if value, ok := nodem["host"]; ok {
|
||||||
|
node.Host = value.(string)
|
||||||
|
}
|
||||||
|
if value, ok := nodem["port"]; ok {
|
||||||
|
node.Port = value.(string)
|
||||||
|
}
|
||||||
|
if value, ok := nodem["user"]; ok {
|
||||||
|
node.User = value.(string)
|
||||||
|
}
|
||||||
|
if value, ok := nodem["pass"]; ok {
|
||||||
|
node.Pass = value.(string)
|
||||||
|
}
|
||||||
|
if value, ok := nodem["name"]; ok {
|
||||||
|
node.Name = value.(string)
|
||||||
|
}
|
||||||
|
if value, ok := nodem["type"]; ok {
|
||||||
|
node.Type = value.(string)
|
||||||
|
}
|
||||||
|
if value, ok := nodem["role"]; ok {
|
||||||
|
node.Role = value.(string)
|
||||||
|
}
|
||||||
|
if value, ok := nodem["charset"]; ok {
|
||||||
|
node.Charset = value.(string)
|
||||||
|
}
|
||||||
|
if value, ok := nodem["priority"]; ok {
|
||||||
|
node.Priority, _ = strconv.Atoi(value.(string))
|
||||||
|
}
|
||||||
|
gdb.AddConfigNode(group, node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var db gdb.Link = nil
|
||||||
|
if len(names) == 0 {
|
||||||
|
if link, err := gdb.Instance(); err == nil {
|
||||||
|
db = link
|
||||||
|
} else {
|
||||||
|
glog.Error(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if link, err := gdb.InstanceByGroup(names[0]); err == nil {
|
||||||
|
db = link
|
||||||
|
} else {
|
||||||
|
glog.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if db != nil {
|
||||||
|
Set(FRAME_CORE_COMPONENT_NAME_DATABASE, db)
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
@ -3,7 +3,6 @@ package gmvc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"gitee.com/johng/gf/g/net/ghttp"
|
"gitee.com/johng/gf/g/net/ghttp"
|
||||||
"gitee.com/johng/gf/g/frame/gbase"
|
|
||||||
"gitee.com/johng/gf/g/net/gsession"
|
"gitee.com/johng/gf/g/net/gsession"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -13,7 +12,6 @@ const (
|
|||||||
|
|
||||||
// 控制器基类
|
// 控制器基类
|
||||||
type Controller struct {
|
type Controller struct {
|
||||||
gbase.Base
|
|
||||||
Server *ghttp.Server // Web Server对象
|
Server *ghttp.Server // Web Server对象
|
||||||
Request *ghttp.ClientRequest // 请求数据对象
|
Request *ghttp.ClientRequest // 请求数据对象
|
||||||
Response *ghttp.ServerResponse // 返回数据对象
|
Response *ghttp.ServerResponse // 返回数据对象
|
||||||
@ -24,7 +22,6 @@ type Controller struct {
|
|||||||
|
|
||||||
// 控制器初始化接口方法
|
// 控制器初始化接口方法
|
||||||
func (c *Controller) Init(s *ghttp.Server, r *ghttp.ClientRequest, w *ghttp.ServerResponse) {
|
func (c *Controller) Init(s *ghttp.Server, r *ghttp.ClientRequest, w *ghttp.ServerResponse) {
|
||||||
c.Base.Init()
|
|
||||||
c.Server = s
|
c.Server = s
|
||||||
c.Request = r
|
c.Request = r
|
||||||
c.Response = w
|
c.Response = w
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// 模型基类
|
// 模型基类
|
||||||
package gmvc
|
package gmvc
|
||||||
|
|
||||||
import "gitee.com/johng/gf/g/frame/gbase"
|
|
||||||
|
|
||||||
type Model struct {
|
type Model struct {
|
||||||
gbase.Base
|
|
||||||
}
|
}
|
@ -4,14 +4,13 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"html/template"
|
"html/template"
|
||||||
"gitee.com/johng/gf/g/os/gview"
|
"gitee.com/johng/gf/g/os/gview"
|
||||||
"gitee.com/johng/gf/g/frame/gbase"
|
|
||||||
"gitee.com/johng/gf/g/os/gfile"
|
"gitee.com/johng/gf/g/os/gfile"
|
||||||
"gitee.com/johng/gf/g/os/gconsole"
|
"gitee.com/johng/gf/g/os/gconsole"
|
||||||
|
"gitee.com/johng/gf/g/frame/ginstance"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 视图对象(一个请求一个视图对象,用完即销毁)
|
// 视图对象(一个请求一个视图对象,用完即销毁)
|
||||||
type View struct {
|
type View struct {
|
||||||
gbase.Base
|
|
||||||
mu sync.RWMutex // 并发互斥锁
|
mu sync.RWMutex // 并发互斥锁
|
||||||
ctl *Controller // 所属控制器
|
ctl *Controller // 所属控制器
|
||||||
view *gview.View // 底层视图对象
|
view *gview.View // 底层视图对象
|
||||||
@ -25,8 +24,10 @@ func NewView(c *Controller) *View {
|
|||||||
if path == "" {
|
if path == "" {
|
||||||
path = gfile.SelfDir()
|
path = gfile.SelfDir()
|
||||||
}
|
}
|
||||||
if r := c.Config.Get("viewpath"); r != nil {
|
if config := ginstance.Config(); config != nil {
|
||||||
path = r.(string)
|
if r := config.Get("viewpath"); r != nil {
|
||||||
|
path = r.(string)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return &View{
|
return &View{
|
||||||
ctl : c,
|
ctl : c,
|
||||||
@ -52,7 +53,11 @@ func (view *View) Assign(key string, value interface{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 解析指定模板
|
// 解析指定模板
|
||||||
func (view *View) Display(file string) error {
|
func (view *View) Display(files...string) error {
|
||||||
|
file := "default"
|
||||||
|
if len(files) > 0 {
|
||||||
|
file = files[0]
|
||||||
|
}
|
||||||
// 查询模板
|
// 查询模板
|
||||||
tpl, err := view.view.Template(file)
|
tpl, err := view.view.Template(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
"host" : "127.0.0.1",
|
"host" : "127.0.0.1",
|
||||||
"port" : "3306",
|
"port" : "3306",
|
||||||
"user" : "root",
|
"user" : "root",
|
||||||
"pass" : "127456",
|
"pass" : "123456",
|
||||||
"name" : "test",
|
"name" : "test",
|
||||||
"type" : "mysql",
|
"type" : "mysql",
|
||||||
"role" : "master",
|
"role" : "master",
|
||||||
@ -17,7 +17,7 @@
|
|||||||
"host" : "127.0.0.1",
|
"host" : "127.0.0.1",
|
||||||
"port" : "3306",
|
"port" : "3306",
|
||||||
"user" : "root",
|
"user" : "root",
|
||||||
"pass" : "127456",
|
"pass" : "123456",
|
||||||
"name" : "test",
|
"name" : "test",
|
||||||
"type" : "mysql",
|
"type" : "mysql",
|
||||||
"role" : "master",
|
"role" : "master",
|
||||||
|
@ -1,18 +1,11 @@
|
|||||||
package main
|
package main
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"gitee.com/johng/gf/g/frame/ginstance"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Add(path string, name ... string) {
|
|
||||||
fmt.Println(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
for _, e := range os.Environ() {
|
db := ginstance.Database()
|
||||||
|
list, _ := db.Table("test").Select()
|
||||||
fmt.Println(e)
|
fmt.Println(list[0])
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
package gf
|
package gf
|
||||||
|
|
||||||
// 框架信息
|
// 框架信息
|
||||||
const VERSION = "0.60"
|
const VERSION = "0.80"
|
||||||
const AUTHORS = "john<john@johng.cn>"
|
const AUTHORS = "john<john@johng.cn>"
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user