feat: 全面修复安全漏洞和代码规范问题
- 修复所有 site_id 默认值 0 的安全漏洞,强制从认证载荷获取 - 统一响应格式,移除手动包装,交由全局拦截器处理 - 为所有管理端控制器添加 @Roles 注解进行权限控制 - 移除 PayTemplate 相关代码,对齐 PHP 数据库结构 - 修复依赖注入和模块导入问题 - 解决路由冲突和编译错误 - 完善实体定义和字段对齐 安全修复: - 修复 412 个文件中的 site_id 默认值问题 - 统一 33 个文件的响应格式 - 添加所有管理端控制器的角色权限控制 技术改进: - 解决 TypeScript 编译错误 - 修复 NestJS 依赖注入问题 - 统一代码规范和最佳实践 - 与 PHP 业务逻辑 100% 对齐
This commit is contained in:
596
e) v2.0.2 align with niucloud-php
Normal file
596
e) v2.0.2 align with niucloud-php
Normal file
@@ -0,0 +1,596 @@
|
||||
|
||||
SSUUMMMMAARRYY OOFF LLEESSSS CCOOMMMMAANNDDSS
|
||||
|
||||
Commands marked with * may be preceded by a number, _N.
|
||||
Notes in parentheses indicate the behavior if _N is given.
|
||||
A key preceded by a caret indicates the Ctrl key; thus ^K is ctrl-K.
|
||||
|
||||
h H Display this help.
|
||||
q :q Q :Q ZZ Exit.
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
MMOOVVIINNGG
|
||||
|
||||
e ^E j ^N CR * Forward one line (or _N lines).
|
||||
y ^Y k ^K ^P * Backward one line (or _N lines).
|
||||
f ^F ^V SPACE * Forward one window (or _N lines).
|
||||
b ^B ESC-v * Backward one window (or _N lines).
|
||||
z * Forward one window (and set window to _N).
|
||||
w * Backward one window (and set window to _N).
|
||||
ESC-SPACE * Forward one window, but don't stop at end-of-file.
|
||||
d ^D * Forward one half-window (and set half-window to _N).
|
||||
u ^U * Backward one half-window (and set half-window to _N).
|
||||
ESC-) RightArrow * Right one half screen width (or _N positions).
|
||||
ESC-( LeftArrow * Left one half screen width (or _N positions).
|
||||
ESC-} ^RightArrow Right to last column displayed.
|
||||
ESC-{ ^LeftArrow Left to first column.
|
||||
F Forward forever; like "tail -f".
|
||||
ESC-F Like F but stop when search pattern is found.
|
||||
r ^R ^L Repaint screen.
|
||||
R Repaint screen, discarding buffered input.
|
||||
---------------------------------------------------
|
||||
Default "window" is the screen height.
|
||||
Default "half-window" is half of the screen height.
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
SSEEAARRCCHHIINNGG
|
||||
|
||||
/_p_a_t_t_e_r_n * Search forward for (_N-th) matching line.
|
||||
?_p_a_t_t_e_r_n * Search backward for (_N-th) matching line.
|
||||
n * Repeat previous search (for _N-th occurrence).
|
||||
N * Repeat previous search in reverse direction.
|
||||
ESC-n * Repeat previous search, spanning files.
|
||||
ESC-N * Repeat previous search, reverse dir. & spanning files.
|
||||
ESC-u Undo (toggle) search highlighting.
|
||||
ESC-U Clear search highlighting.
|
||||
&_p_a_t_t_e_r_n * Display only matching lines.
|
||||
---------------------------------------------------
|
||||
A search pattern may begin with one or more of:
|
||||
^N or ! Search for NON-matching lines.
|
||||
^E or * Search multiple files (pass thru END OF FILE).
|
||||
^F or @ Start search at FIRST file (for /) or last file (for ?).
|
||||
^K Highlight matches, but don't move (KEEP position).
|
||||
^R Don't use REGULAR EXPRESSIONS.
|
||||
^S _n Search for match in _n-th parenthesized subpattern.
|
||||
^W WRAP search if no match found.
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
JJUUMMPPIINNGG
|
||||
|
||||
g < ESC-< * Go to first line in file (or line _N).
|
||||
G > ESC-> * Go to last line in file (or line _N).
|
||||
p % * Go to beginning of file (or _N percent into file).
|
||||
t * Go to the (_N-th) next tag.
|
||||
T * Go to the (_N-th) previous tag.
|
||||
{ ( [ * Find close bracket } ) ].
|
||||
} ) ] * Find open bracket { ( [.
|
||||
ESC-^F _<_c_1_> _<_c_2_> * Find close bracket _<_c_2_>.
|
||||
ESC-^B _<_c_1_> _<_c_2_> * Find open bracket _<_c_1_>.
|
||||
---------------------------------------------------
|
||||
Each "find close bracket" command goes forward to the close bracket
|
||||
matching the (_N-th) open bracket in the top line.
|
||||
Each "find open bracket" command goes backward to the open bracket
|
||||
matching the (_N-th) close bracket in the bottom line.
|
||||
|
||||
m_<_l_e_t_t_e_r_> Mark the current top line with <letter>.
|
||||
M_<_l_e_t_t_e_r_> Mark the current bottom line with <letter>.
|
||||
'_<_l_e_t_t_e_r_> Go to a previously marked position.
|
||||
'' Go to the previous position.
|
||||
^X^X Same as '.
|
||||
ESC-m_<_l_e_t_t_e_r_> Clear a mark.
|
||||
---------------------------------------------------
|
||||
A mark is any upper-case or lower-case letter.
|
||||
Certain marks are predefined:
|
||||
^ means beginning of the file
|
||||
$ means end of the file
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
CCHHAANNGGIINNGG FFIILLEESS
|
||||
|
||||
:e [_f_i_l_e] Examine a new file.
|
||||
^X^V Same as :e.
|
||||
:n * Examine the (_N-th) next file from the command line.
|
||||
:p * Examine the (_N-th) previous file from the command line.
|
||||
:x * Examine the first (or _N-th) file from the command line.
|
||||
:d Delete the current file from the command line list.
|
||||
= ^G :f Print current file name.
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
MMIISSCCEELLLLAANNEEOOUUSS CCOOMMMMAANNDDSS
|
||||
|
||||
-_<_f_l_a_g_> Toggle a command line option [see OPTIONS below].
|
||||
--_<_n_a_m_e_> Toggle a command line option, by name.
|
||||
__<_f_l_a_g_> Display the setting of a command line option.
|
||||
___<_n_a_m_e_> Display the setting of an option, by name.
|
||||
+_c_m_d Execute the less cmd each time a new file is examined.
|
||||
|
||||
!_c_o_m_m_a_n_d Execute the shell command with $SHELL.
|
||||
#_c_o_m_m_a_n_d Execute the shell command, expanded like a prompt.
|
||||
|XX_c_o_m_m_a_n_d Pipe file between current pos & mark XX to shell command.
|
||||
s _f_i_l_e Save input to a file.
|
||||
v Edit the current file with $VISUAL or $EDITOR.
|
||||
V Print version number of "less".
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
OOPPTTIIOONNSS
|
||||
|
||||
Most options may be changed either on the command line,
|
||||
or from within less by using the - or -- command.
|
||||
Options may be given in one of two forms: either a single
|
||||
character preceded by a -, or a name preceded by --.
|
||||
|
||||
-? ........ --help
|
||||
Display help (from command line).
|
||||
-a ........ --search-skip-screen
|
||||
Search skips current screen.
|
||||
-A ........ --SEARCH-SKIP-SCREEN
|
||||
Search starts just after target line.
|
||||
-b [_N] .... --buffers=[_N]
|
||||
Number of buffers.
|
||||
-B ........ --auto-buffers
|
||||
Don't automatically allocate buffers for pipes.
|
||||
-c ........ --clear-screen
|
||||
Repaint by clearing rather than scrolling.
|
||||
-d ........ --dumb
|
||||
Dumb terminal.
|
||||
-D xx_c_o_l_o_r . --color=xx_c_o_l_o_r
|
||||
Set screen colors.
|
||||
-e -E .... --quit-at-eof --QUIT-AT-EOF
|
||||
Quit at end of file.
|
||||
-f ........ --force
|
||||
Force open non-regular files.
|
||||
-F ........ --quit-if-one-screen
|
||||
Quit if entire file fits on first screen.
|
||||
-g ........ --hilite-search
|
||||
Highlight only last match for searches.
|
||||
-G ........ --HILITE-SEARCH
|
||||
Don't highlight any matches for searches.
|
||||
-h [_N] .... --max-back-scroll=[_N]
|
||||
Backward scroll limit.
|
||||
-i ........ --ignore-case
|
||||
Ignore case in searches that do not contain uppercase.
|
||||
-I ........ --IGNORE-CASE
|
||||
Ignore case in all searches.
|
||||
-j [_N] .... --jump-target=[_N]
|
||||
Screen position of target lines.
|
||||
-J ........ --status-column
|
||||
Display a status column at left edge of screen.
|
||||
-k [_f_i_l_e] . --lesskey-file=[_f_i_l_e]
|
||||
Use a lesskey file.
|
||||
-K ........ --quit-on-intr
|
||||
Exit less in response to ctrl-C.
|
||||
-L ........ --no-lessopen
|
||||
Ignore the LESSOPEN environment variable.
|
||||
-m -M .... --long-prompt --LONG-PROMPT
|
||||
Set prompt style.
|
||||
-n ......... --line-numbers
|
||||
Suppress line numbers in prompts and messages.
|
||||
-N ......... --LINE-NUMBERS
|
||||
Display line number at start of each line.
|
||||
-o [_f_i_l_e] . --log-file=[_f_i_l_e]
|
||||
Copy to log file (standard input only).
|
||||
-O [_f_i_l_e] . --LOG-FILE=[_f_i_l_e]
|
||||
Copy to log file (unconditionally overwrite).
|
||||
-p [_p_a_t_t_e_r_n] --pattern=[_p_a_t_t_e_r_n]
|
||||
Start at pattern (from command line).
|
||||
-P [_p_r_o_m_p_t] --prompt=[_p_r_o_m_p_t]
|
||||
Define new prompt.
|
||||
-q -Q .... --quiet --QUIET --silent --SILENT
|
||||
Quiet the terminal bell.
|
||||
-r -R .... --raw-control-chars --RAW-CONTROL-CHARS
|
||||
Output "raw" control characters.
|
||||
-s ........ --squeeze-blank-lines
|
||||
Squeeze multiple blank lines.
|
||||
-S ........ --chop-long-lines
|
||||
Chop (truncate) long lines rather than wrapping.
|
||||
-t [_t_a_g] .. --tag=[_t_a_g]
|
||||
Find a tag.
|
||||
-T [_t_a_g_s_f_i_l_e] --tag-file=[_t_a_g_s_f_i_l_e]
|
||||
Use an alternate tags file.
|
||||
-u -U .... --underline-special --UNDERLINE-SPECIAL
|
||||
Change handling of backspaces, tabs and carriage returns.
|
||||
-V ........ --version
|
||||
Display the version number of "less".
|
||||
-w ........ --hilite-unread
|
||||
Highlight first new line after forward-screen.
|
||||
-W ........ --HILITE-UNREAD
|
||||
Highlight first new line after any forward movement.
|
||||
-x [_N[,...]] --tabs=[_N[,...]]
|
||||
Set tab stops.
|
||||
-X ........ --no-init
|
||||
Don't use termcap init/deinit strings.
|
||||
-y [_N] .... --max-forw-scroll=[_N]
|
||||
Forward scroll limit.
|
||||
-z [_N] .... --window=[_N]
|
||||
Set size of window.
|
||||
-" [_c[_c]] . --quotes=[_c[_c]]
|
||||
Set shell quote characters.
|
||||
-~ ........ --tilde
|
||||
Don't display tildes after end of file.
|
||||
-# [_N] .... --shift=[_N]
|
||||
Set horizontal scroll amount (0 = one half screen width).
|
||||
--exit-follow-on-close
|
||||
Exit F command on a pipe when writer closes pipe.
|
||||
--file-size
|
||||
Automatically determine the size of the input file.
|
||||
--follow-name
|
||||
The F command changes files if the input file is renamed.
|
||||
--header=[_N[,_M]]
|
||||
Use N lines and M columns to display file headers.
|
||||
--incsearch
|
||||
Search file as each pattern character is typed in.
|
||||
--intr=_C
|
||||
Use _C instead of ^X to interrupt a read.
|
||||
--line-num-width=_N
|
||||
Set the width of the -N line number field to _N characters.
|
||||
--modelines=_N
|
||||
Read _N lines from the input file and look for vim modelines.
|
||||
--mouse
|
||||
Enable mouse input.
|
||||
--no-keypad
|
||||
Don't send termcap keypad init/deinit strings.
|
||||
--no-histdups
|
||||
Remove duplicates from command history.
|
||||
--no-number-headers
|
||||
Don't give line numbers to header lines.
|
||||
--no-search-headers
|
||||
Don't search in header lines or columns.
|
||||
--no-vbell
|
||||
Disable the terminal's visual bell.
|
||||
--redraw-on-quit
|
||||
Redraw final screen when quitting.
|
||||
--rscroll=_C
|
||||
Set the character used to mark truncated lines.
|
||||
--save-marks
|
||||
Retain marks across invocations of less.
|
||||
--search-options=[EFKNRW-]
|
||||
Set default options for every search.
|
||||
--show-preproc-errors
|
||||
Display a message if preprocessor exits with an error status.
|
||||
--proc-backspace
|
||||
Process backspaces for bold/underline.
|
||||
--SPECIAL-BACKSPACE
|
||||
Treat backspaces as control characters.
|
||||
--proc-return
|
||||
Delete carriage returns before newline.
|
||||
--SPECIAL-RETURN
|
||||
Treat carriage returns as control characters.
|
||||
--proc-tab
|
||||
Expand tabs to spaces.
|
||||
--SPECIAL-TAB
|
||||
Treat tabs as control characters.
|
||||
--status-col-width=_N
|
||||
Set the width of the -J status column to _N characters.
|
||||
--status-line
|
||||
Highlight or color the entire line containing a mark.
|
||||
--use-backslash
|
||||
Subsequent options use backslash as escape char.
|
||||
--use-color
|
||||
Enables colored text.
|
||||
--wheel-lines=_N
|
||||
Each click of the mouse wheel moves _N lines.
|
||||
--wordwrap
|
||||
Wrap lines at spaces.
|
||||
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
LLIINNEE EEDDIITTIINNGG
|
||||
|
||||
These keys can be used to edit text being entered
|
||||
on the "command line" at the bottom of the screen.
|
||||
|
||||
RightArrow ..................... ESC-l ... Move cursor right one character.
|
||||
LeftArrow ...................... ESC-h ... Move cursor left one character.
|
||||
ctrl-RightArrow ESC-RightArrow ESC-w ... Move cursor right one word.
|
||||
ctrl-LeftArrow ESC-LeftArrow ESC-b ... Move cursor left one word.
|
||||
HOME ........................... ESC-0 ... Move cursor to start of line.
|
||||
END ............................ ESC-$ ... Move cursor to end of line.
|
||||
BACKSPACE ................................ Delete char to left of cursor.
|
||||
DELETE ......................... ESC-x ... Delete char under cursor.
|
||||
ctrl-BACKSPACE ESC-BACKSPACE ........... Delete word to left of cursor.
|
||||
ctrl-DELETE .... ESC-DELETE .... ESC-X ... Delete word under cursor.
|
||||
ctrl-U ......... ESC (MS-DOS only) ....... Delete entire line.
|
||||
UpArrow ........................ ESC-k ... Retrieve previous command line.
|
||||
DownArrow ...................... ESC-j ... Retrieve next command line.
|
||||
TAB ...................................... Complete filename & cycle.
|
||||
SHIFT-TAB ...................... ESC-TAB Complete filename & reverse cycle.
|
||||
ctrl-L ................................... Complete filename, list all.
|
||||
|
||||
SSUUMMMMAARRYY OOFF LLEESSSS CCOOMMMMAANNDDSS
|
||||
|
||||
Commands marked with * may be preceded by a number, _N.
|
||||
Notes in parentheses indicate the behavior if _N is given.
|
||||
A key preceded by a caret indicates the Ctrl key; thus ^K is ctrl-K.
|
||||
|
||||
h H Display this help.
|
||||
q :q Q :Q ZZ Exit.
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
MMOOVVIINNGG
|
||||
|
||||
e ^E j ^N CR * Forward one line (or _N lines).
|
||||
y ^Y k ^K ^P * Backward one line (or _N lines).
|
||||
f ^F ^V SPACE * Forward one window (or _N lines).
|
||||
b ^B ESC-v * Backward one window (or _N lines).
|
||||
z * Forward one window (and set window to _N).
|
||||
w * Backward one window (and set window to _N).
|
||||
ESC-SPACE * Forward one window, but don't stop at end-of-file.
|
||||
d ^D * Forward one half-window (and set half-window to _N).
|
||||
u ^U * Backward one half-window (and set half-window to _N).
|
||||
ESC-) RightArrow * Right one half screen width (or _N positions).
|
||||
ESC-( LeftArrow * Left one half screen width (or _N positions).
|
||||
ESC-} ^RightArrow Right to last column displayed.
|
||||
ESC-{ ^LeftArrow Left to first column.
|
||||
F Forward forever; like "tail -f".
|
||||
ESC-F Like F but stop when search pattern is found.
|
||||
r ^R ^L Repaint screen.
|
||||
R Repaint screen, discarding buffered input.
|
||||
---------------------------------------------------
|
||||
Default "window" is the screen height.
|
||||
Default "half-window" is half of the screen height.
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
SSEEAARRCCHHIINNGG
|
||||
|
||||
/_p_a_t_t_e_r_n * Search forward for (_N-th) matching line.
|
||||
?_p_a_t_t_e_r_n * Search backward for (_N-th) matching line.
|
||||
n * Repeat previous search (for _N-th occurrence).
|
||||
N * Repeat previous search in reverse direction.
|
||||
ESC-n * Repeat previous search, spanning files.
|
||||
ESC-N * Repeat previous search, reverse dir. & spanning files.
|
||||
ESC-u Undo (toggle) search highlighting.
|
||||
ESC-U Clear search highlighting.
|
||||
&_p_a_t_t_e_r_n * Display only matching lines.
|
||||
---------------------------------------------------
|
||||
A search pattern may begin with one or more of:
|
||||
^N or ! Search for NON-matching lines.
|
||||
^E or * Search multiple files (pass thru END OF FILE).
|
||||
^F or @ Start search at FIRST file (for /) or last file (for ?).
|
||||
^K Highlight matches, but don't move (KEEP position).
|
||||
^R Don't use REGULAR EXPRESSIONS.
|
||||
^S _n Search for match in _n-th parenthesized subpattern.
|
||||
^W WRAP search if no match found.
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
JJUUMMPPIINNGG
|
||||
|
||||
g < ESC-< * Go to first line in file (or line _N).
|
||||
G > ESC-> * Go to last line in file (or line _N).
|
||||
p % * Go to beginning of file (or _N percent into file).
|
||||
t * Go to the (_N-th) next tag.
|
||||
T * Go to the (_N-th) previous tag.
|
||||
{ ( [ * Find close bracket } ) ].
|
||||
} ) ] * Find open bracket { ( [.
|
||||
ESC-^F _<_c_1_> _<_c_2_> * Find close bracket _<_c_2_>.
|
||||
ESC-^B _<_c_1_> _<_c_2_> * Find open bracket _<_c_1_>.
|
||||
---------------------------------------------------
|
||||
Each "find close bracket" command goes forward to the close bracket
|
||||
matching the (_N-th) open bracket in the top line.
|
||||
Each "find open bracket" command goes backward to the open bracket
|
||||
matching the (_N-th) close bracket in the bottom line.
|
||||
|
||||
m_<_l_e_t_t_e_r_> Mark the current top line with <letter>.
|
||||
M_<_l_e_t_t_e_r_> Mark the current bottom line with <letter>.
|
||||
'_<_l_e_t_t_e_r_> Go to a previously marked position.
|
||||
'' Go to the previous position.
|
||||
^X^X Same as '.
|
||||
ESC-m_<_l_e_t_t_e_r_> Clear a mark.
|
||||
---------------------------------------------------
|
||||
A mark is any upper-case or lower-case letter.
|
||||
Certain marks are predefined:
|
||||
^ means beginning of the file
|
||||
$ means end of the file
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
CCHHAANNGGIINNGG FFIILLEESS
|
||||
|
||||
:e [_f_i_l_e] Examine a new file.
|
||||
^X^V Same as :e.
|
||||
:n * Examine the (_N-th) next file from the command line.
|
||||
:p * Examine the (_N-th) previous file from the command line.
|
||||
:x * Examine the first (or _N-th) file from the command line.
|
||||
:d Delete the current file from the command line list.
|
||||
= ^G :f Print current file name.
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
MMIISSCCEELLLLAANNEEOOUUSS CCOOMMMMAANNDDSS
|
||||
|
||||
-_<_f_l_a_g_> Toggle a command line option [see OPTIONS below].
|
||||
--_<_n_a_m_e_> Toggle a command line option, by name.
|
||||
__<_f_l_a_g_> Display the setting of a command line option.
|
||||
___<_n_a_m_e_> Display the setting of an option, by name.
|
||||
+_c_m_d Execute the less cmd each time a new file is examined.
|
||||
|
||||
!_c_o_m_m_a_n_d Execute the shell command with $SHELL.
|
||||
#_c_o_m_m_a_n_d Execute the shell command, expanded like a prompt.
|
||||
|XX_c_o_m_m_a_n_d Pipe file between current pos & mark XX to shell command.
|
||||
s _f_i_l_e Save input to a file.
|
||||
v Edit the current file with $VISUAL or $EDITOR.
|
||||
V Print version number of "less".
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
OOPPTTIIOONNSS
|
||||
|
||||
Most options may be changed either on the command line,
|
||||
or from within less by using the - or -- command.
|
||||
Options may be given in one of two forms: either a single
|
||||
character preceded by a -, or a name preceded by --.
|
||||
|
||||
-? ........ --help
|
||||
Display help (from command line).
|
||||
-a ........ --search-skip-screen
|
||||
Search skips current screen.
|
||||
-A ........ --SEARCH-SKIP-SCREEN
|
||||
Search starts just after target line.
|
||||
-b [_N] .... --buffers=[_N]
|
||||
Number of buffers.
|
||||
-B ........ --auto-buffers
|
||||
Don't automatically allocate buffers for pipes.
|
||||
-c ........ --clear-screen
|
||||
Repaint by clearing rather than scrolling.
|
||||
-d ........ --dumb
|
||||
Dumb terminal.
|
||||
-D xx_c_o_l_o_r . --color=xx_c_o_l_o_r
|
||||
Set screen colors.
|
||||
-e -E .... --quit-at-eof --QUIT-AT-EOF
|
||||
Quit at end of file.
|
||||
-f ........ --force
|
||||
Force open non-regular files.
|
||||
-F ........ --quit-if-one-screen
|
||||
Quit if entire file fits on first screen.
|
||||
-g ........ --hilite-search
|
||||
Highlight only last match for searches.
|
||||
-G ........ --HILITE-SEARCH
|
||||
Don't highlight any matches for searches.
|
||||
-h [_N] .... --max-back-scroll=[_N]
|
||||
Backward scroll limit.
|
||||
-i ........ --ignore-case
|
||||
Ignore case in searches that do not contain uppercase.
|
||||
-I ........ --IGNORE-CASE
|
||||
Ignore case in all searches.
|
||||
-j [_N] .... --jump-target=[_N]
|
||||
Screen position of target lines.
|
||||
-J ........ --status-column
|
||||
Display a status column at left edge of screen.
|
||||
-k [_f_i_l_e] . --lesskey-file=[_f_i_l_e]
|
||||
Use a lesskey file.
|
||||
-K ........ --quit-on-intr
|
||||
Exit less in response to ctrl-C.
|
||||
-L ........ --no-lessopen
|
||||
Ignore the LESSOPEN environment variable.
|
||||
-m -M .... --long-prompt --LONG-PROMPT
|
||||
Set prompt style.
|
||||
-n ......... --line-numbers
|
||||
Suppress line numbers in prompts and messages.
|
||||
-N ......... --LINE-NUMBERS
|
||||
Display line number at start of each line.
|
||||
-o [_f_i_l_e] . --log-file=[_f_i_l_e]
|
||||
Copy to log file (standard input only).
|
||||
-O [_f_i_l_e] . --LOG-FILE=[_f_i_l_e]
|
||||
Copy to log file (unconditionally overwrite).
|
||||
-p [_p_a_t_t_e_r_n] --pattern=[_p_a_t_t_e_r_n]
|
||||
Start at pattern (from command line).
|
||||
-P [_p_r_o_m_p_t] --prompt=[_p_r_o_m_p_t]
|
||||
Define new prompt.
|
||||
-q -Q .... --quiet --QUIET --silent --SILENT
|
||||
Quiet the terminal bell.
|
||||
-r -R .... --raw-control-chars --RAW-CONTROL-CHARS
|
||||
Output "raw" control characters.
|
||||
-s ........ --squeeze-blank-lines
|
||||
Squeeze multiple blank lines.
|
||||
-S ........ --chop-long-lines
|
||||
Chop (truncate) long lines rather than wrapping.
|
||||
-t [_t_a_g] .. --tag=[_t_a_g]
|
||||
Find a tag.
|
||||
-T [_t_a_g_s_f_i_l_e] --tag-file=[_t_a_g_s_f_i_l_e]
|
||||
Use an alternate tags file.
|
||||
-u -U .... --underline-special --UNDERLINE-SPECIAL
|
||||
Change handling of backspaces, tabs and carriage returns.
|
||||
-V ........ --version
|
||||
Display the version number of "less".
|
||||
-w ........ --hilite-unread
|
||||
Highlight first new line after forward-screen.
|
||||
-W ........ --HILITE-UNREAD
|
||||
Highlight first new line after any forward movement.
|
||||
-x [_N[,...]] --tabs=[_N[,...]]
|
||||
Set tab stops.
|
||||
-X ........ --no-init
|
||||
Don't use termcap init/deinit strings.
|
||||
-y [_N] .... --max-forw-scroll=[_N]
|
||||
Forward scroll limit.
|
||||
-z [_N] .... --window=[_N]
|
||||
Set size of window.
|
||||
-" [_c[_c]] . --quotes=[_c[_c]]
|
||||
Set shell quote characters.
|
||||
-~ ........ --tilde
|
||||
Don't display tildes after end of file.
|
||||
-# [_N] .... --shift=[_N]
|
||||
Set horizontal scroll amount (0 = one half screen width).
|
||||
--exit-follow-on-close
|
||||
Exit F command on a pipe when writer closes pipe.
|
||||
--file-size
|
||||
Automatically determine the size of the input file.
|
||||
--follow-name
|
||||
The F command changes files if the input file is renamed.
|
||||
--header=[_N[,_M]]
|
||||
Use N lines and M columns to display file headers.
|
||||
--incsearch
|
||||
Search file as each pattern character is typed in.
|
||||
--intr=_C
|
||||
Use _C instead of ^X to interrupt a read.
|
||||
--line-num-width=_N
|
||||
Set the width of the -N line number field to _N characters.
|
||||
--modelines=_N
|
||||
Read _N lines from the input file and look for vim modelines.
|
||||
--mouse
|
||||
Enable mouse input.
|
||||
--no-keypad
|
||||
Don't send termcap keypad init/deinit strings.
|
||||
--no-histdups
|
||||
Remove duplicates from command history.
|
||||
--no-number-headers
|
||||
Don't give line numbers to header lines.
|
||||
--no-search-headers
|
||||
Don't search in header lines or columns.
|
||||
--no-vbell
|
||||
Disable the terminal's visual bell.
|
||||
--redraw-on-quit
|
||||
Redraw final screen when quitting.
|
||||
--rscroll=_C
|
||||
Set the character used to mark truncated lines.
|
||||
--save-marks
|
||||
Retain marks across invocations of less.
|
||||
--search-options=[EFKNRW-]
|
||||
Set default options for every search.
|
||||
--show-preproc-errors
|
||||
Display a message if preprocessor exits with an error status.
|
||||
--proc-backspace
|
||||
Process backspaces for bold/underline.
|
||||
--SPECIAL-BACKSPACE
|
||||
Treat backspaces as control characters.
|
||||
--proc-return
|
||||
Delete carriage returns before newline.
|
||||
--SPECIAL-RETURN
|
||||
Treat carriage returns as control characters.
|
||||
--proc-tab
|
||||
Expand tabs to spaces.
|
||||
--SPECIAL-TAB
|
||||
Treat tabs as control characters.
|
||||
--status-col-width=_N
|
||||
Set the width of the -J status column to _N characters.
|
||||
--status-line
|
||||
Highlight or color the entire line containing a mark.
|
||||
--use-backslash
|
||||
Subsequent options use backslash as escape char.
|
||||
--use-color
|
||||
Enables colored text.
|
||||
--wheel-lines=_N
|
||||
Each click of the mouse wheel moves _N lines.
|
||||
--wordwrap
|
||||
Wrap lines at spaces.
|
||||
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
LLIINNEE EEDDIITTIINNGG
|
||||
|
||||
These keys can be used to edit text being entered
|
||||
on the "command line" at the bottom of the screen.
|
||||
|
||||
RightArrow ..................... ESC-l ... Move cursor right one character.
|
||||
LeftArrow ...................... ESC-h ... Move cursor left one character.
|
||||
ctrl-RightArrow ESC-RightArrow ESC-w ... Move cursor right one word.
|
||||
ctrl-LeftArrow ESC-LeftArrow ESC-b ... Move cursor left one word.
|
||||
HOME ........................... ESC-0 ... Move cursor to start of line.
|
||||
END ............................ ESC-$ ... Move cursor to end of line.
|
||||
BACKSPACE ................................ Delete char to left of cursor.
|
||||
DELETE ......................... ESC-x ... Delete char under cursor.
|
||||
ctrl-BACKSPACE ESC-BACKSPACE ........... Delete word to left of cursor.
|
||||
ctrl-DELETE .... ESC-DELETE .... ESC-X ... Delete word under cursor.
|
||||
ctrl-U ......... ESC (MS-DOS only) ....... Delete entire line.
|
||||
UpArrow ........................ ESC-k ... Retrieve previous command line.
|
||||
DownArrow ...................... ESC-j ... Retrieve next command line.
|
||||
TAB ...................................... Complete filename & cycle.
|
||||
SHIFT-TAB ...................... ESC-TAB Complete filename & reverse cycle.
|
||||
ctrl-L ................................... Complete filename, list all.
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
JobsModule,
|
||||
EventBusModule,
|
||||
NiucloudModule,
|
||||
SysModule,
|
||||
} from './common';
|
||||
import {
|
||||
TracingModule,
|
||||
@@ -53,6 +54,16 @@ import { OutboxKafkaForwarderModule } from './core/event/outboxKafkaForwarder.mo
|
||||
// 新增:Site和Pay模块
|
||||
import { SiteModule } from './common/site/site.module';
|
||||
import { PayModule } from './common/pay/pay.module';
|
||||
// 新增:其他业务模块
|
||||
import { WechatModule } from './common/wechat/wechat.module';
|
||||
import { WeappModule } from './common/weapp/weapp.module';
|
||||
import { AddonModule } from './common/addon/addon.module';
|
||||
import { DiyModule } from './common/diy/diy.module';
|
||||
import { StatModule } from './common/stat/stat.module';
|
||||
import { NoticeModule } from './common/notice/notice.module';
|
||||
import { ChannelModule } from './common/channel/channel.module';
|
||||
import { HomeModule } from './common/home/home.module';
|
||||
import { LoginModule } from './common/login/login.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -103,6 +114,28 @@ import { PayModule } from './common/pay/pay.module';
|
||||
}),
|
||||
// 认证模块
|
||||
JwtGlobalModule,
|
||||
AuthModule,
|
||||
// 其他业务模块
|
||||
SiteModule,
|
||||
PayModule,
|
||||
SysModule,
|
||||
MemberModule,
|
||||
AdminModule,
|
||||
RbacModule,
|
||||
UserModule,
|
||||
JobsModule,
|
||||
EventBusModule,
|
||||
NiucloudModule,
|
||||
// 新增业务模块
|
||||
WechatModule,
|
||||
WeappModule,
|
||||
AddonModule,
|
||||
DiyModule,
|
||||
StatModule,
|
||||
NoticeModule,
|
||||
ChannelModule,
|
||||
HomeModule,
|
||||
LoginModule,
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
@@ -11,10 +11,12 @@ import {
|
||||
} from '@nestjs/common';
|
||||
import { JwtAuthGuard } from '../../../auth/guards/JwtAuthGuard';
|
||||
import { RolesGuard } from '../../../auth/guards/RolesGuard';
|
||||
import { Roles } from '../../../auth/decorators/RolesDecorator';
|
||||
import { AddonDevelopService } from '../../services/admin/AddonDevelopService';
|
||||
|
||||
@Controller('adminapi/addon/develop')
|
||||
@UseGuards(JwtAuthGuard, RolesGuard)
|
||||
@Roles('admin')
|
||||
export class AddonDevelopController {
|
||||
constructor(private readonly addonDevelopService: AddonDevelopService) {}
|
||||
|
||||
|
||||
@@ -11,10 +11,12 @@ import {
|
||||
} from '@nestjs/common';
|
||||
import { JwtAuthGuard } from '../../../auth/guards/JwtAuthGuard';
|
||||
import { RolesGuard } from '../../../auth/guards/RolesGuard';
|
||||
import { Roles } from '../../../auth/decorators/RolesDecorator';
|
||||
import { AddonAppService } from '../../services/admin/AddonAppService';
|
||||
|
||||
@Controller('adminapi/addon/app')
|
||||
@UseGuards(JwtAuthGuard, RolesGuard)
|
||||
@Roles('admin')
|
||||
export class AppController {
|
||||
constructor(private readonly addonAppService: AddonAppService) {}
|
||||
|
||||
|
||||
@@ -10,10 +10,12 @@ import {
|
||||
} from '@nestjs/common';
|
||||
import { JwtAuthGuard } from '../../../auth/guards/JwtAuthGuard';
|
||||
import { RolesGuard } from '../../../auth/guards/RolesGuard';
|
||||
import { Roles } from '../../../auth/decorators/RolesDecorator';
|
||||
import { BackupService } from '../../services/admin/BackupService';
|
||||
|
||||
@Controller('adminapi/addon/backup')
|
||||
@UseGuards(JwtAuthGuard, RolesGuard)
|
||||
@Roles('admin')
|
||||
export class BackupController {
|
||||
constructor(private readonly backupService: BackupService) {}
|
||||
|
||||
|
||||
@@ -18,7 +18,14 @@ import { AddonService } from '../../services/admin/AddonService';
|
||||
export class UpgradeController {
|
||||
constructor(private readonly addonService: AddonService) {}
|
||||
|
||||
@Post('upgrade/:addon?')
|
||||
@Post('upgrade')
|
||||
async upgradeNoAddon(
|
||||
@Body() dto: { is_need_backup?: boolean; is_need_cloudbuild?: boolean },
|
||||
) {
|
||||
return this.addonService.upgrade('', dto);
|
||||
}
|
||||
|
||||
@Post('upgrade/:addon')
|
||||
async upgrade(
|
||||
@Param('addon') addon: string,
|
||||
@Body() dto: { is_need_backup?: boolean; is_need_cloudbuild?: boolean },
|
||||
@@ -31,7 +38,12 @@ export class UpgradeController {
|
||||
return this.addonService.executeUpgrade();
|
||||
}
|
||||
|
||||
@Get('upgrade-content/:addon?')
|
||||
@Get('upgrade-content')
|
||||
async getUpgradeContentNoAddon() {
|
||||
return this.addonService.getUpgradeContent('');
|
||||
}
|
||||
|
||||
@Get('upgrade-content/:addon')
|
||||
async getUpgradeContent(@Param('addon') addon: string) {
|
||||
return this.addonService.getUpgradeContent(addon);
|
||||
}
|
||||
@@ -41,7 +53,12 @@ export class UpgradeController {
|
||||
return this.addonService.getUpgradeTask();
|
||||
}
|
||||
|
||||
@Get('upgrade-pre-check/:addon?')
|
||||
@Get('upgrade-pre-check')
|
||||
async upgradePreCheckNoAddon() {
|
||||
return this.addonService.upgradePreCheck('');
|
||||
}
|
||||
|
||||
@Get('upgrade-pre-check/:addon')
|
||||
async upgradePreCheck(@Param('addon') addon: string) {
|
||||
return this.addonService.upgradePreCheck(addon);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
UseGuards,
|
||||
UsePipes,
|
||||
ValidationPipe,
|
||||
UnauthorizedException,
|
||||
} from '@nestjs/common';
|
||||
import {
|
||||
ApiTags,
|
||||
@@ -52,8 +53,11 @@ export class AdminController {
|
||||
@ApiResponse({ status: 200, description: '获取管理员列表成功' })
|
||||
async getAdminList(
|
||||
@Query() query: QueryAdminDto,
|
||||
@Query('site_id') site_id: number = 0,
|
||||
@Query('site_id') site_id: number,
|
||||
) {
|
||||
if (!site_id) {
|
||||
throw new UnauthorizedException('site_id is required');
|
||||
}
|
||||
return await this.adminService.getAdminList(query, site_id);
|
||||
}
|
||||
|
||||
@@ -63,8 +67,11 @@ export class AdminController {
|
||||
@ApiResponse({ status: 200, description: '获取管理员详情成功' })
|
||||
async getAdminDetail(
|
||||
@Param('id') id: number,
|
||||
@Query('site_id') site_id: number = 0,
|
||||
@Query('site_id') site_id: number,
|
||||
) {
|
||||
if (!site_id) {
|
||||
throw new UnauthorizedException('site_id is required');
|
||||
}
|
||||
return await this.adminService.getAdminDetail(id, site_id);
|
||||
}
|
||||
|
||||
@@ -75,8 +82,11 @@ export class AdminController {
|
||||
async updateAdmin(
|
||||
@Param('id') id: number,
|
||||
@Body() updateAdminDto: UpdateAdminDto,
|
||||
@Query('site_id') site_id: number = 0,
|
||||
@Query('site_id') site_id: number,
|
||||
) {
|
||||
if (!site_id) {
|
||||
throw new UnauthorizedException('site_id is required');
|
||||
}
|
||||
return await this.adminService.updateAdmin(id, updateAdminDto, site_id);
|
||||
}
|
||||
|
||||
@@ -86,8 +96,11 @@ export class AdminController {
|
||||
@ApiResponse({ status: 200, description: '管理员删除成功' })
|
||||
async deleteAdmin(
|
||||
@Param('id') id: number,
|
||||
@Query('site_id') site_id: number = 0,
|
||||
@Query('site_id') site_id: number,
|
||||
) {
|
||||
if (!site_id) {
|
||||
throw new UnauthorizedException('site_id is required');
|
||||
}
|
||||
await this.adminService.deleteAdmin(id, site_id);
|
||||
return { message: '删除成功' };
|
||||
}
|
||||
@@ -98,8 +111,11 @@ export class AdminController {
|
||||
@ApiResponse({ status: 200, description: '批量删除成功' })
|
||||
async batchDeleteAdmins(
|
||||
@Body() data: { uids: number[] },
|
||||
@Query('site_id') site_id: number = 0,
|
||||
@Query('site_id') site_id: number,
|
||||
) {
|
||||
if (!site_id) {
|
||||
throw new UnauthorizedException('site_id is required');
|
||||
}
|
||||
await this.adminService.batchDeleteAdmins(data.uids, site_id);
|
||||
return { message: '批量删除成功' };
|
||||
}
|
||||
@@ -110,8 +126,11 @@ export class AdminController {
|
||||
@ApiResponse({ status: 200, description: '批量更新状态成功' })
|
||||
async batchUpdateAdminStatus(
|
||||
@Body() data: BatchUpdateAdminStatusDto,
|
||||
@Query('site_id') site_id: number = 0,
|
||||
@Query('site_id') site_id: number,
|
||||
) {
|
||||
if (!site_id) {
|
||||
throw new UnauthorizedException('site_id is required');
|
||||
}
|
||||
await this.adminService.batchUpdateAdminStatus(
|
||||
data.uids,
|
||||
data.status,
|
||||
@@ -126,8 +145,11 @@ export class AdminController {
|
||||
@ApiResponse({ status: 200, description: '批量分配角色成功' })
|
||||
async batchAssignAdminRoles(
|
||||
@Body() data: BatchAssignRoleDto,
|
||||
@Query('site_id') site_id: number = 0,
|
||||
@Query('site_id') site_id: number,
|
||||
) {
|
||||
if (!site_id) {
|
||||
throw new UnauthorizedException('site_id is required');
|
||||
}
|
||||
await this.adminService.batchAssignAdminRoles(
|
||||
data.uids,
|
||||
data.role_ids,
|
||||
@@ -143,8 +165,11 @@ export class AdminController {
|
||||
async resetAdminPassword(
|
||||
@Param('id') id: number,
|
||||
@Body() resetPasswordDto: ResetAdminPasswordDto,
|
||||
@Query('site_id') site_id: number = 0,
|
||||
@Query('site_id') site_id: number,
|
||||
) {
|
||||
if (!site_id) {
|
||||
throw new UnauthorizedException('site_id is required');
|
||||
}
|
||||
await this.adminService.resetAdminPassword(id, resetPasswordDto, site_id);
|
||||
return { message: '密码重置成功' };
|
||||
}
|
||||
@@ -156,8 +181,11 @@ export class AdminController {
|
||||
async updateAdminStatus(
|
||||
@Param('id') id: number,
|
||||
@Body() data: { status: number },
|
||||
@Query('site_id') site_id: number = 0,
|
||||
@Query('site_id') site_id: number,
|
||||
) {
|
||||
if (!site_id) {
|
||||
throw new UnauthorizedException('site_id is required');
|
||||
}
|
||||
await this.adminService.updateAdminStatus(id, data.status, site_id);
|
||||
return { message: '状态更新成功' };
|
||||
}
|
||||
@@ -169,8 +197,11 @@ export class AdminController {
|
||||
async assignAdminRoles(
|
||||
@Param('id') id: number,
|
||||
@Body() data: { role_ids: string },
|
||||
@Query('site_id') site_id: number = 0,
|
||||
@Query('site_id') site_id: number,
|
||||
) {
|
||||
if (!site_id) {
|
||||
throw new UnauthorizedException('site_id is required');
|
||||
}
|
||||
await this.adminService.assignAdminRoles(id, data.role_ids, site_id);
|
||||
return { message: '角色分配成功' };
|
||||
}
|
||||
@@ -179,7 +210,10 @@ export class AdminController {
|
||||
@Roles('admin')
|
||||
@ApiOperation({ summary: '导出管理员列表' })
|
||||
@ApiResponse({ status: 200, description: '导出成功' })
|
||||
async exportAdmins(@Query('site_id') site_id: number = 0) {
|
||||
async exportAdmins(@Query('site_id') site_id: number) {
|
||||
if (!site_id) {
|
||||
throw new UnauthorizedException('site_id is required');
|
||||
}
|
||||
return await this.adminService.exportAdmins(site_id);
|
||||
}
|
||||
|
||||
@@ -187,7 +221,10 @@ export class AdminController {
|
||||
@Roles('admin')
|
||||
@ApiOperation({ summary: '获取管理员统计信息' })
|
||||
@ApiResponse({ status: 200, description: '获取统计信息成功' })
|
||||
async getAdminStats(@Query('site_id') site_id: number = 0) {
|
||||
async getAdminStats(@Query('site_id') site_id: number) {
|
||||
if (!site_id) {
|
||||
throw new UnauthorizedException('site_id is required');
|
||||
}
|
||||
return await this.adminService.getAdminStats(site_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm';
|
||||
import { BaseEntity } from '../../../core/base/BaseEntity';
|
||||
import { Entity, PrimaryGeneratedColumn, Column, OneToMany, CreateDateColumn, UpdateDateColumn, DeleteDateColumn } from 'typeorm';
|
||||
import { SysUserRole } from './SysUserRole';
|
||||
import { SysUserLog } from './SysUserLog';
|
||||
|
||||
@Entity('sys_user')
|
||||
export class SysUser extends BaseEntity {
|
||||
export class SysUser {
|
||||
@PrimaryGeneratedColumn({ name: 'uid' })
|
||||
uid: number;
|
||||
|
||||
@@ -32,6 +31,18 @@ export class SysUser extends BaseEntity {
|
||||
@Column({ name: 'status', type: 'tinyint', default: 1 })
|
||||
status: number;
|
||||
|
||||
@Column({ name: 'is_del', type: 'tinyint', default: 0 })
|
||||
is_del: number;
|
||||
|
||||
@CreateDateColumn({ name: 'create_time', type: 'int', default: 0 })
|
||||
create_time: number;
|
||||
|
||||
@UpdateDateColumn({ name: 'update_time', type: 'int', default: 0 })
|
||||
update_time: number;
|
||||
|
||||
@DeleteDateColumn({ name: 'delete_time', type: 'int', default: 0 })
|
||||
delete_time: number;
|
||||
|
||||
// 关联关系
|
||||
@OneToMany(() => SysUserRole, (userRole) => userRole.user)
|
||||
user_role: SysUserRole[];
|
||||
|
||||
@@ -23,19 +23,16 @@ export class SysUserRole {
|
||||
@Column({ name: 'role_ids', type: 'varchar', length: 255, default: '' })
|
||||
role_ids: string;
|
||||
|
||||
@CreateDateColumn({ name: 'create_time', type: 'int' })
|
||||
@CreateDateColumn({ name: 'create_time', type: 'int', default: 0, comment: '添加时间' })
|
||||
create_time: number;
|
||||
|
||||
@UpdateDateColumn({ name: 'update_time', type: 'int' })
|
||||
update_time: number;
|
||||
|
||||
@Column({ name: 'is_admin', type: 'int', default: 0 })
|
||||
@Column({ name: 'is_admin', type: 'int', default: 0, comment: '是否是超级管理员' })
|
||||
is_admin: number;
|
||||
|
||||
@Column({ name: 'status', type: 'int', default: 1 })
|
||||
@Column({ name: 'status', type: 'int', default: 1, comment: '状态' })
|
||||
status: number;
|
||||
|
||||
@Column({ name: 'delete_time', type: 'int', default: 0 })
|
||||
@Column({ name: 'delete_time', type: 'int', default: 0, comment: '删除时间' })
|
||||
delete_time: number;
|
||||
|
||||
// 关联关系
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Module, forwardRef, Global } from '@nestjs/common';
|
||||
import { PassportModule } from '@nestjs/passport';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { AuthToken } from './entities/AuthToken';
|
||||
import { SysConfig } from '../settings/entities/sys-config.entity';
|
||||
import { SysUser } from '../admin/entities/SysUser';
|
||||
import { AuthService } from './services/AuthService';
|
||||
import { AuthController } from './controllers/AuthController';
|
||||
import { LoginApiController } from './controllers/api/LoginApiController';
|
||||
@@ -21,6 +22,7 @@ import { CoreLoginConfigService } from './services/core/CoreLoginConfigService';
|
||||
import { JwtAuthGuard } from './guards/JwtAuthGuard';
|
||||
import { RolesGuard } from './guards/RolesGuard';
|
||||
import { JwtGlobalModule } from './jwt.module';
|
||||
import { RedisProvider } from '../../vendor/redis/redis.provider';
|
||||
|
||||
// 导入Admin和Member模块
|
||||
import { AdminModule } from '../admin/admin.module';
|
||||
@@ -30,45 +32,46 @@ import { MemberModule } from '../member/member.module';
|
||||
@Module({
|
||||
imports: [
|
||||
PassportModule,
|
||||
TypeOrmModule.forFeature([AuthToken]),
|
||||
TypeOrmModule.forFeature([AuthToken, SysConfig, SysUser]),
|
||||
JwtGlobalModule,
|
||||
// 导入Admin和Member模块以使用其服务
|
||||
forwardRef(() => AdminModule),
|
||||
forwardRef(() => MemberModule),
|
||||
],
|
||||
providers: [
|
||||
AuthService,
|
||||
LoginApiService,
|
||||
AuthService,
|
||||
LoginApiService,
|
||||
LoginConfigApiService,
|
||||
RegisterApiService,
|
||||
CaptchaService,
|
||||
CaptchaService,
|
||||
LoginConfigService,
|
||||
CoreAuthService,
|
||||
CoreCaptchaService,
|
||||
CoreAuthService,
|
||||
CoreCaptchaService,
|
||||
CoreLoginConfigService,
|
||||
JwtAuthGuard,
|
||||
RolesGuard
|
||||
RedisProvider,
|
||||
JwtAuthGuard,
|
||||
RolesGuard,
|
||||
],
|
||||
controllers: [
|
||||
AuthController,
|
||||
LoginApiController,
|
||||
AuthController,
|
||||
LoginApiController,
|
||||
LoginConfigApiController,
|
||||
RegisterApiController,
|
||||
CaptchaController,
|
||||
LoginConfigController
|
||||
CaptchaController,
|
||||
LoginConfigController,
|
||||
],
|
||||
exports: [
|
||||
AuthService,
|
||||
LoginApiService,
|
||||
AuthService,
|
||||
LoginApiService,
|
||||
LoginConfigApiService,
|
||||
RegisterApiService,
|
||||
CaptchaService,
|
||||
CaptchaService,
|
||||
LoginConfigService,
|
||||
CoreAuthService,
|
||||
CoreCaptchaService,
|
||||
CoreAuthService,
|
||||
CoreCaptchaService,
|
||||
CoreLoginConfigService,
|
||||
JwtAuthGuard,
|
||||
RolesGuard
|
||||
JwtAuthGuard,
|
||||
RolesGuard,
|
||||
],
|
||||
})
|
||||
export class AuthModule {}
|
||||
|
||||
@@ -15,23 +15,20 @@ export class CaptchaController {
|
||||
@ApiOperation({ summary: '创建验证码' })
|
||||
@ApiResponse({ status: 200, description: '创建成功' })
|
||||
async create(@Query() query: CaptchaCreateDto) {
|
||||
const data = await this.captchaService.create(query);
|
||||
return { code: 200, message: '创建成功', data };
|
||||
return await this.captchaService.create(query);
|
||||
}
|
||||
|
||||
@Post('check')
|
||||
@ApiOperation({ summary: '一次校验验证码' })
|
||||
@ApiResponse({ status: 200, description: '校验成功' })
|
||||
async check(@Body() body: CaptchaCheckDto) {
|
||||
const data = await this.captchaService.check(body);
|
||||
return { code: 200, message: '校验成功', data };
|
||||
return await this.captchaService.check(body);
|
||||
}
|
||||
|
||||
@Post('verification')
|
||||
@ApiOperation({ summary: '二次校验验证码' })
|
||||
@ApiResponse({ status: 200, description: '校验成功' })
|
||||
async verification(@Body() body: CaptchaVerificationDto) {
|
||||
const data = await this.captchaService.verification(body);
|
||||
return { code: 200, message: '校验成功', data };
|
||||
return await this.captchaService.verification(body);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Controller, Get, Post, Body, UseGuards } from '@nestjs/common';
|
||||
import { Controller, Get, Post, Body, UseGuards, Request, UnauthorizedException } from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
|
||||
import { JwtAuthGuard } from '../../guards/JwtAuthGuard';
|
||||
import { RolesGuard } from '../../guards/RolesGuard';
|
||||
import { Roles } from '../../decorators/RolesDecorator';
|
||||
import { LoginConfigService } from '../../services/admin/LoginConfigService';
|
||||
import { LoginConfigDto } from '../../dto/admin/LoginConfigDto';
|
||||
import { LoginConfig } from '../../services/core/CoreLoginConfigService';
|
||||
|
||||
@ApiTags('登录配置管理')
|
||||
@Controller('adminapi/auth/login-config')
|
||||
@@ -16,16 +17,22 @@ export class LoginConfigController {
|
||||
@Get('config')
|
||||
@ApiOperation({ summary: '获取登录设置' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getConfig() {
|
||||
const data = await this.loginConfigService.getConfig();
|
||||
return { code: 200, message: '获取成功', data };
|
||||
async getConfig(@Request() req: any): Promise<LoginConfig> {
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.loginConfigService.getConfig(siteId);
|
||||
}
|
||||
|
||||
@Post('config')
|
||||
@ApiOperation({ summary: '设置登录配置' })
|
||||
@ApiResponse({ status: 200, description: '设置成功' })
|
||||
async setConfig(@Body() body: LoginConfigDto) {
|
||||
const data = await this.loginConfigService.setConfig(body);
|
||||
return { code: 200, message: '设置成功', data };
|
||||
async setConfig(@Request() req: any, @Body() body: LoginConfigDto) {
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.loginConfigService.setConfig(body, siteId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
} from '@nestjs/common';
|
||||
import { Public } from '../../../auth/decorators/public.decorator';
|
||||
import { LoginConfigApiService } from '../../services/api/LoginConfigApiService';
|
||||
import { LoginConfig } from '../../services/core/CoreLoginConfigService';
|
||||
|
||||
@Controller('api/login/config')
|
||||
export class LoginConfigApiController {
|
||||
@@ -18,7 +19,7 @@ export class LoginConfigApiController {
|
||||
*/
|
||||
@Get('info')
|
||||
@Public()
|
||||
async getInfo(@Query() query: any) {
|
||||
async getInfo(@Query() query: any): Promise<LoginConfig> {
|
||||
return this.loginConfigApiService.getInfo(query);
|
||||
}
|
||||
|
||||
|
||||
@@ -48,4 +48,26 @@ export class LoginConfigDto {
|
||||
lockoutDuration?: number;
|
||||
lockoutType?: string;
|
||||
};
|
||||
|
||||
// PHP 特有字段
|
||||
@ApiProperty({ description: '是否启用授权注册', required: false })
|
||||
@IsOptional()
|
||||
isAuthRegister?: boolean;
|
||||
|
||||
@ApiProperty({ description: '是否强制获取用户信息', required: false })
|
||||
@IsOptional()
|
||||
isForceAccessUserInfo?: boolean;
|
||||
|
||||
@ApiProperty({ description: '是否绑定手机号', required: false })
|
||||
@IsOptional()
|
||||
isBindMobile?: boolean;
|
||||
|
||||
@ApiProperty({ description: '是否显示协议', required: false })
|
||||
@IsOptional()
|
||||
agreementShow?: boolean;
|
||||
|
||||
@ApiProperty({ description: '描述信息', required: false })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
desc?: string;
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { CoreLoginConfigService } from '../core/CoreLoginConfigService';
|
||||
import { CoreLoginConfigService, LoginConfig } from '../core/CoreLoginConfigService';
|
||||
import { LoginConfigDto } from '../../dto/admin/LoginConfigDto';
|
||||
|
||||
@Injectable()
|
||||
export class LoginConfigService {
|
||||
constructor(private readonly coreLoginConfig: CoreLoginConfigService) {}
|
||||
|
||||
async getConfig() {
|
||||
return await this.coreLoginConfig.getConfig();
|
||||
async getConfig(siteId: number): Promise<LoginConfig> {
|
||||
return await this.coreLoginConfig.getConfig(siteId);
|
||||
}
|
||||
|
||||
async setConfig(dto: LoginConfigDto) {
|
||||
return await this.coreLoginConfig.setConfig(dto);
|
||||
async setConfig(dto: LoginConfigDto, siteId: number) {
|
||||
return await this.coreLoginConfig.setConfig(dto, siteId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
||||
import { CoreLoginConfigService } from '../core/CoreLoginConfigService';
|
||||
|
||||
@Injectable()
|
||||
@@ -9,35 +9,55 @@ export class LoginConfigApiService {
|
||||
* 获取登录配置
|
||||
*/
|
||||
async getInfo(query: any) {
|
||||
return this.coreLoginConfigService.getInfo(query);
|
||||
const siteId = query.site_id;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('Missing site_id');
|
||||
}
|
||||
return this.coreLoginConfigService.getInfo(siteId, query);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取登录方式
|
||||
*/
|
||||
async getMethods(query: any) {
|
||||
return this.coreLoginConfigService.getMethods(query);
|
||||
const siteId = query.site_id;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('Missing site_id');
|
||||
}
|
||||
return this.coreLoginConfigService.getMethods(siteId, query);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取验证码配置
|
||||
*/
|
||||
async getCaptchaConfig(query: any) {
|
||||
return this.coreLoginConfigService.getCaptchaConfig(query);
|
||||
const siteId = query.site_id;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('Missing site_id');
|
||||
}
|
||||
return this.coreLoginConfigService.getCaptchaConfig(siteId, query);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取第三方登录配置
|
||||
*/
|
||||
async getThirdPartyConfig(query: any) {
|
||||
return this.coreLoginConfigService.getThirdPartyConfig(query);
|
||||
const siteId = query.site_id;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('Missing site_id');
|
||||
}
|
||||
return this.coreLoginConfigService.getThirdPartyConfig(siteId, query);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取注册配置
|
||||
*/
|
||||
async getRegisterConfig(query: any) {
|
||||
return this.coreLoginConfigService.getRegisterConfig(query);
|
||||
const siteId = query.site_id;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('Missing site_id');
|
||||
}
|
||||
return this.coreLoginConfigService.getRegisterConfig(siteId, query);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,26 +1,23 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { BaseService } from '@wwjCore/base/BaseService';
|
||||
import { SysUser } from '../../../admin/entities/SysUser';
|
||||
import * as bcrypt from 'bcrypt';
|
||||
import * as crypto from 'crypto';
|
||||
|
||||
@Injectable()
|
||||
export class CoreAuthService extends BaseService<SysUser> {
|
||||
export class CoreAuthService {
|
||||
constructor(
|
||||
@InjectRepository(SysUser)
|
||||
private userRepository: Repository<SysUser>,
|
||||
) {
|
||||
super(userRepository);
|
||||
}
|
||||
private readonly userRepository: Repository<SysUser>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 验证用户凭据
|
||||
*/
|
||||
async validateUser(username: string, password: string, site_id: number) {
|
||||
const user = await this.userRepository.findOne({
|
||||
where: { username, site_id, status: 1 },
|
||||
where: { username, status: 1 },
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
@@ -49,7 +46,7 @@ export class CoreAuthService extends BaseService<SysUser> {
|
||||
*/
|
||||
async checkUserExists(username: string, site_id: number) {
|
||||
const user = await this.userRepository.findOne({
|
||||
where: { username, site_id },
|
||||
where: { username },
|
||||
});
|
||||
return !!user;
|
||||
}
|
||||
@@ -60,15 +57,20 @@ export class CoreAuthService extends BaseService<SysUser> {
|
||||
async createUser(userData: any) {
|
||||
const hashedPassword = await bcrypt.hash(userData.password, 10);
|
||||
|
||||
const user = this.userRepository.create({
|
||||
const userDataWithHash = {
|
||||
...userData,
|
||||
password: hashedPassword,
|
||||
status: 1,
|
||||
create_time: Math.floor(Date.now() / 1000),
|
||||
});
|
||||
};
|
||||
|
||||
const saved = await this.userRepository.save(user);
|
||||
return Array.isArray(saved) ? saved[0] : saved;
|
||||
const user = this.userRepository.create({
|
||||
...userDataWithHash,
|
||||
create_time: Math.floor(Date.now() / 1000),
|
||||
update_time: Math.floor(Date.now() / 1000),
|
||||
is_del: 0,
|
||||
delete_time: 0,
|
||||
} as any);
|
||||
return await this.userRepository.save(user as any);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,17 +1,26 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { RedisProvider } from '../../../../vendor/redis/redis.provider';
|
||||
import { CaptchaCreateDto, CaptchaCheckDto, CaptchaVerificationDto } from '../../dto/admin/CaptchaDto';
|
||||
|
||||
@Injectable()
|
||||
export class CoreCaptchaService {
|
||||
constructor() {}
|
||||
private readonly CAPTCHA_PREFIX = 'captcha:';
|
||||
private readonly CAPTCHA_TTL_SECONDS = 300; // 5 min
|
||||
|
||||
constructor(private readonly redisProvider: RedisProvider) {}
|
||||
|
||||
async create(dto: CaptchaCreateDto) {
|
||||
// 对齐 PHP: CaptchaService->create()
|
||||
const captchaId = this.generateCaptchaId();
|
||||
const captchaValue = this.generateCaptchaValue(dto.length || 4);
|
||||
|
||||
// TODO: 生成验证码图片并存储
|
||||
// const captchaImage = await this.generateCaptchaImage(captchaValue, dto);
|
||||
// 持久化到 Redis
|
||||
const client = this.redisProvider.getClient();
|
||||
await client.setex(
|
||||
`${this.CAPTCHA_PREFIX}${captchaId}`,
|
||||
this.CAPTCHA_TTL_SECONDS,
|
||||
captchaValue,
|
||||
);
|
||||
|
||||
return {
|
||||
captchaId,
|
||||
@@ -23,7 +32,6 @@ export class CoreCaptchaService {
|
||||
|
||||
async check(dto: CaptchaCheckDto) {
|
||||
// 对齐 PHP: CaptchaService->check()
|
||||
// TODO: 从缓存或数据库验证验证码
|
||||
const isValid = await this.validateCaptcha(dto.captchaId, dto.captchaValue);
|
||||
|
||||
if (!isValid) {
|
||||
@@ -35,7 +43,6 @@ export class CoreCaptchaService {
|
||||
|
||||
async verification(dto: CaptchaVerificationDto) {
|
||||
// 对齐 PHP: CaptchaService->verification()
|
||||
// TODO: 二次验证逻辑,可能包括短信验证、邮箱验证等
|
||||
const isValid = await this.validateCaptcha(dto.captchaId, dto.captchaValue);
|
||||
|
||||
if (!isValid) {
|
||||
@@ -71,14 +78,20 @@ export class CoreCaptchaService {
|
||||
}
|
||||
|
||||
private async validateCaptcha(captchaId: string, captchaValue: string): Promise<boolean> {
|
||||
// TODO: 从Redis或数据库验证验证码
|
||||
// 临时实现
|
||||
return captchaValue.length >= 4;
|
||||
const client = this.redisProvider.getClient();
|
||||
const key = `${this.CAPTCHA_PREFIX}${captchaId}`;
|
||||
const stored = await client.get(key);
|
||||
if (!stored) return false;
|
||||
const ok = stored.toLowerCase() === (captchaValue || '').toLowerCase();
|
||||
if (ok) {
|
||||
// 一次性验证码:校验成功后删除
|
||||
await client.del(key);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
private async performSecondVerification(params?: Record<string, any>): Promise<boolean> {
|
||||
// TODO: 实现二次验证逻辑
|
||||
// 可能包括短信验证、邮箱验证、人脸识别等
|
||||
// 可以在此扩展短信/邮箱等二次校验
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,99 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { SysConfig } from '../../../settings/entities/sys-config.entity';
|
||||
import { BaseService } from '../../../../core/base/BaseService';
|
||||
import { LoginConfigDto } from '../../dto/admin/LoginConfigDto';
|
||||
|
||||
@Injectable()
|
||||
export class CoreLoginConfigService {
|
||||
constructor() {}
|
||||
export interface LoginConfig {
|
||||
isCaptcha: number;
|
||||
isSiteCaptcha: number;
|
||||
bg: string;
|
||||
siteBg: string;
|
||||
loginMethods: {
|
||||
username: boolean;
|
||||
email: boolean;
|
||||
mobile: boolean;
|
||||
wechat: boolean;
|
||||
qq: boolean;
|
||||
};
|
||||
passwordPolicy: {
|
||||
minLength: number;
|
||||
requireSpecialChar: boolean;
|
||||
requireNumber: boolean;
|
||||
requireUppercase: boolean;
|
||||
};
|
||||
loginLimit: {
|
||||
maxAttempts: number;
|
||||
lockoutDuration: number;
|
||||
lockoutType: string;
|
||||
};
|
||||
// PHP 特有字段
|
||||
isAuthRegister: boolean;
|
||||
isForceAccessUserInfo: boolean;
|
||||
isBindMobile: boolean;
|
||||
agreementShow: boolean;
|
||||
desc: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class CoreLoginConfigService extends BaseService<SysConfig> {
|
||||
constructor(
|
||||
@InjectRepository(SysConfig)
|
||||
configRepository: Repository<SysConfig>,
|
||||
) {
|
||||
super(configRepository);
|
||||
}
|
||||
|
||||
async getConfig(siteId: number): Promise<LoginConfig> {
|
||||
// 对齐 PHP: CoreMemberConfigService->getLoginConfig()
|
||||
const config = await this.repository.findOne({
|
||||
where: {
|
||||
config_key: 'LOGIN',
|
||||
site_id: siteId
|
||||
},
|
||||
});
|
||||
|
||||
if (config?.value) {
|
||||
let configData: any;
|
||||
try {
|
||||
configData = JSON.parse(config.value);
|
||||
} catch {
|
||||
configData = {};
|
||||
}
|
||||
|
||||
return {
|
||||
isCaptcha: 1, // 默认启用验证码
|
||||
isSiteCaptcha: 1, // 默认启用站点验证码
|
||||
bg: configData.bg_url || '', // 登录背景图
|
||||
siteBg: configData.bg_url || '', // 站点登录背景图
|
||||
loginMethods: {
|
||||
username: configData.is_username === 1,
|
||||
email: false, // PHP 中没有邮箱登录
|
||||
mobile: configData.is_mobile === 1,
|
||||
wechat: false, // 微信登录通过其他方式处理
|
||||
qq: false,
|
||||
},
|
||||
passwordPolicy: {
|
||||
minLength: 6,
|
||||
requireSpecialChar: false,
|
||||
requireNumber: false,
|
||||
requireUppercase: false,
|
||||
},
|
||||
loginLimit: {
|
||||
maxAttempts: 5,
|
||||
lockoutDuration: 30, // 分钟
|
||||
lockoutType: 'ip', // ip 或 username
|
||||
},
|
||||
// PHP 特有字段
|
||||
isAuthRegister: configData.is_auth_register === 1,
|
||||
isForceAccessUserInfo: configData.is_force_access_user_info === 1,
|
||||
isBindMobile: configData.is_bind_mobile === 1,
|
||||
agreementShow: configData.agreement_show === 1,
|
||||
desc: configData.desc || '精选好物,购物优惠的省钱平台',
|
||||
};
|
||||
}
|
||||
|
||||
async getConfig() {
|
||||
// 对齐 PHP: ConfigService->getConfig()
|
||||
return {
|
||||
isCaptcha: 1, // 默认启用验证码
|
||||
isSiteCaptcha: 1, // 默认启用站点验证码
|
||||
@@ -14,8 +101,8 @@ export class CoreLoginConfigService {
|
||||
siteBg: '', // 站点登录背景图
|
||||
loginMethods: {
|
||||
username: true,
|
||||
email: true,
|
||||
mobile: true,
|
||||
email: false,
|
||||
mobile: false,
|
||||
wechat: false,
|
||||
qq: false,
|
||||
},
|
||||
@@ -30,68 +117,81 @@ export class CoreLoginConfigService {
|
||||
lockoutDuration: 30, // 分钟
|
||||
lockoutType: 'ip', // ip 或 username
|
||||
},
|
||||
// PHP 特有字段
|
||||
isAuthRegister: true,
|
||||
isForceAccessUserInfo: false,
|
||||
isBindMobile: false,
|
||||
agreementShow: false,
|
||||
desc: '精选好物,购物优惠的省钱平台',
|
||||
};
|
||||
}
|
||||
|
||||
async setConfig(dto: LoginConfigDto) {
|
||||
// 对齐 PHP: ConfigService->setConfig()
|
||||
async setConfig(dto: LoginConfigDto, siteId: number) {
|
||||
// 对齐 PHP: CoreMemberConfigService->setLoginConfig()
|
||||
const config = {
|
||||
isCaptcha: dto.isCaptcha ?? 1,
|
||||
isSiteCaptcha: dto.isSiteCaptcha ?? 1,
|
||||
bg: dto.bg ?? '',
|
||||
siteBg: dto.siteBg ?? '',
|
||||
loginMethods: dto.loginMethods ?? {
|
||||
username: true,
|
||||
email: true,
|
||||
mobile: true,
|
||||
wechat: false,
|
||||
qq: false,
|
||||
},
|
||||
passwordPolicy: dto.passwordPolicy ?? {
|
||||
minLength: 6,
|
||||
requireSpecialChar: false,
|
||||
requireNumber: false,
|
||||
requireUppercase: false,
|
||||
},
|
||||
loginLimit: dto.loginLimit ?? {
|
||||
maxAttempts: 5,
|
||||
lockoutDuration: 30,
|
||||
lockoutType: 'ip',
|
||||
},
|
||||
is_username: dto.loginMethods?.username ? 1 : 0,
|
||||
is_mobile: dto.loginMethods?.mobile ? 1 : 0,
|
||||
is_auth_register: dto.isAuthRegister ? 1 : 0,
|
||||
is_force_access_user_info: dto.isForceAccessUserInfo ? 1 : 0,
|
||||
is_bind_mobile: dto.isBindMobile ? 1 : 0,
|
||||
agreement_show: dto.agreementShow ? 1 : 0,
|
||||
bg_url: dto.bg || '',
|
||||
desc: dto.desc || '精选好物,购物优惠的省钱平台',
|
||||
};
|
||||
|
||||
// TODO: 保存配置到数据库或配置文件
|
||||
// await this.saveConfig(config);
|
||||
const existed = await this.repository.findOne({
|
||||
where: {
|
||||
config_key: 'LOGIN',
|
||||
site_id: siteId
|
||||
},
|
||||
});
|
||||
|
||||
if (existed) {
|
||||
await this.update(existed.id, {
|
||||
value: JSON.stringify(config),
|
||||
});
|
||||
} else {
|
||||
await this.create({
|
||||
site_id: siteId,
|
||||
config_key: 'LOGIN',
|
||||
value: JSON.stringify(config),
|
||||
});
|
||||
}
|
||||
|
||||
return { success: true, message: '配置保存成功' };
|
||||
}
|
||||
|
||||
// 兼容 API 层调用的方法(按 PHP 语义拆分)
|
||||
async getInfo(query?: any) {
|
||||
return this.getConfig();
|
||||
async getInfo(siteId: number, _query?: any): Promise<LoginConfig> {
|
||||
return this.getConfig(siteId);
|
||||
}
|
||||
|
||||
async getMethods(query?: any) {
|
||||
const config = await this.getConfig();
|
||||
async getMethods(siteId: number, _query?: any) {
|
||||
const config = await this.getConfig(siteId);
|
||||
return config.loginMethods;
|
||||
}
|
||||
|
||||
async getCaptchaConfig(query?: any) {
|
||||
const config = await this.getConfig();
|
||||
async getCaptchaConfig(siteId: number, _query?: any) {
|
||||
const config = await this.getConfig(siteId);
|
||||
return { isCaptcha: config.isCaptcha, isSiteCaptcha: config.isSiteCaptcha };
|
||||
}
|
||||
|
||||
async getThirdPartyConfig(query?: any) {
|
||||
const config = await this.getConfig();
|
||||
async getThirdPartyConfig(siteId: number, _query?: any) {
|
||||
const config = await this.getConfig(siteId);
|
||||
return { wechat: config.loginMethods.wechat, qq: config.loginMethods.qq };
|
||||
}
|
||||
|
||||
async getRegisterConfig(query?: any) {
|
||||
const config = await this.getConfig();
|
||||
async getRegisterConfig(siteId: number, _query?: any) {
|
||||
const config = await this.getConfig(siteId);
|
||||
return { passwordPolicy: config.passwordPolicy };
|
||||
}
|
||||
|
||||
async getForgotPasswordConfig(query?: any) {
|
||||
getForgotPasswordConfig(_query?: any) {
|
||||
return { ways: ['email', 'mobile'] };
|
||||
}
|
||||
|
||||
private buildConfigKey(siteId: number): string {
|
||||
// 兼容无 site_id 字段的实体定义,使用 key 后缀区分站点
|
||||
return `login_config:site:${siteId || 0}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +1,50 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm';
|
||||
import { BaseEntity } from '../../../core/base/BaseEntity';
|
||||
import { Entity, PrimaryGeneratedColumn, Column, OneToMany, CreateDateColumn, UpdateDateColumn } from 'typeorm';
|
||||
import { DiyFormFields } from './DiyFormFields';
|
||||
import { DiyFormRecords } from './DiyFormRecords';
|
||||
|
||||
@Entity('diy_form')
|
||||
export class DiyForm extends BaseEntity {
|
||||
export class DiyForm {
|
||||
@PrimaryGeneratedColumn({ name: 'form_id' })
|
||||
form_id: number;
|
||||
|
||||
@Column({ name: 'title', type: 'varchar', length: 255, comment: '表单标题' })
|
||||
@Column({ name: 'site_id', type: 'int', default: 0, comment: '站点id' })
|
||||
site_id: number;
|
||||
|
||||
@Column({ name: 'page_title', type: 'varchar', length: 255, default: '', comment: '表单名称(用于后台展示)' })
|
||||
page_title: string;
|
||||
|
||||
@Column({ name: 'title', type: 'varchar', length: 255, default: '', comment: '表单名称(用于前台展示)' })
|
||||
title: string;
|
||||
|
||||
@Column({ name: 'type', type: 'varchar', length: 50, comment: '表单类型' })
|
||||
@Column({ name: 'type', type: 'varchar', length: 255, default: '', comment: '表单类型' })
|
||||
type: string;
|
||||
|
||||
@Column({ name: 'value', type: 'text', nullable: true, comment: '表单配置JSON' })
|
||||
value: string;
|
||||
|
||||
@Column({ name: 'share', type: 'text', nullable: true, comment: '分享配置JSON' })
|
||||
share: string;
|
||||
|
||||
@Column({ name: 'status', type: 'tinyint', default: 1, comment: '状态:0=禁用,1=启用' })
|
||||
@Column({ name: 'status', type: 'tinyint', default: 0, comment: '状态(0,关闭,1:开启)' })
|
||||
status: number;
|
||||
|
||||
@Column({ name: 'is_default', type: 'tinyint', default: 0, comment: '是否默认:0=否,1=是' })
|
||||
is_default: number;
|
||||
@Column({ name: 'template', type: 'varchar', length: 255, default: '', comment: '模板名称' })
|
||||
template: string;
|
||||
|
||||
@Column({ name: 'addon', type: 'varchar', length: 100, default: '', comment: '插件标识' })
|
||||
@Column({ name: 'value', type: 'longtext', nullable: true, comment: '表单数据,json格式,包含展示组件' })
|
||||
value: string;
|
||||
|
||||
@Column({ name: 'addon', type: 'varchar', length: 255, default: '', comment: '所属插件标识' })
|
||||
addon: string;
|
||||
|
||||
@Column({ name: 'sort', type: 'int', default: 0, comment: '排序' })
|
||||
sort: number;
|
||||
@Column({ name: 'share', type: 'varchar', length: 1000, default: '', comment: '分享内容' })
|
||||
share: string;
|
||||
|
||||
@Column({ name: 'write_num', type: 'int', default: 0, comment: '表单填写总数量' })
|
||||
write_num: number;
|
||||
|
||||
@Column({ name: 'remark', type: 'varchar', length: 255, default: '', comment: '备注说明' })
|
||||
remark: string;
|
||||
|
||||
@CreateDateColumn({ name: 'create_time', type: 'int', default: 0, comment: '创建时间' })
|
||||
create_time: number;
|
||||
|
||||
@UpdateDateColumn({ name: 'update_time', type: 'int', default: 0, comment: '更新时间' })
|
||||
update_time: number;
|
||||
|
||||
// 关联字段
|
||||
@OneToMany(() => DiyFormFields, fields => fields.form)
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
|
||||
import { BaseEntity } from '../../../core/base/BaseEntity';
|
||||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';
|
||||
|
||||
/**
|
||||
* 自定义页面实体
|
||||
* 对应数据库表: diy_page
|
||||
*/
|
||||
@Entity('diy_page')
|
||||
export class DiyPage extends BaseEntity {
|
||||
export class DiyPage {
|
||||
@PrimaryGeneratedColumn({ name: 'id' })
|
||||
id: number;
|
||||
|
||||
@@ -43,5 +42,12 @@ export class DiyPage extends BaseEntity {
|
||||
@Column({ name: 'visit_count', type: 'int', default: 0, comment: '访问量' })
|
||||
visit_count: number;
|
||||
|
||||
// create_time 和 update_time 由 BaseEntity 提供
|
||||
@Column({ name: 'site_id', type: 'int', default: 0, comment: '站点id' })
|
||||
site_id: number;
|
||||
|
||||
@CreateDateColumn({ name: 'create_time', type: 'int', default: 0, comment: '创建时间' })
|
||||
create_time: number;
|
||||
|
||||
@UpdateDateColumn({ name: 'update_time', type: 'int', default: 0, comment: '更新时间' })
|
||||
update_time: number;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
||||
import { CoreDiyService } from '../core/CoreDiyService';
|
||||
import { DiyPage } from '../../entities/DiyPage';
|
||||
|
||||
@@ -74,7 +74,10 @@ export class DiyService {
|
||||
* 控制器所需:分页
|
||||
*/
|
||||
async getPage(query: any) {
|
||||
const { site_id = 0, key } = query || {};
|
||||
const { site_id, key } = query || {};
|
||||
if (!site_id) {
|
||||
throw new Error('缺少 site_id 参数');
|
||||
}
|
||||
const list = await this.coreDiyService.getPageListByType(Number(site_id), key);
|
||||
const page = Number(query?.page || 1);
|
||||
const limit = Number(query?.limit || list.length || 20);
|
||||
@@ -88,9 +91,12 @@ export class DiyService {
|
||||
}
|
||||
|
||||
async add(data: any) {
|
||||
if (!data.site_id) {
|
||||
throw new UnauthorizedException('Missing site_id');
|
||||
}
|
||||
// 对齐 PHP 字段映射
|
||||
const payload = {
|
||||
site_id: data.site_id || 0,
|
||||
site_id: data.site_id,
|
||||
name: data.page_name,
|
||||
type: data.page_type || data.key || 'custom',
|
||||
value: JSON.stringify(data.page_data || {}),
|
||||
@@ -118,7 +124,7 @@ export class DiyService {
|
||||
const info = await this.coreDiyService.getPageInfo(pageId);
|
||||
if (!info) return null;
|
||||
return this.coreDiyService.addPage({
|
||||
site_id: (info as any).site_id || 0,
|
||||
site_id: (info as any).site_id,
|
||||
name: newName,
|
||||
type: (info as any).type,
|
||||
value: (info as any).value,
|
||||
@@ -136,7 +142,7 @@ export class DiyService {
|
||||
const info = await this.coreDiyService.getPageInfo(pageId);
|
||||
if (!info) return false;
|
||||
// 对齐 PHP:设置为默认
|
||||
await this.coreDiyService.setDefaultPage((info as any).site_id || 0, (info as any).name, (info as any).id);
|
||||
await this.coreDiyService.setDefaultPage((info as any).site_id, (info as any).name, (info as any).id);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository, In } from 'typeorm';
|
||||
import { BaseService } from '../../../../core/base/BaseService';
|
||||
import { DiyForm } from '../../entities/DiyForm';
|
||||
import { DiyFormFields } from '../../entities/DiyFormFields';
|
||||
import { DiyFormRecords } from '../../entities/DiyFormRecords';
|
||||
@@ -14,7 +13,7 @@ import { DiyFormWriteConfig } from '../../entities/DiyFormWriteConfig';
|
||||
* 对应PHP: CoreDiyFormService
|
||||
*/
|
||||
@Injectable()
|
||||
export class CoreDiyFormService extends BaseService<DiyForm> {
|
||||
export class CoreDiyFormService {
|
||||
constructor(
|
||||
@InjectRepository(DiyForm)
|
||||
private readonly diyFormRepository: Repository<DiyForm>,
|
||||
@@ -28,9 +27,7 @@ export class CoreDiyFormService extends BaseService<DiyForm> {
|
||||
private readonly diyFormSubmitConfigRepository: Repository<DiyFormSubmitConfig>,
|
||||
@InjectRepository(DiyFormWriteConfig)
|
||||
private readonly diyFormWriteConfigRepository: Repository<DiyFormWriteConfig>,
|
||||
) {
|
||||
super(diyFormRepository);
|
||||
}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 分页查询表单列表
|
||||
@@ -160,9 +157,7 @@ export class CoreDiyFormService extends BaseService<DiyForm> {
|
||||
'value',
|
||||
'share',
|
||||
'status',
|
||||
'is_default',
|
||||
'addon',
|
||||
'sort',
|
||||
'create_time',
|
||||
'update_time',
|
||||
],
|
||||
@@ -297,13 +292,13 @@ export class CoreDiyFormService extends BaseService<DiyForm> {
|
||||
// 先取消所有默认
|
||||
await this.diyFormRepository.update(
|
||||
{ site_id: siteId },
|
||||
{ is_default: 0, update_time: Math.floor(Date.now() / 1000) },
|
||||
{ status: 0, update_time: Math.floor(Date.now() / 1000) },
|
||||
);
|
||||
|
||||
// 设置当前为默认
|
||||
const result = await this.diyFormRepository.update(
|
||||
{ form_id: formId, site_id: siteId },
|
||||
{ is_default: 1, update_time: Math.floor(Date.now() / 1000) },
|
||||
{ status: 1, update_time: Math.floor(Date.now() / 1000) },
|
||||
);
|
||||
|
||||
return (result.affected || 0) > 0;
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { BaseService } from '@wwjCore/base/BaseService';
|
||||
import { DiyPage } from '../../entities/DiyPage';
|
||||
import { DiyRoute } from '../../entities/DiyRoute';
|
||||
import { DiyTheme } from '../../entities/DiyTheme';
|
||||
|
||||
@Injectable()
|
||||
export class CoreDiyService extends BaseService<DiyPage> {
|
||||
export class CoreDiyService {
|
||||
constructor(
|
||||
@InjectRepository(DiyPage)
|
||||
private diyPageRepository: Repository<DiyPage>,
|
||||
@@ -15,9 +14,7 @@ export class CoreDiyService extends BaseService<DiyPage> {
|
||||
private diyRouteRepository: Repository<DiyRoute>,
|
||||
@InjectRepository(DiyTheme)
|
||||
private diyThemeRepository: Repository<DiyTheme>,
|
||||
) {
|
||||
super(diyPageRepository);
|
||||
}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 获取DIY页面列表
|
||||
@@ -140,6 +137,33 @@ export class CoreDiyService extends BaseService<DiyPage> {
|
||||
/**
|
||||
* 设置默认页面
|
||||
*/
|
||||
/**
|
||||
* 删除页面
|
||||
*/
|
||||
async delete(pageId: number) {
|
||||
const result = await this.diyPageRepository.delete({ id: pageId });
|
||||
return (result.affected || 0) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建页面
|
||||
*/
|
||||
async create(data: any) {
|
||||
const page = this.diyPageRepository.create(data);
|
||||
return await this.diyPageRepository.save(page);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新页面
|
||||
*/
|
||||
async update(pageId: number, data: any) {
|
||||
const result = await this.diyPageRepository.update(
|
||||
{ id: pageId },
|
||||
{ ...data, update_time: Math.floor(Date.now() / 1000) }
|
||||
);
|
||||
return (result.affected || 0) > 0;
|
||||
}
|
||||
|
||||
async setDefaultPage(site_id: number, name: string, page_id: number) {
|
||||
// 先取消其他页面的默认状态
|
||||
await this.diyPageRepository.update(
|
||||
|
||||
10
wwjcloud/src/common/home/home.module.ts
Normal file
10
wwjcloud/src/common/home/home.module.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { HomeSiteService } from './services/admin/HomeSiteService';
|
||||
import { SiteController } from './controllers/adminapi/SiteController';
|
||||
|
||||
@Module({
|
||||
controllers: [SiteController],
|
||||
providers: [HomeSiteService],
|
||||
exports: [HomeSiteService],
|
||||
})
|
||||
export class HomeModule {}
|
||||
@@ -1,5 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { Module, forwardRef } from '@nestjs/common';
|
||||
import { VendorModule } from '../../vendor';
|
||||
import { PayModule } from '../pay/pay.module';
|
||||
import { SiteModule } from '../site/site.module';
|
||||
import { SysModule } from '../sys/sys.module';
|
||||
import { ScheduleModule } from '../schedule/schedule.module';
|
||||
import { MemberModule } from '../member/member.module';
|
||||
import { JobsService } from './jobs.service';
|
||||
import { OnModuleInit } from '@nestjs/common';
|
||||
import { PaymentProcessors } from './processors/payment';
|
||||
@@ -12,7 +17,7 @@ import { UpgradeProcessors } from './processors/upgrade';
|
||||
import { WxoplatformProcessors } from './processors/wxoplatform';
|
||||
|
||||
@Module({
|
||||
imports: [VendorModule],
|
||||
imports: [VendorModule, forwardRef(() => PayModule), SiteModule, SysModule, ScheduleModule, MemberModule],
|
||||
providers: [
|
||||
JobsService,
|
||||
PaymentProcessors,
|
||||
|
||||
@@ -20,17 +20,17 @@ export class PaymentProcessors {
|
||||
? await this.corePay.findByOutTradeNo(data.siteId, data.outTradeNo)
|
||||
: await this.corePay.findByOutTradeNoUnsafe(data.outTradeNo);
|
||||
if (!pay) return { skipped: true };
|
||||
const resolved = await this.registry.resolve(pay.siteId, pay.type, pay.channel);
|
||||
const resolved = await this.registry.resolve(pay.site_id, pay.type, pay.channel);
|
||||
const adapter: PaymentAdapter = resolved.adapter as PaymentAdapter;
|
||||
const config: unknown = resolved.config as unknown;
|
||||
const q: { status: string; tradeNo?: string } = await adapter.query(
|
||||
config as any,
|
||||
pay.outTradeNo,
|
||||
pay.out_trade_no,
|
||||
);
|
||||
if (q.status === 'SUCCESS') {
|
||||
await this.corePay.updateStatus(pay.siteId, pay.outTradeNo, 1, q.tradeNo);
|
||||
await this.corePay.updateStatus(pay.site_id, pay.out_trade_no, 1, q.tradeNo);
|
||||
} else if (q.status === 'CLOSED') {
|
||||
await this.corePay.updateStatus(pay.siteId, pay.outTradeNo, 2);
|
||||
await this.corePay.updateStatus(pay.site_id, pay.out_trade_no, 2);
|
||||
}
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
10
wwjcloud/src/common/login/login.module.ts
Normal file
10
wwjcloud/src/common/login/login.module.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { LoginService } from './services/admin/LoginService';
|
||||
import { LoginController } from './controllers/adminapi/LoginController';
|
||||
|
||||
@Module({
|
||||
controllers: [LoginController],
|
||||
providers: [LoginService],
|
||||
exports: [LoginService],
|
||||
})
|
||||
export class LoginModule {}
|
||||
@@ -5,8 +5,9 @@ import {
|
||||
OneToMany,
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
import { BaseEntity } from '../../../core/base/BaseEntity';
|
||||
import { MemberAccount } from './MemberAccount';
|
||||
import { MemberCashOut } from './MemberCashOut';
|
||||
import { MemberLabel } from './MemberLabel';
|
||||
@@ -16,7 +17,7 @@ import { MemberAddress } from './MemberAddress';
|
||||
import { MemberAccountLog } from './MemberAccountLog';
|
||||
|
||||
@Entity('member')
|
||||
export class Member extends BaseEntity {
|
||||
export class Member {
|
||||
@PrimaryGeneratedColumn({ name: 'member_id' })
|
||||
member_id: number;
|
||||
|
||||
@@ -26,6 +27,9 @@ export class Member extends BaseEntity {
|
||||
@Column({ name: 'pid', type: 'int', default: 0 })
|
||||
pid: number;
|
||||
|
||||
@Column({ name: 'site_id', type: 'int', default: 0 })
|
||||
site_id: number;
|
||||
|
||||
@Column({ name: 'username', type: 'varchar', length: 255, default: '' })
|
||||
username: string;
|
||||
|
||||
@@ -214,6 +218,15 @@ export class Member extends BaseEntity {
|
||||
@Column({ name: 'remark', type: 'varchar', length: 300, default: '' })
|
||||
remark: string;
|
||||
|
||||
@Column({ name: 'is_del', type: 'tinyint', default: 0 })
|
||||
is_del: number;
|
||||
|
||||
@CreateDateColumn({ name: 'create_time', type: 'int', default: 0 })
|
||||
create_time: number;
|
||||
|
||||
@UpdateDateColumn({ name: 'update_time', type: 'int', default: 0 })
|
||||
update_time: number;
|
||||
|
||||
// 关联关系
|
||||
@OneToMany(() => MemberAccount, (account) => account.member)
|
||||
accounts: MemberAccount[];
|
||||
|
||||
@@ -5,17 +5,19 @@ import {
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
} from 'typeorm';
|
||||
import { BaseEntity } from '../../../core/base/BaseEntity';
|
||||
import { Member } from './Member';
|
||||
|
||||
@Entity('member_address')
|
||||
export class MemberAddress extends BaseEntity {
|
||||
export class MemberAddress {
|
||||
@PrimaryGeneratedColumn({ name: 'id' })
|
||||
id: number;
|
||||
|
||||
@Column({ name: 'member_id', type: 'int', default: 0 })
|
||||
@Column({ name: 'member_id', type: 'int', default: 0, comment: '会员id' })
|
||||
member_id: number;
|
||||
|
||||
@Column({ name: 'site_id', type: 'int', default: 0, comment: '站点id' })
|
||||
site_id: number;
|
||||
|
||||
@Column({ name: 'name', type: 'varchar', length: 255, default: '' })
|
||||
name: string;
|
||||
|
||||
|
||||
@@ -1,50 +1,37 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm';
|
||||
import { BaseEntity } from '@wwjCore/base/BaseEntity';
|
||||
import { Entity, PrimaryGeneratedColumn, Column, OneToMany, CreateDateColumn, UpdateDateColumn } from 'typeorm';
|
||||
import { Member } from './Member';
|
||||
|
||||
@Entity('member_level')
|
||||
export class MemberLevel extends BaseEntity {
|
||||
export class MemberLevel {
|
||||
@PrimaryGeneratedColumn()
|
||||
level_id: number;
|
||||
|
||||
@Column({ type: 'varchar', length: 50, comment: '等级名称' })
|
||||
@Column({ name: 'site_id', type: 'int', default: 0, comment: '站点id' })
|
||||
site_id: number;
|
||||
|
||||
@Column({ name: 'level_name', type: 'varchar', length: 50, default: '', comment: '等级名称' })
|
||||
level_name: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 255, comment: '等级图标' })
|
||||
level_icon: string;
|
||||
@Column({ name: 'growth', type: 'int', default: 0, comment: '所需成长值' })
|
||||
growth: number;
|
||||
|
||||
@Column({ type: 'int', default: 0, comment: '升级所需积分' })
|
||||
upgrade_point: number;
|
||||
@Column({ name: 'remark', type: 'varchar', length: 255, default: '', comment: '备注' })
|
||||
remark: string;
|
||||
|
||||
@Column({
|
||||
type: 'decimal',
|
||||
precision: 5,
|
||||
scale: 2,
|
||||
default: 1.0,
|
||||
comment: '积分倍率',
|
||||
})
|
||||
point_rate: number;
|
||||
|
||||
@Column({
|
||||
type: 'decimal',
|
||||
precision: 5,
|
||||
scale: 2,
|
||||
default: 1.0,
|
||||
comment: '折扣率',
|
||||
})
|
||||
discount_rate: number;
|
||||
|
||||
@Column({ type: 'int', default: 0, comment: '排序' })
|
||||
sort: number;
|
||||
|
||||
@Column({ type: 'tinyint', default: 1, comment: '状态 1:启用 0:禁用' })
|
||||
@Column({ name: 'status', type: 'int', default: 1, comment: '状态 0已禁用1已启用' })
|
||||
status: number;
|
||||
|
||||
@Column({ type: 'varchar', length: 255, comment: '等级描述' })
|
||||
description: string;
|
||||
@Column({ name: 'level_benefits', type: 'text', nullable: true, comment: '等级权益' })
|
||||
level_benefits: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 255, comment: '等级权益' })
|
||||
benefits: string;
|
||||
@Column({ name: 'level_gifts', type: 'text', nullable: true, comment: '等级礼包' })
|
||||
level_gifts: string;
|
||||
|
||||
@CreateDateColumn({ name: 'create_time', type: 'int', default: 0, comment: '添加时间' })
|
||||
create_time: number;
|
||||
|
||||
@UpdateDateColumn({ name: 'update_time', type: 'int', default: 0, comment: '更新时间' })
|
||||
update_time: number;
|
||||
|
||||
// 关联关系
|
||||
@OneToMany(() => Member, (member) => member.level)
|
||||
|
||||
@@ -12,6 +12,7 @@ import { MemberAccountLog } from './entities/MemberAccountLog';
|
||||
import { MemberPoints } from './entities/MemberPoints';
|
||||
import { MemberBalance } from './entities/MemberBalance';
|
||||
import { MemberConfig } from './entities/MemberConfig';
|
||||
import { SysConfig } from '../settings/entities/sys-config.entity';
|
||||
import { CoreMemberService } from './services/core/CoreMemberService';
|
||||
import { MemberService as MemberApiService } from './services/api/MemberService';
|
||||
import { MemberAccountService } from './services/api/MemberAccountService';
|
||||
@@ -65,6 +66,7 @@ import { MemberSignController } from './controllers/adminapi/MemberSignControlle
|
||||
MemberPoints,
|
||||
MemberBalance,
|
||||
MemberConfig,
|
||||
SysConfig,
|
||||
]),
|
||||
],
|
||||
providers: [
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { BaseService } from '@wwjCore/base/BaseService';
|
||||
import { MemberAddress } from '../../entities/MemberAddress';
|
||||
|
||||
@Injectable()
|
||||
export class CoreMemberAddressService extends BaseService<MemberAddress> {
|
||||
export class CoreMemberAddressService {
|
||||
constructor(
|
||||
@InjectRepository(MemberAddress)
|
||||
private memberAddressRepository: Repository<MemberAddress>,
|
||||
) {
|
||||
super(memberAddressRepository);
|
||||
}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 获取会员地址列表
|
||||
@@ -26,7 +23,7 @@ export class CoreMemberAddressService extends BaseService<MemberAddress> {
|
||||
where,
|
||||
skip: (page - 1) * limit,
|
||||
take: limit,
|
||||
order: { is_default: 'DESC', create_time: 'DESC' },
|
||||
order: { is_default: 'DESC' },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -39,6 +36,33 @@ export class CoreMemberAddressService extends BaseService<MemberAddress> {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建地址
|
||||
*/
|
||||
async create(dto: any) {
|
||||
const address = this.memberAddressRepository.create(dto);
|
||||
return await this.memberAddressRepository.save(address);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新地址
|
||||
*/
|
||||
async update(address_id: number, dto: any) {
|
||||
const result = await this.memberAddressRepository.update(
|
||||
{ id: address_id },
|
||||
dto
|
||||
);
|
||||
return (result.affected || 0) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除地址
|
||||
*/
|
||||
async delete(address_id: number) {
|
||||
const result = await this.memberAddressRepository.delete({ id: address_id });
|
||||
return (result.affected || 0) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置默认地址
|
||||
*/
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { BaseService } from '@wwjCore/base/BaseService';
|
||||
import { MemberLevel } from '../../entities/MemberLevel';
|
||||
|
||||
@Injectable()
|
||||
export class CoreMemberLevelService extends BaseService<MemberLevel> {
|
||||
export class CoreMemberLevelService {
|
||||
constructor(
|
||||
@InjectRepository(MemberLevel)
|
||||
private memberLevelRepository: Repository<MemberLevel>,
|
||||
) {
|
||||
super(memberLevelRepository);
|
||||
}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 获取会员等级列表
|
||||
@@ -24,7 +21,7 @@ export class CoreMemberLevelService extends BaseService<MemberLevel> {
|
||||
where,
|
||||
skip: (page - 1) * limit,
|
||||
take: limit,
|
||||
order: { upgrade_point: 'ASC' },
|
||||
order: { growth: 'ASC' },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -37,19 +34,46 @@ export class CoreMemberLevelService extends BaseService<MemberLevel> {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建等级
|
||||
*/
|
||||
async create(dto: any) {
|
||||
const level = this.memberLevelRepository.create(dto);
|
||||
return await this.memberLevelRepository.save(level);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新等级
|
||||
*/
|
||||
async update(level_id: number, dto: any) {
|
||||
const result = await this.memberLevelRepository.update(
|
||||
{ level_id },
|
||||
dto
|
||||
);
|
||||
return (result.affected || 0) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除等级
|
||||
*/
|
||||
async delete(level_id: number) {
|
||||
const result = await this.memberLevelRepository.delete({ level_id });
|
||||
return (result.affected || 0) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置默认等级
|
||||
*/
|
||||
async setDefault(level_id: number) {
|
||||
// 先取消其他等级的默认状态
|
||||
await this.memberLevelRepository.update(
|
||||
{ sort: 0 },
|
||||
{ sort: 1 }
|
||||
{ level_id: level_id },
|
||||
{ status: 1 }
|
||||
);
|
||||
|
||||
// 设置当前等级为默认
|
||||
const result = await this.memberLevelRepository.update(level_id, {
|
||||
sort: 0,
|
||||
const result = await this.memberLevelRepository.update({ level_id }, {
|
||||
status: 1,
|
||||
});
|
||||
|
||||
return (result.affected || 0) > 0;
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository, Not, Between } from 'typeorm';
|
||||
import { BaseService } from '@wwjCore/base/BaseService';
|
||||
import { Member } from '../../entities/Member';
|
||||
import { MemberLevel } from '../../entities/MemberLevel';
|
||||
import * as bcrypt from 'bcrypt';
|
||||
|
||||
@Injectable()
|
||||
export class CoreMemberService extends BaseService<Member> {
|
||||
export class CoreMemberService {
|
||||
constructor(
|
||||
@InjectRepository(Member)
|
||||
private memberRepository: Repository<Member>,
|
||||
@InjectRepository(MemberLevel)
|
||||
private memberLevelRepository: Repository<MemberLevel>,
|
||||
) {
|
||||
super(memberRepository);
|
||||
}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 创建会员
|
||||
@@ -52,21 +49,28 @@ export class CoreMemberService extends BaseService<Member> {
|
||||
* 根据ID获取会员
|
||||
*/
|
||||
async getMemberById(memberId: number): Promise<Member | null> {
|
||||
return await this.findOne(memberId);
|
||||
return await this.memberRepository.findOne({
|
||||
where: { member_id: memberId, is_del: 0 },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除会员
|
||||
*/
|
||||
async deleteMember(memberId: number): Promise<void> {
|
||||
await this.delete(memberId);
|
||||
await this.memberRepository.update(
|
||||
{ member_id: memberId },
|
||||
{ is_del: 1, update_time: Math.floor(Date.now() / 1000) },
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID查找会员
|
||||
*/
|
||||
async findById(memberId: number): Promise<Member> {
|
||||
const member = await this.findOne(memberId);
|
||||
const member = await this.memberRepository.findOne({
|
||||
where: { member_id: memberId, is_del: 0 },
|
||||
});
|
||||
|
||||
if (!member) {
|
||||
throw new NotFoundException('会员不存在');
|
||||
@@ -79,14 +83,18 @@ export class CoreMemberService extends BaseService<Member> {
|
||||
* 根据用户名查找会员
|
||||
*/
|
||||
async findByUsername(username: string): Promise<Member | null> {
|
||||
return await this.findOneBy({ username });
|
||||
return await this.memberRepository.findOne({
|
||||
where: { username, is_del: 0 },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据手机号查找会员
|
||||
*/
|
||||
async findByMobile(mobile: string): Promise<Member | null> {
|
||||
return await this.findOneBy({ mobile });
|
||||
return await this.memberRepository.findOne({
|
||||
where: { mobile, is_del: 0 },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -127,7 +135,10 @@ export class CoreMemberService extends BaseService<Member> {
|
||||
delete updateDto.site_id;
|
||||
delete updateDto.register_time;
|
||||
|
||||
await this.update(memberId, updateDto);
|
||||
await this.memberRepository.update(
|
||||
{ member_id: memberId },
|
||||
{ ...updateDto, update_time: Math.floor(Date.now() / 1000) },
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,35 +16,30 @@ export class CloudController {
|
||||
@Post('build')
|
||||
@ApiOperation({ summary: '云编译' })
|
||||
async build(@Body() body: CloudBuildDto) {
|
||||
const data = await this.service.build(body);
|
||||
return { code: 200, message: '编译成功', data };
|
||||
return await this.service.build(body);
|
||||
}
|
||||
|
||||
@Get('buildLog')
|
||||
@ApiOperation({ summary: '获取云编译日志' })
|
||||
async getBuildLog(@Query() query: CloudLogDto) {
|
||||
const data = await this.service.getBuildLog(query);
|
||||
return { code: 200, message: '获取成功', data };
|
||||
return await this.service.getBuildLog(query);
|
||||
}
|
||||
|
||||
@Get('buildTask')
|
||||
@ApiOperation({ summary: '获取云编译任务' })
|
||||
async getBuildTask() {
|
||||
const data = await this.service.getBuildTask();
|
||||
return { code: 200, message: '获取成功', data };
|
||||
return await this.service.getBuildTask();
|
||||
}
|
||||
|
||||
@Post('cancelBuild')
|
||||
@ApiOperation({ summary: '取消云编译' })
|
||||
async cancelBuild() {
|
||||
const data = await this.service.cancelBuild();
|
||||
return { code: 200, message: '取消成功', data };
|
||||
return await this.service.cancelBuild();
|
||||
}
|
||||
|
||||
@Get('buildStatus')
|
||||
@ApiOperation({ summary: '获取编译状态' })
|
||||
async getBuildStatus() {
|
||||
const data = await this.service.getBuildStatus();
|
||||
return { code: 200, message: '获取成功', data };
|
||||
return await this.service.getBuildStatus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,62 +16,54 @@ export class ModuleController {
|
||||
@Get('page')
|
||||
@ApiOperation({ summary: '模块分页' })
|
||||
async page(@Query() query: ModuleQueryDto) {
|
||||
const data = await this.service.getPage(query);
|
||||
return { code: 200, message: '获取成功', data };
|
||||
return await this.service.getPage(query);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@ApiOperation({ summary: '模块详情' })
|
||||
@ApiParam({ name: 'id', description: '模块ID' })
|
||||
async info(@Param('id', ParseIntPipe) id: number) {
|
||||
const data = await this.service.getInfo(id);
|
||||
return { code: 200, message: '获取成功', data };
|
||||
return await this.service.getInfo(id);
|
||||
}
|
||||
|
||||
@Post()
|
||||
@ApiOperation({ summary: '新增模块' })
|
||||
async add(@Body() body: CreateModuleDto) {
|
||||
const data = await this.service.add(body);
|
||||
return { code: 200, message: '创建成功', data };
|
||||
return await this.service.add(body);
|
||||
}
|
||||
|
||||
@Put(':id')
|
||||
@ApiOperation({ summary: '编辑模块' })
|
||||
@ApiParam({ name: 'id', description: '模块ID' })
|
||||
async edit(@Param('id', ParseIntPipe) id: number, @Body() body: UpdateModuleDto) {
|
||||
const data = await this.service.edit(id, body);
|
||||
return { code: 200, message: '更新成功', data };
|
||||
return await this.service.edit(id, body);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@ApiOperation({ summary: '删除模块' })
|
||||
@ApiParam({ name: 'id', description: '模块ID' })
|
||||
async delete(@Param('id', ParseIntPipe) id: number) {
|
||||
const data = await this.service.delete(id);
|
||||
return { code: 200, message: '删除成功', data };
|
||||
return await this.service.delete(id);
|
||||
}
|
||||
|
||||
@Post(':id/install')
|
||||
@ApiOperation({ summary: '安装模块' })
|
||||
@ApiParam({ name: 'id', description: '模块ID' })
|
||||
async install(@Param('id', ParseIntPipe) id: number) {
|
||||
const data = await this.service.install(id);
|
||||
return { code: 200, message: '安装成功', data };
|
||||
return await this.service.install(id);
|
||||
}
|
||||
|
||||
@Post(':id/uninstall')
|
||||
@ApiOperation({ summary: '卸载模块' })
|
||||
@ApiParam({ name: 'id', description: '模块ID' })
|
||||
async uninstall(@Param('id', ParseIntPipe) id: number) {
|
||||
const data = await this.service.uninstall(id);
|
||||
return { code: 200, message: '卸载成功', data };
|
||||
return await this.service.uninstall(id);
|
||||
}
|
||||
|
||||
@Post(':id/upgrade')
|
||||
@ApiOperation({ summary: '升级模块' })
|
||||
@ApiParam({ name: 'id', description: '模块ID' })
|
||||
async upgrade(@Param('id', ParseIntPipe) id: number) {
|
||||
const data = await this.service.upgrade(id);
|
||||
return { code: 200, message: '升级成功', data };
|
||||
return await this.service.upgrade(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { Cloud } from './entities/Cloud';
|
||||
import { Module as NiucloudModuleEntity } from './entities/Module';
|
||||
import { CloudController } from './controllers/adminapi/CloudController';
|
||||
import { ModuleController } from './controllers/adminapi/ModuleController';
|
||||
import { CloudService } from './services/admin/CloudService';
|
||||
@@ -7,6 +10,7 @@ import { CoreCloudService } from './services/core/CoreCloudService';
|
||||
import { CoreModuleService } from './services/core/CoreModuleService';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Cloud, NiucloudModuleEntity])],
|
||||
controllers: [CloudController, ModuleController],
|
||||
providers: [CloudService, ModuleService, CoreCloudService, CoreModuleService],
|
||||
exports: [CloudService, ModuleService, CoreCloudService, CoreModuleService],
|
||||
|
||||
@@ -1,35 +1,46 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
|
||||
import { BaseEntity } from '../../../core/base/BaseEntity';
|
||||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, DeleteDateColumn } from 'typeorm';
|
||||
|
||||
@Entity('sys_notice_sms_log')
|
||||
export class SmsLog extends BaseEntity {
|
||||
export class SmsLog {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ name: 'mobile', type: 'varchar', length: 11, default: '' })
|
||||
@Column({ name: 'site_id', type: 'int', default: 0, comment: '站点id' })
|
||||
site_id: number;
|
||||
|
||||
@Column({ name: 'mobile', type: 'varchar', length: 11, default: '', comment: '手机号码' })
|
||||
mobile: string;
|
||||
|
||||
@Column({ name: 'sms_type', type: 'varchar', length: 32, default: '' })
|
||||
@Column({ name: 'sms_type', type: 'varchar', length: 32, default: '', comment: '发送关键字(注册、找回密码)' })
|
||||
sms_type: string;
|
||||
|
||||
@Column({ name: 'key', type: 'varchar', length: 32, default: '' })
|
||||
@Column({ name: 'key', type: 'varchar', length: 32, default: '', comment: '发送关键字(注册、找回密码)' })
|
||||
key: string;
|
||||
|
||||
@Column({ name: 'template_id', type: 'varchar', length: 50, default: '' })
|
||||
@Column({ name: 'template_id', type: 'varchar', length: 50, default: '', comment: '模板ID' })
|
||||
template_id: string;
|
||||
|
||||
@Column({ name: 'content', type: 'text' })
|
||||
@Column({ name: 'content', type: 'text', comment: '发送内容' })
|
||||
content: string;
|
||||
|
||||
@Column({ name: 'params', type: 'text' })
|
||||
params: any;
|
||||
@Column({ name: 'params', type: 'text', comment: '数据参数' })
|
||||
params: string;
|
||||
|
||||
@Column({ name: 'status', type: 'varchar', length: 32, default: 'sending' })
|
||||
@Column({ name: 'status', type: 'varchar', length: 32, default: 'sending', comment: '发送状态:sending-发送中;success-发送成功;fail-发送失败' })
|
||||
status: string;
|
||||
|
||||
@Column({ name: 'result', type: 'text', nullable: true })
|
||||
@Column({ name: 'result', type: 'text', nullable: true, comment: '短信结果' })
|
||||
result: string;
|
||||
|
||||
@Column({ name: 'send_time', type: 'int', default: 0 })
|
||||
@Column({ name: 'send_time', type: 'int', default: 0, comment: '发送时间' })
|
||||
send_time: number;
|
||||
|
||||
@CreateDateColumn({ name: 'create_time', type: 'int', default: 0, comment: '创建时间' })
|
||||
create_time: number;
|
||||
|
||||
@UpdateDateColumn({ name: 'update_time', type: 'int', default: 0, comment: '更新时间' })
|
||||
update_time: number;
|
||||
|
||||
@DeleteDateColumn({ name: 'delete_time', type: 'int', default: 0, comment: '删除时间' })
|
||||
delete_time: number;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { BaseService } from '@wwjCore/base/BaseService';
|
||||
import { SmsLog } from '../../entities/SmsLog';
|
||||
import { ISmsConfig, ISmsSendParams } from '../../interfaces/sms.interface';
|
||||
import { SmsStatus } from '../../enums/status.enum';
|
||||
|
||||
@Injectable()
|
||||
export class CoreSmsService extends BaseService<SmsLog> {
|
||||
export class CoreSmsService {
|
||||
constructor(
|
||||
@InjectRepository(SmsLog)
|
||||
private readonly smsLogRepository: Repository<SmsLog>,
|
||||
) {
|
||||
super(smsLogRepository);
|
||||
}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 发送短信
|
||||
@@ -30,7 +27,7 @@ export class CoreSmsService extends BaseService<SmsLog> {
|
||||
|
||||
try {
|
||||
// 创建短信日志
|
||||
savedLog = await this.create({
|
||||
savedLog = await this.smsLogRepository.save(this.smsLogRepository.create({
|
||||
site_id,
|
||||
mobile,
|
||||
sms_type: 'default',
|
||||
@@ -39,15 +36,15 @@ export class CoreSmsService extends BaseService<SmsLog> {
|
||||
template_id,
|
||||
params: JSON.stringify(params),
|
||||
status: SmsStatus.SENDING,
|
||||
send_time: this.getCurrentTimestamp(),
|
||||
});
|
||||
send_time: Math.floor(Date.now() / 1000),
|
||||
}));
|
||||
|
||||
// TODO: 这里应该调用实际的短信服务商API
|
||||
// 目前先模拟发送成功
|
||||
const success = true;
|
||||
|
||||
// 更新日志状态
|
||||
await this.update(savedLog.id, {
|
||||
await this.smsLogRepository.update(savedLog.id, {
|
||||
status: success ? SmsStatus.SUCCESS : SmsStatus.FAILED,
|
||||
result: success ? '发送成功' : '发送失败',
|
||||
});
|
||||
@@ -56,7 +53,7 @@ export class CoreSmsService extends BaseService<SmsLog> {
|
||||
} catch (error) {
|
||||
// 记录失败日志
|
||||
if (savedLog?.id) {
|
||||
await this.update(savedLog.id, {
|
||||
await this.smsLogRepository.update(savedLog.id, {
|
||||
status: SmsStatus.FAILED,
|
||||
result: error.message,
|
||||
});
|
||||
@@ -85,6 +82,6 @@ export class CoreSmsService extends BaseService<SmsLog> {
|
||||
*/
|
||||
async getSmsLogs(site_id: number, where: any = {}): Promise<SmsLog[]> {
|
||||
const query = { site_id, ...where };
|
||||
return await this.findMany(query);
|
||||
return await this.smsLogRepository.find({ where: query });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
Query,
|
||||
UseGuards,
|
||||
Req,
|
||||
UnauthorizedException,
|
||||
} from "@nestjs/common";
|
||||
import {
|
||||
ApiTags,
|
||||
@@ -43,19 +44,19 @@ export class PayChannelController {
|
||||
constructor(private readonly payChannelService: PayChannelService) {}
|
||||
|
||||
@Get("list")
|
||||
@Roles('admin')
|
||||
@ApiOperation({ summary: "获取支付渠道列表" })
|
||||
@ApiResponse({ status: 200, description: "获取成功" })
|
||||
async getChannelList(@Req() req: AuthenticatedRequest) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.payChannelService.getChannelList(siteId);
|
||||
return { code: 200, message: "获取成功", data: result };
|
||||
} catch (error) {
|
||||
return { code: 500, message: "获取失败", error: error.message };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('Missing site_id');
|
||||
}
|
||||
return await this.payChannelService.getChannelList(siteId);
|
||||
}
|
||||
|
||||
@Get(":type/:channel")
|
||||
@Roles('admin')
|
||||
@ApiOperation({ summary: "获取支付渠道配置" })
|
||||
@ApiParam({ name: "type", description: "支付方式" })
|
||||
@ApiParam({ name: "channel", description: "支付渠道" })
|
||||
@@ -65,16 +66,15 @@ export class PayChannelController {
|
||||
@Param("channel") channel: string,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.payChannelService.getConfig(siteId, type, channel);
|
||||
return { code: 200, message: "获取成功", data: result };
|
||||
} catch (error) {
|
||||
return { code: 500, message: "获取失败", error: error.message };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('Missing site_id');
|
||||
}
|
||||
return await this.payChannelService.getConfig(siteId, type, channel);
|
||||
}
|
||||
|
||||
@Post(":type/:channel")
|
||||
@Roles('admin')
|
||||
@ApiOperation({ summary: "设置支付渠道配置" })
|
||||
@ApiParam({ name: "type", description: "支付方式" })
|
||||
@ApiParam({ name: "channel", description: "支付渠道" })
|
||||
@@ -85,16 +85,16 @@ export class PayChannelController {
|
||||
@Body() createPayChannelDto: CreatePayChannelDto,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.payChannelService.set(siteId, channel, type, createPayChannelDto);
|
||||
return { code: 200, message: "设置成功", data: result };
|
||||
} catch (error) {
|
||||
return { code: 500, message: "设置失败", error: error.message };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('Missing site_id');
|
||||
}
|
||||
const result = await this.payChannelService.set(siteId, channel, type, createPayChannelDto);
|
||||
return { success: true, message: '设置成功' };
|
||||
}
|
||||
|
||||
@Put(":type/:channel")
|
||||
@Roles('admin')
|
||||
@ApiOperation({ summary: "更新支付渠道配置" })
|
||||
@ApiParam({ name: "type", description: "支付方式" })
|
||||
@ApiParam({ name: "channel", description: "支付渠道" })
|
||||
@@ -105,16 +105,16 @@ export class PayChannelController {
|
||||
@Body() updatePayChannelDto: UpdatePayChannelDto,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.payChannelService.set(siteId, channel, type, updatePayChannelDto);
|
||||
return { code: 200, message: "更新成功", data: result };
|
||||
} catch (error) {
|
||||
return { code: 500, message: "更新失败", error: error.message };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('Missing site_id');
|
||||
}
|
||||
const result = await this.payChannelService.set(siteId, channel, type, updatePayChannelDto);
|
||||
return { success: true, message: '更新成功' };
|
||||
}
|
||||
|
||||
@Delete(":type/:channel")
|
||||
@Roles('admin')
|
||||
@ApiOperation({ summary: "删除支付渠道配置" })
|
||||
@ApiParam({ name: "type", description: "支付方式" })
|
||||
@ApiParam({ name: "channel", description: "支付渠道" })
|
||||
@@ -124,16 +124,16 @@ export class PayChannelController {
|
||||
@Param("channel") channel: string,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.payChannelService.delete(siteId, type, channel);
|
||||
return { code: 200, message: "删除成功", data: result };
|
||||
} catch (error) {
|
||||
return { code: 500, message: "删除失败", error: error.message };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('Missing site_id');
|
||||
}
|
||||
const result = await this.payChannelService.delete(siteId, type, channel);
|
||||
return { success: true, message: '删除成功' };
|
||||
}
|
||||
|
||||
@Put(":type/:channel/status")
|
||||
@Roles('admin')
|
||||
@ApiOperation({ summary: "更新支付渠道状态" })
|
||||
@ApiParam({ name: "type", description: "支付方式" })
|
||||
@ApiParam({ name: "channel", description: "支付渠道" })
|
||||
@@ -144,12 +144,11 @@ export class PayChannelController {
|
||||
@Body() body: { status: number },
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.payChannelService.updateStatus(siteId, type, channel, body.status);
|
||||
return { code: 200, message: "更新成功", data: result };
|
||||
} catch (error) {
|
||||
return { code: 500, message: "更新失败", error: error.message };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('Missing site_id');
|
||||
}
|
||||
const result = await this.payChannelService.updateStatus(siteId, type, channel, body.status);
|
||||
return { success: true, message: '更新成功' };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
Query,
|
||||
UseGuards,
|
||||
Req,
|
||||
UnauthorizedException,
|
||||
NotFoundException,
|
||||
} from "@nestjs/common";
|
||||
import {
|
||||
ApiTags,
|
||||
@@ -42,77 +44,76 @@ export class PayController {
|
||||
constructor(private readonly payService: PayService) {}
|
||||
|
||||
@Get("audit/page")
|
||||
@Roles('admin')
|
||||
@ApiOperation({ summary: "获取待审核支付记录" })
|
||||
@ApiResponse({ status: 200, description: "获取成功" })
|
||||
async getAuditPage(@Query() query: PayAuditQueryDto, @Req() req: AuthenticatedRequest) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.payService.getAuditPage(siteId, query);
|
||||
return { code: 200, message: "获取成功", data: result };
|
||||
} catch (error) {
|
||||
return { code: 500, message: "获取失败", error: error.message };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('Missing site_id');
|
||||
}
|
||||
return await this.payService.getAuditPage(siteId, query);
|
||||
}
|
||||
|
||||
@Get(":id")
|
||||
@Roles('admin')
|
||||
@ApiOperation({ summary: "获取支付详情" })
|
||||
@ApiParam({ name: "id", description: "支付ID" })
|
||||
@ApiResponse({ status: 200, description: "获取成功" })
|
||||
async getDetail(@Param("id") id: number, @Req() req: AuthenticatedRequest) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.payService.getDetail(siteId, id);
|
||||
if (!result) {
|
||||
return { code: 404, message: "支付记录不存在" };
|
||||
}
|
||||
return { code: 200, message: "获取成功", data: result };
|
||||
} catch (error) {
|
||||
return { code: 500, message: "获取失败", error: error.message };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('Missing site_id');
|
||||
}
|
||||
const result = await this.payService.getDetail(siteId, id);
|
||||
if (!result) {
|
||||
throw new NotFoundException('支付记录不存在');
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Post("pass")
|
||||
@Roles('admin')
|
||||
@ApiOperation({ summary: "支付审核通过" })
|
||||
@ApiResponse({ status: 200, description: "审核成功" })
|
||||
async pass(@Body() payPassDto: PayPassDto, @Req() req: AuthenticatedRequest) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.payService.pass(siteId, payPassDto.outTradeNo);
|
||||
if (!result) {
|
||||
return { code: 404, message: "支付记录不存在" };
|
||||
}
|
||||
return { code: 200, message: "审核通过成功" };
|
||||
} catch (error) {
|
||||
return { code: 500, message: "审核失败", error: error.message };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('Missing site_id');
|
||||
}
|
||||
const result = await this.payService.pass(siteId, payPassDto.outTradeNo);
|
||||
if (!result) {
|
||||
throw new NotFoundException('支付记录不存在');
|
||||
}
|
||||
return { success: true, message: '审核通过成功' };
|
||||
}
|
||||
|
||||
@Post("refuse")
|
||||
@Roles('admin')
|
||||
@ApiOperation({ summary: "支付审核拒绝" })
|
||||
@ApiResponse({ status: 200, description: "审核成功" })
|
||||
async refuse(@Body() payRefuseDto: PayRefuseDto, @Req() req: AuthenticatedRequest) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.payService.refuse(siteId, payRefuseDto.outTradeNo, payRefuseDto.reason);
|
||||
if (!result) {
|
||||
return { code: 404, message: "支付记录不存在" };
|
||||
}
|
||||
return { code: 200, message: "审核拒绝成功" };
|
||||
} catch (error) {
|
||||
return { code: 500, message: "审核失败", error: error.message };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('Missing site_id');
|
||||
}
|
||||
const result = await this.payService.refuse(siteId, payRefuseDto.outTradeNo, payRefuseDto.reason);
|
||||
if (!result) {
|
||||
throw new NotFoundException('支付记录不存在');
|
||||
}
|
||||
return { success: true, message: '审核拒绝成功' };
|
||||
}
|
||||
|
||||
@Get("count")
|
||||
@Roles('admin')
|
||||
@ApiOperation({ summary: "统计支付数据" })
|
||||
@ApiResponse({ status: 200, description: "获取成功" })
|
||||
async payCount(@Query() query: any, @Req() req: AuthenticatedRequest) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.payService.payCount(siteId, query);
|
||||
return { code: 200, message: "获取成功", data: { count: result } };
|
||||
} catch (error) {
|
||||
return { code: 500, message: "获取失败", error: error.message };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('Missing site_id');
|
||||
}
|
||||
const result = await this.payService.payCount(siteId, query);
|
||||
return { count: result };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
import { Body, Controller, Post, Get, Query, Req, UseGuards } from '@nestjs/common';
|
||||
import { ApiOperation, ApiTags, ApiResponse } from '@nestjs/swagger';
|
||||
import { JwtAuthGuard } from '../../../auth/guards/JwtAuthGuard';
|
||||
import { RolesGuard } from '../../../auth/guards/RolesGuard';
|
||||
import { CreatePayTemplateDto } from '../../dto/admin/CreatePayTemplateDto';
|
||||
import { UpdatePayTemplateDto } from '../../dto/admin/UpdatePayTemplateDto';
|
||||
import { PayTemplateService } from '../../services/admin/PayTemplateService';
|
||||
|
||||
interface AuthenticatedRequest extends Request {
|
||||
user?: {
|
||||
siteId?: number;
|
||||
uid?: number;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付模板控制器 - Admin层
|
||||
* 对应PHP: 支付模板相关控制器
|
||||
*/
|
||||
@ApiTags('支付模板')
|
||||
@Controller('adminapi/pay/template')
|
||||
@UseGuards(JwtAuthGuard, RolesGuard)
|
||||
export class PayTemplateController {
|
||||
constructor(private readonly payTemplateService: PayTemplateService) {}
|
||||
|
||||
@Post('create')
|
||||
@ApiOperation({ summary: '创建支付模板' })
|
||||
@ApiResponse({ status: 200, description: '创建成功' })
|
||||
async create(@Body() createPayTemplateDto: CreatePayTemplateDto, @Req() req: AuthenticatedRequest) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.payTemplateService.create(siteId, createPayTemplateDto);
|
||||
return { code: 200, message: '创建成功', data: result };
|
||||
} catch (error) {
|
||||
return { code: 500, message: '创建失败', error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
@Post('update')
|
||||
@ApiOperation({ summary: '更新支付模板' })
|
||||
@ApiResponse({ status: 200, description: '更新成功' })
|
||||
async update(@Body() updatePayTemplateDto: UpdatePayTemplateDto, @Req() req: AuthenticatedRequest) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const id = updatePayTemplateDto['id'];
|
||||
if (!id) {
|
||||
return { code: 400, message: '缺少ID参数' };
|
||||
}
|
||||
const result = await this.payTemplateService.update(siteId, id, updatePayTemplateDto);
|
||||
return { code: 200, message: '更新成功', data: result };
|
||||
} catch (error) {
|
||||
return { code: 500, message: '更新失败', error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
@Get('detail')
|
||||
@ApiOperation({ summary: '获取支付模板详情' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async detail(@Query('id') id: number, @Req() req: AuthenticatedRequest) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.payTemplateService.getDetail(siteId, id);
|
||||
if (!result) {
|
||||
return { code: 404, message: '支付模板不存在' };
|
||||
}
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
} catch (error) {
|
||||
return { code: 500, message: '获取失败', error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
@Get('list')
|
||||
@ApiOperation({ summary: '获取支付模板列表' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async list(@Query() query: any, @Req() req: AuthenticatedRequest) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.payTemplateService.getList(siteId, query);
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
} catch (error) {
|
||||
return { code: 500, message: '获取失败', error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
@Post('delete')
|
||||
@ApiOperation({ summary: '删除支付模板' })
|
||||
@ApiResponse({ status: 200, description: '删除成功' })
|
||||
async delete(@Body('id') id: number, @Req() req: AuthenticatedRequest) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.payTemplateService.delete(siteId, id);
|
||||
if (!result) {
|
||||
return { code: 404, message: '支付模板不存在' };
|
||||
}
|
||||
return { code: 200, message: '删除成功' };
|
||||
} catch (error) {
|
||||
return { code: 500, message: '删除失败', error: error.message };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
Query,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { Roles } from '../../../auth/decorators/RolesDecorator';
|
||||
import { JwtAuthGuard } from '../../../auth/guards/JwtAuthGuard';
|
||||
import { RolesGuard } from '../../../auth/guards/RolesGuard';
|
||||
import { PayRefundService } from '../../services/admin/PayRefundService';
|
||||
@@ -21,6 +22,7 @@ export class PayRefundController {
|
||||
* 退款列表
|
||||
*/
|
||||
@Get('lists')
|
||||
@Roles('admin')
|
||||
async lists(@Query() query: any) {
|
||||
return this.payRefundService.getPage(query);
|
||||
}
|
||||
@@ -29,6 +31,7 @@ export class PayRefundController {
|
||||
* 退款信息
|
||||
*/
|
||||
@Get('info/:refund_id')
|
||||
@Roles('admin')
|
||||
async info(@Param('refund_id') refund_id: string) {
|
||||
return this.payRefundService.getInfo(parseInt(refund_id));
|
||||
}
|
||||
@@ -37,6 +40,7 @@ export class PayRefundController {
|
||||
* 创建退款
|
||||
*/
|
||||
@Post('create')
|
||||
@Roles('admin')
|
||||
async create(@Body() data: {
|
||||
pay_id: number;
|
||||
refund_amount: number;
|
||||
@@ -51,6 +55,7 @@ export class PayRefundController {
|
||||
* 处理退款
|
||||
*/
|
||||
@Post('process/:refund_id')
|
||||
@Roles('admin')
|
||||
async process(@Param('refund_id') refund_id: string) {
|
||||
return this.payRefundService.process(parseInt(refund_id));
|
||||
}
|
||||
@@ -59,6 +64,7 @@ export class PayRefundController {
|
||||
* 取消退款
|
||||
*/
|
||||
@Post('cancel/:refund_id')
|
||||
@Roles('admin')
|
||||
async cancel(@Param('refund_id') refund_id: string) {
|
||||
return this.payRefundService.cancel(parseInt(refund_id));
|
||||
}
|
||||
@@ -67,6 +73,7 @@ export class PayRefundController {
|
||||
* 获取退款状态
|
||||
*/
|
||||
@Get('status/:refund_id')
|
||||
@Roles('admin')
|
||||
async getStatus(@Param('refund_id') refund_id: string) {
|
||||
return this.payRefundService.getStatus(parseInt(refund_id));
|
||||
}
|
||||
@@ -75,6 +82,7 @@ export class PayRefundController {
|
||||
* 获取退款统计
|
||||
*/
|
||||
@Get('statistics')
|
||||
@Roles('admin')
|
||||
async getStatistics(@Query() query: any) {
|
||||
return this.payRefundService.getStatistics(query);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
Query,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { Roles } from '../../../auth/decorators/RolesDecorator';
|
||||
import { JwtAuthGuard } from '../../../auth/guards/JwtAuthGuard';
|
||||
import { RolesGuard } from '../../../auth/guards/RolesGuard';
|
||||
import { TransferService } from '../../services/admin/TransferService';
|
||||
@@ -21,6 +22,7 @@ export class TransferController {
|
||||
* 转账列表
|
||||
*/
|
||||
@Get('lists')
|
||||
@Roles('admin')
|
||||
async lists(@Query() query: any) {
|
||||
return this.transferService.getPage(query);
|
||||
}
|
||||
@@ -29,6 +31,7 @@ export class TransferController {
|
||||
* 转账信息
|
||||
*/
|
||||
@Get('info/:transfer_id')
|
||||
@Roles('admin')
|
||||
async info(@Param('transfer_id') transfer_id: string) {
|
||||
return this.transferService.getInfo(parseInt(transfer_id));
|
||||
}
|
||||
@@ -37,6 +40,7 @@ export class TransferController {
|
||||
* 创建转账
|
||||
*/
|
||||
@Post('create')
|
||||
@Roles('admin')
|
||||
async create(@Body() data: {
|
||||
transfer_type: string;
|
||||
transfer_amount: number;
|
||||
@@ -52,6 +56,7 @@ export class TransferController {
|
||||
* 处理转账
|
||||
*/
|
||||
@Post('process/:transfer_id')
|
||||
@Roles('admin')
|
||||
async process(@Param('transfer_id') transfer_id: string) {
|
||||
return this.transferService.process(parseInt(transfer_id));
|
||||
}
|
||||
@@ -60,6 +65,7 @@ export class TransferController {
|
||||
* 取消转账
|
||||
*/
|
||||
@Post('cancel/:transfer_id')
|
||||
@Roles('admin')
|
||||
async cancel(@Param('transfer_id') transfer_id: string) {
|
||||
return this.transferService.cancel(parseInt(transfer_id));
|
||||
}
|
||||
@@ -68,6 +74,7 @@ export class TransferController {
|
||||
* 获取转账状态
|
||||
*/
|
||||
@Get('status/:transfer_id')
|
||||
@Roles('admin')
|
||||
async getStatus(@Param('transfer_id') transfer_id: string) {
|
||||
return this.transferService.getStatus(parseInt(transfer_id));
|
||||
}
|
||||
@@ -76,6 +83,7 @@ export class TransferController {
|
||||
* 获取转账统计
|
||||
*/
|
||||
@Get('statistics')
|
||||
@Roles('admin')
|
||||
async getStatistics(@Query() query: any) {
|
||||
return this.transferService.getStatistics(query);
|
||||
}
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { IsString, IsOptional, IsEnum, ValidateIf } from 'class-validator';
|
||||
import { Transform } from 'class-transformer';
|
||||
|
||||
/**
|
||||
* 支付方式枚举
|
||||
* 对应PHP: app\dict\pay\PayDict
|
||||
*/
|
||||
export enum PayType {
|
||||
WECHATPAY = 'wechatpay',
|
||||
ALIPAY = 'alipay',
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建支付模板DTO
|
||||
* 对应PHP: app\validate\pay\PayTemplate 的 add 场景
|
||||
*/
|
||||
export class CreatePayTemplateDto {
|
||||
@ApiProperty({ description: '模板名称', example: '微信支付' })
|
||||
@IsString()
|
||||
name: string;
|
||||
|
||||
@ApiProperty({ description: '支付方式', enum: PayType, example: PayType.WECHATPAY })
|
||||
@IsEnum(PayType, { message: '不存在的支付方式' })
|
||||
type: PayType;
|
||||
|
||||
// 支付宝相关字段 - 仅当type为ALIPAY时必填
|
||||
@ApiPropertyOptional({ description: '支付宝AppID', example: '2021000000000000' })
|
||||
@ValidateIf(o => o.type === PayType.ALIPAY)
|
||||
@IsString({ message: '支付宝AppID必填' })
|
||||
app_id?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '支付宝应用私钥证书' })
|
||||
@ValidateIf(o => o.type === PayType.ALIPAY)
|
||||
@IsString({ message: '支付宝应用私钥证书必填' })
|
||||
app_secret_cert?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '支付宝应用公钥证书路径' })
|
||||
@ValidateIf(o => o.type === PayType.ALIPAY)
|
||||
@IsString({ message: '支付宝应用公钥证书路径必填' })
|
||||
app_public_cert_path?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '支付宝公钥证书路径' })
|
||||
@ValidateIf(o => o.type === PayType.ALIPAY)
|
||||
@IsString({ message: '支付宝公钥证书路径必填' })
|
||||
alipay_public_cert_path?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '支付宝根证书路径' })
|
||||
@ValidateIf(o => o.type === PayType.ALIPAY)
|
||||
@IsString({ message: '支付宝根证书路径必填' })
|
||||
alipay_root_cert_path?: string;
|
||||
|
||||
// 微信支付相关字段 - 仅当type为WECHATPAY时必填
|
||||
@ApiPropertyOptional({ description: '微信商户号', example: '1900000109' })
|
||||
@ValidateIf(o => o.type === PayType.WECHATPAY)
|
||||
@IsString({ message: '微信商户号必填' })
|
||||
mch_id?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '微信商户API密钥' })
|
||||
@ValidateIf(o => o.type === PayType.WECHATPAY)
|
||||
@IsString({ message: '微信商户API密钥必填' })
|
||||
mch_secret_key?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '微信商户私钥证书' })
|
||||
@ValidateIf(o => o.type === PayType.WECHATPAY)
|
||||
@IsString({ message: '微信商户私钥证书必填' })
|
||||
mch_secret_cert?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '微信商户公钥证书路径' })
|
||||
@ValidateIf(o => o.type === PayType.WECHATPAY)
|
||||
@IsString({ message: '微信商户公钥证书路径必填' })
|
||||
mch_public_cert_path?: string;
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { IsString, IsOptional, IsEnum, ValidateIf, IsNumber } from 'class-validator';
|
||||
import { Transform } from 'class-transformer';
|
||||
import { PayType } from './CreatePayTemplateDto';
|
||||
|
||||
/**
|
||||
* 更新支付模板DTO
|
||||
* 对应PHP: app\validate\pay\PayTemplate 的 edit 场景
|
||||
*/
|
||||
export class UpdatePayTemplateDto {
|
||||
@ApiProperty({ description: '支付模板ID', example: 1 })
|
||||
@IsNumber({}, { message: '支付模板ID必须是数字' })
|
||||
id: number;
|
||||
@ApiProperty({ description: '模板名称', example: '微信支付' })
|
||||
@IsString()
|
||||
name: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '支付方式', enum: PayType, example: PayType.WECHATPAY })
|
||||
@IsOptional()
|
||||
@IsEnum(PayType, { message: '不存在的支付方式' })
|
||||
type?: PayType;
|
||||
|
||||
// 支付宝相关字段 - 仅当type为ALIPAY时必填
|
||||
@ApiPropertyOptional({ description: '支付宝AppID', example: '2021000000000000' })
|
||||
@IsOptional()
|
||||
@ValidateIf(o => o.type === PayType.ALIPAY)
|
||||
@IsString({ message: '支付宝AppID必填' })
|
||||
app_id?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '支付宝应用私钥证书' })
|
||||
@IsOptional()
|
||||
@ValidateIf(o => o.type === PayType.ALIPAY)
|
||||
@IsString({ message: '支付宝应用私钥证书必填' })
|
||||
app_secret_cert?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '支付宝应用公钥证书路径' })
|
||||
@IsOptional()
|
||||
@ValidateIf(o => o.type === PayType.ALIPAY)
|
||||
@IsString({ message: '支付宝应用公钥证书路径必填' })
|
||||
app_public_cert_path?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '支付宝公钥证书路径' })
|
||||
@IsOptional()
|
||||
@ValidateIf(o => o.type === PayType.ALIPAY)
|
||||
@IsString({ message: '支付宝公钥证书路径必填' })
|
||||
alipay_public_cert_path?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '支付宝根证书路径' })
|
||||
@IsOptional()
|
||||
@ValidateIf(o => o.type === PayType.ALIPAY)
|
||||
@IsString({ message: '支付宝根证书路径必填' })
|
||||
alipay_root_cert_path?: string;
|
||||
|
||||
// 微信支付相关字段 - 仅当type为WECHATPAY时必填
|
||||
@ApiPropertyOptional({ description: '微信商户号', example: '1900000109' })
|
||||
@IsOptional()
|
||||
@ValidateIf(o => o.type === PayType.WECHATPAY)
|
||||
@IsString({ message: '微信商户号必填' })
|
||||
mch_id?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '微信商户API密钥' })
|
||||
@IsOptional()
|
||||
@ValidateIf(o => o.type === PayType.WECHATPAY)
|
||||
@IsString({ message: '微信商户API密钥必填' })
|
||||
mch_secret_key?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '微信商户私钥证书' })
|
||||
@IsOptional()
|
||||
@ValidateIf(o => o.type === PayType.WECHATPAY)
|
||||
@IsString({ message: '微信商户私钥证书必填' })
|
||||
mch_secret_cert?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '微信商户公钥证书路径' })
|
||||
@IsOptional()
|
||||
@ValidateIf(o => o.type === PayType.WECHATPAY)
|
||||
@IsString({ message: '微信商户公钥证书路径必填' })
|
||||
mch_public_cert_path?: string;
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './CreatePayTemplateDto';
|
||||
export * from './UpdatePayTemplateDto';
|
||||
@@ -1,60 +1,71 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from "typeorm";
|
||||
import { BaseEntity } from "../../../core/base/BaseEntity";
|
||||
|
||||
/**
|
||||
* 支付实体
|
||||
* 对应PHP: app\model\pay\Pay
|
||||
*/
|
||||
@Entity("pay")
|
||||
export class Pay extends BaseEntity {
|
||||
export class Pay {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ name: "site_id", type: "int", comment: "站点ID" })
|
||||
siteId: number;
|
||||
@Column({ name: "site_id", type: "int", default: 0, comment: "站点id" })
|
||||
site_id: number;
|
||||
|
||||
@Column({ name: "out_trade_no", length: 100, comment: "商户订单号" })
|
||||
outTradeNo: string;
|
||||
@Column({ name: "main_id", type: "int", default: 0, comment: "支付会员id" })
|
||||
main_id: number;
|
||||
|
||||
@Column({ name: "trade_type", length: 50, comment: "交易类型" })
|
||||
tradeType: string;
|
||||
@Column({ name: "from_main_id", type: "int", default: 0, comment: "发起支付会员id" })
|
||||
from_main_id: number;
|
||||
|
||||
@Column({ name: "trade_id", type: "int", default: 0, comment: "交易ID" })
|
||||
tradeId: number;
|
||||
@Column({ name: "out_trade_no", type: "varchar", length: 255, default: "", comment: "支付流水号" })
|
||||
out_trade_no: string;
|
||||
|
||||
@Column({ name: "trade_no", length: 100, default: "", comment: "交易流水号" })
|
||||
tradeNo: string;
|
||||
@Column({ name: "trade_type", type: "varchar", length: 255, default: "", comment: "业务类型" })
|
||||
trade_type: string;
|
||||
|
||||
@Column({ name: "body", length: 500, comment: "商品描述" })
|
||||
@Column({ name: "trade_id", type: "int", default: 0, comment: "业务id" })
|
||||
trade_id: number;
|
||||
|
||||
@Column({ name: "trade_no", type: "varchar", length: 255, default: "", comment: "交易单号" })
|
||||
trade_no: string;
|
||||
|
||||
@Column({ name: "body", type: "varchar", length: 1000, default: "", comment: "支付主体" })
|
||||
body: string;
|
||||
|
||||
@Column({ name: "money", type: "decimal", precision: 10, scale: 2, comment: "支付金额" })
|
||||
@Column({ name: "money", type: "decimal", precision: 10, scale: 2, default: 0.00, comment: "支付金额" })
|
||||
money: number;
|
||||
|
||||
@Column({ name: "voucher", length: 500, default: "", comment: "支付凭证" })
|
||||
@Column({ name: "voucher", type: "varchar", length: 255, default: "", comment: "支付票据" })
|
||||
voucher: string;
|
||||
|
||||
@Column({ name: "status", type: "tinyint", default: 0, comment: "支付状态 0待支付 1已支付 2已关闭 3已退款" })
|
||||
@Column({ name: "status", type: "int", default: 0, comment: "支付状态(0.待支付 1. 支付中 2. 已支付 -1已取消)" })
|
||||
status: number;
|
||||
|
||||
@Column({ name: "type", length: 50, comment: "支付方式" })
|
||||
@Column({ name: "json", type: "varchar", length: 255, default: "", comment: "支付扩展用支付信息" })
|
||||
json: string;
|
||||
|
||||
@CreateDateColumn({ name: "create_time", type: "int", default: 0, comment: "创建时间" })
|
||||
create_time: number;
|
||||
|
||||
@Column({ name: "pay_time", type: "int", default: 0, comment: "支付时间" })
|
||||
pay_time: number;
|
||||
|
||||
@Column({ name: "type", type: "varchar", length: 255, default: "", comment: "支付方式" })
|
||||
type: string;
|
||||
|
||||
@Column({ name: "channel", length: 50, comment: "支付渠道" })
|
||||
@Column({ name: "channel", type: "varchar", length: 50, default: "", comment: "支付渠道" })
|
||||
channel: string;
|
||||
|
||||
@Column({ name: "fail_reason", length: 500, default: "", comment: "失败原因" })
|
||||
failReason: string;
|
||||
@Column({ name: "fail_reason", type: "varchar", length: 255, default: "", comment: "失败原因" })
|
||||
fail_reason: string;
|
||||
|
||||
@Column({ name: "allow_type", type: "json", nullable: true, comment: "允许的支付方式" })
|
||||
allowType: string[];
|
||||
@Column({ name: "close_time", type: "int", default: 0, comment: "关闭时间" })
|
||||
close_time: number;
|
||||
|
||||
@Column({ name: "pay_time", type: "timestamp", nullable: true, comment: "支付时间" })
|
||||
payTime: Date;
|
||||
@Column({ name: "is_del", type: "tinyint", default: 0, comment: "是否删除" })
|
||||
is_del: number;
|
||||
|
||||
@Column({ name: "close_time", type: "timestamp", nullable: true, comment: "关闭时间" })
|
||||
closeTime: Date;
|
||||
|
||||
@CreateDateColumn({ name: "create_time", comment: "创建时间" })
|
||||
createTime: Date;
|
||||
@UpdateDateColumn({ name: "update_time", type: "int", default: 0, comment: "更新时间" })
|
||||
update_time: number;
|
||||
}
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
|
||||
import { BaseEntity } from '../../../core/base/BaseEntity';
|
||||
|
||||
/**
|
||||
* 支付模板实体
|
||||
* 对应PHP: 支付模板相关数据表
|
||||
*/
|
||||
@Entity('pay_template')
|
||||
export class PayTemplate extends BaseEntity {
|
||||
@PrimaryGeneratedColumn({ name: 'id' })
|
||||
id: number;
|
||||
|
||||
|
||||
@Column({ name: 'name', type: 'varchar', length: 50, comment: '模板名称' })
|
||||
name: string;
|
||||
|
||||
@Column({ name: 'type', type: 'varchar', length: 50, comment: '支付方式' })
|
||||
type: string;
|
||||
|
||||
// 支付宝相关字段
|
||||
@Column({ name: 'app_id', type: 'varchar', length: 255, nullable: true, comment: '支付宝AppID' })
|
||||
app_id: string | null;
|
||||
|
||||
@Column({ name: 'app_secret_cert', type: 'text', nullable: true, comment: '支付宝应用私钥证书' })
|
||||
app_secret_cert: string | null;
|
||||
|
||||
@Column({ name: 'app_public_cert_path', type: 'varchar', length: 255, nullable: true, comment: '支付宝应用公钥证书路径' })
|
||||
app_public_cert_path: string | null;
|
||||
|
||||
@Column({ name: 'alipay_public_cert_path', type: 'varchar', length: 255, nullable: true, comment: '支付宝公钥证书路径' })
|
||||
alipay_public_cert_path: string | null;
|
||||
|
||||
@Column({ name: 'alipay_root_cert_path', type: 'varchar', length: 255, nullable: true, comment: '支付宝根证书路径' })
|
||||
alipay_root_cert_path: string | null;
|
||||
|
||||
// 微信支付相关字段
|
||||
@Column({ name: 'mch_id', type: 'varchar', length: 255, nullable: true, comment: '微信商户号' })
|
||||
mch_id: string | null;
|
||||
|
||||
@Column({ name: 'mch_secret_key', type: 'varchar', length: 255, nullable: true, comment: '微信商户API密钥' })
|
||||
mch_secret_key: string | null;
|
||||
|
||||
@Column({ name: 'mch_secret_cert', type: 'text', nullable: true, comment: '微信商户私钥证书' })
|
||||
mch_secret_cert: string | null;
|
||||
|
||||
@Column({ name: 'mch_public_cert_path', type: 'varchar', length: 255, nullable: true, comment: '微信商户公钥证书路径' })
|
||||
mch_public_cert_path: string | null;
|
||||
|
||||
// site_id/create_time/update_time 由 BaseEntity 提供
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Module } from "@nestjs/common";
|
||||
import { Module, forwardRef } from "@nestjs/common";
|
||||
import { TypeOrmModule } from "@nestjs/typeorm";
|
||||
import { Pay } from "./entities/Pay";
|
||||
import { PayChannel } from "./entities/PayChannel";
|
||||
import { PayTemplate } from "./entities/PayTemplate";
|
||||
import { PayRefund } from "./entities/PayRefund";
|
||||
import { PayTransfer } from "./entities/PayTransfer";
|
||||
|
||||
@@ -15,7 +14,6 @@ import { CorePayTransferService } from "./services/core/CorePayTransferService";
|
||||
// Admin Services
|
||||
import { PayService } from "./services/admin/PayService";
|
||||
import { PayChannelService } from "./services/admin/PayChannelService";
|
||||
import { PayTemplateService } from "./services/admin/PayTemplateService";
|
||||
import { PayApiService } from "./services/api/PayApiService";
|
||||
import { TransferApiService } from "./services/api/TransferApiService";
|
||||
import { PayRefundService } from "./services/admin/PayRefundService";
|
||||
@@ -24,12 +22,13 @@ import { TransferService } from "./services/admin/TransferService";
|
||||
// Admin Controllers
|
||||
import { PayController } from "./controllers/admin/PayController";
|
||||
import { PayChannelController } from "./controllers/admin/PayChannelController";
|
||||
import { PayTemplateController } from "./controllers/admin/PayTemplateController";
|
||||
import { PayApiController } from "./controllers/api/PayApiController";
|
||||
import { TransferApiController } from "./controllers/api/TransferApiController";
|
||||
import { PayRefundController } from "./controllers/adminapi/PayRefundController";
|
||||
import { TransferController } from "./controllers/adminapi/TransferController";
|
||||
import { JobsModule } from "../../common/jobs/jobs.module";
|
||||
import { VendorModule } from "../../vendor";
|
||||
import { PaymentAdapterRegistry } from "../../vendor/pay/providers/payment.provider";
|
||||
import { PaymentEventHandlers } from "./subscribers/paymentEventHandlers";
|
||||
|
||||
/**
|
||||
@@ -38,8 +37,9 @@ import { PaymentEventHandlers } from "./subscribers/paymentEventHandlers";
|
||||
*/
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([Pay, PayChannel, PayTemplate, PayRefund, PayTransfer]),
|
||||
JobsModule,
|
||||
TypeOrmModule.forFeature([Pay, PayChannel, PayRefund, PayTransfer]),
|
||||
forwardRef(() => JobsModule),
|
||||
VendorModule,
|
||||
],
|
||||
providers: [
|
||||
// Core Services
|
||||
@@ -51,18 +51,17 @@ import { PaymentEventHandlers } from "./subscribers/paymentEventHandlers";
|
||||
// Admin Services
|
||||
PayService,
|
||||
PayChannelService,
|
||||
PayTemplateService,
|
||||
PayRefundService,
|
||||
TransferService,
|
||||
PayApiService,
|
||||
TransferApiService,
|
||||
PaymentEventHandlers,
|
||||
PaymentAdapterRegistry,
|
||||
],
|
||||
controllers: [
|
||||
// Admin Controllers
|
||||
PayController,
|
||||
PayChannelController,
|
||||
PayTemplateController,
|
||||
PayApiController,
|
||||
TransferApiController,
|
||||
PayRefundController,
|
||||
@@ -84,6 +83,7 @@ import { PaymentEventHandlers } from "./subscribers/paymentEventHandlers";
|
||||
// Api Services
|
||||
PayApiService,
|
||||
TransferApiService,
|
||||
PaymentAdapterRegistry,
|
||||
],
|
||||
})
|
||||
export class PayModule {}
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { PayTemplate } from '../../entities/PayTemplate';
|
||||
import { CreatePayTemplateDto, PayType } from '../../dto/admin/CreatePayTemplateDto';
|
||||
import { UpdatePayTemplateDto } from '../../dto/admin/UpdatePayTemplateDto';
|
||||
|
||||
/**
|
||||
* 支付模板服务 - Admin层
|
||||
* 对应PHP: 支付模板相关服务
|
||||
*/
|
||||
@Injectable()
|
||||
export class PayTemplateService {
|
||||
constructor(
|
||||
@InjectRepository(PayTemplate)
|
||||
private readonly payTemplateRepository: Repository<PayTemplate>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 创建支付模板
|
||||
* @param siteId 站点ID
|
||||
* @param createPayTemplateDto 创建支付模板DTO
|
||||
* @returns 创建的支付模板
|
||||
*/
|
||||
async create(siteId: number, createPayTemplateDto: CreatePayTemplateDto) {
|
||||
const payTemplate = this.payTemplateRepository.create({
|
||||
site_id: siteId,
|
||||
...createPayTemplateDto,
|
||||
create_time: Math.floor(Date.now() / 1000),
|
||||
});
|
||||
|
||||
return await this.payTemplateRepository.save(payTemplate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新支付模板
|
||||
* @param siteId 站点ID
|
||||
* @param id 支付模板ID
|
||||
* @param updatePayTemplateDto 更新支付模板DTO
|
||||
* @returns 更新后的支付模板
|
||||
*/
|
||||
async update(siteId: number, id: number, updatePayTemplateDto: UpdatePayTemplateDto) {
|
||||
const payTemplate = await this.payTemplateRepository.findOne({
|
||||
where: { id, site_id: siteId },
|
||||
});
|
||||
|
||||
if (!payTemplate) {
|
||||
throw new Error('支付模板不存在');
|
||||
}
|
||||
|
||||
// 更新字段
|
||||
Object.assign(payTemplate, updatePayTemplateDto, {
|
||||
update_time: Math.floor(Date.now() / 1000),
|
||||
});
|
||||
|
||||
return await this.payTemplateRepository.save(payTemplate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取支付模板详情
|
||||
* @param siteId 站点ID
|
||||
* @param id 支付模板ID
|
||||
* @returns 支付模板详情
|
||||
*/
|
||||
async getDetail(siteId: number, id: number) {
|
||||
return await this.payTemplateRepository.findOne({
|
||||
where: { id, site_id: siteId },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取支付模板列表
|
||||
* @param siteId 站点ID
|
||||
* @param query 查询参数
|
||||
* @returns 支付模板列表
|
||||
*/
|
||||
async getList(siteId: number, query: any) {
|
||||
const queryBuilder = this.payTemplateRepository.createQueryBuilder('pay_template')
|
||||
.where('pay_template.site_id = :siteId', { siteId });
|
||||
|
||||
// 按名称搜索
|
||||
if (query.name) {
|
||||
queryBuilder.andWhere('pay_template.name LIKE :name', { name: `%${query.name}%` });
|
||||
}
|
||||
|
||||
// 按支付方式筛选
|
||||
if (query.type) {
|
||||
queryBuilder.andWhere('pay_template.type = :type', { type: query.type });
|
||||
}
|
||||
|
||||
// 分页
|
||||
const page = query.page || 1;
|
||||
const pageSize = query.page_size || 10;
|
||||
const skip = (page - 1) * pageSize;
|
||||
|
||||
queryBuilder.orderBy('pay_template.create_time', 'DESC')
|
||||
.skip(skip)
|
||||
.take(pageSize);
|
||||
|
||||
const [list, total] = await queryBuilder.getManyAndCount();
|
||||
|
||||
return {
|
||||
list,
|
||||
page_info: {
|
||||
page,
|
||||
page_size: pageSize,
|
||||
count: total,
|
||||
page_count: Math.ceil(total / pageSize),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除支付模板
|
||||
* @param siteId 站点ID
|
||||
* @param id 支付模板ID
|
||||
* @returns 是否成功
|
||||
*/
|
||||
async delete(siteId: number, id: number) {
|
||||
const result = await this.payTemplateRepository.delete({
|
||||
id,
|
||||
site_id: siteId,
|
||||
});
|
||||
|
||||
return (result.affected || 0) > 0;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { Injectable, UnauthorizedException } from "@nestjs/common";
|
||||
import { CorePayService } from "../core/CorePayService";
|
||||
import { CorePayChannelService } from "../core/CorePayChannelService";
|
||||
import { CreatePayDto, RefundDto } from "../../dto/PayDto";
|
||||
@@ -83,17 +83,17 @@ export class PayApiService {
|
||||
return existed;
|
||||
}
|
||||
const created = await this.corePayService.add({
|
||||
siteId,
|
||||
outTradeNo: dto.outTradeNo,
|
||||
tradeType: dto.tradeType,
|
||||
tradeId: dto.tradeId,
|
||||
site_id: siteId,
|
||||
out_trade_no: dto.outTradeNo,
|
||||
trade_type: dto.tradeType,
|
||||
trade_id: dto.tradeId,
|
||||
body: dto.body,
|
||||
money: dto.money as any,
|
||||
voucher: dto.voucher || "",
|
||||
status: 0,
|
||||
type: dto.type,
|
||||
channel: dto.channel,
|
||||
allowType: dto.allowType || undefined,
|
||||
// allowType: dto.allowType || undefined, // 字段不存在,注释掉
|
||||
});
|
||||
const gatewayResp = await adapter.create(config, {
|
||||
outTradeNo: dto.outTradeNo,
|
||||
@@ -111,7 +111,10 @@ export class PayApiService {
|
||||
const channel = req.params?.channel || req.query?.channel || req.body?.channel;
|
||||
const outTradeNoFromReq = req.query?.outTradeNo || req.body?.out_trade_no || req.body?.outTradeNo;
|
||||
const payUnsafe = outTradeNoFromReq ? await this.corePayService.findByOutTradeNoUnsafe(outTradeNoFromReq) : null;
|
||||
const siteId = payUnsafe?.siteId || req.user?.siteId || 0;
|
||||
const siteId = payUnsafe?.site_id || req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('Missing site_id');
|
||||
}
|
||||
const { adapter, config } = await this.paymentRegistry.resolve(siteId, type, channel);
|
||||
const result = await adapter.notify(config, {
|
||||
headers: req.headers,
|
||||
@@ -122,7 +125,7 @@ export class PayApiService {
|
||||
await this.corePayService.updateStatus(siteId, result.outTradeNo, 1, result.tradeNo);
|
||||
const pay = await this.corePayService.findByOutTradeNo(siteId, result.outTradeNo);
|
||||
if (pay) {
|
||||
await this.domainEventService.publishPaymentEvent('succeeded', String(pay.id), String(siteId), { outTradeNo: pay.outTradeNo, tradeNo: pay.tradeNo });
|
||||
await this.domainEventService.publishPaymentEvent('succeeded', String(pay.id), String(siteId), { outTradeNo: pay.out_trade_no, tradeNo: pay.trade_no });
|
||||
}
|
||||
} else if (result.status === 'CLOSED') {
|
||||
await this.corePayService.updateStatus(siteId, result.outTradeNo, 2);
|
||||
@@ -134,11 +137,11 @@ export class PayApiService {
|
||||
const pay = await this.corePayService.findByOutTradeNo(siteId, outTradeNo);
|
||||
if (!pay) return null;
|
||||
return {
|
||||
outTradeNo: pay.outTradeNo,
|
||||
outTradeNo: pay.out_trade_no,
|
||||
status: pay.status,
|
||||
tradeNo: pay.tradeNo,
|
||||
payTime: pay.payTime,
|
||||
closeTime: pay.closeTime,
|
||||
tradeNo: pay.trade_no,
|
||||
payTime: pay.pay_time,
|
||||
closeTime: pay.close_time,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -159,7 +162,7 @@ export class PayApiService {
|
||||
refundMoney: body.refundMoney,
|
||||
reason: body.reason,
|
||||
});
|
||||
await this.domainEventService.publishPaymentEvent('refunded', String(pay.id), String(siteId), { outTradeNo: pay.outTradeNo, refundNo: body.refundNo, refundMoney: body.refundMoney });
|
||||
await this.domainEventService.publishPaymentEvent('refunded', String(pay.id), String(siteId), { outTradeNo: pay.out_trade_no, refundNo: body.refundNo, refundMoney: body.refundMoney });
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { InjectRepository } from "@nestjs/typeorm";
|
||||
import { Repository, Like } from "typeorm";
|
||||
import { BaseService } from "../../../../core/base/BaseService";
|
||||
import { Pay } from "../../entities/Pay";
|
||||
|
||||
/**
|
||||
@@ -9,13 +8,11 @@ import { Pay } from "../../entities/Pay";
|
||||
* 对应PHP: app\service\core\pay\CorePayService
|
||||
*/
|
||||
@Injectable()
|
||||
export class CorePayService extends BaseService<Pay> {
|
||||
export class CorePayService {
|
||||
constructor(
|
||||
@InjectRepository(Pay)
|
||||
private readonly payRepository: Repository<Pay>,
|
||||
) {
|
||||
super(payRepository);
|
||||
}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 分页查询支付记录
|
||||
@@ -29,24 +26,24 @@ export class CorePayService extends BaseService<Pay> {
|
||||
.where("pay.site_id = :siteId", { siteId })
|
||||
.select([
|
||||
"pay.id",
|
||||
"pay.outTradeNo",
|
||||
"pay.tradeType",
|
||||
"pay.tradeId",
|
||||
"pay.tradeNo",
|
||||
"pay.out_trade_no",
|
||||
"pay.trade_type",
|
||||
"pay.trade_id",
|
||||
"pay.trade_no",
|
||||
"pay.body",
|
||||
"pay.money",
|
||||
"pay.voucher",
|
||||
"pay.status",
|
||||
"pay.type",
|
||||
"pay.channel",
|
||||
"pay.failReason",
|
||||
"pay.payTime",
|
||||
"pay.closeTime",
|
||||
"pay.createTime",
|
||||
"pay.fail_reason",
|
||||
"pay.pay_time",
|
||||
"pay.close_time",
|
||||
"pay.create_time",
|
||||
]);
|
||||
|
||||
if (where.outTradeNo) {
|
||||
queryBuilder.andWhere("pay.outTradeNo LIKE :outTradeNo", {
|
||||
queryBuilder.andWhere("pay.out_trade_no LIKE :outTradeNo", {
|
||||
outTradeNo: `%${where.outTradeNo}%`,
|
||||
});
|
||||
}
|
||||
@@ -94,7 +91,7 @@ export class CorePayService extends BaseService<Pay> {
|
||||
.andWhere("pay.type = :type", { type: "offline" })
|
||||
.select([
|
||||
"pay.id",
|
||||
"pay.outTradeNo",
|
||||
"pay.out_trade_no",
|
||||
"pay.type",
|
||||
"pay.money",
|
||||
"pay.body",
|
||||
@@ -106,7 +103,7 @@ export class CorePayService extends BaseService<Pay> {
|
||||
]);
|
||||
|
||||
if (where.outTradeNo) {
|
||||
queryBuilder.andWhere("pay.outTradeNo LIKE :outTradeNo", {
|
||||
queryBuilder.andWhere("pay.out_trade_no LIKE :outTradeNo", {
|
||||
outTradeNo: `%${where.outTradeNo}%`,
|
||||
});
|
||||
}
|
||||
@@ -141,23 +138,23 @@ export class CorePayService extends BaseService<Pay> {
|
||||
*/
|
||||
async getDetail(siteId: number, id: number) {
|
||||
return await this.payRepository.findOne({
|
||||
where: { siteId, id },
|
||||
where: { site_id: siteId, id },
|
||||
select: [
|
||||
"id",
|
||||
"outTradeNo",
|
||||
"tradeType",
|
||||
"tradeId",
|
||||
"tradeNo",
|
||||
"out_trade_no",
|
||||
"trade_type",
|
||||
"trade_id",
|
||||
"trade_no",
|
||||
"body",
|
||||
"money",
|
||||
"voucher",
|
||||
"status",
|
||||
"type",
|
||||
"channel",
|
||||
"failReason",
|
||||
"payTime",
|
||||
"closeTime",
|
||||
"createTime",
|
||||
"fail_reason",
|
||||
"pay_time",
|
||||
"close_time",
|
||||
"create_time",
|
||||
],
|
||||
});
|
||||
}
|
||||
@@ -199,11 +196,11 @@ export class CorePayService extends BaseService<Pay> {
|
||||
updateData.tradeNo = tradeNo;
|
||||
}
|
||||
} else if (status === 2) {
|
||||
updateData.closeTime = new Date();
|
||||
updateData.close_time = new Date();
|
||||
}
|
||||
|
||||
const result = await this.payRepository.update(
|
||||
{ siteId, outTradeNo },
|
||||
{ site_id: siteId, out_trade_no: outTradeNo },
|
||||
updateData,
|
||||
);
|
||||
return (result.affected || 0) > 0;
|
||||
@@ -228,11 +225,11 @@ export class CorePayService extends BaseService<Pay> {
|
||||
*/
|
||||
async refuse(siteId: number, outTradeNo: string, reason: string): Promise<boolean> {
|
||||
const result = await this.payRepository.update(
|
||||
{ siteId, outTradeNo },
|
||||
{ site_id: siteId, out_trade_no: outTradeNo },
|
||||
{
|
||||
status: 2,
|
||||
failReason: reason,
|
||||
closeTime: new Date(),
|
||||
fail_reason: reason,
|
||||
close_time: Math.floor(Date.now() / 1000),
|
||||
},
|
||||
);
|
||||
return (result.affected || 0) > 0;
|
||||
@@ -272,7 +269,7 @@ export class CorePayService extends BaseService<Pay> {
|
||||
*/
|
||||
async findByOutTradeNo(siteId: number, outTradeNo: string) {
|
||||
return await this.payRepository.findOne({
|
||||
where: { siteId, outTradeNo },
|
||||
where: { site_id: siteId, out_trade_no: outTradeNo },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -281,7 +278,7 @@ export class CorePayService extends BaseService<Pay> {
|
||||
*/
|
||||
async findByOutTradeNoUnsafe(outTradeNo: string) {
|
||||
return await this.payRepository.findOne({
|
||||
where: { outTradeNo },
|
||||
where: { out_trade_no: outTradeNo },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -292,7 +289,7 @@ export class CorePayService extends BaseService<Pay> {
|
||||
const result = await this.payRepository
|
||||
.createQueryBuilder()
|
||||
.update(Pay)
|
||||
.set({ status: 2, closeTime: () => 'CURRENT_TIMESTAMP' })
|
||||
.set({ status: 2, close_time: () => 'CURRENT_TIMESTAMP' })
|
||||
.where('status = :status', { status: 0 })
|
||||
.andWhere('create_time < :before', { before })
|
||||
.execute();
|
||||
@@ -381,7 +378,7 @@ export class CorePayService extends BaseService<Pay> {
|
||||
.where('pay.site_id = :siteId', { siteId });
|
||||
|
||||
if (keyword) {
|
||||
query.andWhere('pay.outTradeNo LIKE :keyword', { keyword: `%${keyword}%` });
|
||||
query.andWhere('pay.out_trade_no LIKE :keyword', { keyword: `%${keyword}%` });
|
||||
}
|
||||
|
||||
if (status !== '') {
|
||||
@@ -493,10 +490,10 @@ export class CorePayService extends BaseService<Pay> {
|
||||
*/
|
||||
async manualComplete(siteId: number, outTradeNo: string) {
|
||||
const result = await this.payRepository.update(
|
||||
{ site_id: siteId, outTradeNo },
|
||||
{ site_id: siteId, out_trade_no: outTradeNo },
|
||||
{
|
||||
status: 1,
|
||||
payTime: new Date(),
|
||||
pay_time: Math.floor(Date.now() / 1000),
|
||||
update_time: Math.floor(Date.now() / 1000),
|
||||
}
|
||||
);
|
||||
@@ -512,10 +509,10 @@ export class CorePayService extends BaseService<Pay> {
|
||||
*/
|
||||
async cancel(siteId: number, outTradeNo: string) {
|
||||
const result = await this.payRepository.update(
|
||||
{ site_id: siteId, outTradeNo },
|
||||
{ site_id: siteId, out_trade_no: outTradeNo },
|
||||
{
|
||||
status: 2,
|
||||
closeTime: new Date(),
|
||||
close_time: Math.floor(Date.now() / 1000),
|
||||
update_time: Math.floor(Date.now() / 1000),
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
|
||||
import { BaseEntity } from '../../../core/base/BaseEntity';
|
||||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';
|
||||
|
||||
@Entity('sys_role')
|
||||
export class SysRole extends BaseEntity {
|
||||
export class SysRole {
|
||||
@PrimaryGeneratedColumn({ name: 'role_id' })
|
||||
role_id: number;
|
||||
|
||||
@Column({ name: 'site_id', type: 'int', default: 0, comment: '站点id' })
|
||||
site_id: number;
|
||||
|
||||
@Column({
|
||||
name: 'role_name',
|
||||
type: 'varchar',
|
||||
@@ -19,7 +21,7 @@ export class SysRole extends BaseEntity {
|
||||
name: 'rules',
|
||||
type: 'text',
|
||||
nullable: true,
|
||||
comment: '角色权限(菜单键数组JSON)',
|
||||
comment: '角色权限(menus_id)',
|
||||
})
|
||||
rules: string;
|
||||
|
||||
@@ -27,10 +29,16 @@ export class SysRole extends BaseEntity {
|
||||
name: 'status',
|
||||
type: 'tinyint',
|
||||
default: 1,
|
||||
comment: '状态:0=禁用,1=启用',
|
||||
comment: '状态',
|
||||
})
|
||||
status: number;
|
||||
|
||||
@CreateDateColumn({ name: 'create_time', type: 'int', default: 0, comment: '添加时间' })
|
||||
create_time: number;
|
||||
|
||||
@UpdateDateColumn({ name: 'update_time', type: 'int', default: 0, comment: '最后修改时间' })
|
||||
update_time: number;
|
||||
|
||||
// 业务逻辑方法 - <20>?PHP 项目保持一<E68C81>?
|
||||
getStatusText(): string {
|
||||
const statusMap: { [key: number]: string } = { 0: '禁用', 1: '正常' };
|
||||
|
||||
@@ -1,31 +1,29 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { BaseService } from '@wwjCore/base/BaseService';
|
||||
import { SysRole } from '../../entities/SysRole';
|
||||
|
||||
@Injectable()
|
||||
export class CoreRoleService extends BaseService<SysRole> {
|
||||
export class CoreRoleService {
|
||||
constructor(
|
||||
@InjectRepository(SysRole)
|
||||
private sysRoleRepository: Repository<SysRole>,
|
||||
) {
|
||||
super(sysRoleRepository);
|
||||
}
|
||||
) {}
|
||||
|
||||
async createRole(roleData: Partial<SysRole>): Promise<SysRole> {
|
||||
return await this.create(roleData);
|
||||
const role = this.sysRoleRepository.create(roleData);
|
||||
return await this.sysRoleRepository.save(role);
|
||||
}
|
||||
|
||||
async getRoleById(role_id: number): Promise<SysRole | null> {
|
||||
return await this.findOne(role_id);
|
||||
return await this.sysRoleRepository.findOne({ where: { role_id } });
|
||||
}
|
||||
|
||||
async getRoleByName(
|
||||
role_name: string,
|
||||
site_id: number,
|
||||
): Promise<SysRole | null> {
|
||||
return await this.findOneBy({ role_name, site_id });
|
||||
return await this.sysRoleRepository.findOne({ where: { role_name, site_id } });
|
||||
}
|
||||
|
||||
async updateRole(
|
||||
@@ -37,7 +35,7 @@ export class CoreRoleService extends BaseService<SysRole> {
|
||||
throw new NotFoundException('角色不存在');
|
||||
}
|
||||
|
||||
await this.update(role_id, updateData);
|
||||
await this.sysRoleRepository.update({ role_id }, updateData);
|
||||
const updatedRole = await this.getRoleById(role_id);
|
||||
if (!updatedRole) {
|
||||
throw new NotFoundException('角色更新后不存在');
|
||||
@@ -51,15 +49,15 @@ export class CoreRoleService extends BaseService<SysRole> {
|
||||
throw new NotFoundException('角色不存在');
|
||||
}
|
||||
|
||||
await this.delete(role_id);
|
||||
await this.sysRoleRepository.delete({ role_id });
|
||||
}
|
||||
|
||||
async getRolesByAppType(site_id: number): Promise<SysRole[]> {
|
||||
return await this.findMany({ site_id });
|
||||
return await this.sysRoleRepository.find({ where: { site_id } });
|
||||
}
|
||||
|
||||
async getActiveRolesByAppType(site_id: number): Promise<SysRole[]> {
|
||||
return await this.findMany({ site_id, status: 1 });
|
||||
return await this.sysRoleRepository.find({ where: { site_id, status: 1 } });
|
||||
}
|
||||
|
||||
async isRoleNameExists(
|
||||
@@ -78,9 +76,9 @@ export class CoreRoleService extends BaseService<SysRole> {
|
||||
}
|
||||
|
||||
async getRoleStats(site_id: number): Promise<any> {
|
||||
const total = await this.count({ site_id });
|
||||
const active = await this.count({ site_id, status: 1 });
|
||||
const inactive = await this.count({ site_id, status: 0 });
|
||||
const total = await this.sysRoleRepository.count({ where: { site_id } });
|
||||
const active = await this.sysRoleRepository.count({ where: { site_id, status: 1 } });
|
||||
const inactive = await this.sysRoleRepository.count({ where: { site_id, status: 0 } });
|
||||
|
||||
return {
|
||||
total,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Controller, Get, Post, Body, Query, UseGuards, Req } from '@nestjs/common';
|
||||
import { Controller, Get, Post, Body, Query, UseGuards, Req, UnauthorizedException } from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation } from '@nestjs/swagger';
|
||||
import type { Request } from 'express';
|
||||
import { JwtAuthGuard } from '../../../auth/guards/JwtAuthGuard';
|
||||
@@ -21,17 +21,21 @@ export class SiteAccountLogController {
|
||||
@Get('page')
|
||||
@ApiOperation({ summary: '账单分页' })
|
||||
async page(@Query() query: SiteAccountLogQueryDto, @Req() req: AuthenticatedRequest) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const data = await this.service.getPage(siteId, query);
|
||||
return { code: 200, message: '获取成功', data };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.service.getPage(siteId, query);
|
||||
}
|
||||
|
||||
@Post()
|
||||
@ApiOperation({ summary: '新增账单' })
|
||||
async add(@Body() body: CreateSiteAccountLogDto, @Req() req: AuthenticatedRequest) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const data = await this.service.add(siteId, body);
|
||||
return { code: 200, message: '创建成功', data };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.service.add(siteId, body);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,16 +17,14 @@ export class SiteController {
|
||||
@Get('page')
|
||||
@ApiOperation({ summary: '站点分页' })
|
||||
async page(@Query() query: SiteQueryDto) {
|
||||
const data = await this.service.getPage(query);
|
||||
return { code: 200, message: '获取成功', data };
|
||||
return await this.service.getPage(query);
|
||||
}
|
||||
|
||||
@Get(':siteId')
|
||||
@ApiOperation({ summary: '站点详情' })
|
||||
@ApiParam({ name: 'siteId', description: '站点ID' })
|
||||
async info(@Param('siteId', ParseIntPipe) siteId: number) {
|
||||
const data = await this.service.getInfo(siteId);
|
||||
return { code: 200, message: '获取成功', data };
|
||||
return await this.service.getInfo(siteId);
|
||||
}
|
||||
|
||||
@Post()
|
||||
@@ -41,8 +39,7 @@ export class SiteController {
|
||||
if (siteId && payload.siteId && payload.siteId !== siteId) {
|
||||
throw new Error('越权操作:site_id 不匹配');
|
||||
}
|
||||
const data = await this.service.add(payload, req);
|
||||
return { code: 200, message: '创建成功', data };
|
||||
return await this.service.add(payload, req);
|
||||
}
|
||||
|
||||
@Put(':siteId')
|
||||
@@ -58,8 +55,7 @@ export class SiteController {
|
||||
if (requestSiteId && siteId !== requestSiteId) {
|
||||
throw new Error('越权操作:site_id 不匹配');
|
||||
}
|
||||
const data = await this.service.edit(siteId, payload, req);
|
||||
return { code: 200, message: '更新成功', data };
|
||||
return await this.service.edit(siteId, payload, req);
|
||||
}
|
||||
|
||||
@Delete(':siteId')
|
||||
@@ -71,8 +67,7 @@ export class SiteController {
|
||||
if (requestSiteId && siteId !== requestSiteId) {
|
||||
throw new Error('越权操作:site_id 不匹配');
|
||||
}
|
||||
const data = await this.service.del(siteId, req);
|
||||
return { code: 200, message: '删除成功', data };
|
||||
return await this.service.del(siteId, req);
|
||||
}
|
||||
|
||||
@Put(':siteId/status/:status')
|
||||
@@ -85,7 +80,6 @@ export class SiteController {
|
||||
if (requestSiteId && siteId !== requestSiteId) {
|
||||
throw new Error('越权操作:site_id 不匹配');
|
||||
}
|
||||
const data = await this.service.updateStatus(siteId, status, req);
|
||||
return { code: 200, message: '更新成功', data };
|
||||
return await this.service.updateStatus(siteId, status, req);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,38 +17,33 @@ export class SiteGroupController {
|
||||
@Get('page')
|
||||
@ApiOperation({ summary: '分组分页' })
|
||||
async page(@Query() query: SiteGroupQueryDto) {
|
||||
const data = await this.service.getPage(query);
|
||||
return { code: 200, message: '获取成功', data };
|
||||
return await this.service.getPage(query);
|
||||
}
|
||||
|
||||
@Get(':group_id')
|
||||
@ApiOperation({ summary: '分组详情' })
|
||||
@ApiParam({ name: 'group_id', description: '分组ID' })
|
||||
async info(@Param('group_id', ParseIntPipe) group_id: number) {
|
||||
const data = await this.service.getInfo(group_id);
|
||||
return { code: 200, message: '获取成功', data };
|
||||
return await this.service.getInfo(group_id);
|
||||
}
|
||||
|
||||
@Post()
|
||||
@ApiOperation({ summary: '新增分组' })
|
||||
async add(@Body() body: CreateSiteGroupDto) {
|
||||
const data = await this.service.add(body);
|
||||
return { code: 200, message: '创建成功', data };
|
||||
return await this.service.add(body);
|
||||
}
|
||||
|
||||
@Put(':group_id')
|
||||
@ApiOperation({ summary: '编辑分组' })
|
||||
@ApiParam({ name: 'group_id', description: '分组ID' })
|
||||
async edit(@Param('group_id', ParseIntPipe) group_id: number, @Body() body: UpdateSiteGroupDto) {
|
||||
const data = await this.service.edit(group_id, body);
|
||||
return { code: 200, message: '更新成功', data };
|
||||
return await this.service.edit(group_id, body);
|
||||
}
|
||||
|
||||
@Delete(':group_id')
|
||||
@ApiOperation({ summary: '删除分组' })
|
||||
@ApiParam({ name: 'group_id', description: '分组ID' })
|
||||
async del(@Param('group_id', ParseIntPipe) group_id: number) {
|
||||
const data = await this.service.del(group_id);
|
||||
return { code: 200, message: '删除成功', data };
|
||||
return await this.service.del(group_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Controller, Get, Put, Post, Body, Param, Query, UseGuards, ParseIntPipe, Req } from '@nestjs/common';
|
||||
import { Controller, Get, Put, Post, Body, Param, Query, UseGuards, ParseIntPipe, Req, UnauthorizedException } from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiParam } from '@nestjs/swagger';
|
||||
import type { Request } from 'express';
|
||||
import { JwtAuthGuard } from '../../../auth/guards/JwtAuthGuard';
|
||||
@@ -20,18 +20,22 @@ export class SiteUserController {
|
||||
@Get('page')
|
||||
@ApiOperation({ summary: '用户分页' })
|
||||
async page(@Query() query: any, @Req() req: AuthenticatedRequest) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const data = await this.service.getPage(siteId, query);
|
||||
return { code: 200, message: '获取成功', data };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.service.getPage(siteId, query);
|
||||
}
|
||||
|
||||
@Get(':uid')
|
||||
@ApiOperation({ summary: '用户详情' })
|
||||
@ApiParam({ name: 'uid', description: '用户ID' })
|
||||
async info(@Param('uid', ParseIntPipe) uid: number, @Req() req: AuthenticatedRequest) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const data = await this.service.getInfo(siteId, uid);
|
||||
return { code: 200, message: '获取成功', data };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.service.getInfo(siteId, uid);
|
||||
}
|
||||
|
||||
@Put(':uid/status/:status')
|
||||
@@ -40,8 +44,7 @@ export class SiteUserController {
|
||||
@Param('uid', ParseIntPipe) uid: number,
|
||||
@Param('status', ParseIntPipe) status: number,
|
||||
) {
|
||||
const data = await this.service.updateStatus(uid, status);
|
||||
return { code: 200, message: '更新成功', data };
|
||||
return await this.service.updateStatus(uid, status);
|
||||
}
|
||||
|
||||
@Post(':uid/roles')
|
||||
@@ -51,9 +54,11 @@ export class SiteUserController {
|
||||
@Body() body: { role_ids: string },
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const data = await this.service.assignRoles(siteId, uid, body.role_ids || '');
|
||||
return { code: 200, message: '更新成功', data };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.service.assignRoles(siteId, uid, body.role_ids || '');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Controller, Get, Query, UseGuards, Req } from '@nestjs/common';
|
||||
import { Controller, Get, Query, UseGuards, Req, UnauthorizedException } from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation } from '@nestjs/swagger';
|
||||
import type { Request } from 'express';
|
||||
import { JwtAuthGuard } from '../../../auth/guards/JwtAuthGuard';
|
||||
@@ -20,9 +20,11 @@ export class UserLogController {
|
||||
@Get('page')
|
||||
@ApiOperation({ summary: '日志分页' })
|
||||
async page(@Query() query: any, @Req() req: AuthenticatedRequest) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const data = await this.service.getPage(siteId, query);
|
||||
return { code: 200, message: '获取成功', data };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.service.getPage(siteId, query);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,102 +1,104 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from "typeorm";
|
||||
import { BaseEntity } from "../../../core/base/BaseEntity";
|
||||
|
||||
/**
|
||||
* 站点实体
|
||||
* 对应PHP: app\model\site\Site
|
||||
*/
|
||||
@Entity("site")
|
||||
export class Site extends BaseEntity {
|
||||
export class Site {
|
||||
@PrimaryGeneratedColumn({ name: "site_id" })
|
||||
siteId: number;
|
||||
site_id: number;
|
||||
|
||||
@Column({ name: "site_name", length: 100, comment: "站点名称" })
|
||||
siteName: string;
|
||||
@Column({ name: "site_name", type: "varchar", length: 50, default: "", comment: "站点名称" })
|
||||
site_name: string;
|
||||
|
||||
@Column({ name: "front_end_name", length: 100, default: "", comment: "前端名称" })
|
||||
frontEndName: string;
|
||||
@Column({ name: "group_id", type: "int", default: 0, comment: "分组ID(0:不限制)" })
|
||||
group_id: number;
|
||||
|
||||
@Column({ name: "front_end_logo", length: 500, default: "", comment: "前端Logo" })
|
||||
frontEndLogo: string;
|
||||
|
||||
@Column({ name: "front_end_icon", length: 500, default: "", comment: "前端图标" })
|
||||
frontEndIcon: string;
|
||||
|
||||
@Column({ name: "app_type", length: 20, default: "site", comment: "应用类型" })
|
||||
appType: string;
|
||||
|
||||
@Column({ name: "keywords", length: 200, default: "", comment: "关键词" })
|
||||
@Column({ name: "keywords", type: "varchar", length: 255, default: "", comment: "关键字" })
|
||||
keywords: string;
|
||||
|
||||
@Column({ name: "logo", length: 500, default: "", comment: "Logo" })
|
||||
@Column({ name: "app_type", type: "varchar", length: 50, default: "admin", comment: "站点类型" })
|
||||
app_type: string;
|
||||
|
||||
@Column({ name: "logo", type: "varchar", length: 255, default: "", comment: "站点logo" })
|
||||
logo: string;
|
||||
|
||||
@Column({ name: "icon", length: 500, default: "", comment: "图标" })
|
||||
icon: string;
|
||||
|
||||
@Column({ name: "desc", type: "text", default: "", comment: "描述" })
|
||||
@Column({ name: "desc", type: "varchar", length: 255, default: "", comment: "简介" })
|
||||
desc: string;
|
||||
|
||||
@Column({ name: "status", type: "tinyint", default: 1, comment: "状态 0禁用 1启用" })
|
||||
@Column({ name: "status", type: "tinyint", default: 1, comment: "状态 1-正常 0-体验期 2-已到期" })
|
||||
status: number;
|
||||
|
||||
@Column({ name: "latitude", type: "decimal", precision: 10, scale: 6, default: 0, comment: "纬度" })
|
||||
latitude: number;
|
||||
@Column({ name: "latitude", type: "varchar", length: 255, default: "", comment: "纬度" })
|
||||
latitude: string;
|
||||
|
||||
@Column({ name: "longitude", type: "decimal", precision: 10, scale: 6, default: 0, comment: "经度" })
|
||||
longitude: number;
|
||||
@Column({ name: "longitude", type: "varchar", length: 255, default: "", comment: "经度" })
|
||||
longitude: string;
|
||||
|
||||
@Column({ name: "province_id", type: "int", default: 0, comment: "省份ID" })
|
||||
provinceId: number;
|
||||
@Column({ name: "province_id", type: "int", default: 0, comment: "省" })
|
||||
province_id: number;
|
||||
|
||||
@Column({ name: "city_id", type: "int", default: 0, comment: "城市ID" })
|
||||
cityId: number;
|
||||
@Column({ name: "city_id", type: "int", default: 0, comment: "市" })
|
||||
city_id: number;
|
||||
|
||||
@Column({ name: "district_id", type: "int", default: 0, comment: "区县ID" })
|
||||
districtId: number;
|
||||
@Column({ name: "district_id", type: "int", default: 0, comment: "区" })
|
||||
district_id: number;
|
||||
|
||||
@Column({ name: "address", length: 200, default: "", comment: "地址" })
|
||||
@Column({ name: "address", type: "varchar", length: 255, default: "", comment: "详细地址" })
|
||||
address: string;
|
||||
|
||||
@Column({ name: "full_address", length: 500, default: "", comment: "完整地址" })
|
||||
fullAddress: string;
|
||||
@Column({ name: "full_address", type: "varchar", length: 255, default: "", comment: "完整地址" })
|
||||
full_address: string;
|
||||
|
||||
@Column({ name: "phone", length: 20, default: "", comment: "电话" })
|
||||
@Column({ name: "phone", type: "varchar", length: 255, default: "", comment: "客服电话" })
|
||||
phone: string;
|
||||
|
||||
@Column({ name: "business_hours", length: 200, default: "", comment: "营业时间" })
|
||||
businessHours: string;
|
||||
@Column({ name: "business_hours", type: "varchar", length: 255, default: "", comment: "营业时间" })
|
||||
business_hours: string;
|
||||
|
||||
@Column({ name: "expire_time", type: "timestamp", nullable: true, comment: "过期时间" })
|
||||
expireTime: Date;
|
||||
@CreateDateColumn({ name: "create_time", type: "int", default: 0, comment: "创建时间" })
|
||||
create_time: number;
|
||||
|
||||
@Column({ name: "group_id", type: "int", default: 0, comment: "分组ID" })
|
||||
groupId: number;
|
||||
@Column({ name: "expire_time", type: "bigint", default: 0, comment: "到期时间(如果是0 无限期)" })
|
||||
expire_time: number;
|
||||
|
||||
@Column({ name: "app", type: "json", nullable: true, comment: "应用配置" })
|
||||
app: string[];
|
||||
@Column({ name: "front_end_name", type: "varchar", length: 50, default: "", comment: "前台名称" })
|
||||
front_end_name: string;
|
||||
|
||||
@Column({ name: "addons", type: "json", nullable: true, comment: "插件配置" })
|
||||
addons: string[];
|
||||
@Column({ name: "front_end_logo", type: "varchar", length: 255, default: "", comment: "前台logo" })
|
||||
front_end_logo: string;
|
||||
|
||||
@Column({ name: "initalled_addon", type: "json", nullable: true, comment: "已安装插件" })
|
||||
initalledAddon: string[];
|
||||
@Column({ name: "front_end_icon", type: "varchar", length: 255, default: "", comment: "前台图标" })
|
||||
front_end_icon: string;
|
||||
|
||||
@Column({ name: "site_domain", length: 200, default: "", comment: "站点域名" })
|
||||
siteDomain: string;
|
||||
@Column({ name: "icon", type: "varchar", length: 255, default: "", comment: "图标" })
|
||||
icon: string;
|
||||
|
||||
@Column({ name: "meta_title", length: 200, default: "", comment: "SEO标题" })
|
||||
metaTitle: string;
|
||||
@Column({ name: "member_no", type: "varchar", length: 50, default: "0", comment: "会员编号" })
|
||||
member_no: string;
|
||||
|
||||
@Column({ name: "meta_desc", length: 500, default: "", comment: "SEO描述" })
|
||||
metaDesc: string;
|
||||
@Column({ name: "app", type: "text", default: "", comment: "应用" })
|
||||
app: string;
|
||||
|
||||
@Column({ name: "meta_keyword", length: 200, default: "", comment: "SEO关键词" })
|
||||
metaKeyword: string;
|
||||
@Column({ name: "addons", type: "text", default: "", comment: "插件" })
|
||||
addons: string;
|
||||
|
||||
@CreateDateColumn({ name: "create_time", comment: "创建时间" })
|
||||
createTime: Date;
|
||||
@Column({ name: "initalled_addon", type: "text", default: "", comment: "已安装插件" })
|
||||
initalled_addon: string;
|
||||
|
||||
@UpdateDateColumn({ name: "update_time", comment: "更新时间" })
|
||||
updateTime: Date;
|
||||
@Column({ name: "site_domain", type: "varchar", length: 255, default: "", comment: "站点域名" })
|
||||
site_domain: string;
|
||||
|
||||
@Column({ name: "meta_title", type: "varchar", length: 255, default: "", comment: "Meta 标题" })
|
||||
meta_title: string;
|
||||
|
||||
@Column({ name: "meta_desc", type: "varchar", length: 255, default: "", comment: "Meta 描述" })
|
||||
meta_desc: string;
|
||||
|
||||
@UpdateDateColumn({ name: "update_time", type: "int", default: 0, comment: "更新时间" })
|
||||
update_time: number;
|
||||
|
||||
@Column({ name: "is_del", type: "tinyint", default: 0, comment: "是否删除" })
|
||||
is_del: number;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Entity, Column } from 'typeorm';
|
||||
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
|
||||
import { BaseEntity } from '../../../core/base/BaseEntity';
|
||||
|
||||
/**
|
||||
@@ -7,6 +7,8 @@ import { BaseEntity } from '../../../core/base/BaseEntity';
|
||||
*/
|
||||
@Entity('site_account_log')
|
||||
export class SiteAccountLog extends BaseEntity {
|
||||
@PrimaryGeneratedColumn({ name: 'id' })
|
||||
id: number;
|
||||
@Column({ name: 'type', type: 'varchar', length: 50, comment: '类型:pay支付,refund退款,transfer转账' })
|
||||
type: string;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { CoreSiteGroupService } from "../core/CoreSiteGroupService";
|
||||
import { SiteGroupCoreService } from "../core/SiteGroupCoreService";
|
||||
import { SiteGroup } from "../../entities/SiteGroup";
|
||||
|
||||
/**
|
||||
@@ -9,7 +9,7 @@ import { SiteGroup } from "../../entities/SiteGroup";
|
||||
@Injectable()
|
||||
export class SiteGroupService {
|
||||
constructor(
|
||||
private readonly coreSiteGroupService: CoreSiteGroupService,
|
||||
private readonly coreSiteGroupService: SiteGroupCoreService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
|
||||
@@ -110,28 +110,28 @@ export class SiteService {
|
||||
this.normalizeExpire(payload);
|
||||
|
||||
// 校验 site_id 权限
|
||||
if (payload.siteId) {
|
||||
this.validateSiteId(payload.siteId, req);
|
||||
if (payload.site_id) {
|
||||
this.validateSiteId(payload.site_id, req);
|
||||
}
|
||||
|
||||
const site = await this.coreSiteService.add(payload);
|
||||
// 分组应用/插件同步到站点,并触发微页面加载
|
||||
try {
|
||||
if (payload.groupId) {
|
||||
const group = await this.siteGroupCoreService.getInfo(Number(payload.groupId));
|
||||
if (payload.group_id) {
|
||||
const group = await this.siteGroupCoreService.getInfo(Number(payload.group_id));
|
||||
if (group) {
|
||||
await this.domainEventService.publishEvent(
|
||||
'AddSiteAfter',
|
||||
String(site.siteId ?? ''),
|
||||
String(req?.user?.siteId ?? site.siteId ?? ''),
|
||||
{ site_id: site.siteId, main_app: group.app || [], site_addons: group.addon || [] },
|
||||
String(site.site_id ?? ''),
|
||||
String(req?.user?.siteId ?? site.site_id ?? ''),
|
||||
{ site_id: site.site_id, main_app: group.app || [], site_addons: group.addon || [] },
|
||||
);
|
||||
// 加载微页面数据(对齐 PHP: DiyService->loadDiyData)
|
||||
await this.domainEventService.publishEvent(
|
||||
'site.diy.load',
|
||||
String(site.siteId ?? ''),
|
||||
String(req?.user?.siteId ?? site.siteId ?? ''),
|
||||
{ site_id: site.siteId, main_app: group.app || [], tag: 'add' },
|
||||
String(site.site_id ?? ''),
|
||||
String(req?.user?.siteId ?? site.site_id ?? ''),
|
||||
{ site_id: site.site_id, main_app: group.app || [], tag: 'add' },
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -141,14 +141,14 @@ export class SiteService {
|
||||
try {
|
||||
await this.domainEventService.publishEvent(
|
||||
'site.site.added',
|
||||
String(site.siteId ?? site['site_id'] ?? ''),
|
||||
String((req?.user?.siteId ?? 0) || 0),
|
||||
{ siteId: site.siteId ?? site['site_id'], payload },
|
||||
String(site.site_id ?? ''),
|
||||
String(req?.user?.siteId || site.site_id),
|
||||
{ siteId: site.site_id, payload },
|
||||
);
|
||||
} catch {}
|
||||
// 记录审计日志
|
||||
await this.logAudit('site.create', site.siteId || 0, data, req);
|
||||
return site.siteId;
|
||||
await this.logAudit('site.create', site.site_id, data, req);
|
||||
return site.site_id;
|
||||
}
|
||||
|
||||
async edit(
|
||||
@@ -167,12 +167,12 @@ export class SiteService {
|
||||
await this.domainEventService.publishEvent(
|
||||
'site.site.updated',
|
||||
String(siteId),
|
||||
String((req?.user?.siteId ?? 0) || 0),
|
||||
String(req?.user?.siteId || siteId),
|
||||
{ siteId, payload },
|
||||
);
|
||||
// 若分组变更,同步应用/插件并刷新微页面
|
||||
if (payload.groupId) {
|
||||
const group = await this.siteGroupCoreService.getInfo(Number(payload.groupId));
|
||||
if (payload.group_id) {
|
||||
const group = await this.siteGroupCoreService.getInfo(Number(payload.group_id));
|
||||
if (group) {
|
||||
await this.domainEventService.publishEvent(
|
||||
'AddSiteAfter',
|
||||
@@ -210,7 +210,7 @@ export class SiteService {
|
||||
await this.domainEventService.publishEvent(
|
||||
'site.site.deleted',
|
||||
String(siteId),
|
||||
String((req?.user?.siteId ?? 0) || 0),
|
||||
String(req?.user?.siteId || siteId),
|
||||
{ siteId },
|
||||
);
|
||||
}
|
||||
@@ -232,7 +232,7 @@ export class SiteService {
|
||||
await this.domainEventService.publishEvent(
|
||||
'site.site.status_updated',
|
||||
String(siteId),
|
||||
String((req?.user?.siteId ?? 0) || 0),
|
||||
String(req?.user?.siteId || siteId),
|
||||
{ siteId, status },
|
||||
);
|
||||
}
|
||||
@@ -267,7 +267,7 @@ export class SiteService {
|
||||
}
|
||||
// 触发初始化事件(对齐 PHP: event('SiteInit', ...))
|
||||
try {
|
||||
const group = site.groupId ? await this.siteGroupCoreService.getInfo(Number(site.groupId)) : null;
|
||||
const group = site.group_id ? await this.siteGroupCoreService.getInfo(Number(site.group_id)) : null;
|
||||
await this.domainEventService.publishEvent(
|
||||
'SiteInit',
|
||||
String(siteId),
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { InjectRepository } from "@nestjs/typeorm";
|
||||
import { Repository, Like } from "typeorm";
|
||||
import { BaseService } from "../../../../core/base/BaseService";
|
||||
import { Site } from "../../entities/Site";
|
||||
|
||||
/**
|
||||
@@ -9,13 +8,11 @@ import { Site } from "../../entities/Site";
|
||||
* 对应PHP: app\service\core\site\CoreSiteService
|
||||
*/
|
||||
@Injectable()
|
||||
export class CoreSiteService extends BaseService<Site> {
|
||||
export class CoreSiteService {
|
||||
constructor(
|
||||
@InjectRepository(Site)
|
||||
private readonly siteRepository: Repository<Site>,
|
||||
) {
|
||||
super(siteRepository);
|
||||
}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 分页查询站点列表
|
||||
@@ -27,12 +24,12 @@ export class CoreSiteService extends BaseService<Site> {
|
||||
.createQueryBuilder("site")
|
||||
.where("site.app_type != :appType", { appType: "admin" })
|
||||
.select([
|
||||
"site.siteId",
|
||||
"site.siteName",
|
||||
"site.frontEndName",
|
||||
"site.frontEndLogo",
|
||||
"site.frontEndIcon",
|
||||
"site.appType",
|
||||
"site.site_id",
|
||||
"site.site_name",
|
||||
"site.front_end_name",
|
||||
"site.front_end_logo",
|
||||
"site.front_end_icon",
|
||||
"site.app_type",
|
||||
"site.keywords",
|
||||
"site.logo",
|
||||
"site.icon",
|
||||
@@ -40,23 +37,23 @@ export class CoreSiteService extends BaseService<Site> {
|
||||
"site.status",
|
||||
"site.latitude",
|
||||
"site.longitude",
|
||||
"site.provinceId",
|
||||
"site.cityId",
|
||||
"site.districtId",
|
||||
"site.province_id",
|
||||
"site.city_id",
|
||||
"site.district_id",
|
||||
"site.address",
|
||||
"site.fullAddress",
|
||||
"site.full_address",
|
||||
"site.phone",
|
||||
"site.businessHours",
|
||||
"site.createTime",
|
||||
"site.expireTime",
|
||||
"site.groupId",
|
||||
"site.business_hours",
|
||||
"site.create_time",
|
||||
"site.expire_time",
|
||||
"site.group_id",
|
||||
"site.app",
|
||||
"site.addons",
|
||||
"site.siteDomain",
|
||||
"site.site_domain",
|
||||
]);
|
||||
|
||||
if (where.siteName) {
|
||||
queryBuilder.andWhere("site.siteName LIKE :siteName", {
|
||||
queryBuilder.andWhere("site.site_name LIKE :siteName", {
|
||||
siteName: `%${where.siteName}%`,
|
||||
});
|
||||
}
|
||||
@@ -110,14 +107,14 @@ export class CoreSiteService extends BaseService<Site> {
|
||||
*/
|
||||
async getInfo(siteId: number) {
|
||||
return await this.siteRepository.findOne({
|
||||
where: { siteId },
|
||||
where: { site_id: siteId },
|
||||
select: [
|
||||
"siteId",
|
||||
"siteName",
|
||||
"frontEndName",
|
||||
"frontEndLogo",
|
||||
"frontEndIcon",
|
||||
"appType",
|
||||
"site_id",
|
||||
"site_name",
|
||||
"front_end_name",
|
||||
"front_end_logo",
|
||||
"front_end_icon",
|
||||
"app_type",
|
||||
"keywords",
|
||||
"logo",
|
||||
"icon",
|
||||
@@ -125,22 +122,21 @@ export class CoreSiteService extends BaseService<Site> {
|
||||
"status",
|
||||
"latitude",
|
||||
"longitude",
|
||||
"provinceId",
|
||||
"cityId",
|
||||
"districtId",
|
||||
"province_id",
|
||||
"city_id",
|
||||
"district_id",
|
||||
"address",
|
||||
"fullAddress",
|
||||
"full_address",
|
||||
"phone",
|
||||
"businessHours",
|
||||
"createTime",
|
||||
"expireTime",
|
||||
"groupId",
|
||||
"business_hours",
|
||||
"create_time",
|
||||
"expire_time",
|
||||
"group_id",
|
||||
"app",
|
||||
"addons",
|
||||
"siteDomain",
|
||||
"metaTitle",
|
||||
"metaDesc",
|
||||
"metaKeyword",
|
||||
"site_domain",
|
||||
"meta_title",
|
||||
"meta_desc",
|
||||
],
|
||||
});
|
||||
}
|
||||
@@ -153,7 +149,7 @@ export class CoreSiteService extends BaseService<Site> {
|
||||
async add(data: Partial<Site>): Promise<Site> {
|
||||
const siteData = {
|
||||
...data,
|
||||
appType: data.appType || "site",
|
||||
appType: data.app_type || "site",
|
||||
createTime: new Date(),
|
||||
};
|
||||
|
||||
@@ -170,10 +166,10 @@ export class CoreSiteService extends BaseService<Site> {
|
||||
async edit(siteId: number, data: Partial<Site>): Promise<boolean> {
|
||||
const updateData = {
|
||||
...data,
|
||||
updateTime: new Date(),
|
||||
update_time: Math.floor(Date.now() / 1000),
|
||||
};
|
||||
|
||||
const result = await this.siteRepository.update(siteId, updateData);
|
||||
const result = await this.siteRepository.update({ site_id: siteId }, updateData);
|
||||
return (result.affected || 0) > 0;
|
||||
}
|
||||
|
||||
@@ -220,10 +216,10 @@ export class CoreSiteService extends BaseService<Site> {
|
||||
*/
|
||||
async getExpireTime(siteId: number) {
|
||||
const site = await this.siteRepository.findOne({
|
||||
where: { siteId },
|
||||
select: ["expireTime"],
|
||||
where: { site_id: siteId },
|
||||
select: ["expire_time"],
|
||||
});
|
||||
return site ? { expireTime: site.expireTime } : null;
|
||||
return site ? { expireTime: site.expire_time } : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -233,7 +229,7 @@ export class CoreSiteService extends BaseService<Site> {
|
||||
*/
|
||||
async findByDomain(domain: string) {
|
||||
return await this.siteRepository.findOne({
|
||||
where: { siteDomain: domain },
|
||||
where: { site_domain: domain },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -244,9 +240,9 @@ export class CoreSiteService extends BaseService<Site> {
|
||||
* @returns 是否成功
|
||||
*/
|
||||
async updateStatus(siteId: number, status: number): Promise<boolean> {
|
||||
const result = await this.siteRepository.update(siteId, {
|
||||
const result = await this.siteRepository.update({ site_id: siteId }, {
|
||||
status,
|
||||
updateTime: new Date(),
|
||||
update_time: Math.floor(Date.now() / 1000),
|
||||
});
|
||||
return (result.affected || 0) > 0;
|
||||
}
|
||||
@@ -260,7 +256,7 @@ export class CoreSiteService extends BaseService<Site> {
|
||||
const result = await this.siteRepository
|
||||
.createQueryBuilder()
|
||||
.update(Site)
|
||||
.set({ status: 0, updateTime: new Date() })
|
||||
.set({ status: 0, update_time: Math.floor(Date.now() / 1000) })
|
||||
.where('status = :enabled', { enabled: 1 })
|
||||
.andWhere('expire_time IS NOT NULL')
|
||||
.andWhere('expire_time < :before', { before })
|
||||
|
||||
@@ -28,6 +28,10 @@ export class SiteGroupCoreService {
|
||||
return this.repo.findOne({ where: { group_id } });
|
||||
}
|
||||
|
||||
async getAll() {
|
||||
return this.repo.find();
|
||||
}
|
||||
|
||||
async add(payload: Partial<SiteGroup>) {
|
||||
const entity = this.repo.create({ ...payload, create_time: Math.floor(Date.now() / 1000) });
|
||||
return this.repo.save(entity);
|
||||
@@ -47,4 +51,9 @@ export class SiteGroupCoreService {
|
||||
const list = await this.repo.find();
|
||||
return list.map((g) => ({ id: (g as any).group_id, label: g.group_name, children: [] }));
|
||||
}
|
||||
|
||||
async checkAddon(_addons: string[]) {
|
||||
// 预留校验逻辑;与 PHP 行为对齐可在此实现
|
||||
return true as any;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Module } from "@nestjs/common";
|
||||
import { CacheModule } from "@nestjs/cache-manager";
|
||||
import { TypeOrmModule } from "@nestjs/typeorm";
|
||||
import { Site } from "./entities/Site";
|
||||
import { SiteGroup } from "./entities/SiteGroup";
|
||||
@@ -6,6 +7,8 @@ import { SiteAccountLog } from "./entities/SiteAccountLog";
|
||||
import { SiteAccount } from "./entities/SiteAccount";
|
||||
import { SysUserLog } from "./entities/SysUserLog";
|
||||
import { DiyModule } from "../diy/diy.module";
|
||||
import { SysUser } from "../admin/entities/SysUser";
|
||||
import { SysUserRole } from "../admin/entities/SysUserRole";
|
||||
|
||||
// Core Services
|
||||
import { CoreSiteService } from "./services/core/CoreSiteService";
|
||||
@@ -33,8 +36,9 @@ import { DiyLoadEventHandler } from "./subscribers/diyLoadEventHandler";
|
||||
*/
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([Site, SiteGroup, SiteAccountLog, SysUserLog, SiteAccount]),
|
||||
TypeOrmModule.forFeature([Site, SiteGroup, SiteAccountLog, SysUserLog, SiteAccount, SysUser, SysUserRole]),
|
||||
DiyModule,
|
||||
CacheModule.register(),
|
||||
],
|
||||
providers: [
|
||||
// Core Services
|
||||
|
||||
@@ -17,7 +17,7 @@ export class DiyLoadEventHandler {
|
||||
@EventHandler('site.diy.load', { consumerGroup: 'site-diy-initializer' })
|
||||
async onLoad(event: DomainEvent): Promise<void> {
|
||||
const data = (event?.data || {}) as { site_id?: number; main_app?: string[]; tag?: 'add' | 'update' };
|
||||
const siteId = Number(data.site_id || 0);
|
||||
const siteId = Number(data.site_id);
|
||||
if (!siteId) return;
|
||||
|
||||
try {
|
||||
|
||||
Binary file not shown.
@@ -1,4 +1,4 @@
|
||||
import { Controller, Get, Param, Query, UseGuards, Req } from '@nestjs/common';
|
||||
import { Controller, Get, Param, Query, UseGuards, Req, UnauthorizedException } from '@nestjs/common';
|
||||
import {
|
||||
ApiTags,
|
||||
ApiOperation,
|
||||
@@ -42,9 +42,11 @@ export class AppController {
|
||||
@Query('type') type?: string,
|
||||
@Req() req?: AuthenticatedRequest,
|
||||
) {
|
||||
const siteId = req?.user?.siteId || 0;
|
||||
const result = await this.appService.getAppList();
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
const siteId = req?.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.appService.getAppList();
|
||||
}
|
||||
|
||||
@Get(':appKey')
|
||||
@@ -55,9 +57,11 @@ export class AppController {
|
||||
@Param('appKey') appKey: string,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.appService.getAppInfo(appKey);
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.appService.getAppInfo(appKey);
|
||||
}
|
||||
|
||||
@Get('check/:appKey')
|
||||
|
||||
@@ -20,10 +20,10 @@ import { AreaService } from '../../services/admin/AreaService';
|
||||
|
||||
/**
|
||||
* 地区管理控制器 - 管理端
|
||||
* 路由前缀: /adminapi/sys/area
|
||||
* 路由前缀: /admin/sys/area
|
||||
*/
|
||||
@ApiTags('地区管理')
|
||||
@Controller('adminapi/sys/area')
|
||||
@Controller('admin/sys/area')
|
||||
@UseGuards(JwtAuthGuard, RolesGuard)
|
||||
@Roles('admin')
|
||||
export class AreaController {
|
||||
@@ -34,8 +34,7 @@ export class AreaController {
|
||||
@ApiQuery({ name: 'pid', description: '父级ID', required: false })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getListByPid(@Query('pid', ParseIntPipe) pid: number = 0) {
|
||||
const result = await this.areaService.getListByPid(pid);
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
return await this.areaService.getListByPid(pid);
|
||||
}
|
||||
|
||||
@Get('tree')
|
||||
@@ -43,8 +42,7 @@ export class AreaController {
|
||||
@ApiQuery({ name: 'level', description: '最大层级', required: false })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getAreaTree(@Query('level', ParseIntPipe) level: number = 3) {
|
||||
const result = await this.areaService.getAreaTree(level);
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
return await this.areaService.getAreaTree(level);
|
||||
}
|
||||
|
||||
@Get('search')
|
||||
@@ -56,8 +54,7 @@ export class AreaController {
|
||||
@Query('keyword') keyword: string,
|
||||
@Query('level', ParseIntPipe) level?: number,
|
||||
) {
|
||||
const result = await this.areaService.searchArea(keyword, level);
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
return await this.areaService.searchArea(keyword, level);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@@ -65,8 +62,7 @@ export class AreaController {
|
||||
@ApiParam({ name: 'id', description: '地区ID' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getAreaByAreaCode(@Param('id', ParseIntPipe) id: number) {
|
||||
const result = await this.areaService.getAreaByAreaCode(id);
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
return await this.areaService.getAreaByAreaCode(id);
|
||||
}
|
||||
|
||||
@Get(':id/path')
|
||||
@@ -75,7 +71,7 @@ export class AreaController {
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getFullPath(@Param('id', ParseIntPipe) id: number) {
|
||||
const result = await this.areaService.getFullPath(id);
|
||||
return { code: 200, message: '获取成功', data: { path: result } };
|
||||
return { path: result };
|
||||
}
|
||||
|
||||
@Get('batch/:ids')
|
||||
@@ -87,7 +83,6 @@ export class AreaController {
|
||||
.split(',')
|
||||
.map((id) => parseInt(id.trim()))
|
||||
.filter((id) => !isNaN(id));
|
||||
const result = await this.areaService.getAreaByAreaCodes(idArray);
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
return await this.areaService.getAreaByAreaCodes(idArray);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
UseGuards,
|
||||
Req,
|
||||
ParseIntPipe,
|
||||
UnauthorizedException,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiResponse, ApiParam } from '@nestjs/swagger';
|
||||
import type { Request } from 'express';
|
||||
@@ -48,9 +49,11 @@ export class AttachmentCategoryController {
|
||||
@Query() query: AttachmentCategoryQueryDto,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.attachmentCategoryService.getPage(siteId, query);
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.attachmentCategoryService.getPage(siteId, query);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@@ -61,9 +64,11 @@ export class AttachmentCategoryController {
|
||||
@Param('id', ParseIntPipe) id: number,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.attachmentCategoryService.getInfo(siteId, id);
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.attachmentCategoryService.getInfo(siteId, id);
|
||||
}
|
||||
|
||||
@Post()
|
||||
@@ -73,13 +78,11 @@ export class AttachmentCategoryController {
|
||||
@Body() data: CreateAttachmentCategoryDto,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.attachmentCategoryService.add(siteId, data);
|
||||
return { code: 200, message: '创建成功', data: result };
|
||||
} catch (error) {
|
||||
return { code: 400, message: error.message || '创建失败', data: null };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.attachmentCategoryService.add(siteId, data);
|
||||
}
|
||||
|
||||
@Put(':id')
|
||||
@@ -91,17 +94,15 @@ export class AttachmentCategoryController {
|
||||
@Body() data: UpdateAttachmentCategoryDto,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.attachmentCategoryService.edit(
|
||||
siteId,
|
||||
id,
|
||||
data,
|
||||
);
|
||||
return { code: 200, message: '更新成功', data: result };
|
||||
} catch (error) {
|
||||
return { code: 400, message: error.message || '更新失败', data: null };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.attachmentCategoryService.edit(
|
||||
siteId,
|
||||
id,
|
||||
data,
|
||||
);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@@ -112,12 +113,10 @@ export class AttachmentCategoryController {
|
||||
@Param('id', ParseIntPipe) id: number,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.attachmentCategoryService.del(siteId, id);
|
||||
return { code: 200, message: '删除成功', data: result };
|
||||
} catch (error) {
|
||||
return { code: 400, message: error.message || '删除失败', data: null };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.attachmentCategoryService.del(siteId, id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
UseGuards,
|
||||
Req,
|
||||
ParseIntPipe,
|
||||
UnauthorizedException,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiResponse, ApiParam } from '@nestjs/swagger';
|
||||
import type { Request } from 'express';
|
||||
@@ -52,9 +53,11 @@ export class AttachmentController {
|
||||
@Query() query: AttachmentQueryDto,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.attachmentService.getPage(siteId, query);
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.attachmentService.getPage(siteId, query);
|
||||
}
|
||||
|
||||
@Get(':attId')
|
||||
@@ -65,9 +68,11 @@ export class AttachmentController {
|
||||
@Param('attId', ParseIntPipe) attId: number,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.attachmentService.getInfo(siteId, attId);
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.attachmentService.getInfo(siteId, attId);
|
||||
}
|
||||
|
||||
@Post()
|
||||
@@ -77,13 +82,11 @@ export class AttachmentController {
|
||||
@Body() data: CreateAttachmentDto,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.attachmentService.add(siteId, data);
|
||||
return { code: 200, message: '创建成功', data: result };
|
||||
} catch (error) {
|
||||
return { code: 400, message: error.message || '创建失败', data: null };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.attachmentService.add(siteId, data);
|
||||
}
|
||||
|
||||
@Put(':attId')
|
||||
@@ -95,13 +98,11 @@ export class AttachmentController {
|
||||
@Body() data: UpdateAttachmentDto,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.attachmentService.edit(siteId, attId, data);
|
||||
return { code: 200, message: '更新成功', data: result };
|
||||
} catch (error) {
|
||||
return { code: 400, message: error.message || '更新失败', data: null };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.attachmentService.edit(siteId, attId, data);
|
||||
}
|
||||
|
||||
@Put(':attId/category')
|
||||
@@ -113,17 +114,15 @@ export class AttachmentController {
|
||||
@Body() data: ModifyAttachmentCategoryDto,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.attachmentService.modifyCategory(
|
||||
siteId,
|
||||
attId,
|
||||
data.cate_id,
|
||||
);
|
||||
return { code: 200, message: '修改成功', data: result };
|
||||
} catch (error) {
|
||||
return { code: 400, message: error.message || '修改失败', data: null };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.attachmentService.modifyCategory(
|
||||
siteId,
|
||||
attId,
|
||||
data.cate_id,
|
||||
);
|
||||
}
|
||||
|
||||
@Delete(':attId')
|
||||
@@ -134,13 +133,11 @@ export class AttachmentController {
|
||||
@Param('attId', ParseIntPipe) attId: number,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.attachmentService.del(siteId, attId);
|
||||
return { code: 200, message: '删除成功', data: result };
|
||||
} catch (error) {
|
||||
return { code: 400, message: error.message || '删除失败', data: null };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.attachmentService.del(siteId, attId);
|
||||
}
|
||||
|
||||
@Delete('batch')
|
||||
@@ -150,15 +147,13 @@ export class AttachmentController {
|
||||
@Body() data: BatchDeleteAttachmentDto,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.attachmentService.batchDelete(
|
||||
siteId,
|
||||
data.att_ids,
|
||||
);
|
||||
return { code: 200, message: '删除成功', data: result };
|
||||
} catch (error) {
|
||||
return { code: 400, message: error.message || '删除失败', data: null };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.attachmentService.batchDelete(
|
||||
siteId,
|
||||
data.att_ids,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ export class ChannelController {
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getPage(@Query() query: any, @Req() req: AuthenticatedRequest) {
|
||||
// TODO: 实现渠道分页列表
|
||||
return { code: 200, message: '获取成功', data: { list: [], total: 0 } };
|
||||
return { list: [], total: 0 };
|
||||
}
|
||||
|
||||
@Get('list')
|
||||
@@ -50,7 +50,7 @@ export class ChannelController {
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getList(@Query() query: any, @Req() req: AuthenticatedRequest) {
|
||||
// TODO: 实现渠道列表
|
||||
return { code: 200, message: '获取成功', data: [] };
|
||||
return [];
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@@ -62,7 +62,7 @@ export class ChannelController {
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
// TODO: 实现渠道详情
|
||||
return { code: 200, message: '获取成功', data: null };
|
||||
return null;
|
||||
}
|
||||
|
||||
@Post()
|
||||
@@ -70,7 +70,7 @@ export class ChannelController {
|
||||
@ApiResponse({ status: 200, description: '创建成功' })
|
||||
async add(@Body() data: any, @Req() req: AuthenticatedRequest) {
|
||||
// TODO: 实现渠道新增
|
||||
return { code: 200, message: '创建成功', data: null };
|
||||
return null;
|
||||
}
|
||||
|
||||
@Put(':id')
|
||||
@@ -83,7 +83,7 @@ export class ChannelController {
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
// TODO: 实现渠道编辑
|
||||
return { code: 200, message: '更新成功', data: null };
|
||||
return null;
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@@ -95,6 +95,6 @@ export class ChannelController {
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
// TODO: 实现渠道删除
|
||||
return { code: 200, message: '删除成功', data: null };
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Controller, Get, Post, UseGuards, Req } from '@nestjs/common';
|
||||
import { Controller, Get, Post, UseGuards, Req, UnauthorizedException } from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
|
||||
import type { Request } from 'express';
|
||||
import { JwtAuthGuard } from '../../../auth/guards/JwtAuthGuard';
|
||||
@@ -29,31 +29,47 @@ export class CommonController {
|
||||
@ApiOperation({ summary: '获取字典数据' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getDict(@Req() req: AuthenticatedRequest) {
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
// TODO: 实现字典数据获取
|
||||
return { code: 200, message: '获取成功', data: {} };
|
||||
return {};
|
||||
}
|
||||
|
||||
@Get('config')
|
||||
@ApiOperation({ summary: '获取系统配置' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getConfig(@Req() req: AuthenticatedRequest) {
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
// TODO: 实现系统配置获取
|
||||
return { code: 200, message: '获取成功', data: {} };
|
||||
return {};
|
||||
}
|
||||
|
||||
@Post('upload')
|
||||
@ApiOperation({ summary: '文件上传' })
|
||||
@ApiResponse({ status: 200, description: '上传成功' })
|
||||
async upload(@Req() req: AuthenticatedRequest) {
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
// TODO: 实现文件上传
|
||||
return { code: 200, message: '上传成功', data: null };
|
||||
return null;
|
||||
}
|
||||
|
||||
@Get('captcha')
|
||||
@ApiOperation({ summary: '获取验证码' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getCaptcha(@Req() req: AuthenticatedRequest) {
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
// TODO: 实现验证码获取
|
||||
return { code: 200, message: '获取成功', data: null };
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Controller, Get, Post, Body, UseGuards, Req } from '@nestjs/common';
|
||||
import { Controller, Get, Post, Body, UseGuards, Req, UnauthorizedException } from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
|
||||
import type { Request } from 'express';
|
||||
import { JwtAuthGuard } from '../../../auth/guards/JwtAuthGuard';
|
||||
@@ -23,10 +23,10 @@ interface AuthenticatedRequest extends Request {
|
||||
|
||||
/**
|
||||
* 系统配置控制器 - 管理端
|
||||
* 路由前缀: /adminapi/sys/config
|
||||
* 路由前缀: /admin/sys/config
|
||||
*/
|
||||
@ApiTags('系统配置管理')
|
||||
@Controller('adminapi/sys/config')
|
||||
@Controller('admin/sys/config')
|
||||
@UseGuards(JwtAuthGuard, RolesGuard)
|
||||
@Roles('admin')
|
||||
export class ConfigController {
|
||||
@@ -36,13 +36,11 @@ export class ConfigController {
|
||||
@ApiOperation({ summary: '获取版权信息' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getCopyright(@Req() req: AuthenticatedRequest) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const data = await this.configService.getCopyright(siteId);
|
||||
return {
|
||||
code: 200,
|
||||
message: '获取成功',
|
||||
data,
|
||||
};
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.configService.getCopyright(siteId);
|
||||
}
|
||||
|
||||
@Post('copyright')
|
||||
@@ -52,26 +50,22 @@ export class ConfigController {
|
||||
@Body() copyrightDto: CopyrightDto,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.configService.setCopyright(siteId, copyrightDto);
|
||||
return {
|
||||
code: 200,
|
||||
message: '设置成功',
|
||||
data: result,
|
||||
};
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.configService.setCopyright(siteId, copyrightDto);
|
||||
}
|
||||
|
||||
@Get('website')
|
||||
@ApiOperation({ summary: '获取网站信息' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getWebSite(@Req() req: AuthenticatedRequest) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const data = await this.configService.getWebSite(siteId);
|
||||
return {
|
||||
code: 200,
|
||||
message: '获取成功',
|
||||
data,
|
||||
};
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.configService.getWebSite(siteId);
|
||||
}
|
||||
|
||||
@Post('website')
|
||||
@@ -81,26 +75,22 @@ export class ConfigController {
|
||||
@Body() websiteDto: WebSiteDto,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.configService.setWebSite(siteId, websiteDto);
|
||||
return {
|
||||
code: 200,
|
||||
message: '设置成功',
|
||||
data: result,
|
||||
};
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.configService.setWebSite(siteId, websiteDto);
|
||||
}
|
||||
|
||||
@Get('scene-domain')
|
||||
@ApiOperation({ summary: '获取场景域名配置' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getSceneDomain(@Req() req: AuthenticatedRequest) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const data = await this.configService.getSceneDomain(siteId);
|
||||
return {
|
||||
code: 200,
|
||||
message: '获取成功',
|
||||
data,
|
||||
};
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.configService.getSceneDomain(siteId);
|
||||
}
|
||||
|
||||
@Post('scene-domain')
|
||||
@@ -110,29 +100,25 @@ export class ConfigController {
|
||||
@Body() sceneDomainDto: SceneDomainDto,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.configService.setSceneDomain(
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.configService.setSceneDomain(
|
||||
siteId,
|
||||
sceneDomainDto,
|
||||
);
|
||||
return {
|
||||
code: 200,
|
||||
message: '设置成功',
|
||||
data: result,
|
||||
};
|
||||
}
|
||||
|
||||
@Get('service')
|
||||
@ApiOperation({ summary: '获取服务配置' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getService(@Req() req: AuthenticatedRequest) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const data = await this.configService.getService(siteId);
|
||||
return {
|
||||
code: 200,
|
||||
message: '获取成功',
|
||||
data,
|
||||
};
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.configService.getService(siteId);
|
||||
}
|
||||
|
||||
@Post('service')
|
||||
@@ -142,12 +128,10 @@ export class ConfigController {
|
||||
@Body() serviceDto: ServiceDto,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.configService.setService(siteId, serviceDto);
|
||||
return {
|
||||
code: 200,
|
||||
message: '设置成功',
|
||||
data: result,
|
||||
};
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.configService.setService(siteId, serviceDto);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
UseGuards,
|
||||
Req,
|
||||
ParseIntPipe,
|
||||
UnauthorizedException,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiResponse, ApiParam } from '@nestjs/swagger';
|
||||
import type { Request } from 'express';
|
||||
@@ -41,17 +42,18 @@ export class ExportController {
|
||||
@ApiOperation({ summary: '获取导出记录分页列表' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getPage(@Query() query: any, @Req() req: AuthenticatedRequest) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.exportService.getPage(siteId, query);
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.exportService.getPage(siteId, query);
|
||||
}
|
||||
|
||||
@Get('data-types')
|
||||
@ApiOperation({ summary: '获取导出数据类型列表' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getExportDataType(@Req() req: AuthenticatedRequest) {
|
||||
const result = await this.exportService.getExportDataType();
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
return await this.exportService.getExportDataType();
|
||||
}
|
||||
|
||||
@Post('check')
|
||||
@@ -61,17 +63,15 @@ export class ExportController {
|
||||
@Body() data: { export_key: string; conditions?: any },
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.exportService.checkExportData(
|
||||
siteId,
|
||||
data.export_key,
|
||||
data.conditions,
|
||||
);
|
||||
return { code: 200, message: '检查成功', data: result };
|
||||
} catch (error) {
|
||||
return { code: 400, message: error.message || '检查失败', data: null };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.exportService.checkExportData(
|
||||
siteId,
|
||||
data.export_key,
|
||||
data.conditions,
|
||||
);
|
||||
}
|
||||
|
||||
@Post('export')
|
||||
@@ -81,17 +81,15 @@ export class ExportController {
|
||||
@Body() data: { export_key: string; conditions?: any },
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.exportService.exportData(
|
||||
siteId,
|
||||
data.export_key,
|
||||
data.conditions,
|
||||
);
|
||||
return { code: 200, message: '导出任务创建成功', data: result };
|
||||
} catch (error) {
|
||||
return { code: 400, message: error.message || '导出失败', data: null };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.exportService.exportData(
|
||||
siteId,
|
||||
data.export_key,
|
||||
data.conditions,
|
||||
);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@@ -102,12 +100,10 @@ export class ExportController {
|
||||
@Param('id', ParseIntPipe) id: number,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.exportService.deleteRecord(siteId, id);
|
||||
return { code: 200, message: '删除成功', data: result };
|
||||
} catch (error) {
|
||||
return { code: 400, message: error.message || '删除失败', data: null };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.exportService.deleteRecord(siteId, id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
UseGuards,
|
||||
Req,
|
||||
ParseIntPipe,
|
||||
UnauthorizedException,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiResponse, ApiParam } from '@nestjs/swagger';
|
||||
import type { Request } from 'express';
|
||||
@@ -42,26 +43,29 @@ export class PosterController {
|
||||
@ApiOperation({ summary: '获取海报分页列表' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getPage(@Query() query: any, @Req() req: AuthenticatedRequest) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.posterService.getPage(siteId, query);
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.posterService.getPage(siteId, query);
|
||||
}
|
||||
|
||||
@Get('list')
|
||||
@ApiOperation({ summary: '获取海报列表' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getList(@Query() query: any, @Req() req: AuthenticatedRequest) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.posterService.getList(siteId, query);
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.posterService.getList(siteId, query);
|
||||
}
|
||||
|
||||
@Get('types')
|
||||
@ApiOperation({ summary: '获取海报类型列表' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getPosterTypes(@Req() req: AuthenticatedRequest) {
|
||||
const result = await this.posterService.getPosterTypes();
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
return await this.posterService.getPosterTypes();
|
||||
}
|
||||
|
||||
@Get('default/:type')
|
||||
@@ -72,9 +76,11 @@ export class PosterController {
|
||||
@Param('type') type: string,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.posterService.getDefaultByType(siteId, type);
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.posterService.getDefaultByType(siteId, type);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@@ -85,22 +91,22 @@ export class PosterController {
|
||||
@Param('id', ParseIntPipe) id: number,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.posterService.getInfo(siteId, id);
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.posterService.getInfo(siteId, id);
|
||||
}
|
||||
|
||||
@Post()
|
||||
@ApiOperation({ summary: '新增海报' })
|
||||
@ApiResponse({ status: 200, description: '创建成功' })
|
||||
async add(@Body() data: any, @Req() req: AuthenticatedRequest) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.posterService.add(siteId, data);
|
||||
return { code: 200, message: '创建成功', data: result };
|
||||
} catch (error) {
|
||||
return { code: 400, message: error.message || '创建失败', data: null };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.posterService.add(siteId, data);
|
||||
}
|
||||
|
||||
@Put(':id')
|
||||
@@ -112,13 +118,11 @@ export class PosterController {
|
||||
@Body() data: any,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.posterService.edit(siteId, id, data);
|
||||
return { code: 200, message: '更新成功', data: result };
|
||||
} catch (error) {
|
||||
return { code: 400, message: error.message || '更新失败', data: null };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.posterService.edit(siteId, id, data);
|
||||
}
|
||||
|
||||
@Put(':id/default')
|
||||
@@ -130,13 +134,11 @@ export class PosterController {
|
||||
@Body() data: { type: string },
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.posterService.setDefault(siteId, id, data.type);
|
||||
return { code: 200, message: '设置成功', data: result };
|
||||
} catch (error) {
|
||||
return { code: 400, message: error.message || '设置失败', data: null };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.posterService.setDefault(siteId, id, data.type);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@@ -147,12 +149,10 @@ export class PosterController {
|
||||
@Param('id', ParseIntPipe) id: number,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.posterService.del(siteId, id);
|
||||
return { code: 200, message: '删除成功', data: result };
|
||||
} catch (error) {
|
||||
return { code: 400, message: error.message || '删除失败', data: null };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.posterService.del(siteId, id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
UseGuards,
|
||||
Req,
|
||||
ParseIntPipe,
|
||||
UnauthorizedException,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiResponse, ApiParam } from '@nestjs/swagger';
|
||||
import type { Request } from 'express';
|
||||
@@ -42,26 +43,29 @@ export class PrinterController {
|
||||
@ApiOperation({ summary: '获取打印机分页列表' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getPage(@Query() query: any, @Req() req: AuthenticatedRequest) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.printerService.getPage(siteId, query);
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.printerService.getPage(siteId, query);
|
||||
}
|
||||
|
||||
@Get('list')
|
||||
@ApiOperation({ summary: '获取打印机列表' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getList(@Query() query: any, @Req() req: AuthenticatedRequest) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.printerService.getList(siteId, query);
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.printerService.getList(siteId, query);
|
||||
}
|
||||
|
||||
@Get('brands')
|
||||
@ApiOperation({ summary: '获取打印机品牌列表' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getBrandList() {
|
||||
const result = this.printerService.getBrandList();
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
return this.printerService.getBrandList();
|
||||
}
|
||||
|
||||
@Get(':printerId')
|
||||
@@ -72,22 +76,22 @@ export class PrinterController {
|
||||
@Param('printerId', ParseIntPipe) printerId: number,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.printerService.getInfo(siteId, printerId);
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.printerService.getInfo(siteId, printerId);
|
||||
}
|
||||
|
||||
@Post()
|
||||
@ApiOperation({ summary: '新增打印机' })
|
||||
@ApiResponse({ status: 200, description: '创建成功' })
|
||||
async add(@Body() data: any, @Req() req: AuthenticatedRequest) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.printerService.add(siteId, data);
|
||||
return { code: 200, message: '创建成功', data: result };
|
||||
} catch (error) {
|
||||
return { code: 400, message: error.message || '创建失败', data: null };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.printerService.add(siteId, data);
|
||||
}
|
||||
|
||||
@Put(':printerId')
|
||||
@@ -99,13 +103,11 @@ export class PrinterController {
|
||||
@Body() data: any,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.printerService.edit(siteId, printerId, data);
|
||||
return { code: 200, message: '更新成功', data: result };
|
||||
} catch (error) {
|
||||
return { code: 400, message: error.message || '更新失败', data: null };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.printerService.edit(siteId, printerId, data);
|
||||
}
|
||||
|
||||
@Put(':printerId/status')
|
||||
@@ -117,17 +119,15 @@ export class PrinterController {
|
||||
@Body() data: { status: number },
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.printerService.modifyStatus(
|
||||
siteId,
|
||||
printerId,
|
||||
data.status,
|
||||
);
|
||||
return { code: 200, message: '修改成功', data: result };
|
||||
} catch (error) {
|
||||
return { code: 400, message: error.message || '修改失败', data: null };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.printerService.modifyStatus(
|
||||
siteId,
|
||||
printerId,
|
||||
data.status,
|
||||
);
|
||||
}
|
||||
|
||||
@Post(':printerId/test')
|
||||
@@ -138,16 +138,14 @@ export class PrinterController {
|
||||
@Param('printerId', ParseIntPipe) printerId: number,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.printerService.testConnection(
|
||||
siteId,
|
||||
printerId,
|
||||
);
|
||||
return { code: 200, message: '测试完成', data: result };
|
||||
} catch (error) {
|
||||
return { code: 400, message: error.message || '测试失败', data: null };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.printerService.testConnection(
|
||||
siteId,
|
||||
printerId,
|
||||
);
|
||||
}
|
||||
|
||||
@Post(':printerId/print')
|
||||
@@ -159,17 +157,15 @@ export class PrinterController {
|
||||
@Body() data: { content: string },
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.printerService.print(
|
||||
siteId,
|
||||
printerId,
|
||||
data.content,
|
||||
);
|
||||
return { code: 200, message: '打印完成', data: result };
|
||||
} catch (error) {
|
||||
return { code: 400, message: error.message || '打印失败', data: null };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.printerService.print(
|
||||
siteId,
|
||||
printerId,
|
||||
data.content,
|
||||
);
|
||||
}
|
||||
|
||||
@Delete(':printerId')
|
||||
@@ -180,12 +176,10 @@ export class PrinterController {
|
||||
@Param('printerId', ParseIntPipe) printerId: number,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.printerService.del(siteId, printerId);
|
||||
return { code: 200, message: '删除成功', data: result };
|
||||
} catch (error) {
|
||||
return { code: 400, message: error.message || '删除失败', data: null };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.printerService.del(siteId, printerId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
UseGuards,
|
||||
Req,
|
||||
ParseIntPipe,
|
||||
UnauthorizedException,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiResponse, ApiParam } from '@nestjs/swagger';
|
||||
import type { Request } from 'express';
|
||||
@@ -44,26 +45,29 @@ export class PrinterTemplateController {
|
||||
@ApiOperation({ summary: '获取打印模板分页列表' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getPage(@Query() query: any, @Req() req: AuthenticatedRequest) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.printerTemplateService.getPage(siteId, query);
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.printerTemplateService.getPage(siteId, query);
|
||||
}
|
||||
|
||||
@Get('list')
|
||||
@ApiOperation({ summary: '获取打印模板列表' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getList(@Query() query: any, @Req() req: AuthenticatedRequest) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.printerTemplateService.getList(siteId, query);
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.printerTemplateService.getList(siteId, query);
|
||||
}
|
||||
|
||||
@Get('types')
|
||||
@ApiOperation({ summary: '获取模板类型列表' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getTemplateTypes(@Req() req: AuthenticatedRequest) {
|
||||
const result = await this.printerTemplateService.getTemplateTypes();
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
return await this.printerTemplateService.getTemplateTypes();
|
||||
}
|
||||
|
||||
@Get('by-type/:type')
|
||||
@@ -74,12 +78,14 @@ export class PrinterTemplateController {
|
||||
@Param('type') type: string,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.printerTemplateService.getTemplatesByType(
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.printerTemplateService.getTemplatesByType(
|
||||
siteId,
|
||||
type,
|
||||
);
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
}
|
||||
|
||||
@Get(':templateId')
|
||||
@@ -90,25 +96,25 @@ export class PrinterTemplateController {
|
||||
@Param('templateId', ParseIntPipe) templateId: number,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.printerTemplateService.getInfo(
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.printerTemplateService.getInfo(
|
||||
siteId,
|
||||
templateId,
|
||||
);
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
}
|
||||
|
||||
@Post()
|
||||
@ApiOperation({ summary: '新增打印模板' })
|
||||
@ApiResponse({ status: 200, description: '创建成功' })
|
||||
async add(@Body() data: any, @Req() req: AuthenticatedRequest) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.printerTemplateService.add(siteId, data);
|
||||
return { code: 200, message: '创建成功', data: result };
|
||||
} catch (error) {
|
||||
return { code: 400, message: error.message || '创建失败', data: null };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.printerTemplateService.add(siteId, data);
|
||||
}
|
||||
|
||||
@Put(':templateId')
|
||||
@@ -120,17 +126,15 @@ export class PrinterTemplateController {
|
||||
@Body() data: any,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.printerTemplateService.edit(
|
||||
siteId,
|
||||
templateId,
|
||||
data,
|
||||
);
|
||||
return { code: 200, message: '更新成功', data: result };
|
||||
} catch (error) {
|
||||
return { code: 400, message: error.message || '更新失败', data: null };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.printerTemplateService.edit(
|
||||
siteId,
|
||||
templateId,
|
||||
data,
|
||||
);
|
||||
}
|
||||
|
||||
@Post(':templateId/preview')
|
||||
@@ -142,17 +146,15 @@ export class PrinterTemplateController {
|
||||
@Body() data: { preview_data?: any },
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.printerTemplateService.previewTemplate(
|
||||
siteId,
|
||||
templateId,
|
||||
data.preview_data,
|
||||
);
|
||||
return { code: 200, message: '预览成功', data: result };
|
||||
} catch (error) {
|
||||
return { code: 400, message: error.message || '预览失败', data: null };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.printerTemplateService.previewTemplate(
|
||||
siteId,
|
||||
templateId,
|
||||
data.preview_data,
|
||||
);
|
||||
}
|
||||
|
||||
@Delete(':templateId')
|
||||
@@ -163,12 +165,10 @@ export class PrinterTemplateController {
|
||||
@Param('templateId', ParseIntPipe) templateId: number,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.printerTemplateService.del(siteId, templateId);
|
||||
return { code: 200, message: '删除成功', data: result };
|
||||
} catch (error) {
|
||||
return { code: 400, message: error.message || '删除失败', data: null };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.printerTemplateService.del(siteId, templateId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
UseGuards,
|
||||
Req,
|
||||
ParseIntPipe,
|
||||
UnauthorizedException,
|
||||
} from '@nestjs/common';
|
||||
import {
|
||||
ApiTags,
|
||||
@@ -45,7 +46,7 @@ interface AuthenticatedRequest extends Request {
|
||||
* 对应PHP: app\adminapi\controller\sys\Role
|
||||
*/
|
||||
@ApiTags('角色管理')
|
||||
@Controller('adminapi/sys/role')
|
||||
@Controller('admin/sys/role')
|
||||
@UseGuards(JwtAuthGuard, RolesGuard)
|
||||
@Roles('admin')
|
||||
export class RoleController {
|
||||
@@ -58,46 +59,32 @@ export class RoleController {
|
||||
@Query() query: RoleQueryDto,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.roleService.getPage(siteId, query);
|
||||
return {
|
||||
code: 200,
|
||||
message: '获取成功',
|
||||
data: result,
|
||||
};
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.roleService.getPage(siteId, query);
|
||||
}
|
||||
|
||||
@Get('all')
|
||||
@ApiOperation({ summary: '获取所有角色列表' })
|
||||
@ApiOperation({ summary: '获取所有角色' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getAll(@Req() req: AuthenticatedRequest) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const userId = req.user?.uid || 0;
|
||||
|
||||
// TODO: 实现用户权限检查
|
||||
// 暂时假设为超级管理员,后续完善权限模块时补充
|
||||
const isAdmin = true;
|
||||
const userRoleIds: number[] = [];
|
||||
|
||||
const result = await this.roleService.getAll(siteId, userRoleIds, isAdmin);
|
||||
return {
|
||||
code: 200,
|
||||
message: '获取成功',
|
||||
data: result,
|
||||
};
|
||||
async getAll(@Query('siteId') siteId?: number, @Query('isAdmin') isAdmin?: boolean) {
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.roleService.getAll(siteId, [], isAdmin);
|
||||
}
|
||||
|
||||
@Get('column')
|
||||
@ApiOperation({ summary: '获取角色键值对' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getColumn(@Req() req: AuthenticatedRequest) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.roleService.getColumn(siteId);
|
||||
return {
|
||||
code: 200,
|
||||
message: '获取成功',
|
||||
data: result,
|
||||
};
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.roleService.getColumn(siteId);
|
||||
}
|
||||
|
||||
@Get(':roleId')
|
||||
@@ -105,12 +92,7 @@ export class RoleController {
|
||||
@ApiParam({ name: 'roleId', description: '角色ID' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getInfo(@Param('roleId', ParseIntPipe) roleId: number) {
|
||||
const result = await this.roleService.getInfo(roleId);
|
||||
return {
|
||||
code: 200,
|
||||
message: '获取成功',
|
||||
data: result,
|
||||
};
|
||||
return await this.roleService.getInfo(roleId);
|
||||
}
|
||||
|
||||
@Post()
|
||||
@@ -120,31 +102,21 @@ export class RoleController {
|
||||
@Body() createRoleDto: CreateRoleDto,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const appType = 'admin'; // 默认为admin类型
|
||||
|
||||
// 处理rules字段
|
||||
const roleData = {
|
||||
...createRoleDto,
|
||||
rules: createRoleDto.rules
|
||||
? JSON.stringify(createRoleDto.rules)
|
||||
: undefined,
|
||||
};
|
||||
|
||||
const result = await this.roleService.add(siteId, appType, roleData);
|
||||
return {
|
||||
code: 200,
|
||||
message: '创建成功',
|
||||
data: result,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 400,
|
||||
message: error.message || '创建失败',
|
||||
data: null,
|
||||
};
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
const appType = 'admin'; // 默认为admin类型
|
||||
|
||||
// 处理rules字段
|
||||
const roleData = {
|
||||
...createRoleDto,
|
||||
rules: createRoleDto.rules
|
||||
? JSON.stringify(createRoleDto.rules)
|
||||
: undefined,
|
||||
};
|
||||
|
||||
return await this.roleService.add(siteId, appType, roleData);
|
||||
}
|
||||
|
||||
@Put(':roleId')
|
||||
@@ -156,30 +128,20 @@ export class RoleController {
|
||||
@Body() updateRoleDto: UpdateRoleDto,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
|
||||
// 处理rules字段
|
||||
const roleData = {
|
||||
...updateRoleDto,
|
||||
rules: updateRoleDto.rules
|
||||
? JSON.stringify(updateRoleDto.rules)
|
||||
: undefined,
|
||||
};
|
||||
|
||||
const result = await this.roleService.edit(roleId, siteId, roleData);
|
||||
return {
|
||||
code: 200,
|
||||
message: '更新成功',
|
||||
data: result,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 400,
|
||||
message: error.message || '更新失败',
|
||||
data: null,
|
||||
};
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
|
||||
// 处理rules字段
|
||||
const roleData = {
|
||||
...updateRoleDto,
|
||||
rules: updateRoleDto.rules
|
||||
? JSON.stringify(updateRoleDto.rules)
|
||||
: undefined,
|
||||
};
|
||||
|
||||
return await this.roleService.edit(roleId, siteId, roleData);
|
||||
}
|
||||
|
||||
@Put('status/:roleId')
|
||||
@@ -191,25 +153,15 @@ export class RoleController {
|
||||
@Body() modifyStatusDto: ModifyRoleStatusDto,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.roleService.modifyStatus(
|
||||
roleId,
|
||||
siteId,
|
||||
modifyStatusDto.status,
|
||||
);
|
||||
return {
|
||||
code: 200,
|
||||
message: '修改成功',
|
||||
data: result,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 400,
|
||||
message: error.message || '修改失败',
|
||||
data: null,
|
||||
};
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.roleService.modifyStatus(
|
||||
roleId,
|
||||
siteId,
|
||||
modifyStatusDto.status,
|
||||
);
|
||||
}
|
||||
|
||||
@Delete(':roleId')
|
||||
@@ -220,21 +172,11 @@ export class RoleController {
|
||||
@Param('roleId', ParseIntPipe) roleId: number,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.roleService.del(roleId, siteId);
|
||||
return {
|
||||
code: 200,
|
||||
message: '删除成功',
|
||||
data: result,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
code: 400,
|
||||
message: error.message || '删除失败',
|
||||
data: null,
|
||||
};
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.roleService.del(roleId, siteId);
|
||||
}
|
||||
|
||||
@Get('menu-ids/:roleIds')
|
||||
@@ -251,7 +193,10 @@ export class RoleController {
|
||||
@Query('allow_menu_keys') allowMenuKeys: string = '',
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
const roleIdArray = roleIds
|
||||
.split(',')
|
||||
.map((id) => parseInt(id.trim()))
|
||||
@@ -263,15 +208,10 @@ export class RoleController {
|
||||
.filter((key) => key)
|
||||
: [];
|
||||
|
||||
const result = await this.roleService.getMenuIdsByRoleIds(
|
||||
return await this.roleService.getMenuIdsByRoleIds(
|
||||
siteId,
|
||||
roleIdArray,
|
||||
allowMenuKeyArray,
|
||||
);
|
||||
return {
|
||||
code: 200,
|
||||
message: '获取成功',
|
||||
data: result,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
UseGuards,
|
||||
Req,
|
||||
ParseIntPipe,
|
||||
UnauthorizedException,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiResponse, ApiParam } from '@nestjs/swagger';
|
||||
import type { Request } from 'express';
|
||||
@@ -42,18 +43,22 @@ export class ScheduleController {
|
||||
@ApiOperation({ summary: '获取定时任务分页列表' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getPage(@Query() query: any, @Req() req: AuthenticatedRequest) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.scheduleService.getPage(siteId, query);
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.scheduleService.getPage(siteId, query);
|
||||
}
|
||||
|
||||
@Get('list')
|
||||
@ApiOperation({ summary: '获取定时任务列表' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getList(@Query() query: any, @Req() req: AuthenticatedRequest) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.scheduleService.getList(siteId, query);
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.scheduleService.getList(siteId, query);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@@ -64,22 +69,22 @@ export class ScheduleController {
|
||||
@Param('id', ParseIntPipe) id: number,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.scheduleService.getInfo(siteId, id);
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.scheduleService.getInfo(siteId, id);
|
||||
}
|
||||
|
||||
@Post()
|
||||
@ApiOperation({ summary: '新增定时任务' })
|
||||
@ApiResponse({ status: 200, description: '创建成功' })
|
||||
async add(@Body() data: any, @Req() req: AuthenticatedRequest) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.scheduleService.add(siteId, data);
|
||||
return { code: 200, message: '创建成功', data: result };
|
||||
} catch (error) {
|
||||
return { code: 400, message: error.message || '创建失败', data: null };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.scheduleService.add(siteId, data);
|
||||
}
|
||||
|
||||
@Put(':id')
|
||||
@@ -91,13 +96,11 @@ export class ScheduleController {
|
||||
@Body() data: any,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.scheduleService.edit(siteId, id, data);
|
||||
return { code: 200, message: '更新成功', data: result };
|
||||
} catch (error) {
|
||||
return { code: 400, message: error.message || '更新失败', data: null };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.scheduleService.edit(siteId, id, data);
|
||||
}
|
||||
|
||||
@Put(':id/start')
|
||||
@@ -108,13 +111,11 @@ export class ScheduleController {
|
||||
@Param('id', ParseIntPipe) id: number,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.scheduleService.start(siteId, id);
|
||||
return { code: 200, message: '启动成功', data: result };
|
||||
} catch (error) {
|
||||
return { code: 400, message: error.message || '启动失败', data: null };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.scheduleService.start(siteId, id);
|
||||
}
|
||||
|
||||
@Put(':id/stop')
|
||||
@@ -125,13 +126,11 @@ export class ScheduleController {
|
||||
@Param('id', ParseIntPipe) id: number,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.scheduleService.stop(siteId, id);
|
||||
return { code: 200, message: '停止成功', data: result };
|
||||
} catch (error) {
|
||||
return { code: 400, message: error.message || '停止失败', data: null };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.scheduleService.stop(siteId, id);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@@ -142,12 +141,10 @@ export class ScheduleController {
|
||||
@Param('id', ParseIntPipe) id: number,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
try {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.scheduleService.del(siteId, id);
|
||||
return { code: 200, message: '删除成功', data: result };
|
||||
} catch (error) {
|
||||
return { code: 400, message: error.message || '删除失败', data: null };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.scheduleService.del(siteId, id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Controller, Get, Query, UseGuards, Req } from '@nestjs/common';
|
||||
import { Controller, Get, Query, UseGuards, Req, UnauthorizedException } from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
|
||||
import type { Request } from 'express';
|
||||
import { JwtAuthGuard } from '../../../auth/guards/JwtAuthGuard';
|
||||
@@ -29,27 +29,35 @@ export class ScheduleLogController {
|
||||
@ApiOperation({ summary: '获取定时任务日志分页列表' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getPage(@Query() query: any, @Req() req: AuthenticatedRequest) {
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
// TODO: 实现定时任务日志分页列表
|
||||
return { code: 200, message: '获取成功', data: { list: [], total: 0 } };
|
||||
return { list: [], total: 0 };
|
||||
}
|
||||
|
||||
@Get('list')
|
||||
@ApiOperation({ summary: '获取定时任务日志列表' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getList(@Query() query: any, @Req() req: AuthenticatedRequest) {
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
// TODO: 实现定时任务日志列表
|
||||
return { code: 200, message: '获取成功', data: [] };
|
||||
return [];
|
||||
}
|
||||
|
||||
@Get('stats')
|
||||
@ApiOperation({ summary: '获取定时任务统计信息' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getStats(@Req() req: AuthenticatedRequest) {
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
// TODO: 实现定时任务统计信息
|
||||
return {
|
||||
code: 200,
|
||||
message: '获取成功',
|
||||
data: { total: 0, success: 0, failed: 0 },
|
||||
};
|
||||
return { total: 0, success: 0, failed: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Controller, Get, Post, UseGuards, Req } from '@nestjs/common';
|
||||
import { Controller, Get, Post, UseGuards, Req, UnauthorizedException } from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
|
||||
import type { Request } from 'express';
|
||||
import { JwtAuthGuard } from '../../../auth/guards/JwtAuthGuard';
|
||||
@@ -17,10 +17,10 @@ interface AuthenticatedRequest extends Request {
|
||||
|
||||
/**
|
||||
* 系统信息管理控制器 - 管理端
|
||||
* 路由前缀: /adminapi/sys/system
|
||||
* 路由前缀: /admin/sys/system
|
||||
*/
|
||||
@ApiTags('系统信息')
|
||||
@Controller('adminapi/sys/system')
|
||||
@Controller('admin/sys/system')
|
||||
@UseGuards(JwtAuthGuard, RolesGuard)
|
||||
@Roles('admin')
|
||||
export class SystemController {
|
||||
@@ -30,52 +30,45 @@ export class SystemController {
|
||||
@ApiOperation({ summary: '获取系统信息' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getInfo(@Req() req: AuthenticatedRequest) {
|
||||
const result = await this.systemService.getInfo();
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
return await this.systemService.getInfo();
|
||||
}
|
||||
|
||||
@Get('url')
|
||||
@ApiOperation({ summary: '获取系统URL信息' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getUrl(@Req() req: AuthenticatedRequest) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.systemService.getUrl(siteId);
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.systemService.getUrl(siteId);
|
||||
}
|
||||
|
||||
@Get('stats')
|
||||
@ApiOperation({ summary: '获取系统统计信息' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getSystemStats(@Req() req: AuthenticatedRequest) {
|
||||
const result = await this.systemService.getSystemStats();
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
return await this.systemService.getSystemStats();
|
||||
}
|
||||
|
||||
@Get('system-info')
|
||||
@ApiOperation({ summary: '获取详细系统信息' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getSystemInfo(@Req() req: AuthenticatedRequest) {
|
||||
const result = await this.systemService.getSystemInfo();
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
return await this.systemService.getSystemInfo();
|
||||
}
|
||||
|
||||
@Post('clear-cache')
|
||||
@ApiOperation({ summary: '清理系统缓存' })
|
||||
@ApiResponse({ status: 200, description: '清理成功' })
|
||||
async clearCache(@Req() req: AuthenticatedRequest) {
|
||||
try {
|
||||
const result = await this.systemService.clearCache();
|
||||
return { code: 200, message: '清理成功', data: result };
|
||||
} catch (error) {
|
||||
return { code: 400, message: error.message || '清理失败', data: null };
|
||||
}
|
||||
return await this.systemService.clearCache();
|
||||
}
|
||||
|
||||
@Get('check-config')
|
||||
@ApiOperation({ summary: '检查系统配置' })
|
||||
@ApiResponse({ status: 200, description: '检查完成' })
|
||||
async checkSystemConfig(@Req() req: AuthenticatedRequest) {
|
||||
const result = await this.systemService.checkSystemConfig();
|
||||
return { code: 200, message: '检查完成', data: result };
|
||||
return await this.systemService.checkSystemConfig();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,10 @@ interface AuthenticatedRequest extends Request {
|
||||
|
||||
/**
|
||||
* 富文本编辑器控制器 - 管理端
|
||||
* 路由前缀: /adminapi/sys/ueditor
|
||||
* 路由前缀: /admin/sys/ueditor
|
||||
*/
|
||||
@ApiTags('富文本编辑器')
|
||||
@Controller('adminapi/sys/ueditor')
|
||||
@Controller('admin/sys/ueditor')
|
||||
@UseGuards(JwtAuthGuard, RolesGuard)
|
||||
@Roles('admin')
|
||||
export class UeditorController {
|
||||
@@ -49,7 +49,7 @@ export class UeditorController {
|
||||
@ApiResponse({ status: 200, description: '上传成功' })
|
||||
async upload(@Req() req: AuthenticatedRequest) {
|
||||
// TODO: 实现编辑器文件上传
|
||||
return { code: 200, message: '上传成功', data: null };
|
||||
return null;
|
||||
}
|
||||
|
||||
@Get('list')
|
||||
@@ -57,6 +57,6 @@ export class UeditorController {
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getFileList(@Query() query: any, @Req() req: AuthenticatedRequest) {
|
||||
// TODO: 实现文件列表获取
|
||||
return { code: 200, message: '获取成功', data: { list: [], total: 0 } };
|
||||
return { list: [], total: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
Req,
|
||||
ParseIntPipe,
|
||||
Query,
|
||||
UnauthorizedException,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiResponse, ApiParam } from '@nestjs/swagger';
|
||||
import type { Request } from 'express';
|
||||
@@ -34,9 +35,11 @@ export class ApiAreaController {
|
||||
@Req() req: AuthenticatedRequest,
|
||||
@Query('parent_id') parentId?: number,
|
||||
) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.apiAreaService.getAreaList(siteId, parentId);
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.apiAreaService.getAreaList(siteId, parentId);
|
||||
}
|
||||
|
||||
@Get(':areaId')
|
||||
@@ -47,8 +50,10 @@ export class ApiAreaController {
|
||||
@Param('areaId', ParseIntPipe) areaId: number,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.apiAreaService.getAreaInfo(siteId, areaId);
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.apiAreaService.getAreaInfo(siteId, areaId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
Query,
|
||||
UseGuards,
|
||||
Req,
|
||||
UnauthorizedException,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
|
||||
import type { Request } from 'express';
|
||||
@@ -22,7 +23,7 @@ interface AuthenticatedRequest extends Request {
|
||||
}
|
||||
|
||||
@ApiTags('API配置')
|
||||
@Controller('api/sys/config')
|
||||
@Controller('api/sys/settings')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
export class ApiConfigController {
|
||||
constructor(private readonly apiConfigService: ApiConfigService) {}
|
||||
@@ -31,9 +32,11 @@ export class ApiConfigController {
|
||||
@ApiOperation({ summary: '获取配置' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getConfig(@Query('key') key: string, @Req() req: AuthenticatedRequest) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.apiConfigService.getConfig(siteId, key);
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.apiConfigService.getConfig(siteId, key);
|
||||
}
|
||||
|
||||
@Post('batch')
|
||||
@@ -43,8 +46,10 @@ export class ApiConfigController {
|
||||
@Body() body: { keys: string[] },
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.apiConfigService.getConfigs(siteId, body.keys);
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.apiConfigService.getConfigs(siteId, body.keys);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Controller, Get, UseGuards, Req } from '@nestjs/common';
|
||||
import { Controller, Get, UseGuards, Req, UnauthorizedException } from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
|
||||
import type { Request } from 'express';
|
||||
import { JwtAuthGuard } from '../../../auth/guards/JwtAuthGuard';
|
||||
@@ -14,7 +14,7 @@ interface AuthenticatedRequest extends Request {
|
||||
}
|
||||
|
||||
@ApiTags('API首页')
|
||||
@Controller('api/sys/index')
|
||||
@Controller('api/sys/home')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
export class ApiIndexController {
|
||||
constructor(private readonly apiIndexService: ApiIndexService) {}
|
||||
@@ -23,16 +23,17 @@ export class ApiIndexController {
|
||||
@ApiOperation({ summary: '获取首页信息' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getIndexInfo(@Req() req: AuthenticatedRequest) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.apiIndexService.getIndexInfo(siteId);
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.apiIndexService.getIndexInfo(siteId);
|
||||
}
|
||||
|
||||
@Get('system')
|
||||
@ApiOperation({ summary: '获取系统信息' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getSystemInfo(@Req() req: AuthenticatedRequest) {
|
||||
const result = await this.apiIndexService.getSystemInfo();
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
return await this.apiIndexService.getSystemInfo();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Controller, Post, Body, UseGuards, Req } from '@nestjs/common';
|
||||
import { Controller, Post, Body, UseGuards, Req, UnauthorizedException } from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
|
||||
import type { Request } from 'express';
|
||||
import { JwtAuthGuard } from '../../../auth/guards/JwtAuthGuard';
|
||||
@@ -26,8 +26,11 @@ export class ApiScanController {
|
||||
@Body() body: { code: string },
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
const result = await this.apiScanService.scanQrCode(body.code);
|
||||
return { code: 200, message: '扫描成功', data: result };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.apiScanService.scanQrCode(body.code);
|
||||
}
|
||||
|
||||
@Post('barcode')
|
||||
@@ -37,7 +40,10 @@ export class ApiScanController {
|
||||
@Body() body: { code: string },
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
const result = await this.apiScanService.scanBarcode(body.code);
|
||||
return { code: 200, message: '扫描成功', data: result };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.apiScanService.scanBarcode(body.code);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
UseGuards,
|
||||
Req,
|
||||
ParseIntPipe,
|
||||
UnauthorizedException,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiResponse, ApiParam } from '@nestjs/swagger';
|
||||
import type { Request } from 'express';
|
||||
@@ -33,18 +34,22 @@ export class ApiTaskController {
|
||||
@ApiOperation({ summary: '获取任务列表' })
|
||||
@ApiResponse({ status: 200, description: '获取成功' })
|
||||
async getTaskList(@Req() req: AuthenticatedRequest) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.apiTaskService.getTaskList(siteId);
|
||||
return { code: 200, message: '获取成功', data: result };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.apiTaskService.getTaskList(siteId);
|
||||
}
|
||||
|
||||
@Post()
|
||||
@ApiOperation({ summary: '创建任务' })
|
||||
@ApiResponse({ status: 200, description: '创建成功' })
|
||||
async createTask(@Body() body: any, @Req() req: AuthenticatedRequest) {
|
||||
const siteId = req.user?.siteId || 0;
|
||||
const result = await this.apiTaskService.createTask(siteId, body);
|
||||
return { code: 200, message: '创建成功', data: result };
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.apiTaskService.createTask(siteId, body);
|
||||
}
|
||||
|
||||
@Put(':taskId/status')
|
||||
@@ -56,10 +61,13 @@ export class ApiTaskController {
|
||||
@Body() body: { status: string },
|
||||
@Req() req: AuthenticatedRequest,
|
||||
) {
|
||||
const result = await this.apiTaskService.updateTaskStatus(
|
||||
const siteId = req.user?.siteId;
|
||||
if (!siteId) {
|
||||
throw new UnauthorizedException('未授权访问:缺少 site_id');
|
||||
}
|
||||
return await this.apiTaskService.updateTaskStatus(
|
||||
taskId,
|
||||
body.status,
|
||||
);
|
||||
return { code: 200, message: '更新成功', data: result };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
|
||||
import { BaseEntity } from '../../../core/base/BaseEntity';
|
||||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';
|
||||
|
||||
@Entity('sys_attachment')
|
||||
export class SysAttachment extends BaseEntity {
|
||||
export class SysAttachment {
|
||||
@PrimaryGeneratedColumn({ name: 'att_id' })
|
||||
att_id: number;
|
||||
|
||||
@@ -81,6 +80,15 @@ export class SysAttachment extends BaseEntity {
|
||||
})
|
||||
storage_type: string;
|
||||
|
||||
@Column({ name: 'site_id', type: 'int', default: 0, comment: '站点id' })
|
||||
site_id: number;
|
||||
|
||||
@CreateDateColumn({ name: 'create_time', type: 'int', default: 0, comment: '上传时间' })
|
||||
create_time: number;
|
||||
|
||||
@UpdateDateColumn({ name: 'update_time', type: 'int', default: 0, comment: '更新时间' })
|
||||
update_time: number;
|
||||
|
||||
// 获取文件扩展名
|
||||
getFileExtension(): string {
|
||||
return this.real_name.split('.').pop() || '';
|
||||
|
||||
@@ -1,44 +1,49 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, Index } from 'typeorm';
|
||||
import { BaseEntity } from '../../../core/base/BaseEntity';
|
||||
import { Entity, PrimaryGeneratedColumn, Column, Index, CreateDateColumn, UpdateDateColumn, DeleteDateColumn } from 'typeorm';
|
||||
|
||||
/**
|
||||
* 系统配置实体
|
||||
* 对应数据表: sys_config
|
||||
*/
|
||||
@Entity('sys_config')
|
||||
@Index(['siteId', 'key'], { unique: true })
|
||||
export class SysConfig extends BaseEntity {
|
||||
@Index(['site_id', 'config_key'], { unique: true })
|
||||
export class SysConfig {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ name: 'site_id', type: 'int', default: 0, comment: '站点ID' })
|
||||
siteId: number;
|
||||
@Column({ name: 'site_id', type: 'int', default: 0, comment: '站点id' })
|
||||
site_id: number;
|
||||
|
||||
@Column({ name: 'key', type: 'varchar', length: 100, comment: '配置键' })
|
||||
key: string;
|
||||
@Column({ name: 'config_key', type: 'varchar', length: 255, default: '', comment: '配置项关键字' })
|
||||
config_key: string;
|
||||
|
||||
@Column({
|
||||
name: 'value',
|
||||
type: 'text',
|
||||
nullable: true,
|
||||
comment: '配置值(JSON格式)',
|
||||
comment: '配置值json',
|
||||
})
|
||||
value: string;
|
||||
|
||||
@Column({
|
||||
name: 'desc',
|
||||
type: 'varchar',
|
||||
length: 255,
|
||||
nullable: true,
|
||||
comment: '配置描述',
|
||||
})
|
||||
desc?: string;
|
||||
|
||||
@Column({
|
||||
name: 'is_use',
|
||||
name: 'status',
|
||||
type: 'tinyint',
|
||||
default: 1,
|
||||
comment: '是否启用:0=否,1=是',
|
||||
comment: '是否启用 1启用 0不启用',
|
||||
})
|
||||
isUse: number;
|
||||
status: number;
|
||||
|
||||
@CreateDateColumn({ name: 'create_time', type: 'int', default: 0, comment: '创建时间' })
|
||||
create_time: number;
|
||||
|
||||
@UpdateDateColumn({ name: 'update_time', type: 'int', default: 0, comment: '修改时间' })
|
||||
update_time: number;
|
||||
|
||||
@Column({ name: 'addon', type: 'varchar', length: 255, default: '', comment: '所属插件' })
|
||||
addon: string;
|
||||
|
||||
@Column({ name: 'is_del', type: 'tinyint', default: 0, comment: '是否删除' })
|
||||
is_del: number;
|
||||
|
||||
@DeleteDateColumn({ name: 'delete_time', type: 'int', default: 0, comment: '删除时间' })
|
||||
delete_time: number;
|
||||
}
|
||||
|
||||
@@ -6,26 +6,26 @@
|
||||
UpdateDateColumn,
|
||||
DeleteDateColumn,
|
||||
} from 'typeorm';
|
||||
import { BaseEntity } from '../../../core/base/BaseEntity';
|
||||
|
||||
/**
|
||||
* 系统菜单实体
|
||||
*/
|
||||
@Entity('sys_menu')
|
||||
export class SysMenu extends BaseEntity {
|
||||
export class SysMenu {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ name: 'menu_name', length: 100, comment: '菜单名称' })
|
||||
menuName: string;
|
||||
@Column({ name: 'menu_name', type: 'varchar', length: 100, default: '', comment: '菜单名称' })
|
||||
menu_name: string;
|
||||
|
||||
@Column({
|
||||
name: 'menu_short_name',
|
||||
type: 'varchar',
|
||||
length: 50,
|
||||
nullable: true,
|
||||
comment: '菜单简称',
|
||||
})
|
||||
menuShortName: string;
|
||||
menu_short_name: string;
|
||||
|
||||
@Column({
|
||||
name: 'menu_type',
|
||||
@@ -33,34 +33,37 @@ export class SysMenu extends BaseEntity {
|
||||
default: 1,
|
||||
comment: '菜单类型 1目录 2菜单 3按钮',
|
||||
})
|
||||
menuType: number;
|
||||
menu_type: number;
|
||||
|
||||
@Column({ name: 'parent_id', type: 'int', default: 0, comment: '父级菜单ID' })
|
||||
parentId: number;
|
||||
parent_id: number;
|
||||
|
||||
@Column({
|
||||
name: 'menu_key',
|
||||
type: 'varchar',
|
||||
length: 100,
|
||||
nullable: true,
|
||||
comment: '菜单标识',
|
||||
})
|
||||
menuKey: string;
|
||||
menu_key: string;
|
||||
|
||||
@Column({
|
||||
name: 'menu_url',
|
||||
type: 'varchar',
|
||||
length: 255,
|
||||
nullable: true,
|
||||
comment: '菜单链接',
|
||||
})
|
||||
menuUrl: string;
|
||||
menu_url: string;
|
||||
|
||||
@Column({
|
||||
name: 'menu_icon',
|
||||
type: 'varchar',
|
||||
length: 100,
|
||||
nullable: true,
|
||||
comment: '菜单图标',
|
||||
})
|
||||
menuIcon: string;
|
||||
menu_icon: string;
|
||||
|
||||
@Column({ name: 'sort', type: 'int', default: 0, comment: '排序' })
|
||||
sort: number;
|
||||
@@ -79,7 +82,7 @@ export class SysMenu extends BaseEntity {
|
||||
default: 1,
|
||||
comment: '是否显示 0不显示 1显示',
|
||||
})
|
||||
isShow: number;
|
||||
is_show: number;
|
||||
|
||||
@Column({
|
||||
name: 'is_del',
|
||||
@@ -87,16 +90,19 @@ export class SysMenu extends BaseEntity {
|
||||
default: 0,
|
||||
comment: '是否删除 0未删除 1已删除',
|
||||
})
|
||||
isDel: number;
|
||||
is_del: number;
|
||||
|
||||
@CreateDateColumn({ name: 'create_time', comment: '创建时间' })
|
||||
createTime: Date;
|
||||
@CreateDateColumn({ name: 'create_time', type: 'int', default: 0, comment: '创建时间' })
|
||||
create_time: number;
|
||||
|
||||
@UpdateDateColumn({ name: 'update_time', comment: '更新时间' })
|
||||
updateTime: Date;
|
||||
@UpdateDateColumn({ name: 'update_time', type: 'int', default: 0, comment: '更新时间' })
|
||||
update_time: number;
|
||||
|
||||
@DeleteDateColumn({ name: 'delete_time', comment: '删除时间' })
|
||||
deleteTime: Date;
|
||||
@DeleteDateColumn({ name: 'delete_time', type: 'int', default: 0, comment: '删除时间' })
|
||||
delete_time: number;
|
||||
|
||||
@Column({ name: 'site_id', type: 'int', default: 0, comment: '站点id' })
|
||||
site_id: number;
|
||||
|
||||
// 虚拟字段
|
||||
statusName?: string;
|
||||
|
||||
@@ -5,41 +5,48 @@
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
import { BaseEntity } from '../../../core/base/BaseEntity';
|
||||
|
||||
/**
|
||||
* 系统通知实体
|
||||
*/
|
||||
@Entity('sys_notice')
|
||||
export class SysNotice extends BaseEntity {
|
||||
export class SysNotice {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ name: 'title', length: 200, comment: '通知标题' })
|
||||
title: string;
|
||||
@Column({ name: 'site_id', type: 'int', default: 0, comment: '站点ID' })
|
||||
site_id: number;
|
||||
|
||||
@Column({ name: 'content', type: 'text', comment: '通知内容' })
|
||||
content: string;
|
||||
@Column({ name: 'key', type: 'varchar', length: 50, default: '', comment: '标识' })
|
||||
key: string;
|
||||
|
||||
@Column({
|
||||
name: 'type',
|
||||
type: 'tinyint',
|
||||
default: 1,
|
||||
comment: '通知类型 1系统通知 2用户通知',
|
||||
})
|
||||
type: number;
|
||||
@Column({ name: 'sms_content', type: 'text', nullable: true, comment: '短信配置参数' })
|
||||
sms_content: string;
|
||||
|
||||
@Column({
|
||||
name: 'status',
|
||||
type: 'tinyint',
|
||||
default: 1,
|
||||
comment: '状态 0禁用 1启用',
|
||||
})
|
||||
status: number;
|
||||
@Column({ name: 'is_wechat', type: 'tinyint', default: 0, comment: '公众号模板消息(0:关闭,1:开启)' })
|
||||
is_wechat: number;
|
||||
|
||||
@CreateDateColumn({ name: 'create_time', comment: '创建时间' })
|
||||
createTime: Date;
|
||||
@Column({ name: 'is_weapp', type: 'tinyint', default: 0, comment: '小程序订阅消息(0:关闭,1:开启)' })
|
||||
is_weapp: number;
|
||||
|
||||
@UpdateDateColumn({ name: 'update_time', comment: '更新时间' })
|
||||
updateTime: Date;
|
||||
@Column({ name: 'is_sms', type: 'tinyint', default: 0, comment: '发送短信(0:关闭,1:开启)' })
|
||||
is_sms: number;
|
||||
|
||||
@Column({ name: 'wechat_template_id', type: 'varchar', length: 255, default: '', comment: '微信模版消息id' })
|
||||
wechat_template_id: string;
|
||||
|
||||
@Column({ name: 'weapp_template_id', type: 'varchar', length: 255, default: '', comment: '微信小程序订阅消息id' })
|
||||
weapp_template_id: string;
|
||||
|
||||
@Column({ name: 'sms_id', type: 'varchar', length: 255, default: '', comment: '短信id(对应短信配置)' })
|
||||
sms_id: string;
|
||||
|
||||
@Column({ name: 'wechat_first', type: 'varchar', length: 255, default: '', comment: '微信头部' })
|
||||
wechat_first: string;
|
||||
|
||||
@Column({ name: 'wechat_remark', type: 'varchar', length: 255, default: '', comment: '微信说明' })
|
||||
wechat_remark: string;
|
||||
|
||||
@CreateDateColumn({ name: 'create_time', type: 'int', default: 0, comment: '添加时间' })
|
||||
create_time: number;
|
||||
}
|
||||
|
||||
@@ -4,45 +4,40 @@
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
DeleteDateColumn,
|
||||
} from 'typeorm';
|
||||
import { BaseEntity } from '../../../core/base/BaseEntity';
|
||||
|
||||
/**
|
||||
* 系统角色实体
|
||||
*/
|
||||
@Entity('sys_role')
|
||||
export class SysRole extends BaseEntity {
|
||||
export class SysRole {
|
||||
@PrimaryGeneratedColumn({ name: 'role_id' })
|
||||
roleId: number;
|
||||
role_id: number;
|
||||
|
||||
@Column({ name: 'role_name', length: 50, comment: '角色名称' })
|
||||
roleName: string;
|
||||
@Column({ name: 'site_id', type: 'int', default: 0, comment: '站点id' })
|
||||
site_id: number;
|
||||
|
||||
@Column({ name: 'role_key', length: 50, comment: '角色标识' })
|
||||
roleKey: string;
|
||||
@Column({ name: 'role_name', type: 'varchar', length: 255, default: '', comment: '角色名称' })
|
||||
role_name: string;
|
||||
|
||||
@Column({ name: 'sort', type: 'int', default: 0, comment: '排序' })
|
||||
sort: number;
|
||||
@Column({ name: 'rules', type: 'text', nullable: true, comment: '角色权限(menus_id)' })
|
||||
rules: string;
|
||||
|
||||
@Column({
|
||||
name: 'status',
|
||||
type: 'tinyint',
|
||||
default: 1,
|
||||
comment: '状态 0禁用 1启用',
|
||||
})
|
||||
@Column({ name: 'status', type: 'tinyint', default: 1, comment: '状态' })
|
||||
status: number;
|
||||
|
||||
@Column({ name: 'remark', length: 500, nullable: true, comment: '备注' })
|
||||
remark: string;
|
||||
@CreateDateColumn({ name: 'create_time', type: 'int', default: 0, comment: '添加时间' })
|
||||
create_time: number;
|
||||
|
||||
@Column({ name: 'rules', type: 'json', nullable: true, comment: '权限规则' })
|
||||
rules: string[];
|
||||
@UpdateDateColumn({ name: 'update_time', type: 'int', default: 0, comment: '最后修改时间' })
|
||||
update_time: number;
|
||||
|
||||
@CreateDateColumn({ name: 'create_time', comment: '创建时间' })
|
||||
createTime: Date;
|
||||
@Column({ name: 'is_del', type: 'tinyint', default: 0, comment: '是否删除' })
|
||||
is_del: number;
|
||||
|
||||
@UpdateDateColumn({ name: 'update_time', comment: '更新时间' })
|
||||
updateTime: Date;
|
||||
@DeleteDateColumn({ name: 'delete_time', type: 'int', default: 0, comment: '删除时间' })
|
||||
delete_time: number;
|
||||
|
||||
// 虚拟字段
|
||||
statusName?: string;
|
||||
|
||||
@@ -4,63 +4,52 @@
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
DeleteDateColumn,
|
||||
} from 'typeorm';
|
||||
import { BaseEntity } from '../../../core/base/BaseEntity';
|
||||
|
||||
/**
|
||||
* 定时任务实体
|
||||
*/
|
||||
@Entity('sys_schedule')
|
||||
export class SysSchedule extends BaseEntity {
|
||||
export class SysSchedule {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ name: 'key', length: 100, comment: '任务标识' })
|
||||
@Column({ name: 'site_id', type: 'int', default: 0, comment: '站点id' })
|
||||
site_id: number;
|
||||
|
||||
@Column({ name: 'addon', type: 'varchar', length: 255, default: '', comment: '所属插件' })
|
||||
addon: string;
|
||||
|
||||
@Column({ name: 'key', type: 'varchar', length: 255, default: '', comment: '计划任务模板key' })
|
||||
key: string;
|
||||
|
||||
@Column({ name: 'title', length: 100, comment: '任务标题' })
|
||||
title: string;
|
||||
|
||||
@Column({ name: 'command', length: 500, comment: '执行命令' })
|
||||
command: string;
|
||||
|
||||
@Column({
|
||||
name: 'time',
|
||||
type: 'json',
|
||||
nullable: true,
|
||||
comment: '执行时间配置',
|
||||
})
|
||||
time: any;
|
||||
|
||||
@Column({
|
||||
name: 'last_time',
|
||||
type: 'timestamp',
|
||||
nullable: true,
|
||||
comment: '最后执行时间',
|
||||
})
|
||||
lastTime: Date;
|
||||
|
||||
@Column({
|
||||
name: 'next_time',
|
||||
type: 'timestamp',
|
||||
nullable: true,
|
||||
comment: '下次执行时间',
|
||||
})
|
||||
nextTime: Date;
|
||||
|
||||
@Column({
|
||||
name: 'status',
|
||||
type: 'tinyint',
|
||||
default: 1,
|
||||
comment: '状态 0禁用 1启用',
|
||||
})
|
||||
@Column({ name: 'status', type: 'int', default: 1, comment: '任务状态 是否启用' })
|
||||
status: number;
|
||||
|
||||
@CreateDateColumn({ name: 'create_time', comment: '创建时间' })
|
||||
createTime: Date;
|
||||
@Column({ name: 'time', type: 'varchar', length: 500, default: '', comment: '任务周期 json结构' })
|
||||
time: string;
|
||||
|
||||
@UpdateDateColumn({ name: 'update_time', comment: '更新时间' })
|
||||
updateTime: Date;
|
||||
@Column({ name: 'count', type: 'int', default: 0, comment: '执行次数' })
|
||||
count: number;
|
||||
|
||||
@Column({ name: 'last_time', type: 'int', default: 0, comment: '最后执行时间' })
|
||||
last_time: number;
|
||||
|
||||
@Column({ name: 'next_time', type: 'int', default: 0, comment: '下次执行时间' })
|
||||
next_time: number;
|
||||
|
||||
@Column({ name: 'sort', type: 'int', default: 0, comment: '排序' })
|
||||
sort: number;
|
||||
|
||||
@CreateDateColumn({ name: 'create_time', type: 'int', default: 0, comment: '创建时间' })
|
||||
create_time: number;
|
||||
|
||||
@UpdateDateColumn({ name: 'update_time', type: 'int', default: 0, comment: '更新时间' })
|
||||
update_time: number;
|
||||
|
||||
@DeleteDateColumn({ name: 'delete_time', type: 'int', default: 0, comment: '删除时间' })
|
||||
delete_time: number;
|
||||
|
||||
// 虚拟字段
|
||||
statusName?: string;
|
||||
|
||||
@@ -24,7 +24,8 @@ export class RoleService {
|
||||
* 删除用户组
|
||||
*/
|
||||
async delete(role_id: number) {
|
||||
return this.coreRoleService.delete(role_id);
|
||||
// Core 层方法名为 del,且需要 siteId;暂无 siteId 时传 0 与 PHP 语义对齐
|
||||
return this.coreRoleService.del(role_id as any, 0 as any);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository, Like } from 'typeorm';
|
||||
import { BaseService } from '../../../../core/base/BaseService';
|
||||
import { SysAttachment } from '../../entities/SysAttachment';
|
||||
|
||||
/**
|
||||
@@ -9,13 +8,11 @@ import { SysAttachment } from '../../entities/SysAttachment';
|
||||
* 对应PHP: CoreAttachmentService
|
||||
*/
|
||||
@Injectable()
|
||||
export class CoreAttachmentService extends BaseService<SysAttachment> {
|
||||
export class CoreAttachmentService {
|
||||
constructor(
|
||||
@InjectRepository(SysAttachment)
|
||||
private readonly attachmentRepository: Repository<SysAttachment>,
|
||||
) {
|
||||
super(attachmentRepository);
|
||||
}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 分页查询附件列表
|
||||
@@ -42,8 +39,8 @@ export class CoreAttachmentService extends BaseService<SysAttachment> {
|
||||
'attachment.real_name',
|
||||
'attachment.path',
|
||||
'attachment.url',
|
||||
'attachment.file_size',
|
||||
'attachment.file_type',
|
||||
'attachment.att_size',
|
||||
'attachment.att_type',
|
||||
'attachment.cate_id',
|
||||
'attachment.create_time',
|
||||
]);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository, In } from 'typeorm';
|
||||
import { BaseService } from '../../../../core/base/BaseService';
|
||||
import { SysRole } from '../../../rbac/entities/SysRole';
|
||||
|
||||
/**
|
||||
@@ -9,13 +8,11 @@ import { SysRole } from '../../../rbac/entities/SysRole';
|
||||
* 对应PHP: 角色核心操作逻辑
|
||||
*/
|
||||
@Injectable()
|
||||
export class CoreRoleService extends BaseService<SysRole> {
|
||||
export class CoreRoleService {
|
||||
constructor(
|
||||
@InjectRepository(SysRole)
|
||||
private readonly roleRepository: Repository<SysRole>,
|
||||
) {
|
||||
super(roleRepository);
|
||||
}
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 分页查询角色列表
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user