Commit 0de8032172753afc3fc17487908f40b5c56d5c19

Authored by liuzujun
1 parent b1daa4a8

创建游戏数据库,role对应mysql数据表,以及存储读取实现

src/actions/HttpAction.lua
... ... @@ -134,7 +134,7 @@ function _M.gm_action(query)
134 134 return isOn
135 135 end
136 136 -- 离线操作
137   - local role = require("models.Role").new({key = string.format("role:%d", query.id)})
  137 + local role = require("models.Role").new({key = string.format("%d", query.id)})
138 138 local ret = role:load()
139 139 if not ret then
140 140 return "角色不存在"
... ...
src/actions/RoleAction.lua
... ... @@ -84,12 +84,12 @@ function _M.loginRpc( agent, data )
84 84 local role = agent.role
85 85 -- 2
86 86 if not role then
87   - local roleKey = string_format("role:%d", roleId)
88   - if not redisproxy:exists(roleKey) then
89   - response.result = "DB_ERROR"
90   - SendPacket(actionCodes.Role_loginRpc, MsgPack.pack(response))
91   - return true
92   - end
  87 + local roleKey = string_format("%d", roleId)
  88 + --if not redisproxy:exists(roleKey) then
  89 + -- response.result = "DB_ERROR"
  90 + -- SendPacket(actionCodes.Role_loginRpc, MsgPack.pack(response))
  91 + -- return true
  92 + --end
93 93 -- 2a
94 94 role = require("models.Role").new({key = roleKey})
95 95 role:load()
... ... @@ -333,7 +333,7 @@ function _M.createRpc(agent, data)
333 333 end
334 334 local roleName = setRoleName(msg.uid, roleId)
335 335 local newRole = require("models.Role").new({
336   - key = string_format("role:%d", roleId),
  336 + key = string_format("%d", roleId),
337 337 id = roleId,
338 338 uid = tostring(msg.uid),
339 339 sid = msg.subId or 0,
... ...
src/agent.lua
... ... @@ -12,6 +12,7 @@ local xxtea = require "xxtea"
12 12  
13 13 skynet = require "skynet"
14 14 redisproxy = require "shared.redisproxy"
  15 +mysqlproxy = require "shared.mysqlproxy"
15 16 datacenter = require "skynet.datacenter"
16 17 mcast_util = require "services/mcast_util"
17 18 csvdb = require "shared.csvdata"
... ...
src/main.lua
... ... @@ -21,6 +21,13 @@ skynet.start(function()
21 21 })
22 22 end
23 23  
  24 + -- 启动mysql
  25 + for i = 1, work_count do
  26 + local redisd = skynet.newservice("services/mysqld", i)
  27 + skynet.call(redisd, "lua", "open", {
  28 + })
  29 + end
  30 +
24 31 --启动log
25 32 if use_logd == 1 then
26 33 for i = 1, work_count * 2 do
... ...
src/models/Role.lua
1   -local Role = class("Role", require("shared.ModelBase"))
  1 +local Role = class("Role", require("shared.ModelBaseMysql"))
2 2  
3 3 local RoleLog = import(".RoleLog") --日志相关
4 4 local RolePlugin = import(".RolePlugin") --基础功能
... ... @@ -40,11 +40,12 @@ function Role:ctor( properties )
40 40 self.sendMailFlag = false --发送邮件标识
41 41 end
42 42  
  43 +-- type, default value, key type, length
43 44 Role.schema = {
44   - id = {"number"},
  45 + id = {"number", 0, "pri"},
45 46 uid = {"string", ""},
46 47 name = {"string", ""},
47   - intro = {"string", ""},
  48 + intro = {"string", "", "", 1024},
48 49 headId = {"number", globalCsv.defaultHead},
49 50 sid = {"number", 0},
50 51 device = {"string", ""},
... ... @@ -61,11 +62,11 @@ Role.schema = {
61 62 diamond = {"number", 0},
62 63 reDiamond = {"number", 0},
63 64 setting = {"table", {}}, --设置
64   - codeStr = {"string", ""}, --已经领过的礼包码
  65 + codeStr = {"string", "", "blob"}, --已经领过的礼包码
65 66 -- roleInfo
66 67 level = {"number", 1},
67 68 exp = {"number", 0},
68   - items = {"string", ""},
  69 + items = {"string", "", "blob"},
69 70 expireItem = {"table", {}}, --物品过期检查
70 71 funcOpen = {"table", {}}, --功能是否开放
71 72 funcLv = {"table", {}}, --功能等级
... ... @@ -77,7 +78,7 @@ Role.schema = {
77 78  
78 79 --冒险相关
79 80 advPass = {"table", {}}, -- 通关记录 {chapterId = layer}
80   - advItems = {"string", ""}, -- 冒险临时背包
  81 + advItems = {"string", "", "blob"}, -- 冒险临时背包
81 82 advInfo = {"table", {}}, -- 冒险关卡信息
82 83 advTeam = {"table", {}}, -- 冒险玩家队伍信息
83 84 advHang = {"table", {}}, -- 挂机信息 -- {chapterId = {format = teaminfo, time = endtime}}
... ... @@ -118,8 +119,8 @@ Role.schema = {
118 119 bonusStar = {"table", {}}, -- 奖励关卡 通关星星 {[id] = 1} 三个二进制位 表示三个星 从低到高 (1 << 0) (1 << 1) (1 << 2) 满星 (1 << 3) - 1
119 120  
120 121 --引导相关
121   - newerGuide = {"string","1=1"}, -- 新手引导 master=slave
122   - funcGuide = {"string",""}, -- 功能引导 0=0跳过次数(999永久跳过) 1=1功能1触发情况
  122 + newerGuide = {"string","1=1", "", 10}, -- 新手引导 master=slave
  123 + funcGuide = {"string","", "blob"}, -- 功能引导 0=0跳过次数(999永久跳过) 1=1功能1触发情况
123 124  
124 125 pvpTC = {"table", {}}, -- pvp 编队普通
125 126 pvpTSC = {"table", {}}, -- pvp 他人可读的队伍信息
... ...
src/services/dbseed.lua
... ... @@ -8,6 +8,7 @@ require &quot;skynet.manager&quot;
8 8 skynet = require "skynet"
9 9  
10 10 redisproxy = require("shared.redisproxy")
  11 +mysqlproxy = require "shared.mysqlproxy"
11 12  
12 13 SendPacket = function ( ... ) end
13 14  
... ... @@ -26,22 +27,103 @@ local function initRedisDb( ... )
26 27 end
27 28 end
28 29  
  30 +-- 初始化服务器数据库以及服务器信息表
  31 +local function initServerDatabase()
  32 + local servId = skynet.getenv("servId")
  33 + mysqlproxy:query(string.format("CREATE DATABASE IF NOT EXISTS server_%s DEFAULT CHARSET = utf8mb4 COLLATE utf8mb4_general_ci;", servId))
  34 + mysqlproxy:query(string.format("use server_%s", servId))
  35 +
  36 + -- 服务器信息表 开服时间
  37 + mysqlproxy:query [[
  38 + CREATE TABLE IF NOT EXISTS `server_info` (
  39 + `key` varchar(45) NOT NULL,
  40 + `int_value` int(11) DEFAULT NULL,
  41 + `str_value` varchar(128) DEFAULT NULL,
  42 + PRIMARY KEY (`key`)
  43 + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
  44 + ]]
  45 +
  46 + local res = mysqlproxy:query("SELECT * FROM `server_info` where `key` = 'server_start';")
  47 + if not next(res) then
  48 + mysqlproxy:query(string.format("INSERT INTO `server_info`(`key`, `str_value`) VALUES('server_start', '%s');",
  49 + os.date("%Y%m%d", skynet.timex())))
  50 + end
  51 +end
  52 +
  53 +local function initAutoIncreUidTable()
  54 + mysqlproxy:query [[
  55 + CREATE TABLE IF NOT EXISTS `auto_increment_uid` (
  56 + `key` varchar(45) NOT NULL,
  57 + `value` int(11) DEFAULT NULL,
  58 + PRIMARY KEY (`key`)
  59 + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
  60 + ]]
  61 + local servId = tonumber(skynet.getenv("servId"))
  62 + if servId then
  63 + local tpl = "INSERT INTO `auto_increment_uid`(`key`, `value`) values('%s', %d)"
  64 + mysqlproxy:query(string.format(tpl, "role", servId * MAX_ROLE_NUM))
  65 + mysqlproxy:query(string.format(tpl, "union", servId * MAX_ROLE_NUM))
  66 + mysqlproxy:query(string.format(tpl, "trade", servId * MAX_ROLE_NUM * 100))
  67 + mysqlproxy:query(string.format(tpl, "email", 0))
  68 + mysqlproxy:query(string.format(tpl, "emailTimestamp", 0))
  69 + mysqlproxy:query(string.format(tpl, "delay_email", 0))
  70 + end
  71 +end
  72 +
  73 +local function initAdvSeasonTable()
  74 + mysqlproxy:query [[
  75 + CREATE TABLE IF NOT EXISTS `adv_season` (
  76 + `key` varchar(45) NOT NULL,
  77 + `value` int(11) DEFAULT NULL,
  78 + PRIMARY KEY (`key`)
  79 + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
  80 + ]]
  81 + local servId = tonumber(skynet.getenv("servId"))
  82 + if servId then
  83 + local tpl = "INSERT INTO `adv_season`(`key`, `value`) values('%s', %d)"
  84 +
  85 + mysqlproxy:query(string.format(tpl, "idx", 0))
  86 + mysqlproxy:query(string.format(tpl, "chapter", globalCsv.adv_endless_default_chapter))
  87 + mysqlproxy:query(string.format(tpl, "overTime", 0))
  88 + end
  89 +end
  90 +
  91 +local function checkRoleTables()
  92 + local role = require("models.Role").new({key = "key"})
  93 + role:checkTableSchema()
  94 +end
  95 +
29 96 local steps = {
30 97 [1] = {
31 98 handler = initRedisDb,
32 99 desc = "initialize redis database "
  100 + },
  101 + [2] = {
  102 + handler = initServerDatabase,
  103 + desc = "initialize server database "
  104 + },
  105 + [3] = {
  106 + handler = initAutoIncreUidTable,
  107 + desc = "initialize auto_increment_uid table "
  108 + },
  109 + [4] = {
  110 + handler = initAdvSeasonTable,
  111 + desc = "initialize adv_season table "
  112 + },
  113 + [5] = {
  114 + handler = checkRoleTables,
  115 + desc = "check role tables "
33 116 }
34 117 }
35 118  
36 119 skynet.start(function ()
37 120 redisproxy = require("shared.redisproxy")
38   -
39   - local new = redisproxy:hsetnx("autoincrement_set", "server_start", os.date("%Y%m%d", skynet.timex())) == 1
40   - if not new then
41   - print("server has been initialized...")
42   - skynet.exit()
43   - return
44   - end
  121 + --local new = redisproxy:hsetnx("autoincrement_set", "server_start", os.date("%Y%m%d", skynet.timex())) == 1
  122 + --if not new then
  123 + -- print("server has been initialized...")
  124 + -- skynet.exit()
  125 + -- return
  126 + --end
45 127 csvdb = require "shared.csvdata"
46 128 globalCsv = csvdb["GlobalDefineCsv"]
47 129  
... ...
src/services/mysqld.lua 0 → 100644
... ... @@ -0,0 +1,42 @@
  1 +local skynet = require "skynet"
  2 +require "skynet.manager"
  3 +local mysql = require "skynet.db.mysql"
  4 +
  5 +local db
  6 +local idx = ...
  7 +local command = {}
  8 +
  9 +function command.open(conf)
  10 + local function on_connect(db)
  11 + db:query("set charset utf8mb4");
  12 + end
  13 + db=mysql.connect({
  14 + host="127.0.0.1",
  15 + port=3306,
  16 + database="mysql",
  17 + user="root",
  18 + password="123456",
  19 + max_packet_size = 1024 * 1024,
  20 + on_connect = on_connect
  21 + })
  22 + if not db then
  23 + print("failed to connect")
  24 + end
  25 + local servId = skynet.getenv("servId")
  26 + db:query(string.format("use server_%s", servId))
  27 +end
  28 +
  29 +skynet.start(function()
  30 + skynet.dispatch("lua", function(session, address, cmd, ...)
  31 + if cmd == "open" then
  32 + local f = command[string.lower(cmd)]
  33 + skynet.ret(skynet.pack(f(...)))
  34 + else
  35 + skynet.ret(skynet.pack(db[string.lower(cmd)](db, ...)))
  36 + end
  37 + end)
  38 + skynet.info_func(function()
  39 + return skynet.stat("mqlen")
  40 + end)
  41 + skynet.register(".mysql" .. idx)
  42 +end)
0 43 \ No newline at end of file
... ...
src/shared/ModelBaseMysql.lua 0 → 100644
... ... @@ -0,0 +1,466 @@
  1 +local ModelBaseMysql = class("ModelBaseMysql")
  2 +ModelBaseMysql.key = "key"
  3 +ModelBaseMysql.schema = {}
  4 +
  5 +local string_format = string.format
  6 +local table_insert = table.insert
  7 +local table_unpack = table.unpack
  8 +local assert = assert
  9 +local next = next
  10 +local ipairs = ipairs
  11 +local pairs = pairs
  12 +local tostring = tostring
  13 +local tonumber = tonumber
  14 +local mysqlproxy = mysqlproxy
  15 +
  16 +local function filterProperties(properties, filter)
  17 + for i, field in ipairs(filter) do
  18 + properties[field] = nil
  19 + end
  20 +end
  21 +
  22 +function ModelBaseMysql:ctor(properties)
  23 + self.cacheFields = {} --缓存字段 不更新数据库的字段
  24 +
  25 + self[self.class.key .. "_"] = properties[self.class.key] --数据库key
  26 + properties[self.class.key] = nil
  27 +
  28 + if not self:isValidKey() then
  29 + print(string_format("%s [%s:key] should be give in new(ctor)", tostring(self), self.class.__cname))
  30 + return
  31 + end
  32 +
  33 + if type(properties) ~= "table" then properties = {} end
  34 + self:loadProperties(properties) --缺少的域将设置默认值
  35 + self:getPriKey()
  36 +end
  37 +
  38 +-- startCache 和 endCache 在恰当的时候*配对使用* 嵌套使用多次增加引用计数 直到引用计数为0 写入
  39 +function ModelBaseMysql:startCache( ... )
  40 + for _, field in ipairs({ ... }) do
  41 + if self.class.schema[field] then
  42 + self.cacheFields[field] = (self.cacheFields[field] or 0) + 1
  43 + end
  44 + end
  45 +end
  46 +
  47 +--减少缓存引用计数 为时写入, 无参数 强制刷新所有缓存
  48 +function ModelBaseMysql:endCache( ... )
  49 + local args = { ... }
  50 + local params = {}
  51 +
  52 + local function doOneCache(field)
  53 + local propname = field .. "_"
  54 + table_insert(params, field)
  55 + if self.class.schema[field][1] == "table" then
  56 + table_insert(params, MsgPack.pack(self[propname]))
  57 + else
  58 + table_insert(params, self[propname])
  59 + end
  60 + end
  61 +
  62 + if not next(args) then
  63 + for field, _ in pairs(self.cacheFields) do
  64 + doOneCache(field)
  65 + end
  66 + self.cacheFields = {}
  67 + else
  68 + for _, field in ipairs(args) do
  69 + if self.cacheFields[field] then
  70 + self.cacheFields[field] = self.cacheFields[field] - 1
  71 + if self.cacheFields[field] <= 0 then
  72 + self.cacheFields[field] = nil
  73 + doOneCache(field)
  74 + end
  75 + end
  76 + end
  77 + end
  78 +
  79 + if next(params) then
  80 + redisproxy:hmset(self:getKey(), table_unpack(params))
  81 + end
  82 +end
  83 +--[[--
  84 +
  85 +返回对象的 ID 值。
  86 +
  87 +**Returns:**
  88 +
  89 +- ID 值
  90 +
  91 +]]
  92 +function ModelBaseMysql:getKey()
  93 + local id = self[self.class.key .. "_"]
  94 + assert(id ~= nil, string_format("%s [%s:getKey()] Invalid key", tostring(self), self.class.__cname))
  95 + return id
  96 +end
  97 +
  98 +function ModelBaseMysql:getPriKey()
  99 + for k, v in pairs(self.class.schema) do
  100 + local objType, def, keyType, length = table_unpack(v)
  101 + if keyType == "pri" then
  102 + self.pri_key = k
  103 + end
  104 + end
  105 +end
  106 +
  107 +function ModelBaseMysql:load(properties)
  108 + if not self:isValidKey() then
  109 + print(string_format("%s [%s:id] should be set before load", tostring(self), self.class.__cname))
  110 + return false
  111 + end
  112 +
  113 + if not properties then
  114 + print(string_format("SELECT * from %s where `%s` = %s;", self.class.__cname, self.pri_key, self:getKey()))
  115 + properties = mysqlproxy:query(string_format("SELECT * from %s where `%s` = %s;", self.class.__cname, self.pri_key, self:getKey()))
  116 + end
  117 + if not next(properties) then return false end
  118 +
  119 + self:loadProperties(properties[1])
  120 +
  121 + self:onLoad()
  122 +
  123 + return true
  124 +end
  125 +
  126 +--创建model对应的redis数据, 必须已经设置了ID
  127 +function ModelBaseMysql:create()
  128 + if not self:isValidKey() then
  129 + print(string_format("%s [%s:key] should be set before create", tostring(self), self.class.__cname))
  130 + return nil
  131 + end
  132 +
  133 + self:save()
  134 + self:onCreate()
  135 +
  136 + return self
  137 +end
  138 +
  139 +-- save 忽略 缓存配置
  140 +function ModelBaseMysql:save()
  141 + local redisProperties = self:getProperties()
  142 +
  143 + local params = {}
  144 + for fieldName, value in pairs(redisProperties) do
  145 + local propname = fieldName .. "_"
  146 + if self.class.schema[fieldName][1] == "table" then
  147 + if not next(self[propname]) then
  148 + params[fieldName] = "NULL"
  149 + else
  150 + params[fieldName] = "'" .. MsgPack.pack(self[propname]) .. "'"
  151 + end
  152 + elseif self.class.schema[fieldName][1] == "string" then
  153 + params[fieldName] = "'" .. self[propname] .. "'"
  154 + else
  155 + params[fieldName] = self[propname]
  156 + end
  157 + end
  158 + if next(params) then
  159 + -- insert update
  160 + local sql = "INSERT INTO %s (%s) VALUES (%s) ON DUPLICATE KEY UPDATE %s;"
  161 + local tbName = self.class.__cname
  162 + local key_list = ""
  163 + local value_list = ""
  164 + local update_list = ""
  165 + for k, v in pairs(params) do
  166 + if key_list ~= "" then
  167 + key_list = key_list .. ","
  168 + end
  169 + if value_list ~= "" then
  170 + value_list = value_list .. ","
  171 + end
  172 + if update_list ~= "" then
  173 + update_list = update_list .. ","
  174 + end
  175 +
  176 + key_list = key_list .. k
  177 + value_list = value_list .. v
  178 + update_list = update_list .. k .. "=" .. v
  179 + end
  180 + sql = string_format(sql, tbName, key_list, value_list, update_list)
  181 + mysqlproxy:query(sql)
  182 + end
  183 +end
  184 +
  185 +--[[--
  186 +
  187 +确定对象是否设置了有效的 key。
  188 +
  189 +]]
  190 +function ModelBaseMysql:isValidKey()
  191 + local propname = self.class.key .. "_"
  192 + local key = self[propname]
  193 + return type(key) == "string" and key ~= ""
  194 +end
  195 +
  196 +--[[--
  197 +
  198 +加载对象的属性进内存。
  199 +NOTE: 如果properties缺少schema中的域, 将用默认值来填充
  200 +
  201 +**Parameters:**
  202 +
  203 +- properties: 包含属性值的数组
  204 +
  205 +]]
  206 +function ModelBaseMysql:loadProperties(properties)
  207 + assert(type(properties) == "table", "Invalid properties")
  208 + for field, schema in pairs(self.class.schema) do
  209 + local typ, def = table_unpack(schema)
  210 + local propname = field .. "_"
  211 +
  212 + if typ == "table" and type(properties[field]) == "string" then
  213 + properties[field] = MsgPack.unpack(properties[field])
  214 + end
  215 +
  216 + local val = properties[field] or def
  217 + if val ~= nil then
  218 + if typ == "number" then val = tonumber(val) end
  219 + assert(type(val) == typ,
  220 + string_format("%s [%s:loadProperties()] Type mismatch, %s expected %s, actual is %s",
  221 + tostring(self), self.class.__cname, field, typ, type(val)))
  222 + self[propname] = val
  223 + end
  224 + end
  225 +end
  226 +
  227 +--[[--
  228 +
  229 +取得对象的属性值。
  230 +
  231 +**Parameters:**
  232 +
  233 +- fields: 要取得哪些属性的值,如果未指定该参数,则返回 fields 中设定的属性
  234 +- filter: 要从结果中过滤掉哪些属性,如果未指定则不过滤
  235 +
  236 +**Returns:**
  237 +
  238 +- 包含属性值的数组
  239 +
  240 +]]
  241 +function ModelBaseMysql:getProperties(fields, filter)
  242 + local schema = self.class.schema
  243 + if type(fields) ~= "table" then fields = table.keys(self.class.schema) end
  244 +
  245 + local properties = {}
  246 + for i, field in ipairs(fields) do
  247 + local propname = field .. "_"
  248 + local typ = schema[field][1]
  249 + local val = self[propname]
  250 + assert(type(val) == typ,
  251 + string_format("%s [%s:getProperties()] Type mismatch, %s expected %s, actual is %s",
  252 + tostring(self), self.class.__cname, field, typ, type(val)))
  253 + properties[field] = val
  254 + end
  255 +
  256 + if type(filter) == "table" then
  257 + filterProperties(properties, filter)
  258 + end
  259 +
  260 + return properties
  261 +end
  262 +
  263 +function ModelBaseMysql:getProperty(property)
  264 + if type(property) ~= "string" then return nil end
  265 + if not self.class.schema[property] then return nil end
  266 + return self:getProperties({property})[property]
  267 +end
  268 +
  269 +function ModelBaseMysql:setProperty(property, value)
  270 + if not self.class.schema[property] then
  271 + print(string_format("%s [%s:setProperty()] Invalid property : %s",
  272 + tostring(self), self.class.__cname, property))
  273 + return
  274 + end
  275 +
  276 + local typ, def = table_unpack(self.class.schema[property])
  277 + local propname = property .. "_"
  278 +
  279 + if typ == "number" then value = tonumber(value) end
  280 + if typ == "table" and not value then
  281 + value = self[propname] -- table 可以用自己的缓冲
  282 + end
  283 + assert(type(value) == typ,
  284 + string_format("%s [%s:setProperties()] Type mismatch, %s expected %s, actual is %s",
  285 + tostring(self), self.class.__cname, property, typ, type(value)))
  286 + self[propname] = value
  287 +
  288 + if not self.cacheFields[property] then
  289 + -- table 使用msgpack
  290 + if typ == "table" then
  291 + value = MsgPack.pack(value)
  292 + end
  293 + --redisproxy:hset(self:getKey(), property, value)
  294 + self:save()
  295 + end
  296 +end
  297 +
  298 +function ModelBaseMysql:setProperties(fields)
  299 + local result = {}
  300 + for property, value in pairs(fields) do
  301 + if not self.class.schema[property] then
  302 + print(string_format("%s [%s:setProperty()] Invalid property : %s",
  303 + tostring(self), self.class.__cname, property))
  304 + else
  305 + local typ, def = table_unpack(self.class.schema[property])
  306 + local propname = property .. "_"
  307 + if typ == "number" then value = tonumber(value) end
  308 + if typ == "table" and not value then
  309 + value = self[propname] -- table 可以用自己的缓冲
  310 + end
  311 + assert(type(value) == typ,
  312 + string_format("%s [%s:setProperties()] Type mismatch, %s expected %s, actual is %s",
  313 + tostring(self), self.class.__cname, property, typ, type(value)))
  314 + self[propname] = value
  315 +
  316 + if not self.cacheFields[property] then
  317 + table_insert(result, property)
  318 + if typ == "table" then
  319 + table_insert(result, MsgPack.pack(self[propname]))
  320 + else
  321 + table_insert(result, self[propname])
  322 + end
  323 + end
  324 + end
  325 + end
  326 + if next(result) then
  327 + --redisproxy:hmset(self:getKey(), table_unpack(result))
  328 + self:save()
  329 + end
  330 +end
  331 +
  332 +function ModelBaseMysql:incrProperty(property, value)
  333 + if not self.class.schema[property] then
  334 + print(string_format("%s [%s:setProperty()] Invalid property : %s",
  335 + tostring(self), self.class.__cname, property))
  336 + return
  337 + end
  338 +
  339 + local typ, def = table_unpack(self.class.schema[property])
  340 + local propname = property .. "_"
  341 +
  342 + if typ == "table" then return end
  343 + if typ == "number" then value = tonumber(value) end
  344 +
  345 + assert(type(value) == typ,
  346 + string_format("%s [%s:setProperties()] Type mismatch, %s expected %s, actual is %s",
  347 + tostring(self), self.class.__cname, property, typ, type(value)))
  348 + self[propname] = self[propname] + value
  349 +
  350 + if not self.cacheFields[property] then
  351 + --return redisproxy:hincrby(self:getKey(), property, value)
  352 + self:save()
  353 + end
  354 +end
  355 +
  356 +function ModelBaseMysql:onLoad()
  357 +end
  358 +
  359 +function ModelBaseMysql:onCreate()
  360 +end
  361 +
  362 +function ModelBaseMysql:checkTableSchema()
  363 + -- 1.检测是否表存在
  364 + local typeMap = {
  365 + number = {"int", 0, 10},
  366 + string = {"varchar", "", 128},
  367 + table = {"blob", "NULL"}
  368 + }
  369 + local tbName = self.class.__cname
  370 + local create_sql = [[
  371 + CREATE TABLE IF NOT EXISTS `%s` (
  372 + %s
  373 + PRIMARY KEY (`%s`)
  374 + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
  375 + ]]
  376 + local alter_sql = [[
  377 + ALTER TABLE `%s` ADD COLUMN (
  378 + %s
  379 + ) ;
  380 + ]]
  381 + local field_tpl_str = "`%s` %s%s DEFAULT %s"
  382 + local field_str = ""
  383 +
  384 + local res = mysqlproxy:query("desc ".. tbName .. ";")
  385 + if res["err"] then -- 表不存在
  386 + local schema = {}
  387 + for k, v in pairs(self.class.schema) do
  388 + local keyType = v[3]
  389 + if keyType == "pri" then
  390 + self.pri_key = k
  391 + table_insert(schema, 1, {k, v})
  392 + else
  393 + table_insert(schema, {k, v})
  394 + end
  395 + end
  396 + for _, tbl in ipairs(schema) do
  397 + local k, v = tbl[1], tbl[2]
  398 + local objType, def, keyType, length = table_unpack(v)
  399 + assert(typeMap[objType], string_format("schema invalid type, %s, %s", tbName, k))
  400 +
  401 + local info = typeMap[objType]
  402 + local suffix = ""
  403 + local fieldType = info[1]
  404 + if objType == "table" or not def or def == "" then def = info[2] end
  405 + if type(def) == "string" and def ~= "NULL" then def = "'" .. def .. "'" end
  406 + if info[3] and not length then length = info[3] end
  407 + -- 设置字段长度
  408 + if info[3] then suffix = string.format("(%d)", length) end
  409 + -- 很长的string使用blob
  410 + if keyType == "blob" then
  411 + fieldType = keyType
  412 + suffix = ""
  413 + def = "NULL"
  414 + end
  415 +
  416 + field_str = field_str .. string.format(field_tpl_str..",", k, fieldType, suffix, def)
  417 + end
  418 +
  419 + assert(self.pri_key, string_format("table not include primary key, [%s]", tbName))
  420 + -- 创建表格
  421 + mysqlproxy:query(string_format(create_sql, tbName, field_str, self.pri_key))
  422 + else -- 检测是否有添加新字段
  423 + local addCol = {}
  424 + local curCols = {}
  425 + for _, col in ipairs(res) do
  426 + curCols[col["Field"]] = 1
  427 + end
  428 + for k, v in pairs(self.class.schema) do
  429 + local objType, def, keyType, length = table_unpack(v)
  430 + if keyType == "pri" then
  431 + self.pri_key = k
  432 + end
  433 + if not curCols[k] then
  434 + print(string_format("table [%s] add new column [%s]", tbName, k))
  435 + assert(typeMap[objType], string_format("schema invalid type, [%s], [%s]", tbName, k))
  436 +
  437 + local info = typeMap[objType]
  438 + local suffix = ""
  439 + local fieldType = info[1]
  440 + if objType == "table" or not def or def == "" then def = info[2] end
  441 + if type(def) == "string" and def ~= "NULL" then def = "'" .. def .. "'" end
  442 + if info[3] and not length then length = info[3] end
  443 + -- 设置字段长度
  444 + if info[3] then suffix = string.format("(%d)", length) end
  445 + -- 很长的string使用blob
  446 + if keyType == "blob" then
  447 + fieldType = keyType
  448 + suffix = ""
  449 + def = "NULL"
  450 + end
  451 + local sep = ","
  452 + if field_str == "" then
  453 + sep = ""
  454 + end
  455 + field_str = field_str .. string.format(sep..field_tpl_str, k, fieldType, suffix, def)
  456 + end
  457 + end
  458 + -- 添加新列
  459 + if field_str ~= "" then
  460 + mysqlproxy:query(string_format(alter_sql, tbName, field_str))
  461 + end
  462 + end
  463 +
  464 +end
  465 +
  466 +return ModelBaseMysql
0 467 \ No newline at end of file
... ...
src/shared/mysqlproxy.lua 0 → 100644
... ... @@ -0,0 +1,34 @@
  1 +local skynet = require "skynet"
  2 +require "utils.init"
  3 +
  4 +local mysqld_count = tonumber(skynet.getenv("thread"))
  5 +local mysqld
  6 +skynet.init(function()
  7 + local idx = math.randomInt(1, mysqld_count)
  8 + mysqld = skynet.localname(".mysql" .. idx)
  9 +
  10 +end)
  11 +
  12 +local table_insert = table.insert
  13 +
  14 +local mysqlproxy = {}
  15 +
  16 +
  17 +setmetatable(mysqlproxy, { __index = function(t, k)
  18 + local cmd = string.upper(k)
  19 + local f = function (self, ...)
  20 + if k == "query" then
  21 + --print(...)
  22 + end
  23 + local ok, result = pcall(skynet.call, mysqld, "lua", cmd, ...)
  24 + if not ok then
  25 + skynet.error(cmd, ..., "\n", debug.traceback(coroutine.running(), nil))
  26 + return
  27 + end
  28 + return result
  29 + end
  30 + t[k] = f
  31 + return f
  32 +end})
  33 +
  34 +return mysqlproxy
0 35 \ No newline at end of file
... ...