feat: 商业版支持邀请推广功能

This commit is contained in:
xiaojunnuo
2026-05-26 01:08:17 +08:00
parent ba1fe54ef8
commit f1d2a1033a
18 changed files with 531 additions and 264 deletions
@@ -5,10 +5,11 @@ CREATE TABLE `cd_invite_level`
(
`id` bigint PRIMARY KEY AUTO_INCREMENT NOT NULL,
`name` varchar(100),
`icon` varchar(100) NOT NULL DEFAULT 'fluent-emoji-flat:2nd-place-medal',
`sort` bigint NOT NULL DEFAULT 0,
`min_amount` bigint NOT NULL DEFAULT 0,
`commission_rate` bigint NOT NULL DEFAULT 0,
`is_hidden` boolean NOT NULL DEFAULT false,
`level_type` varchar(30) NOT NULL DEFAULT 'normal',
`disabled` boolean NOT NULL DEFAULT false,
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP
@@ -28,8 +29,8 @@ CREATE TABLE `cd_invite_user_plan`
);
CREATE UNIQUE INDEX `index_invite_user_plan_user_id` ON `cd_invite_user_plan` (`user_id`);
INSERT INTO `cd_invite_level` (`name`, `sort`, `min_amount`, `commission_rate`, `is_hidden`, `disabled`)
VALUES ('青铜', 10, 0, 10, false, false),
('白银', 20, 100000, 15, false, false),
('黄金', 30, 500000, 20, false, false),
('钻石', 40, 1000000, 30, false, false);
INSERT INTO `cd_invite_level` (`name`, `icon`, `sort`, `min_amount`, `commission_rate`, `level_type`, `disabled`)
VALUES ('青铜', 'fluent-emoji-flat:2nd-place-medal', 10, 0, 10, 'normal', false),
('白银', 'fluent-emoji-flat:1st-place-medal', 20, 100000, 15, 'normal', false),
('黄金', 'fluent-emoji-flat:3rd-place-medal', 30, 500000, 20, 'normal', false),
('钻石', 'streamline-color:diamond-2', 40, 1000000, 30, 'normal', false);
@@ -5,10 +5,11 @@ CREATE TABLE "cd_invite_level"
(
"id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL,
"name" varchar(100),
"icon" varchar(100) NOT NULL DEFAULT 'fluent-emoji-flat:2nd-place-medal',
"sort" bigint NOT NULL DEFAULT 0,
"min_amount" bigint NOT NULL DEFAULT 0,
"commission_rate" bigint NOT NULL DEFAULT 0,
"is_hidden" boolean NOT NULL DEFAULT (false),
"level_type" varchar(30) NOT NULL DEFAULT 'normal',
"disabled" boolean NOT NULL DEFAULT (false),
"create_time" timestamp NOT NULL DEFAULT (CURRENT_TIMESTAMP),
"update_time" timestamp NOT NULL DEFAULT (CURRENT_TIMESTAMP)
@@ -28,8 +29,8 @@ CREATE TABLE "cd_invite_user_plan"
);
CREATE UNIQUE INDEX "index_invite_user_plan_user_id" ON "cd_invite_user_plan" ("user_id");
INSERT INTO "cd_invite_level" ("name", "sort", "min_amount", "commission_rate", "is_hidden", "disabled")
VALUES ('青铜', 10, 0, 10, false, false),
('白银', 20, 100000, 15, false, false),
('黄金', 30, 500000, 20, false, false),
('钻石', 40, 1000000, 30, false, false);
INSERT INTO "cd_invite_level" ("name", "icon", "sort", "min_amount", "commission_rate", "level_type", "disabled")
VALUES ('青铜', 'fluent-emoji-flat:2nd-place-medal', 10, 0, 10, 'normal', false),
('白银', 'fluent-emoji-flat:1st-place-medal', 20, 100000, 15, 'normal', false),
('黄金', 'fluent-emoji-flat:3rd-place-medal', 30, 500000, 20, 'normal', false),
('钻石', 'streamline-color:diamond-2', 40, 1000000, 30, 'normal', false);
@@ -5,10 +5,11 @@ CREATE TABLE "cd_invite_level"
(
"id" integer PRIMARY KEY AUTOINCREMENT NOT NULL,
"name" varchar(100),
"icon" varchar(100) NOT NULL DEFAULT 'fluent-emoji-flat:2nd-place-medal',
"sort" integer NOT NULL DEFAULT 0,
"min_amount" integer NOT NULL DEFAULT 0,
"commission_rate" integer NOT NULL DEFAULT 0,
"is_hidden" boolean NOT NULL DEFAULT (false),
"level_type" varchar(30) NOT NULL DEFAULT 'normal',
"disabled" boolean NOT NULL DEFAULT (false),
"create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP),
"update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP)
@@ -28,8 +29,8 @@ CREATE TABLE "cd_invite_user_plan"
);
CREATE UNIQUE INDEX "index_invite_user_plan_user_id" ON "cd_invite_user_plan" ("user_id");
INSERT INTO "cd_invite_level" ("name", "sort", "min_amount", "commission_rate", "is_hidden", "disabled")
VALUES ('青铜', 10, 0, 10, false, false),
('白银', 20, 100000, 15, false, false),
('黄金', 30, 500000, 20, false, false),
('钻石', 40, 1000000, 30, false, false);
INSERT INTO "cd_invite_level" ("name", "icon", "sort", "min_amount", "commission_rate", "level_type", "disabled")
VALUES ('青铜', 'fluent-emoji-flat:2nd-place-medal', 10, 0, 10, 'normal', false),
('白银', 'fluent-emoji-flat:1st-place-medal', 20, 100000, 15, 'normal', false),
('黄金', 'fluent-emoji-flat:3rd-place-medal', 30, 500000, 20, 'normal', false),
('钻石', 'streamline-color:diamond-2', 40, 1000000, 30, 'normal', false);
@@ -137,6 +137,7 @@ export class MainConfiguration {
});
logger.info('当前环境:', this.app.getEnv()); // prod
// throw new Error("address family not supported")
}
}
@@ -3,7 +3,7 @@
import assert from "node:assert/strict";
import { getImageDownloadOptions, isImageFile } from "./file-controller.js";
import { FileController, getImageDownloadOptions, isImageFile } from "./file-controller.js";
describe("FileController.isImageFile", () => {
it("detects uploaded logo image files", () => {
@@ -37,3 +37,23 @@ describe("FileController.isImageFile", () => {
assert.equal(getImageDownloadOptions("data/upload/private/user/cert.pem"), undefined);
});
});
describe("FileController.upload", () => {
it("auto saves uploaded file to public directory when autoSave is true", async () => {
const controller = new FileController();
controller.fileService = {
async saveFile(userId: number, key: string, permission: string) {
assert.equal(userId, 1);
assert.equal(permission, "public");
assert.equal(key.startsWith("tmpfile_key_"), true);
return "/public/1/logo.png";
},
} as any;
controller.ctx = { user: { id: 1 } } as any;
const res = await controller.upload([{ filename: "logo.png", data: "tmp/logo.png" }] as any, {}, "true");
assert.equal(res.data.key, "/public/1/logo.png");
assert.equal(res.data.url, "/api/basic/file/download?key=%2Fpublic%2F1%2Flogo.png");
});
});
@@ -37,7 +37,7 @@ export class FileController extends BaseController {
authService: AuthService;
@Post('/upload', { description: Constants.per.authOnly })
async upload(@Files() files: UploadFileInfo<string>[], @Fields() fields: any) {
async upload(@Files() files: UploadFileInfo<string>[], @Fields() fields: any, @Query('autoSave') autoSave: string) {
console.log('files', files, fields);
const cacheKey = uploadTmpFileCacheKey + nanoid();
const file = files[0];
@@ -51,6 +51,13 @@ export class FileController extends BaseController {
ttl: 1000 * 60 * 60,
}
);
if (autoSave === 'true') {
const key = await this.fileService.saveFile(this.getUserId(), cacheKey, 'public');
return this.ok({
key,
url: `/api/basic/file/download?key=${encodeURIComponent(key)}`,
});
}
return this.ok({
key: cacheKey,
});