2021SC@SDUSC
此次继续分析文件src/controller/admin/model.js
该文件中的方法为模型操作,在整个项目中有着举足轻重的地位。
一、extAction()
我们使用一个map对象设置数据库查询时的条件: status 为 [’>’, -1] , ismod 为 1 。 countSelect 为分页查询,一般结合page方法一起使用。 data 是 map 限定条件下在数据库中的分页查询结果。data 的数据结构包括:pageSize(每页显示的条数),currentPag(当前页),count(总条数),totalPages(总页数),data(当前页下的数据列表)。 pagination 方法对 data 进行分页展示,存储在 html 中。 assign 方法用于将所有可枚举属性的值从一个或多个源对象分配到目标对象。第一次 assign 方法将 html 分配给 pagerData ,第二次 assign 方法将 data.data(当前页下的数据列表)分配给 list。 这个方法和上一篇里的 indexAction 几乎完全一致。
async extAction() {
const map = {'status': ['>', -1], 'ismod': 1};
const data = await this.db.where(map).page(this.get(`page`)).countSelect();
const html = this.pagination(data);
this.assign('pagerData', html);
this.assign('list', data.data);
this.meta_title = '独立模型管理';
this.tactive = 'ext';
return this.display();
}
二、extunAction()
think.getdirFiles 方法用于获取文件夹下的所有文件,此处将文件夹 ${think.APP_PATH}/controller/mod 里的所有文件放入数组中并赋值给dir。 对dir中的每一个元素进行处理并push到数组 modarr 的末尾。 借助 modarr ,我们可以找出未安装的插件和未安装插件的配置。 由此我们得到一个未安装的独立模型。
async extunAction() {
const dir = think.getdirFiles(`${think.APP_PATH}/controller/mod`);
console.log(dir);
const modarr = [];
for (const v of dir) {
modarr.push(think._.head(think._.split(v, path.sep, 1)));
}
console.log(modarr);
const uniarr = [];
for (const d of think._.uniq(modarr)) {
const map = {'status': ['>', -1], 'ismod': 1};
map.name = d;
const ism = await this.db.where(map).find();
if (think.isEmpty(ism)) {
uniarr.push(d);
}
}
const unilist = [];
for (const modName of uniarr) {
unilist.push(think.app.controllers[`mod/${modName}/config`]);
}
console.log(unilist);
this.assign('list', unilist);
this.meta_title = '未安装的模型';
this.tactive = 'ext';
this.active = 'admin/model/ext';
return this.display();
}
三、installextAction()
此方法用于安装插件。 首先导入数据库文件,将数据库文件存放于变量 sqlpath 中。sqlpath 即数据库文件的路径,通过 think.isFile() 方法判断,若该路径下确实为文件,那么继续导入数据库。 后面大块的代码只有一个目的:导入数据库,包括数据库的各个表。 代码后面进行了异常处理,但是并没有进行具体的异常分析,而是直接笼统地 catch 了所有 exception ,并告诉系统:数据表导入失败,请在控制台下查看具体的错误信息,并在 GitHub 上发 issue。 若数据库导入成功,那么就可以添加数据模型。注意要添加模型添加的时间。 添加成功后,更新缓存,并给主进程发送重启的指令,方法结束,返回 success。
async installextAction() {
const mod = this.get('mod');
const modconfig = think.app.controllers[`mod/${mod}/config`];
const sqlpath = path.join(`${think.APP_PATH}/controller/mod/${mod}`, `${mod}.sql`);
if (think.isFile(sqlpath)) {
const sqlfile = fs.readFileSync(sqlpath, 'utf8');
let content = sqlfile.split(/(?:\r\n|\r|\n)/g).filter(item => {
item = item.trim();
const ignoreList = ['--', 'SET', '#', 'LOCK', 'UNLOCK', 'INSERT'];
for (const it of ignoreList) {
if (item.indexOf(it) === 0) {
return false;
}
}
return true;
}).join('');
content = content.replace(/\/\*.*?\*\//g, '');
content = content.substring(0, content.length - 1);
const arr = content.split(';');
const insert = sqlfile.split(/(?:\r\n|\r|\n)/g).filter(item => {
item = item.trim();
if (item.indexOf('INSERT') === 0) {
return true;
}
return false;
});
const sqlarr = arr.concat(insert);
try {
for (let item of sqlarr) {
item = item.trim();
if (item) {
item = item.replace(/cmswing_/g, this.config('model.mysql.prefix') || '');
think.logger.info(item);
await this.model('mysql').execute(item);
}
}
} catch (e) {
think.logger.error(e);
return this.fail('数据表导入失败,请在控制台下查看具体的错误信息,并在 GitHub 上发 issue。');
}
}
modconfig.create_time = new Date().getTime();
await this.model('model').add(modconfig);
await update_cache('model');
process.send('think-cluster-reload-workers');
return this.success({name: '安装成功!'});
}
四、addextAction()
此方法用于新建独立模型。 如果请求加载了数据,那么首先生成插件目录。需要注意的是,此插件目录需要生成,若该目录已存在,说明模型已经被建立(有可能是同名或者项目逻辑出现问题),此方法无法执行,返回错误信息。 获取post的数据,并设置数据的创建时间和更新时间。如果获取的数据 post.attribute_list 为数组,那么将它作为字符串返回,数组中的元素由分隔符“,”分隔。 变量res用于确定数据是否成功更新到数据库中。若更新成功,那么初始化表结构,并将模型添加到钩子;之后创建插件目录和钩子控制器(此处有一些尚未被完成的方法,主要是用钩子进行一些业务处理);最后,创建插件view目录、model目录、service目录、后台管理控制器、前台访问控制器,并更新缓存,告知系统更新模型成功。若更新失败,则告知系统更新模型失败 获取请求的信息,赋值给 data 。 如果没有请求加载数据,此方法也就没有意义了。
async addextAction() {
if (this.isPost) {
const data = this.post();
const moddir = `${think.APP_PATH}/controller/mod`;
const modpath = `${moddir}/${data.name}`;
if (think.isDirectory(modpath)) {
return this.fail(`${data.name} 模型目录已经存在`);
};
data.create_time = new Date().valueOf();
data.update_time = new Date().valueOf();
data.status = 1;
data.ismod = 1;
if (think.isArray(data.attribute_list)) {
data.attribute_list = data.attribute_list.join(',');
}
const res = await this.db.add(data);
if (res) {
const addtable = await this.model('cmswing/attribute').addtable(res);
if (!think.isEmpty(data.hooks)) {
for (const h of data.hooks) {
const hooks = await this.model('hooks').where({name: h}).find();
let extarr = hooks.ext ? hooks.ext.split(',') : [];
extarr.push(data.name);
extarr = think._.uniq(extarr);
await this.model('hooks').where({name: h}).update({ext: extarr.join(',')});
}
}
think.mkdir(modpath);
fs.writeFileSync(`${modpath}/config.js`, `module.exports = ${JSON.stringify(data)}`);
const hookaction = [];
if (!think.isArray(data.hooks)) {
data.hooks = data.hooks.split(',');
}
for (const v of data.hooks) {
const type = await this.model('hooks').where({name: v}).getField('type', true);
console.log(type);
if (Number(type) === 1) {
hookaction.push(`/**
* 实现的AdminIndex钩子方法
* 【视图】
* @param ...val
*/
async ${v}(...val) {
// 钩子业务处理
const html = await this.hookRender('${v}', '${data.name}');
return html;
}`);
const mvp = `${modpath}/view`;
think.mkdir(`${mvp}/pc`);
fs.writeFileSync(`${mvp}/pc/hooks_${v}.html`, `${data.name}`);
think.mkdir(`${mvp}/mobile`);
fs.writeFileSync(`${mvp}/mobile/hooks_${v}.html`, `${data.name}`);
} else {
hookaction.push(` // 实现的${v}钩子方法
${v}() {
// 钩子业务处理
}`);
}
}
const hookhtml = hookaction.join(';\n ');
const hooksstr = `// hooks
module.exports = class extends think.cmswing.modIndex {
${hookhtml}
}`;
fs.writeFileSync(`${modpath}/hooks.js`, hooksstr);
this.copy(`${moddir}/demo/view`, `${modpath}/view`, this.copy);
this.copy(`${moddir}/demo/model`, `${modpath}/model`, this.copy);
this.copy(`${moddir}/demo/service`, `${modpath}/service`, this.copy);
this.copy(`${moddir}/demo/logic`, `${modpath}/logic`, this.copy);
this.copy(`${moddir}/demo/admin.js`, `${modpath}/admin.js`);
this.copy(`${moddir}/demo/index.js`, `${modpath}/index.js`);
await update_cache('model');
return this.success({name: '添加成功', url: '/admin/model/ext'});
} else {
return this.fail('添加失败!');
}
} else {
const hooks = await this.model('hooks').select();
this.assign('hooks', hooks);
this.tactive = 'ext';
this.active = 'admin/model/ext';
this.meta_title = '添加独立模型';
return this.display();
}
}
|