如何使用 Xorm - 1
如何在Golang项目中使用Xorm,这是Xorm系列文章中的第一篇

简介

在使用Golang开发使用数据库的项目时,就需要考虑ORM组件了,目前比较热门的Golang的ORM组件,有两个选择: gormxorm,在比较之后,我选择了xorm.

本文是Xorm系列文章的第一篇,介绍下xorm的CRUD(Create, Retrive, Update, Delete).

环境

  • 数据库: PostgreSQL;
  • 数据库的客户端: DBeaver(这是个开源免费的软件);
  • Golang: 1.19 windows/amd64;
  • VSC: 微软推出的不开源,但是免费的文本/代码编辑软件Visual Studio Code;

准备个PostgreSQL数据库及用户

# 登录PostgreSQL管理员账户
psql -U postgres
# 输入用户 postgres 的密码,以创建数据库及数据库相关的用户

# 创建PostgreSQL用户账户
CREATE USER goxormdemo_user WITH PASSWORD 'goxormdemo_pwd';
# 创建数据库
CREATE DATABASE goxormdemo_db;

# 把刚创建的数据库授权给刚创建的用户
GRANT ALL PRIVILEGES ON DATABASE goxormdemo_db to goxormdemo_user;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO goxormdemo_user;

# 退出psql
\q

goxorm-01

准备个Golang项目


# 创建项目的根目录,并进入项目的根目录
mkdir goxormdemo && cd goxormdemo

# 准备所需的目录,然后回到项目的根目录
mkdir models
mkdir pkgs && cd pkgs && mkdir env  && cd .. 

# 声明下该项目的命名空间
go mod init goxormdemo

# 使用VSC创建个文件

Code main.go

然后输入:


package main

import(
  "log"
  _ "github.com/lib/pq"
)
func main() {

  log.Println("goXormDemo running...")
}

创建与数据库连接相关的文件


cd pkgs/env


Code x.go

输入如下内容:


package env

import (
  "fmt"

  "xorm.io/xorm"
)

var X *xorm.Engine

type DbConfig struct {
  Host     string
  Port     int
  ShowSQL  bool
  DbName   string
  User     string
  Password string
}

func InitDB(cfg *DbConfig) error {

  x, err := connectDB(cfg)
  if err != nil {
    return err
  }
  X = x
  return nil
}

func connectDB(cfg *DbConfig) (*xorm.Engine, error) {
  dataSrc := fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=disable",
    cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.DbName)
  engine, err := xorm.NewEngine("postgres", dataSrc)
  if err != nil {
    return nil, err
  }
  engine.ShowSQL(cfg.ShowSQL)
  return engine, nil
}

更新项目根目录下的main.go:


package main

import (
  "goxormdemo/pkgs/env"
  "log"

  _ "github.com/lib/pq"
)

func main() {

  log.Println("goXormDemo running...")

  dbConfig := &env.DbConfig{
    Host:     "127.0.0.1",
    Port:     5432,
    ShowSQL:  true,
    User:     "goxormdemo_user",
    Password: "goxormdemo_pwd",
    DbName:   "goxormdemo_db",
  }
  err := env.InitDB(dbConfig)

  if err != nil {

    log.Fatal(err.Error())
  }

  log.Println("已连接数据库")

}

/*

go run main.go

*/

在项目的根目录下执行:


go mod tidy

以获取下所依赖的组件。

在项目的根目录下执行:


go run main.go

看到类似下面的输出,说明数据库连接没问题


λ go run main.go
2022/08/25 01:55:53 goXormDemo running...
2022/08/25 01:55:53 已连接数据库

准备数据库交互文件

models目录下,创建个文件


cd models

Code user.go

内容:

package models

import (
  "errors"
  "goxormdemo/pkgs/env"
  "time"
)

type User struct {
  ID       int64     `xorm:"bigint pk autoincr 'id'" json:"id"`
  Email    string    `xorm:"VARCHAR(254) notnull unique" json:"emial"`
  Username string    `xorm:"VARCHAR(64)" json:"username"`
  Password string    `xorm:"VARCHAR(80)" json:"-"`
  Nickname string    `xorm:"VARCHAR(36)" json:"nickname"`
  Gender   string    `xorm:"VARCHAR(2)" json:"gender"` //可用值:f或者m,分别表示female和male
  Created  time.Time `xorm:"created"  json:"created"`
  Updated  time.Time ` xorm:"updated"  json:"updated"`
  Version  int       `xorm:"version"`
}

// 把上面的User的字段结构,同步到数据库中的数据表user中去,如果没有这个数据表,将会创建.
func InitUser() error {

  return env.X.Sync2(new(User))

}

// 添加对象
func (obj *User) Insert() (int64, error) {

  //模拟下去重,实际业务中,应在插入前,检查是否有重复的若干字段,比如是否有重复的用户名及邮箱等,这里以昵称为例
  if HasUserByNickname(obj.Nickname) {

    return 0, errors.New("昵称已被使用")
  }
  //实际业务中,在存储之前,应对密码做hash处理
  _, err := env.X.Insert(obj)
  if err != nil {
    return 0, err
  }

  return obj.ID, nil
}

// 更新对象
func (obj *User) Update() error {
  //实际业务中,更新前,可能需要做些数据校验或处理
  rows, err := env.X.ID(obj.ID).AllCols().Update(obj)
  if err != nil {

    return err
  }
  if rows > 0 {
    return nil
  }
  return errors.New("更新出错了")

}

// 删除对象
func (obj *User) Delete() error {

  if obj.ID != 0 {
    rows, err := env.X.Delete(obj)
    if err != nil {

      return err
    }
    if rows == 0 {
      return errors.New("not_found")
    }
    return nil

  }

  return errors.New("error_not_be_detected")

}

// 查询单个对象
func GetUserByID(id int64) (User, error) {
  userPtr, err := GetUserPtrByID(id)
  return *userPtr, err
}

// 查询单个对象,并返回指针
func GetUserPtrByID(id int64) (*User, error) {
  if id < int64(1) {

    return nil, errors.New("invalid_id")
  }

  user := &User{ID: id}
  ok, err := env.X.Get(user)
  if ok {
    return user, nil
  } else if err != nil {
    return nil, err
  }
  return nil, errors.New("not_found")
}

// 查询单个对象,并返回指针
func GetUserPtrByNickname(nickname string) (*User, error) {
  if len(nickname) == 0 {

    return nil, errors.New("invalid_nickname")
  }

  user := &User{Nickname: nickname}
  ok, err := env.X.Get(user)
  if ok {
    return user, nil
  } else if err != nil {
    return nil, err
  }
  return nil, errors.New("not_found")
}

// 查询某些对象
func GetUsersByGender(gender string) ([]User, error) {
  list := make([]User, 0)
  err := env.X.Where("gender=?", gender).Desc("id").Find(&list)
  if err != nil {
    return list, err
  }
  return list, nil
}

func HasUserByNickname(nickname string) bool {

  userPtr := &User{
    Nickname: nickname,
  }
  has, err := env.X.Exist(userPtr)

  if err != nil {
    return false
  }
  return has
}

将User同步到数据表中

更新项目根目录下的main.go:


package main

import (
  "goxormdemo/models"
  "goxormdemo/pkgs/env"
  "log"

  _ "github.com/lib/pq"
)

func main() {

  log.Println("goXormDemo running...")

  dbConfig := &env.DbConfig{
    Host:     "127.0.0.1",
    Port:     5432,
    ShowSQL:  true,
    User:     "goxormdemo_user",
    Password: "goxormdemo_pwd",
    DbName:   "goxormdemo_db",
  }
  err := env.InitDB(dbConfig)

  if err != nil {

    log.Fatal(err.Error())
  }

  log.Println("已连接数据库")

  err = models.InitUser()
  if err != nil {

    log.Fatal(err.Error())
  }
  log.Println("已同步model到数据表")
}

/*

go run main.go

*/

然后执行:


go run main.go

此时可见如下输出:


λ go run main.go
2022/08/25 02:05:11 goXormDemo running...
2022/08/25 02:05:11 已连接数据库
[xorm] [info]  2022/08/25 02:05:11.447752 [SQL] SELECT tablename FROM pg_tables WHERE schemaname = $1 [public] - 140.6097ms
[xorm] [info]  2022/08/25 02:05:11.463316 [SQL] CREATE TABLE IF NOT EXISTS "public"."user" ("id" BIGSERIAL PRIMARY KEY  NOT NULL, "email" VARCHAR(254) NOT NULL, "username" VARCHAR(64) NULL, "password" VARCHAR(80) NULL, "nickname" VARCHAR(36) NULL, "gender" VARCHAR(2) NULL, "created" TIMESTAMP NULL, "updated" TIMESTAMP NULL, "version" INTEGER NULL);  [] - 14.1727ms
[xorm] [info]  2022/08/25 02:05:11.471764 [SQL] CREATE UNIQUE INDEX "UQE_user_email" ON "user" ("email") [] - 7.3539ms
2022/08/25 02:05:11 已同步model到数据表

使用 DBeaver 连接这个数据库,可以见到数据表user的数据结构:

goxorm-02

添加数据-创建个几个用户

更新根目录下的main.go:


package main

import (
  "goxormdemo/models"
  "goxormdemo/pkgs/env"
  "log"

  _ "github.com/lib/pq"
)

func initDb() {
  dbConfig := &env.DbConfig{
    Host:     "127.0.0.1",
    Port:     5432,
    ShowSQL:  true,
    User:     "goxormdemo_user",
    Password: "goxormdemo_pwd",
    DbName:   "goxormdemo_db",
  }
  err := env.InitDB(dbConfig)

  if err != nil {

    log.Fatalln(err.Error())
  }

  log.Println("已连接数据库")
}

func AutoMigrate() {
  err := models.InitUser()
  if err != nil {

    log.Fatalln(err.Error())
  }
  log.Println("已同步model到数据表")
}

func CreateUsers() {
  //模拟创建几个用户
  users := make([]*models.User, 0)
  mm := &models.User{
    Nickname: "曼曼",
    Email:    "mm@fakemail.com",
    Gender:   "f",
  }
  users = append(users, mm)
  mm2 := &models.User{
    Nickname: "蔓蔓",
    Email:    "mm2@fakemail.com",
    Gender:   "f",
  }
  users = append(users, mm2)
  hh := &models.User{
    Nickname: "灰灰",
    Email:    "hh@fakemail.com",
    Gender:   "m",
  }
  users = append(users, hh)
  yy := &models.User{
    Nickname: "雨雨",
    Email:    "yy@fakemail.com",
    Gender:   "f",
  }
  users = append(users, yy)

  var err error
  for _, user := range users {
    _, err = user.Insert()
    if err != nil {
      log.Println("插入'" + user.Nickname + "'时出错了:" + err.Error())
    }
  }
}

func main() {

  log.Println("goXormDemo running...")

  initDb()
  //AutoMigrate()
  //CreateUsers()

}

/*

go run main.go

*/

然后执行下go run main.go,没啥错误提示的话,就创建成功了。

查询数据-查询单条或多条数据

更新下根目录下的main.go:

package main

import (
  "fmt"
  "goxormdemo/models"
  "goxormdemo/pkgs/env"
  "log"

  _ "github.com/lib/pq"
)

func initDb() {
  dbConfig := &env.DbConfig{
    Host:     "127.0.0.1",
    Port:     5432,
    ShowSQL:  true,
    User:     "goxormdemo_user",
    Password: "goxormdemo_pwd",
    DbName:   "goxormdemo_db",
  }
  err := env.InitDB(dbConfig)

  if err != nil {

    log.Fatalln(err.Error())
  }

  log.Println("已连接数据库")
}

func AutoMigrate() {
  err := models.InitUser()
  if err != nil {

    log.Fatalln(err.Error())
  }
  log.Println("已同步model到数据表")
}

func QueryOneUserByNickname(nickname string) {

  userPtr, err := models.GetUserPtrByNickname(nickname)
  if err != nil {

    log.Println(err.Error())
    return
  }

  fmt.Printf("%+v\n", userPtr)

}
func QueryMultipleUsersByGender(gender string) {

  users, err := models.GetUsersByGender(gender)
  if err != nil {

    log.Println(err.Error())
    return
  }

  if len(users) == 0 {

    log.Println("没找到符合条件的用户")
    return
  }
  for _, user := range users {

    fmt.Printf("%+v\n", user)
  }

}
func CreateUsers() {
  //模拟创建几个用户
  users := make([]*models.User, 0)
  mm := &models.User{
    Nickname: "曼曼",
    Email:    "mm@fakemail.com",
    Gender:   "f",
  }
  users = append(users, mm)
  mm2 := &models.User{
    Nickname: "蔓蔓",
    Email:    "mm2@fakemail.com",
    Gender:   "f",
  }
  users = append(users, mm2)
  hh := &models.User{
    Nickname: "灰灰",
    Email:    "hh@fakemail.com",
    Gender:   "m",
  }
  users = append(users, hh)
  yy := &models.User{
    Nickname: "雨雨",
    Email:    "yy@fakemail.com",
    Gender:   "f",
  }
  users = append(users, yy)

  var err error
  for _, user := range users {
    _, err = user.Insert()
    if err != nil {
      log.Println("插入'" + user.Nickname + "'时出错了:" + err.Error())
    }
  }
}

func main() {

  log.Println("goXormDemo running...")

  initDb()
  //AutoMigrate()
  //CreateUsers()
  log.Println("查询单个用户:")
  QueryOneUserByNickname("蔓蔓")
  log.Println("查询多个用户:")
  QueryMultipleUsersByGender("f")

}

/*

go run main.go

*/

然后执行下go run main.go,可见到类似下面的输出:


λ go run main.go
2022/08/25 02:37:52 goXormDemo running...
2022/08/25 02:37:53 已连接数据库
2022/08/25 02:37:53 查询单个用户:
[xorm] [info]  2022/08/25 02:37:53.154797 [SQL] SELECT "id", "email", "username", "password", "nickname", "gender", "created", "updated", "version" FROM "user" WHERE "nickname"=$1 LIMIT 1 [蔓蔓] - 135.4354ms
&{ID:4 Email:mm2@fakemail.com Username: Password: Nickname:蔓蔓 Gender:f Created:2022-08-25 02:24:22 +0800 CST Updated:2022-08-25 02:2
4:22 +0800 CST Version:1}
2022/08/25 02:37:53 查询多个用户:
[xorm] [info]  2022/08/25 02:37:53.157193 [SQL] SELECT "id", "email", "username", "password", "nickname", "gender", "created", "updated", "version" FROM "user" WHERE (gender=$1) ORDER BY "id" DESC [f] - 502µs
{ID:4 Email:mm2@fakemail.com Username: Password: Nickname:蔓蔓 Gender:f Created:2022-08-25 02:24:22 +0800 CST Updated:2022-08-25 02:24
:22 +0800 CST Version:1}
{ID:3 Email:yy@fakemail.com Username: Password: Nickname:雨雨 Gender:f Created:2022-08-25 02:23:04 +0800 CST Updated:2022-08-25 02:23:
04 +0800 CST Version:1}
{ID:1 Email:mm@fakemail.com Username: Password: Nickname:曼曼 Gender:f Created:2022-08-25 02:23:04 +0800 CST Updated:2022-08-25 02:23:
04 +0800 CST Version:1}

删除数据-删除某个用户

更新根目录下的main.go:


package main

import (
  "fmt"
  "goxormdemo/models"
  "goxormdemo/pkgs/env"
  "log"

  _ "github.com/lib/pq"
)

func initDb() {
  dbConfig := &env.DbConfig{
    Host:     "127.0.0.1",
    Port:     5432,
    ShowSQL:  true,
    User:     "goxormdemo_user",
    Password: "goxormdemo_pwd",
    DbName:   "goxormdemo_db",
  }
  err := env.InitDB(dbConfig)

  if err != nil {

    log.Fatalln(err.Error())
  }

  log.Println("已连接数据库")
}

func AutoMigrate() {
  err := models.InitUser()
  if err != nil {

    log.Fatalln(err.Error())
  }
  log.Println("已同步model到数据表")
}

func QueryOneUserByNickname(nickname string) {

  userPtr, err := models.GetUserPtrByNickname(nickname)
  if err != nil {

    log.Println(err.Error())
    return
  }

  fmt.Printf("%+v\n", userPtr)

}
func QueryMultipleUsersByGender(gender string) {

  users, err := models.GetUsersByGender(gender)
  if err != nil {

    log.Println(err.Error())
    return
  }

  if len(users) == 0 {

    log.Println("没找到符合条件的用户")
    return
  }
  for _, user := range users {

    fmt.Printf("%+v\n", user)
  }

}
func CreateUsers() {
  //模拟创建几个用户
  users := make([]*models.User, 0)
  mm := &models.User{
    Nickname: "曼曼",
    Email:    "mm@fakemail.com",
    Gender:   "f",
  }
  users = append(users, mm)
  mm2 := &models.User{
    Nickname: "蔓蔓",
    Email:    "mm2@fakemail.com",
    Gender:   "f",
  }
  users = append(users, mm2)
  hh := &models.User{
    Nickname: "灰灰",
    Email:    "hh@fakemail.com",
    Gender:   "m",
  }
  users = append(users, hh)
  yy := &models.User{
    Nickname: "雨雨",
    Email:    "yy@fakemail.com",
    Gender:   "f",
  }
  users = append(users, yy)

  var err error
  for _, user := range users {
    _, err = user.Insert()
    if err != nil {
      log.Println("插入'" + user.Nickname + "'时出错了:" + err.Error())
    }
  }
}

func main() {

  log.Println("goXormDemo running...")

  initDb()
  //AutoMigrate()
  //CreateUsers()
  log.Println("查询单个用户:")
  QueryOneUserByNickname("蔓蔓")
  log.Println("查询多个用户:")
  QueryMultipleUsersByGender("f")
  log.Println("删除昵称为‘曼曼’的用户:")
  userPtr, err := models.GetUserPtrByNickname("曼曼")
  if err != nil {
    log.Println(err.Error())
  } else {

    err = userPtr.Delete()
    if err != nil {
      log.Println(err.Error())
    } else {

      log.Println("删除用户成功")
    }
  }

}

/*

go run main.go

*/

执行go run main.go,可见类似下面的输出:


λ go run main.go
2022/08/25 02:41:36 goXormDemo running...
2022/08/25 02:41:36 已连接数据库
2022/08/25 02:41:36 查询单个用户:
[xorm] [info]  2022/08/25 02:41:37.082253 [SQL] SELECT "id", "email", "username", "password", "nickname", "gender", "created", "updated", "version" FROM "user" WHERE "nickname"=$1 LIMIT 1 [蔓蔓] - 133.4841ms
&{ID:4 Email:mm2@fakemail.com Username: Password: Nickname:蔓蔓 Gender:f Created:2022-08-25 02:24:22 +0800 CST Updated:2022-08-25 02:2
4:22 +0800 CST Version:1}
2022/08/25 02:41:37 查询多个用户:
[xorm] [info]  2022/08/25 02:41:37.085280 [SQL] SELECT "id", "email", "username", "password", "nickname", "gender", "created", "updated", "version" FROM "user" WHERE (gender=$1) ORDER BY "id" DESC [f] - 541.7µs
{ID:4 Email:mm2@fakemail.com Username: Password: Nickname:蔓蔓 Gender:f Created:2022-08-25 02:24:22 +0800 CST Updated:2022-08-25 02:24
:22 +0800 CST Version:1}
{ID:3 Email:yy@fakemail.com Username: Password: Nickname:雨雨 Gender:f Created:2022-08-25 02:23:04 +0800 CST Updated:2022-08-25 02:23:
04 +0800 CST Version:1}
{ID:1 Email:mm@fakemail.com Username: Password: Nickname:曼曼 Gender:f Created:2022-08-25 02:23:04 +0800 CST Updated:2022-08-25 02:23:
04 +0800 CST Version:1}
2022/08/25 02:41:37 删除昵称为‘曼曼’的用户:
[xorm] [info]  2022/08/25 02:41:37.088416 [SQL] SELECT "id", "email", "username", "password", "nickname", "gender", "created", "updated", "version" FROM "user" WHERE "nickname"=$1 LIMIT 1 [曼曼]
[xorm] [info]  2022/08/25 02:41:37.091021 [SQL] DELETE FROM "user" WHERE "id"=$1 AND "email"=$2 AND "nickname"=$3 AND "gender"=$4 AND "created"=$5 AND "updated"=$6 AND "version"=$7 [1 mm@fakemail.com 曼曼 f 2022-08-25 02:23:04 2022-08-25 02:23:04 1] - 1.567ms
2022/08/25 02:41:37 删除用户成功

更新数据-更新某个用户的邮箱


package main

import (
  "fmt"
  "goxormdemo/models"
  "goxormdemo/pkgs/env"
  "log"

  _ "github.com/lib/pq"
)

func initDb() {
  dbConfig := &env.DbConfig{
    Host:     "127.0.0.1",
    Port:     5432,
    ShowSQL:  true,
    User:     "goxormdemo_user",
    Password: "goxormdemo_pwd",
    DbName:   "goxormdemo_db",
  }
  err := env.InitDB(dbConfig)

  if err != nil {

    log.Fatalln(err.Error())
  }

  log.Println("已连接数据库")
}

func AutoMigrate() {
  err := models.InitUser()
  if err != nil {

    log.Fatalln(err.Error())
  }
  log.Println("已同步model到数据表")
}

func QueryOneUserByNickname(nickname string) {

  userPtr, err := models.GetUserPtrByNickname(nickname)
  if err != nil {

    log.Println(err.Error())
    return
  }

  fmt.Printf("%+v\n", userPtr)

}
func QueryMultipleUsersByGender(gender string) {

  users, err := models.GetUsersByGender(gender)
  if err != nil {

    log.Println(err.Error())
    return
  }

  if len(users) == 0 {

    log.Println("没找到符合条件的用户")
    return
  }
  for _, user := range users {

    fmt.Printf("%+v\n", user)
  }

}
func CreateUsers() {
  //模拟创建几个用户
  users := make([]*models.User, 0)
  mm := &models.User{
    Nickname: "曼曼",
    Email:    "mm@fakemail.com",
    Gender:   "f",
  }
  users = append(users, mm)
  mm2 := &models.User{
    Nickname: "蔓蔓",
    Email:    "mm2@fakemail.com",
    Gender:   "f",
  }
  users = append(users, mm2)
  hh := &models.User{
    Nickname: "灰灰",
    Email:    "hh@fakemail.com",
    Gender:   "m",
  }
  users = append(users, hh)
  yy := &models.User{
    Nickname: "雨雨",
    Email:    "yy@fakemail.com",
    Gender:   "f",
  }
  users = append(users, yy)

  var err error
  for _, user := range users {
    _, err = user.Insert()
    if err != nil {
      log.Println("插入'" + user.Nickname + "'时出错了:" + err.Error())
    }
  }
}

func main() {

  log.Println("goXormDemo running...")

  initDb()
  //AutoMigrate()
  //CreateUsers()
  log.Println("更新‘蔓蔓’的邮箱:")
  userPtr, err := models.GetUserPtrByNickname("蔓蔓")
  if err != nil {
    log.Println(err.Error())
  } else {
    userPtr.Email = "manman@fakemail.com"
    err = userPtr.Update()
    if err != nil {
      log.Println(err.Error())
    } else {

      log.Println("更新用户数据成功")
    }
  }

  QueryOneUserByNickname("蔓蔓")

}

/*

go run main.go

*/

执行go run main.go, 可见类似下面的输出:


2022/08/25 02:47:45 goXormDemo running...
2022/08/25 02:47:45 已连接数据库
2022/08/25 02:47:45 更新‘蔓蔓’的邮箱:
[xorm] [info]  2022/08/25 02:47:46.057502 [SQL] SELECT "id", "email", "username", "password", "nickname", "gender", "created", "updated", "version" FROM "user" WHERE "nickname"=$1 LIMIT 1 [蔓蔓] - 131.739ms
[xorm] [info]  2022/08/25 02:47:46.059269 [SQL] UPDATE "user" SET "email" = $1, "username" = $2, "password" = $3, "nickname" = $4, "gender" = $5, "updated" = $6, "version" = "version" + 1 WHERE "id"=$7 AND "version"=$8 [manman@fakemail.com   蔓蔓 f 2022-08-25 02:47:46
 4 2] - 1.0231ms
2022/08/25 02:47:46 更新用户数据成功
[xorm] [info]  2022/08/25 02:47:46.060187 [SQL] SELECT "id", "email", "username", "password", "nickname", "gender", "created", "updated", "version" FROM "user" WHERE "nickname"=$1 LIMIT 1 [蔓蔓]
&{ID:4 Email:manman@fakemail.com Username: Password: Nickname:蔓蔓 Gender:f Created:2022-08-25 02:24:22 +0800 CST Updated:2022-08-25 0
2:47:46 +0800 CST Version:3}

最后修改于 2022-08-25

禁止评论