兼容二进制打包

This commit is contained in:
COOL 2025-01-15 20:55:44 +08:00
parent fcef095b2a
commit 3392f0a307
26 changed files with 771 additions and 80 deletions

View File

@ -49,7 +49,7 @@
},
"scripts": {
"start": "NODE_ENV=production node ./bootstrap.js",
"dev": "rimraf src/index.ts && cool check entity && cross-env NODE_ENV=local mwtsc --cleanOutDir --watch --run @midwayjs/mock/app.js",
"dev": "rimraf src/index.ts && cool check entity --clear && cross-env NODE_ENV=local mwtsc --cleanOutDir --watch --run @midwayjs/mock/app.js",
"test": "cross-env NODE_ENV=unittest jest",
"cov": "jest --coverage",
"lint": "mwts check",

View File

@ -1,25 +1,2 @@
// 自动生成的文件,请勿手动修改
import * as entity0 from './modules/plugin/entity/info';
import * as entity1 from './modules/base/entity/sys/user_role';
import * as entity2 from './modules/base/entity/sys/user';
import * as entity3 from './modules/base/entity/sys/role_menu';
import * as entity4 from './modules/base/entity/sys/role_department';
import * as entity5 from './modules/base/entity/sys/role';
import * as entity6 from './modules/base/entity/sys/param';
import * as entity7 from './modules/base/entity/sys/menu';
import * as entity8 from './modules/base/entity/sys/log';
import * as entity9 from './modules/base/entity/sys/department';
import * as entity10 from './modules/base/entity/sys/conf';
export const entities = [
...Object.values(entity0),
...Object.values(entity1),
...Object.values(entity2),
...Object.values(entity3),
...Object.values(entity4),
...Object.values(entity5),
...Object.values(entity6),
...Object.values(entity7),
...Object.values(entity8),
...Object.values(entity9),
...Object.values(entity10),
];
export const entities = [];

View File

@ -1,55 +0,0 @@
/** This file generated by @midwayjs/bundle-helper */
export { MainConfiguration as Configuration } from './configuration';
export * from './comm/utils';
export * from './config/config.default';
export * from './config/config.local';
export * from './modules/plugin/entity/info';
export * from './modules/base/entity/sys/user_role';
export * from './modules/base/entity/sys/user';
export * from './modules/base/entity/sys/role_menu';
export * from './modules/base/entity/sys/role_department';
export * from './modules/base/entity/sys/role';
export * from './modules/base/entity/sys/param';
export * from './modules/base/entity/sys/menu';
export * from './modules/base/entity/sys/log';
export * from './modules/base/entity/sys/department';
export * from './modules/base/entity/sys/conf';
export * from './entities';
export * from './config/config.prod';
export * from './interface';
export * from './modules/base/service/sys/conf';
export * from './modules/base/service/sys/log';
export * from './modules/base/middleware/log';
export * from './modules/base/middleware/authority';
export * from './modules/base/config';
export * from './modules/plugin/interface';
export * from './modules/plugin/service/center';
export * from './modules/plugin/event/init';
export * from './modules/plugin/service/types';
export * from './modules/plugin/service/info';
export * from './modules/base/dto/login';
export * from './modules/base/service/sys/data';
export * from './modules/base/service/sys/menu';
export * from './modules/base/service/sys/department';
export * from './modules/base/service/sys/perms';
export * from './modules/base/service/sys/role';
export * from './modules/base/service/sys/login';
export * from './modules/base/service/sys/user';
export * from './modules/base/controller/admin/comm';
export * from './modules/base/service/sys/param';
export * from './modules/base/controller/admin/open';
export * from './modules/base/controller/admin/sys/department';
export * from './modules/base/controller/admin/sys/log';
export * from './modules/base/controller/admin/sys/menu';
export * from './modules/base/controller/admin/sys/param';
export * from './modules/base/controller/admin/sys/role';
export * from './modules/base/controller/admin/sys/user';
export * from './modules/base/controller/app/comm';
export * from './modules/base/event/menu';
export * from './modules/base/job/log';
export * from './modules/plugin/config';
export * from './modules/plugin/controller/admin/info';
export * from './modules/plugin/event/app';
export * from './modules/plugin/hooks/base';
export * from './modules/plugin/hooks/upload/interface';
export * from './modules/plugin/hooks/upload/index';

View File

@ -0,0 +1,19 @@
import { ModuleConfig } from '@cool-midway/core';
/**
*
*/
export default () => {
return {
// 模块名称
name: '字典管理',
// 模块描述
description: '数据字典等',
// 中间件,只对本模块有效
middlewares: [],
// 中间件,全局有效
globalMiddlewares: [],
// 模块加载顺序默认为0值越大越优先加载
order: 0,
} as ModuleConfig;
};

View File

@ -0,0 +1,30 @@
import { DictInfoEntity } from './../../entity/info';
import { Body, Inject, Post, Provide } from '@midwayjs/core';
import { CoolController, BaseController } from '@cool-midway/core';
import { DictInfoService } from '../../service/info';
/**
*
*/
@Provide()
@CoolController({
api: ['add', 'delete', 'update', 'info', 'list', 'page'],
entity: DictInfoEntity,
service: DictInfoService,
listQueryOp: {
fieldEq: ['typeId'],
keyWordLikeFields: ['name'],
addOrderBy: {
createTime: 'ASC',
},
},
})
export class AdminDictInfoController extends BaseController {
@Inject()
dictInfoService: DictInfoService;
@Post('/data', { summary: '获得字典数据' })
async data(@Body('types') types: string[] = []) {
return this.ok(await this.dictInfoService.data(types));
}
}

View File

@ -0,0 +1,18 @@
import { DictTypeEntity } from './../../entity/type';
import { Provide } from '@midwayjs/core';
import { CoolController, BaseController } from '@cool-midway/core';
import { DictTypeService } from '../../service/type';
/**
*
*/
@Provide()
@CoolController({
api: ['add', 'delete', 'update', 'info', 'list', 'page'],
entity: DictTypeEntity,
service: DictTypeService,
listQueryOp: {
keyWordLikeFields: ['name'],
},
})
export class AdminDictTypeController extends BaseController {}

View File

@ -0,0 +1,26 @@
import { Body, Inject, Post, Provide } from '@midwayjs/core';
import {
CoolController,
BaseController,
CoolUrlTag,
TagTypes,
CoolTag,
} from '@cool-midway/core';
import { DictInfoService } from '../../service/info';
/**
*
*/
@Provide()
@CoolController()
@CoolUrlTag()
export class AppDictInfoController extends BaseController {
@Inject()
dictInfoService: DictInfoService;
@CoolTag(TagTypes.IGNORE_TOKEN)
@Post('/data', { summary: '获得字典数据' })
async data(@Body('types') types: string[] = []) {
return this.ok(await this.dictInfoService.data(types));
}
}

88
src/modules/dict/db.json Normal file
View File

@ -0,0 +1,88 @@
{
"dict_info": [
{
"id": 21,
"typeId": 19,
"name": "COOL",
"orderNum": 1,
"remark": null,
"parentId": null,
"value": "cool"
},
{
"id": 22,
"typeId": 19,
"name": "闪酷",
"orderNum": 2,
"remark": null,
"parentId": null,
"value": "https://show.cool-admin.com/api/public/uploads/20230308/c731b0cba84046268b10edbbcf36f948_315c243a448e1369fa145c5ea3f020da.gif"
},
{
"id": 23,
"typeId": 20,
"name": "法师",
"orderNum": 1,
"remark": null,
"parentId": null,
"value": "4"
},
{
"id": 24,
"typeId": 20,
"name": "战士",
"orderNum": 2,
"remark": null,
"parentId": null,
"value": "3"
},
{
"id": 25,
"typeId": 20,
"name": "坦克",
"orderNum": 3,
"remark": null,
"parentId": null,
"value": "2"
},
{
"id": 26,
"typeId": 20,
"name": "刺客",
"orderNum": 4,
"remark": null,
"parentId": null,
"value": "1"
},
{
"id": 27,
"typeId": 20,
"name": "射手",
"orderNum": 5,
"remark": null,
"parentId": null,
"value": "0"
},
{
"id": 30,
"typeId": 20,
"name": "幻影刺客",
"orderNum": 1,
"remark": null,
"parentId": 26,
"value": "5"
}
],
"dict_type": [
{
"id": 19,
"name": "品牌",
"key": "brand"
},
{
"id": 20,
"name": "职业",
"key": "occupation"
}
]
}

View File

@ -0,0 +1,26 @@
import { BaseEntity } from '@cool-midway/core';
import { Column, Entity } from 'typeorm';
/**
*
*/
@Entity('dict_info')
export class DictInfoEntity extends BaseEntity {
@Column({ comment: '类型ID' })
typeId: number;
@Column({ comment: '名称' })
name: string;
@Column({ comment: '值', nullable: true })
value: string;
@Column({ comment: '排序', default: 0 })
orderNum: number;
@Column({ comment: '备注', nullable: true })
remark: string;
@Column({ comment: '父ID', default: null })
parentId: number;
}

View File

@ -0,0 +1,14 @@
import { BaseEntity } from '@cool-midway/core';
import { Column, Entity } from 'typeorm';
/**
*
*/
@Entity('dict_type')
export class DictTypeEntity extends BaseEntity {
@Column({ comment: '名称' })
name: string;
@Column({ comment: '标识' })
key: string;
}

View File

@ -0,0 +1,137 @@
import { DictTypeEntity } from './../entity/type';
import { DictInfoEntity } from './../entity/info';
import { Config, Provide } from '@midwayjs/core';
import { BaseService } from '@cool-midway/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository, In } from 'typeorm';
import * as _ from 'lodash';
/**
*
*/
@Provide()
export class DictInfoService extends BaseService {
@InjectEntityModel(DictInfoEntity)
dictInfoEntity: Repository<DictInfoEntity>;
@InjectEntityModel(DictTypeEntity)
dictTypeEntity: Repository<DictTypeEntity>;
@Config('typeorm.dataSource.default.type')
ormType: string;
/**
*
* @param types
*/
async data(types: string[]) {
const result = {};
let typeData = await this.dictTypeEntity.find();
if (!_.isEmpty(types)) {
typeData = await this.dictTypeEntity.findBy({ key: In(types) });
}
if (_.isEmpty(typeData)) {
return {};
}
const data = await this.dictInfoEntity
.createQueryBuilder('a')
.select([
'a.id',
'a.name',
'a.typeId',
'a.parentId',
'a.orderNum',
'a.value',
])
.where('a.typeId in(:...typeIds)', {
typeIds: typeData.map(e => {
return e.id;
}),
})
.orderBy('a.orderNum', 'ASC')
.addOrderBy('a.createTime', 'ASC')
.getMany();
for (const item of typeData) {
result[item.key] = _.filter(data, { typeId: item.id }).map(e => {
const value = e.value ? Number(e.value) : e.value;
return {
...e,
value: isNaN(value) ? e.value : value,
};
});
}
return result;
}
/**
*
* @param value
* @param key
* @returns
*/
async getValues(value: string | string[], key: string) {
// 获取字典类型
const type = await this.dictTypeEntity.findOneBy({ key });
if (!type) {
return null; // 或者适当的错误处理
}
// 根据typeId获取所有相关的字典信息
const dictValues = await this.dictInfoEntity.find({
where: { typeId: type.id },
});
// 如果value是字符串直接查找
if (typeof value === 'string') {
return this.findValueInDictValues(value, dictValues);
}
// 如果value是数组遍历数组对每个元素进行查找
return value.map(val => this.findValueInDictValues(val, dictValues));
}
/**
*
* @param value
* @param dictValues
* @returns
*/
findValueInDictValues(value: string, dictValues: any[]) {
let result = dictValues.find(dictValue => dictValue.value === value);
if (!result) {
result = dictValues.find(dictValue => dictValue.id === parseInt(value));
}
return result ? result.name : null; // 或者适当的错误处理
}
/**
*
* @param data
* @param type
*/
async modifyAfter(data: any, type: 'delete' | 'update' | 'add') {
if (type === 'delete') {
for (const id of data) {
await this.delChildDict(id);
}
}
}
/**
*
* @param id
*/
private async delChildDict(id) {
const delDict = await this.dictInfoEntity.findBy({ parentId: id });
if (_.isEmpty(delDict)) {
return;
}
const delDictIds = delDict.map(e => {
return e.id;
});
await this.dictInfoEntity.delete(delDictIds);
for (const dictId of delDictIds) {
await this.delChildDict(dictId);
}
}
}

View File

@ -0,0 +1,25 @@
import { DictInfoEntity } from './../entity/info';
import { Provide } from '@midwayjs/core';
import { BaseService } from '@cool-midway/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository, In } from 'typeorm';
/**
*
*/
@Provide()
export class DictTypeService extends BaseService {
@InjectEntityModel(DictInfoEntity)
dictInfoEntity: Repository<DictInfoEntity>;
/**
*
* @param ids
*/
async delete(ids) {
super.delete(ids);
await this.dictInfoEntity.delete({
typeId: In(ids),
});
}
}

View File

@ -0,0 +1,19 @@
import { ModuleConfig } from '@cool-midway/core';
/**
*
*/
export default () => {
return {
// 模块名称
name: '数据回收',
// 模块描述
description: '收集被删除的数据,管理和恢复',
// 中间件,只对本模块有效
middlewares: [],
// 中间件,全局有效
globalMiddlewares: [],
// 模块加载顺序默认为0值越大越优先加载
order: 0,
} as ModuleConfig;
};

View File

@ -0,0 +1,35 @@
import { BaseSysUserEntity } from './../../../base/entity/sys/user';
import { RecycleDataEntity } from './../../entity/data';
import { Body, Inject, Post, Provide } from '@midwayjs/core';
import { CoolController, BaseController } from '@cool-midway/core';
import { RecycleDataService } from '../../service/data';
/**
*
*/
@Provide()
@CoolController({
api: ['info', 'page'],
entity: RecycleDataEntity,
pageQueryOp: {
keyWordLikeFields: ['b.name', 'a.url'],
select: ['a.*', 'b.name as userName'],
join: [
{
entity: BaseSysUserEntity,
alias: 'b',
condition: 'a.userId = b.id',
},
],
},
})
export class AdminRecycleDataController extends BaseController {
@Inject()
recycleDataService: RecycleDataService;
@Post('/restore', { summary: '恢复数据' })
async restore(@Body('ids') ids: number[]) {
await this.recycleDataService.restore(ids);
return this.ok();
}
}

View File

@ -0,0 +1,32 @@
import { BaseEntity } from '@cool-midway/core';
import { Entity, Column, Index } from 'typeorm';
/**
*
*/
@Entity('recycle_data')
export class RecycleDataEntity extends BaseEntity {
@Column({ comment: '表', type: 'json' })
entityInfo: {
// 数据源名称
dataSourceName: string;
// entity
entity: string;
};
@Index()
@Column({ comment: '操作人', nullable: true })
userId: number;
@Column({ comment: '被删除的数据', type: 'json' })
data: object[];
@Column({ comment: '请求的接口', nullable: true })
url: string;
@Column({ comment: '请求参数', nullable: true, type: 'json' })
params: string;
@Column({ comment: '删除数据条数', default: 1 })
count: number;
}

View File

@ -0,0 +1,21 @@
import { CoolEvent, EVENT, Event } from '@cool-midway/core';
import { Inject } from '@midwayjs/core';
import { RecycleDataService } from '../service/data';
/**
*
*/
@CoolEvent()
export class RecycleDataEvent {
@Inject()
recycleDataService: RecycleDataService;
/**
*
* @param params
*/
@Event(EVENT.SOFT_DELETE)
async softDelete(params) {
await this.recycleDataService.record(params);
}
}

View File

@ -0,0 +1,32 @@
import {
Provide,
Inject,
CommonSchedule,
TaskLocal,
FORMAT,
} from '@midwayjs/core';
import { ILogger } from '@midwayjs/logger';
import { RecycleDataService } from '../service/data';
/**
*
*/
@Provide()
export class BaseRecycleSchedule implements CommonSchedule {
@Inject()
recycleDataService: RecycleDataService;
@Inject()
logger: ILogger;
// 定时执行的具体任务
@TaskLocal(FORMAT.CRONTAB.EVERY_DAY)
async exec() {
this.logger.info('清除回收站数据定时任务开始执行');
const startTime = Date.now();
await this.recycleDataService.clear();
this.logger.info(
`清除回收站数据定时任务结束,耗时:${Date.now() - startTime}ms`
);
}
}

View File

@ -0,0 +1,84 @@
import { RecycleDataEntity } from './../entity/data';
import { Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
import { BaseService } from '@cool-midway/core';
import { InjectEntityModel, TypeORMDataSourceManager } from '@midwayjs/typeorm';
import { LessThan, Repository } from 'typeorm';
import * as _ from 'lodash';
import * as moment from 'moment';
import { BaseSysConfService } from '../../base/service/sys/conf';
/**
*
*/
@Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true })
export class RecycleDataService extends BaseService {
@InjectEntityModel(RecycleDataEntity)
recycleDataEntity: Repository<RecycleDataEntity>;
@Inject()
typeORMDataSourceManager: TypeORMDataSourceManager;
@Inject()
baseSysConfService: BaseSysConfService;
/**
*
* @param ids
*/
async restore(ids: number[]) {
for (const id of ids) {
const info = await this.recycleDataEntity.findOneBy({ id });
if (!info) {
continue;
}
let entityModel = this.typeORMDataSourceManager
.getDataSource(info.entityInfo.dataSourceName)
.getRepository(info.entityInfo.entity);
await entityModel.save(info.data);
await this.recycleDataEntity.delete(id);
}
}
/**
*
* @param params
*/
async record(params) {
const { ctx, data, entity } = params;
if (!ctx?.req) return;
const dataSourceName =
this.typeORMDataSourceManager.getDataSourceNameByModel(entity.target);
const url = ctx?.url;
await this.recycleDataEntity.save({
entityInfo: {
dataSourceName,
entity: entity.target.name,
},
url,
params:
ctx?.req.method === 'GET' ? ctx?.request.query : ctx?.request.body,
data,
count: data.length,
userId: _.startsWith(url, '/admin/') ? ctx?.admin.userId : ctx?.user?.id,
});
}
/**
*
* @param isAll
*/
async clear(isAll?) {
if (isAll) {
await this.recycleDataEntity.clear();
return;
}
const keepDay = await this.baseSysConfService.getValue('recycleKeep');
if (keepDay) {
const beforeDate = moment().add(-keepDay, 'days').startOf('day').toDate();
await this.recycleDataEntity.delete({ createTime: LessThan(beforeDate) });
} else {
await this.recycleDataEntity.clear();
}
}
}

View File

@ -0,0 +1,24 @@
import { ModuleConfig } from '@cool-midway/core';
/**
*
*/
export default () => {
return {
// 模块名称
name: '文件空间',
// 模块描述
description: '上传和管理文件资源',
// 中间件,只对本模块有效
middlewares: [],
// 中间件,全局有效
globalMiddlewares: [],
// 模块加载顺序默认为0值越大越优先加载
order: 0,
// wps的配置
wps: {
// 这是个测试的appId会有水印
appId: 'SX20230111NDUAGQ',
},
} as ModuleConfig;
};

View File

@ -0,0 +1,18 @@
import { Provide } from '@midwayjs/core';
import { CoolController, BaseController } from '@cool-midway/core';
import { SpaceInfoEntity } from '../../entity/info';
import { SpaceInfoService } from '../../service/info';
/**
*
*/
@Provide()
@CoolController({
api: ['add', 'delete', 'update', 'info', 'list', 'page'],
entity: SpaceInfoEntity,
service: SpaceInfoService,
pageQueryOp: {
fieldEq: ['type', 'classifyId'],
},
})
export class BaseAppSpaceInfoController extends BaseController {}

View File

@ -0,0 +1,15 @@
import { Provide } from '@midwayjs/core';
import { CoolController, BaseController } from '@cool-midway/core';
import { SpaceTypeEntity } from '../../entity/type';
import { SpaceTypeService } from '../../service/type';
/**
*
*/
@Provide()
@CoolController({
api: ['add', 'delete', 'update', 'info', 'list', 'page'],
entity: SpaceTypeEntity,
service: SpaceTypeService,
})
export class BaseAppSpaceTypeController extends BaseController {}

View File

@ -0,0 +1 @@
编写接口

View File

@ -0,0 +1,33 @@
import { BaseEntity } from '@cool-midway/core';
import { Column, Index, Entity } from 'typeorm';
/**
*
*/
@Entity('space_info')
export class SpaceInfoEntity extends BaseEntity {
@Column({ comment: '地址' })
url: string;
@Column({ comment: '类型' })
type: string;
@Column({ comment: '分类ID', nullable: true })
classifyId: number;
@Index()
@Column({ comment: '文件id' })
fileId: string;
@Column({ comment: '文件名' })
name: string;
@Column({ comment: '文件大小' })
size: number;
@Column({ comment: '文档版本', default: 1 })
version: number;
@Column({ comment: '文件位置' })
key: string;
}

View File

@ -0,0 +1,14 @@
import { BaseEntity } from '@cool-midway/core';
import { Column, Entity } from 'typeorm';
/**
*
*/
@Entity('space_type')
export class SpaceTypeEntity extends BaseEntity {
@Column({ comment: '类别名称' })
name: string;
@Column({ comment: '父分类ID', nullable: true })
parentId: number;
}

View File

@ -0,0 +1,30 @@
import { SpaceInfoEntity } from './../entity/info';
import { Inject, Provide } from '@midwayjs/core';
import { BaseService, MODETYPE } from '@cool-midway/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
import { PluginService } from '../../plugin/service/info';
/**
*
*/
@Provide()
export class SpaceInfoService extends BaseService {
@InjectEntityModel(SpaceInfoEntity)
spaceInfoEntity: Repository<SpaceInfoEntity>;
@Inject()
pluginService: PluginService;
/**
*
*/
async add(param) {
const result = await this.pluginService.invoke('upload', 'getMode');
const config = await this.pluginService.getConfig('upload');
if (result.mode == MODETYPE.LOCAL) {
param.key = param.url.replace(config.domain, '');
}
return super.add(param);
}
}

View File

@ -0,0 +1,28 @@
import { Provide } from '@midwayjs/core';
import { BaseService } from '@cool-midway/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { In, Repository } from 'typeorm';
import { SpaceTypeEntity } from '../entity/type';
import { SpaceInfoEntity } from '../entity/info';
/**
*
*/
@Provide()
export class SpaceTypeService extends BaseService {
@InjectEntityModel(SpaceTypeEntity)
spaceTypeEntity: Repository<SpaceTypeEntity>;
@InjectEntityModel(SpaceInfoEntity)
spaceInfoEntity: Repository<SpaceInfoEntity>;
/**
*
* @param ids
*/
async delete(ids: any) {
await super.delete(ids);
// 删除该分类下的文件信息
await this.spaceInfoEntity.delete({ classifyId: In(ids) });
}
}