1 Commits
main ... sync

Author SHA1 Message Date
f27b647eb0 sync 2025-03-13 20:45:12 +07:00
83 changed files with 4692 additions and 718 deletions

3
.gitignore vendored
View File

@@ -6,6 +6,3 @@ nto-cli/
/**/node_modules/ /**/node_modules/
/**/dist/ /**/dist/
.xlsx .xlsx
build/appimage
build/nfpm
build/nsis

4
.idea/.gigateam/gigateam.properties generated Normal file
View File

@@ -0,0 +1,4 @@
## changed at Sun Feb 09 09:09:47 KRAT 2025
#Sun Feb 09 09:09:47 KRAT 2025
com.dtl.jetbrains.utils.IdeaSlowOperationsAssertionUtils.isUnreadDefault=false
com.dtl.jetbrains.utils.IdeaSlowOperationsAssertionUtils.defaultValue=false

8
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

4
.idea/boilerplate.iml generated
View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="Go" enabled="true" />
</module>

31
.idea/codeStyles/Project.xml generated Normal file
View File

@@ -0,0 +1,31 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<DBN-PSQL>
<case-options enabled="true">
<option name="KEYWORD_CASE" value="lower" />
<option name="FUNCTION_CASE" value="lower" />
<option name="PARAMETER_CASE" value="lower" />
<option name="DATATYPE_CASE" value="lower" />
<option name="OBJECT_CASE" value="preserve" />
</case-options>
<formatting-settings enabled="false" />
</DBN-PSQL>
<DBN-SQL>
<case-options enabled="true">
<option name="KEYWORD_CASE" value="lower" />
<option name="FUNCTION_CASE" value="lower" />
<option name="PARAMETER_CASE" value="lower" />
<option name="DATATYPE_CASE" value="lower" />
<option name="OBJECT_CASE" value="preserve" />
</case-options>
<formatting-settings enabled="false">
<option name="STATEMENT_SPACING" value="one_line" />
<option name="CLAUSE_CHOP_DOWN" value="chop_down_if_statement_long" />
<option name="ITERATION_ELEMENTS_WRAPPING" value="chop_down_if_not_single" />
</formatting-settings>
</DBN-SQL>
<ScalaCodeStyleSettings>
<option name="MULTILINE_STRING_CLOSING_QUOTES_ON_NEW_LINE" value="true" />
</ScalaCodeStyleSettings>
</code_scheme>
</component>

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View File

@@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

405
.idea/dbnavigator.xml generated Normal file
View File

@@ -0,0 +1,405 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DBNavigator.Project.DatabaseFileManager">
<open-files />
</component>
<component name="DBNavigator.Project.Settings">
<connections />
<browser-settings>
<general>
<display-mode value="TABBED" />
<navigation-history-size value="100" />
<show-object-details value="false" />
<enable-sticky-paths value="true" />
</general>
<filters>
<object-type-filter>
<object-type name="SCHEMA" enabled="true" />
<object-type name="USER" enabled="true" />
<object-type name="ROLE" enabled="true" />
<object-type name="PRIVILEGE" enabled="true" />
<object-type name="CHARSET" enabled="true" />
<object-type name="TABLE" enabled="true" />
<object-type name="VIEW" enabled="true" />
<object-type name="MATERIALIZED_VIEW" enabled="true" />
<object-type name="NESTED_TABLE" enabled="true" />
<object-type name="COLUMN" enabled="true" />
<object-type name="INDEX" enabled="true" />
<object-type name="CONSTRAINT" enabled="true" />
<object-type name="DATASET_TRIGGER" enabled="true" />
<object-type name="DATABASE_TRIGGER" enabled="true" />
<object-type name="SYNONYM" enabled="true" />
<object-type name="SEQUENCE" enabled="true" />
<object-type name="PROCEDURE" enabled="true" />
<object-type name="FUNCTION" enabled="true" />
<object-type name="PACKAGE" enabled="true" />
<object-type name="TYPE" enabled="true" />
<object-type name="TYPE_ATTRIBUTE" enabled="true" />
<object-type name="ARGUMENT" enabled="true" />
<object-type name="DIMENSION" enabled="true" />
<object-type name="CLUSTER" enabled="true" />
<object-type name="DBLINK" enabled="true" />
</object-type-filter>
</filters>
<sorting>
<object-type name="COLUMN" sorting-type="NAME" />
<object-type name="FUNCTION" sorting-type="NAME" />
<object-type name="PROCEDURE" sorting-type="NAME" />
<object-type name="ARGUMENT" sorting-type="POSITION" />
<object-type name="TYPE ATTRIBUTE" sorting-type="POSITION" />
</sorting>
<default-editors>
<object-type name="VIEW" editor-type="SELECTION" />
<object-type name="PACKAGE" editor-type="SELECTION" />
<object-type name="TYPE" editor-type="SELECTION" />
</default-editors>
</browser-settings>
<navigation-settings>
<lookup-filters>
<lookup-objects>
<object-type name="SCHEMA" enabled="true" />
<object-type name="USER" enabled="false" />
<object-type name="ROLE" enabled="false" />
<object-type name="PRIVILEGE" enabled="false" />
<object-type name="CHARSET" enabled="false" />
<object-type name="TABLE" enabled="true" />
<object-type name="VIEW" enabled="true" />
<object-type name="MATERIALIZED VIEW" enabled="true" />
<object-type name="INDEX" enabled="true" />
<object-type name="CONSTRAINT" enabled="true" />
<object-type name="DATASET TRIGGER" enabled="true" />
<object-type name="DATABASE TRIGGER" enabled="true" />
<object-type name="SYNONYM" enabled="false" />
<object-type name="SEQUENCE" enabled="true" />
<object-type name="PROCEDURE" enabled="true" />
<object-type name="FUNCTION" enabled="true" />
<object-type name="PACKAGE" enabled="true" />
<object-type name="TYPE" enabled="true" />
<object-type name="DIMENSION" enabled="false" />
<object-type name="CLUSTER" enabled="false" />
<object-type name="DBLINK" enabled="true" />
</lookup-objects>
<force-database-load value="false" />
<prompt-connection-selection value="true" />
<prompt-schema-selection value="true" />
</lookup-filters>
</navigation-settings>
<dataset-grid-settings>
<general>
<enable-zooming value="true" />
<enable-column-tooltip value="true" />
</general>
<sorting>
<nulls-first value="true" />
<max-sorting-columns value="4" />
</sorting>
<audit-columns>
<column-names value="" />
<visible value="true" />
<editable value="false" />
</audit-columns>
</dataset-grid-settings>
<dataset-editor-settings>
<text-editor-popup>
<active value="false" />
<active-if-empty value="false" />
<data-length-threshold value="100" />
<popup-delay value="1000" />
</text-editor-popup>
<values-actions-popup>
<show-popup-button value="true" />
<element-count-threshold value="1000" />
<data-length-threshold value="250" />
</values-actions-popup>
<general>
<fetch-block-size value="100" />
<fetch-timeout value="30" />
<trim-whitespaces value="true" />
<convert-empty-strings-to-null value="true" />
<select-content-on-cell-edit value="true" />
<large-value-preview-active value="true" />
</general>
<filters>
<prompt-filter-dialog value="true" />
<default-filter-type value="BASIC" />
</filters>
<qualified-text-editor text-length-threshold="300">
<content-types>
<content-type name="Text" enabled="true" />
<content-type name="Properties" enabled="true" />
<content-type name="XML" enabled="true" />
<content-type name="DTD" enabled="true" />
<content-type name="HTML" enabled="true" />
<content-type name="XHTML" enabled="true" />
<content-type name="CSS" enabled="true" />
<content-type name="Java" enabled="true" />
<content-type name="SQL" enabled="true" />
<content-type name="PL/SQL" enabled="true" />
<content-type name="JavaScript" enabled="true" />
<content-type name="JSON" enabled="true" />
<content-type name="JSON5" enabled="true" />
<content-type name="Groovy" enabled="true" />
<content-type name="YAML" enabled="true" />
<content-type name="Manifest" enabled="true" />
</content-types>
</qualified-text-editor>
<record-navigation>
<navigation-target value="VIEWER" />
</record-navigation>
</dataset-editor-settings>
<code-editor-settings>
<general>
<show-object-navigation-gutter value="false" />
<show-spec-declaration-navigation-gutter value="true" />
<enable-spellchecking value="true" />
<enable-reference-spellchecking value="false" />
</general>
<confirmations>
<save-changes value="false" />
<revert-changes value="true" />
<exit-on-changes value="ASK" />
</confirmations>
</code-editor-settings>
<code-completion-settings>
<filters>
<basic-filter>
<filter-element type="RESERVED_WORD" id="keyword" selected="true" />
<filter-element type="RESERVED_WORD" id="function" selected="true" />
<filter-element type="RESERVED_WORD" id="parameter" selected="true" />
<filter-element type="RESERVED_WORD" id="datatype" selected="true" />
<filter-element type="RESERVED_WORD" id="exception" selected="true" />
<filter-element type="OBJECT" id="schema" selected="true" />
<filter-element type="OBJECT" id="role" selected="true" />
<filter-element type="OBJECT" id="user" selected="true" />
<filter-element type="OBJECT" id="privilege" selected="true" />
<user-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="false" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</user-schema>
<public-schema>
<filter-element type="OBJECT" id="table" selected="false" />
<filter-element type="OBJECT" id="view" selected="false" />
<filter-element type="OBJECT" id="materialized view" selected="false" />
<filter-element type="OBJECT" id="index" selected="false" />
<filter-element type="OBJECT" id="constraint" selected="false" />
<filter-element type="OBJECT" id="trigger" selected="false" />
<filter-element type="OBJECT" id="synonym" selected="false" />
<filter-element type="OBJECT" id="sequence" selected="false" />
<filter-element type="OBJECT" id="procedure" selected="false" />
<filter-element type="OBJECT" id="function" selected="false" />
<filter-element type="OBJECT" id="package" selected="false" />
<filter-element type="OBJECT" id="type" selected="false" />
<filter-element type="OBJECT" id="dimension" selected="false" />
<filter-element type="OBJECT" id="cluster" selected="false" />
<filter-element type="OBJECT" id="dblink" selected="false" />
</public-schema>
<any-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="true" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</any-schema>
</basic-filter>
<extended-filter>
<filter-element type="RESERVED_WORD" id="keyword" selected="true" />
<filter-element type="RESERVED_WORD" id="function" selected="true" />
<filter-element type="RESERVED_WORD" id="parameter" selected="true" />
<filter-element type="RESERVED_WORD" id="datatype" selected="true" />
<filter-element type="RESERVED_WORD" id="exception" selected="true" />
<filter-element type="OBJECT" id="schema" selected="true" />
<filter-element type="OBJECT" id="user" selected="true" />
<filter-element type="OBJECT" id="role" selected="true" />
<filter-element type="OBJECT" id="privilege" selected="true" />
<user-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="true" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</user-schema>
<public-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="true" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</public-schema>
<any-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="true" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</any-schema>
</extended-filter>
</filters>
<sorting enabled="true">
<sorting-element type="RESERVED_WORD" id="keyword" />
<sorting-element type="RESERVED_WORD" id="datatype" />
<sorting-element type="OBJECT" id="column" />
<sorting-element type="OBJECT" id="table" />
<sorting-element type="OBJECT" id="view" />
<sorting-element type="OBJECT" id="materialized view" />
<sorting-element type="OBJECT" id="index" />
<sorting-element type="OBJECT" id="constraint" />
<sorting-element type="OBJECT" id="trigger" />
<sorting-element type="OBJECT" id="synonym" />
<sorting-element type="OBJECT" id="sequence" />
<sorting-element type="OBJECT" id="procedure" />
<sorting-element type="OBJECT" id="function" />
<sorting-element type="OBJECT" id="package" />
<sorting-element type="OBJECT" id="type" />
<sorting-element type="OBJECT" id="dimension" />
<sorting-element type="OBJECT" id="cluster" />
<sorting-element type="OBJECT" id="dblink" />
<sorting-element type="OBJECT" id="schema" />
<sorting-element type="OBJECT" id="role" />
<sorting-element type="OBJECT" id="user" />
<sorting-element type="RESERVED_WORD" id="function" />
<sorting-element type="RESERVED_WORD" id="parameter" />
</sorting>
<format>
<enforce-code-style-case value="true" />
</format>
</code-completion-settings>
<execution-engine-settings>
<statement-execution>
<fetch-block-size value="100" />
<execution-timeout value="20" />
<debug-execution-timeout value="600" />
<focus-result value="false" />
<prompt-execution value="false" />
</statement-execution>
<script-execution>
<command-line-interfaces />
<execution-timeout value="300" />
</script-execution>
<method-execution>
<execution-timeout value="30" />
<debug-execution-timeout value="600" />
<parameter-history-size value="10" />
</method-execution>
</execution-engine-settings>
<operation-settings>
<transactions>
<uncommitted-changes>
<on-project-close value="ASK" />
<on-disconnect value="ASK" />
<on-autocommit-toggle value="ASK" />
</uncommitted-changes>
<multiple-uncommitted-changes>
<on-commit value="ASK" />
<on-rollback value="ASK" />
</multiple-uncommitted-changes>
</transactions>
<session-browser>
<disconnect-session value="ASK" />
<kill-session value="ASK" />
<reload-on-filter-change value="false" />
</session-browser>
<compiler>
<compile-type value="KEEP" />
<compile-dependencies value="ASK" />
<always-show-controls value="false" />
</compiler>
</operation-settings>
<ddl-file-settings>
<extensions>
<mapping file-type-id="VIEW" extensions="vw" />
<mapping file-type-id="TRIGGER" extensions="trg" />
<mapping file-type-id="PROCEDURE" extensions="prc" />
<mapping file-type-id="FUNCTION" extensions="fnc" />
<mapping file-type-id="PACKAGE" extensions="pkg" />
<mapping file-type-id="PACKAGE_SPEC" extensions="pks" />
<mapping file-type-id="PACKAGE_BODY" extensions="pkb" />
<mapping file-type-id="TYPE" extensions="tpe" />
<mapping file-type-id="TYPE_SPEC" extensions="tps" />
<mapping file-type-id="TYPE_BODY" extensions="tpb" />
</extensions>
<general>
<lookup-ddl-files value="true" />
<create-ddl-files value="false" />
<synchronize-ddl-files value="true" />
<use-qualified-names value="false" />
<make-scripts-rerunnable value="true" />
</general>
</ddl-file-settings>
<general-settings>
<regional-settings>
<date-format value="MEDIUM" />
<number-format value="UNGROUPED" />
<locale value="SYSTEM_DEFAULT" />
<use-custom-formats value="false" />
</regional-settings>
<environment>
<environment-types>
<environment-type id="development" name="Development" description="Development environment" color="-2430209/-12296320" readonly-code="false" readonly-data="false" />
<environment-type id="integration" name="Integration" description="Integration environment" color="-2621494/-12163514" readonly-code="true" readonly-data="false" />
<environment-type id="production" name="Production" description="Productive environment" color="-11574/-10271420" readonly-code="true" readonly-data="true" />
<environment-type id="other" name="Other" description="" color="-1576/-10724543" readonly-code="false" readonly-data="false" />
</environment-types>
<visibility-settings>
<connection-tabs value="true" />
<dialog-headers value="true" />
<object-editor-tabs value="true" />
<script-editor-tabs value="false" />
<execution-result-tabs value="true" />
</visibility-settings>
</environment>
</general-settings>
</component>
</project>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/nto_starterkit.iml" filepath="$PROJECT_DIR$/.idea/nto_starterkit.iml" />
</modules>
</component>
</project>

9
.idea/nto_starterkit.iml generated Normal file
View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="Go" enabled="true" />
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

2
.idea/vcs.xml generated
View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="VcsDirectoryMappings"> <component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" /> <mapping directory="$PROJECT_DIR$" vcs="Git" />
</component> </component>
</project> </project>

118
.idea/workspace.xml generated
View File

@@ -1,118 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="ALL" />
</component>
<component name="ChangeListManager">
<list default="true" id="0f2c8145-9000-45fe-a871-9ff0914e0e13" name="Changes" comment="">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/frontend/bindings/app/internal/services/excelmodule.ts" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/frontend/bindings/app/internal/services/index.ts" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/internal/dal/gen.go" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/internal/dal/gen_test.go" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/internal/dal/posts.gen.go" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/internal/dal/posts.gen_test.go" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/main.go" beforeDir="false" afterPath="$PROJECT_DIR$/main.go" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Go File" />
</list>
</option>
</component>
<component name="GOROOT" url="file://$USER_HOME$/go/go1.24.1" />
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="ProjectCodeStyleSettingsMigration">
<option name="version" value="2" />
</component>
<component name="ProjectColorInfo">{
&quot;associatedIndex&quot;: 8
}</component>
<component name="ProjectId" id="2sRWGnZjOMe2yOSS8et0US4qUZq" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;DefaultGoTemplateProperty&quot;: &quot;Go File&quot;,
&quot;Go Build.go build app.executor&quot;: &quot;Debug&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.git.unshallow&quot;: &quot;true&quot;,
&quot;RunOnceActivity.go.formatter.settings.were.checked&quot;: &quot;true&quot;,
&quot;RunOnceActivity.go.migrated.go.modules.settings&quot;: &quot;true&quot;,
&quot;RunOnceActivity.go.modules.go.list.on.any.changes.was.set&quot;: &quot;true&quot;,
&quot;git-widget-placeholder&quot;: &quot;main&quot;,
&quot;go.import.settings.migrated&quot;: &quot;true&quot;,
&quot;go.sdk.automatically.set&quot;: &quot;true&quot;,
&quot;kotlin-language-version-configured&quot;: &quot;true&quot;,
&quot;last_opened_file_path&quot;: &quot;/home/gogacoder/dev/go/nto/boilerplate&quot;,
&quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
&quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
&quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;go.sdk&quot;,
&quot;ts.external.directory.path&quot;: &quot;/home/gogacoder/dev/go/nto/boilerplate/frontend/node_modules/typescript/lib&quot;,
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
}
}</component>
<component name="RecentsManager">
<key name="MoveFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/internal/addons/excel" />
<recent name="$PROJECT_DIR$/internal/utils" />
<recent name="$PROJECT_DIR$/internal/extras" />
</key>
</component>
<component name="RunManager">
<configuration name="go build app" type="GoApplicationRunConfiguration" factoryName="Go Application" nameIsGenerated="true">
<module name="nto_starterkit" />
<working_directory value="$PROJECT_DIR$" />
<kind value="PACKAGE" />
<package value="app" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$" />
<method v="2" />
</configuration>
<configuration name="go build app" type="GoApplicationRunConfiguration" factoryName="Go Application" nameIsGenerated="true">
<module name="nto_starterkit" />
<working_directory value="$PROJECT_DIR$" />
<kind value="PACKAGE" />
<package value="app" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$" />
<method v="2" />
</configuration>
</component>
<component name="SharedIndexes">
<attachedChunks>
<set>
<option value="bundled-gosdk-d297c17c1fbd-57c114c3cede-org.jetbrains.plugins.go.sharedIndexes.bundled-GO-243.25659.52" />
<option value="bundled-js-predefined-d6986cc7102b-1632447f56bf-JavaScript-GO-243.25659.52" />
</set>
</attachedChunks>
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="0f2c8145-9000-45fe-a871-9ff0914e0e13" name="Changes" comment="" />
<created>1739066986825</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1739066986825</updated>
</task>
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
<component name="VgoProject">
<settings-migrated>true</settings-migrated>
</component>
</project>

10
LICENSE
View File

@@ -1,10 +0,0 @@
MIT License
Copyright (c) 2025 Georgiy Derbenev & Chabanenko Vladislav
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -6,9 +6,9 @@
- [x] Crud service generator (based on gorm gen) - [x] Crud service generator (based on gorm gen)
- [ ] Easy init without git repository (like create-nto-app) - [ ] Easy init without git repository (like create-nto-app)
- [x] Excel export - [x] Excel export
- [x] Models validation logic (callbacks and other stuff) - [ ] Models validation logic (callbacks and other stuff)
- [ ] Models [linter](https://git.gogacoder.ru/NTO/gormlint) - [ ] Models [linter](https://git.gogacoder.ru/NTO/gormlint)
- [x] Auto-generated frontend - [ ] Auto-generated frontend
## Установка ## Установка
@@ -16,12 +16,13 @@
- [Node.js](https://nodejs.org/en) - [Node.js](https://nodejs.org/en)
- [Golang](https://go.dev/dl/) - [Golang](https://go.dev/dl/)
- [VSCode](https://code.visualstudio.com/) или [Goland](https://cloud.gogacoder.ru/s/pZJzpeNSprmWDB8) - VSCode или Goland
- ~~[UPX](https://github.com/upx/upx/releases/latest) - для сжатия.~~ - [UPX](https://github.com/upx/upx/releases/latest) - для сжатия.
**Необходимо зазеркалировать с Github и добавить в PATH!!!**
- [Git](https://git-scm.com/) - [Git](https://git-scm.com/)
- [Obsidian](https://obsidian.md/) - Wails3:`go install -v github.com/wailsapp/wails/v3/cmd/wails3@latest`
- Wails3 - MinGw-64: https://jmeubank.github.io/tdm-gcc/
- MinGw-64 (необязательно): https://jmeubank.github.io/tdm-gcc/ **Необходимо зазеркалировать с Github!!!**
## Разработка ## Разработка
@@ -33,6 +34,9 @@
wails3 dev wails3 dev
``` ```
DevServer также можно открыть по адресу http://localhost:34115.
Этот инструмент предоставляет возможность вызывать Go код прямо из инструментов разработчика.
### Генерация TS биндингов ### Генерация TS биндингов
Для обновления API для TypeScript используйте команду: Для обновления API для TypeScript используйте команду:
@@ -41,36 +45,19 @@ wails3 dev
wails3 generate bindings -ts wails3 generate bindings -ts
``` ```
## Начало работы
Установите следующие утилиты перед началом работы:
```shell
go install -v github.com/wailsapp/wails/v3/cmd/wails3@latest
```
```shell
go install github.com/opbnq-q/nto-cli@latest
```
```shell
go install git.gogacoder.ru/NTO/crudgen/cmd/crudgen@latest
```
## Сборка ## Сборка
Для финальной сборки запустите эту команду в директории проекта: Для финальной сборки запустите эту команду в директории проекта:
На Linux/Mac OS:
``` ```
PRODUCTION=true wails3 build go env -w CGO_ENABLED=1
``` wails3 build -clean -upx -v 2 -webview2 embed
На Windows:
```
$env:PRODUCTION="true"; wails3 build
``` ```
**Перед релизом не забыть**: **Перед релизом не забыть**:
- убедиться, что дефолтные данные правильные
- убедиться, что приложение запускается - поместить все нужные asset'ы в папку assets
- (опционально) поместить все нужные asset'ы в папку assets - изменить версию схемы БД (пока не нужно)
- приложить сопроводительную записку. - приложить сопроводительную записку.
## Работа без GitHub ## Работа без GitHub

View File

@@ -10,7 +10,6 @@ tasks:
- ../dal/*.go - ../dal/*.go
cmds: cmds:
- go run gen.go - go run gen.go
method: none
go:gen:crudgen: go:gen:crudgen:
summary: Runs crudgen for crud generating summary: Runs crudgen for crud generating
@@ -22,7 +21,6 @@ tasks:
- go:gen:dal - go:gen:dal
cmds: cmds:
- crudgen -p internal - crudgen -p internal
method: none
go:mod:tidy: go:mod:tidy:
summary: Runs `go mod tidy` summary: Runs `go mod tidy`

View File

@@ -13,7 +13,7 @@ tasks:
cmds: cmds:
- go build {{.BUILD_FLAGS}} -o {{.BIN_DIR}}/{{.APP_NAME}} - go build {{.BUILD_FLAGS}} -o {{.BIN_DIR}}/{{.APP_NAME}}
vars: vars:
BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags "production" -trimpath -ldflags="-w -s"{{else}}-gcflags=all="-l" {{end}}' BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags production -trimpath -ldflags="-w -s"{{else}}-gcflags=all="-l"{{end}}'
env: env:
GOOS: darwin GOOS: darwin
CGO_ENABLED: 1 CGO_ENABLED: 1

View File

@@ -13,7 +13,7 @@ tasks:
cmds: cmds:
- go build {{.BUILD_FLAGS}} -o {{.BIN_DIR}}/{{.APP_NAME}} - go build {{.BUILD_FLAGS}} -o {{.BIN_DIR}}/{{.APP_NAME}}
vars: vars:
BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags "production" -trimpath -ldflags="-w -s"{{else}}-gcflags=all="-l" {{end}}' BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags production -trimpath -ldflags="-w -s"{{else}}-gcflags=all="-l"{{end}}'
env: env:
GOOS: linux GOOS: linux
CGO_ENABLED: 1 CGO_ENABLED: 1

View File

@@ -18,10 +18,10 @@ tasks:
- cmd: rm -f *.syso - cmd: rm -f *.syso
platforms: [linux, darwin] platforms: [linux, darwin]
vars: vars:
BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags "production" -trimpath -ldflags="-w -s -H windowsgui"{{else}}-gcflags=all="-l" {{end}}' BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags production -trimpath -ldflags="-w -s -H windowsgui"{{else}}-gcflags=all="-l"{{end}}'
env: env:
GOOS: windows GOOS: windows
CGO_ENABLED: 0 CGO_ENABLED: 1
GOARCH: '{{.ARCH | default ARCH}}' GOARCH: '{{.ARCH | default ARCH}}'
PRODUCTION: '{{.PRODUCTION | default "false"}}' PRODUCTION: '{{.PRODUCTION | default "false"}}'

View File

@@ -1,7 +1,4 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT // This file is automatically generated. DO NOT EDIT
import * as ExcelModule from "./excelmodule.js"; export * from "./models.js";
export {
ExcelModule
};

View File

@@ -0,0 +1,190 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import {Create as $Create} from "@wailsio/runtime";
export class Author {
"Id": number;
"Name": string;
"Posts": Post[];
"Comments": Comment[];
/** Creates a new Author instance. */
constructor($$source: Partial<Author> = {}) {
if (!("Id" in $$source)) {
this["Id"] = 0;
}
if (!("Name" in $$source)) {
this["Name"] = "";
}
if (!("Posts" in $$source)) {
this["Posts"] = [];
}
if (!("Comments" in $$source)) {
this["Comments"] = [];
}
Object.assign(this, $$source);
}
/**
* Creates a new Author instance from a string or object.
*/
static createFrom($$source: any = {}): Author {
const $$createField2_0 = $$createType1;
const $$createField3_0 = $$createType3;
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
if ("Posts" in $$parsedSource) {
$$parsedSource["Posts"] = $$createField2_0($$parsedSource["Posts"]);
}
if ("Comments" in $$parsedSource) {
$$parsedSource["Comments"] = $$createField3_0($$parsedSource["Comments"]);
}
return new Author($$parsedSource as Partial<Author>);
}
}
export class Comment {
"Id": number;
"Text": string;
"AuthorId": number;
"Author": Author;
"Posts": Post[];
/** Creates a new Comment instance. */
constructor($$source: Partial<Comment> = {}) {
if (!("Id" in $$source)) {
this["Id"] = 0;
}
if (!("Text" in $$source)) {
this["Text"] = "";
}
if (!("AuthorId" in $$source)) {
this["AuthorId"] = 0;
}
if (!("Author" in $$source)) {
this["Author"] = (new Author());
}
if (!("Posts" in $$source)) {
this["Posts"] = [];
}
Object.assign(this, $$source);
}
/**
* Creates a new Comment instance from a string or object.
*/
static createFrom($$source: any = {}): Comment {
const $$createField3_0 = $$createType4;
const $$createField4_0 = $$createType1;
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
if ("Author" in $$parsedSource) {
$$parsedSource["Author"] = $$createField3_0($$parsedSource["Author"]);
}
if ("Posts" in $$parsedSource) {
$$parsedSource["Posts"] = $$createField4_0($$parsedSource["Posts"]);
}
return new Comment($$parsedSource as Partial<Comment>);
}
}
export class Post {
"Id": number;
"Text": string;
"Deadline": number;
"CreatedAt": number;
"AuthorId": number;
"Author": Author;
"PostTypeId": number;
"PostType": PostType;
"Comments": Comment[];
/** Creates a new Post instance. */
constructor($$source: Partial<Post> = {}) {
if (!("Id" in $$source)) {
this["Id"] = 0;
}
if (!("Text" in $$source)) {
this["Text"] = "";
}
if (!("Deadline" in $$source)) {
this["Deadline"] = 0;
}
if (!("CreatedAt" in $$source)) {
this["CreatedAt"] = 0;
}
if (!("AuthorId" in $$source)) {
this["AuthorId"] = 0;
}
if (!("Author" in $$source)) {
this["Author"] = (new Author());
}
if (!("PostTypeId" in $$source)) {
this["PostTypeId"] = 0;
}
if (!("PostType" in $$source)) {
this["PostType"] = (new PostType());
}
if (!("Comments" in $$source)) {
this["Comments"] = [];
}
Object.assign(this, $$source);
}
/**
* Creates a new Post instance from a string or object.
*/
static createFrom($$source: any = {}): Post {
const $$createField5_0 = $$createType4;
const $$createField7_0 = $$createType5;
const $$createField8_0 = $$createType3;
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
if ("Author" in $$parsedSource) {
$$parsedSource["Author"] = $$createField5_0($$parsedSource["Author"]);
}
if ("PostType" in $$parsedSource) {
$$parsedSource["PostType"] = $$createField7_0($$parsedSource["PostType"]);
}
if ("Comments" in $$parsedSource) {
$$parsedSource["Comments"] = $$createField8_0($$parsedSource["Comments"]);
}
return new Post($$parsedSource as Partial<Post>);
}
}
export class PostType {
"Id": number;
"Name": string;
/** Creates a new PostType instance. */
constructor($$source: Partial<PostType> = {}) {
if (!("Id" in $$source)) {
this["Id"] = 0;
}
if (!("Name" in $$source)) {
this["Name"] = "";
}
Object.assign(this, $$source);
}
/**
* Creates a new PostType instance from a string or object.
*/
static createFrom($$source: any = {}): PostType {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new PostType($$parsedSource as Partial<PostType>);
}
}
// Private type creation functions
const $$createType0 = Post.createFrom;
const $$createType1 = $Create.Array($$createType0);
const $$createType2 = Comment.createFrom;
const $$createType3 = $Create.Array($$createType2);
const $$createType4 = Author.createFrom;
const $$createType5 = PostType.createFrom;

View File

@@ -0,0 +1,74 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import {Call as $Call, Create as $Create} from "@wailsio/runtime";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import * as models$0 from "../models/models.js";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import * as $models from "./models.js";
export function Count(): Promise<number> & { cancel(): void } {
let $resultPromise = $Call.ByID(3969879864) as any;
return $resultPromise;
}
export function Create(item: $models.Author): Promise<$models.Author> & { cancel(): void } {
let $resultPromise = $Call.ByID(3684602449, item) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType0($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
export function Delete(id: number): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(2096845974, id) as any;
return $resultPromise;
}
export function GetAll(): Promise<($models.Author | null)[]> & { cancel(): void } {
let $resultPromise = $Call.ByID(3248293926) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType2($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
export function GetById(id: number): Promise<$models.Author | null> & { cancel(): void } {
let $resultPromise = $Call.ByID(1703016211, id) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType1($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
export function SortedByOrder(fieldsSortOrder: { [_: string]: string }): Promise<($models.Author | null)[]> & { cancel(): void } {
let $resultPromise = $Call.ByID(3046628691, fieldsSortOrder) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType2($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
export function Update(item: $models.Author): Promise<$models.Author> & { cancel(): void } {
let $resultPromise = $Call.ByID(2240704960, item) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType0($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
// Private type creation functions
const $$createType0 = models$0.Author.createFrom;
const $$createType1 = $Create.Nullable($$createType0);
const $$createType2 = $Create.Array($$createType1);

View File

@@ -0,0 +1,74 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import {Call as $Call, Create as $Create} from "@wailsio/runtime";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import * as models$0 from "../models/models.js";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import * as $models from "./models.js";
export function Count(): Promise<number> & { cancel(): void } {
let $resultPromise = $Call.ByID(3225397984) as any;
return $resultPromise;
}
export function Create(item: $models.Comment): Promise<$models.Comment> & { cancel(): void } {
let $resultPromise = $Call.ByID(4239106089, item) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType0($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
export function Delete(id: number): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(2553503582, id) as any;
return $resultPromise;
}
export function GetAll(): Promise<($models.Comment | null)[]> & { cancel(): void } {
let $resultPromise = $Call.ByID(1805763390) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType2($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
export function GetById(id: number): Promise<$models.Comment | null> & { cancel(): void } {
let $resultPromise = $Call.ByID(3217823099, id) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType1($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
export function SortedByOrder(fieldsSortOrder: { [_: string]: string }): Promise<($models.Comment | null)[]> & { cancel(): void } {
let $resultPromise = $Call.ByID(4244533291, fieldsSortOrder) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType2($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
export function Update(item: $models.Comment): Promise<$models.Comment> & { cancel(): void } {
let $resultPromise = $Call.ByID(3080970936, item) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType0($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
// Private type creation functions
const $$createType0 = models$0.Comment.createFrom;
const $$createType1 = $Create.Nullable($$createType0);
const $$createType2 = $Create.Array($$createType1);

View File

@@ -6,11 +6,11 @@
import {Call as $Call, Create as $Create} from "@wailsio/runtime"; import {Call as $Call, Create as $Create} from "@wailsio/runtime";
export function ExportAllEntities(): Promise<void> & { cancel(): void } { export function ExportAllEntities(): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(4195777221) as any; let $resultPromise = $Call.ByID(794946560) as any;
return $resultPromise; return $resultPromise;
} }
export function ImportAllEntities(): Promise<void> & { cancel(): void } { export function ImportAllEntities(): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(2803141560) as any; let $resultPromise = $Call.ByID(2992267629) as any;
return $resultPromise; return $resultPromise;
} }

View File

@@ -0,0 +1,17 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import * as AuthorService from "./authorservice.js";
import * as CommentService from "./commentservice.js";
import * as ExcelModule from "./excelmodule.js";
import * as PostService from "./postservice.js";
import * as PostTypeService from "./posttypeservice.js";
export {
AuthorService,
CommentService,
ExcelModule,
PostService,
PostTypeService
};
export * from "./models.js";

View File

@@ -0,0 +1,22 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import {Create as $Create} from "@wailsio/runtime";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import * as models$0 from "../models/models.js";
export const Author = models$0.Author;
export type Author = models$0.Author;
export const Comment = models$0.Comment;
export type Comment = models$0.Comment;
export const Post = models$0.Post;
export type Post = models$0.Post;
export const PostType = models$0.PostType;
export type PostType = models$0.PostType;

View File

@@ -0,0 +1,74 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import {Call as $Call, Create as $Create} from "@wailsio/runtime";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import * as models$0 from "../models/models.js";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import * as $models from "./models.js";
export function Count(): Promise<number> & { cancel(): void } {
let $resultPromise = $Call.ByID(3109924027) as any;
return $resultPromise;
}
export function Create(item: $models.Post): Promise<$models.Post> & { cancel(): void } {
let $resultPromise = $Call.ByID(1443399856, item) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType0($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
export function Delete(id: number): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(2924549135, id) as any;
return $resultPromise;
}
export function GetAll(): Promise<($models.Post | null)[]> & { cancel(): void } {
let $resultPromise = $Call.ByID(65691059) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType2($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
export function GetById(id: number): Promise<$models.Post | null> & { cancel(): void } {
let $resultPromise = $Call.ByID(4074736792, id) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType1($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
export function SortedByOrder(fieldsSortOrder: { [_: string]: string }): Promise<($models.Post | null)[]> & { cancel(): void } {
let $resultPromise = $Call.ByID(471862744, fieldsSortOrder) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType2($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
export function Update(item: $models.Post): Promise<$models.Post> & { cancel(): void } {
let $resultPromise = $Call.ByID(137798821, item) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType0($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
// Private type creation functions
const $$createType0 = models$0.Post.createFrom;
const $$createType1 = $Create.Nullable($$createType0);
const $$createType2 = $Create.Array($$createType1);

View File

@@ -0,0 +1,74 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import {Call as $Call, Create as $Create} from "@wailsio/runtime";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import * as models$0 from "../models/models.js";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import * as $models from "./models.js";
export function Count(): Promise<number> & { cancel(): void } {
let $resultPromise = $Call.ByID(554487955) as any;
return $resultPromise;
}
export function Create(item: $models.PostType): Promise<$models.PostType> & { cancel(): void } {
let $resultPromise = $Call.ByID(1092898136, item) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType0($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
export function Delete(id: number): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(2114913543, id) as any;
return $resultPromise;
}
export function GetAll(): Promise<($models.PostType | null)[]> & { cancel(): void } {
let $resultPromise = $Call.ByID(416231387) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType2($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
export function GetById(id: number): Promise<$models.PostType | null> & { cancel(): void } {
let $resultPromise = $Call.ByID(3237123344, id) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType1($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
export function SortedByOrder(fieldsSortOrder: { [_: string]: string }): Promise<($models.PostType | null)[]> & { cancel(): void } {
let $resultPromise = $Call.ByID(1097313920, fieldsSortOrder) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType2($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
export function Update(item: $models.PostType): Promise<$models.PostType> & { cancel(): void } {
let $resultPromise = $Call.ByID(2773888269, item) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType0($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
// Private type creation functions
const $$createType0 = models$0.PostType.createFrom;
const $$createType1 = $Create.Nullable($$createType0);
const $$createType2 = $Create.Array($$createType1);

View File

@@ -0,0 +1,90 @@
<script setup lang="ts">
import Table from "../table/Table.vue";
import { onMounted, reactive } from "vue";
import { getDefaultValues } from "../utils/structs/defaults.util";
import Service from "./author.service.ts";
import type { Scheme } from "../types/scheme.type";
import { Author } from "../../bindings/app/internal/services";
import { SortedByOrder } from "../../bindings/app/internal/services/authorservice.ts";
import { ref } from "vue";
import type { Validate } from "../types/validate.type";
import PostService from "../post/post.service.ts";
const postService = new PostService();
import CommentService from "../comment/comment.service.ts";
const commentService = new CommentService();
const service = new Service();
const items = ref<Author[]>([]);
const load = async () => {
(scheme as any).Posts.type!.nested!.values = await postService.readAll();
(scheme as any).Comments.type!.nested!.values =
await commentService.readAll();
items.value = await service.readAll();
return items.value;
};
onMounted(async () => {
await load();
console.log(await SortedByOrder({"Comments": "DESC"}))
});
const scheme: Scheme<Author> = reactive({
entityId: "AuthorId",
Id: {
hidden: true,
type: {
primitive: "number",
},
},
Name: {
russian: "Имя",
type: {
primitive: "string",
},
},
Posts: {
russian: "Посты",
many: true,
type: {
nested: {
values: [],
field: ["Text"],
},
},
},
Comments: {
russian: "Комментарии",
many: true,
type: {
nested: {
values: [],
field: ["Text"],
},
},
},
});
const getDefaults = () => getDefaultValues(scheme);
const validate: Validate<Author> = (entity) => {
return {
status: "success",
};
};
</script>
<template>
<main class="w-screen h-screen">
<Table name="Author" :scheme :service :get-defaults :load :items :validate></Table>
</main>
</template>

View File

@@ -0,0 +1,36 @@
import {
GetAll,
Create,
Delete,
GetById,
Update,
Count,
} from "../../bindings/app/internal/services/authorservice.ts";
import type { Author } from "../../bindings/app/internal/services";
import type { IService } from "../types/service.type.ts";
export default class AuthorService implements IService<Author> {
async read(id: number) {
return (await GetById(id)) as Author;
}
async readAll() {
return (await GetAll()) as Author[];
}
async create(item: Author) {
await Create(item);
}
async delete(id: number) {
return await Delete(id);
}
async update(item: Author) {
await Update(item);
}
async count() {
return await Count();
}
}

View File

@@ -0,0 +1,93 @@
<script setup lang="ts">
import Table from "../table/Table.vue";
import { onMounted, reactive } from "vue";
import { getDefaultValues } from "../utils/structs/defaults.util";
import Service from "./comment.service.ts";
import type { Scheme } from "../types/scheme.type";
import { Comment } from "../../bindings/app/internal/services";
import { ref } from "vue";
import type { Validate } from "../types/validate.type";
import AuthorService from "../author/author.service.ts";
const authorService = new AuthorService();
import PostService from "../post/post.service.ts";
const postService = new PostService();
const service = new Service();
const items = ref<Comment[]>([]);
const load = async () => {
(scheme as any).Author.type!.nested!.values = await authorService.readAll();
(scheme as any).Posts.type!.nested!.values = await postService.readAll();
items.value = await service.readAll();
return items.value;
};
onMounted(async () => {
load();
});
const scheme: Scheme<Comment> = reactive({
entityId: "CommentId",
Id: {
hidden: true,
type: {
primitive: "number",
},
},
Text: {
russian: "Текст",
type: {
primitive: "string",
},
},
AuthorId: {
hidden: true,
type: {
primitive: "number",
},
},
Author: {
russian: "Автор",
type: {
nested: {
values: [],
field: ["Name"],
},
},
},
Posts: {
russian: "Посты",
many: true,
type: {
nested: {
values: [],
field: ["Text"],
},
},
},
});
const getDefaults = () => getDefaultValues(scheme);
const validate: Validate<Comment> = (entity) => {
return {
status: "success",
};
};
</script>
<template>
<main class="w-screen h-screen">
<Table :scheme :service :get-defaults :load :items :validate></Table>
</main>
</template>

View File

@@ -0,0 +1,36 @@
import {
GetAll,
Create,
Delete,
GetById,
Update,
Count,
} from "../../bindings/app/internal/services/commentservice.ts";
import type { Comment } from "../../bindings/app/internal/services";
import type { IService } from "../types/service.type.ts";
export default class CommentService implements IService<Comment> {
async read(id: number) {
return (await GetById(id)) as Comment;
}
async readAll() {
return (await GetAll()) as Comment[];
}
async create(item: Comment) {
await Create(item);
}
async delete(id: number) {
return await Delete(id);
}
async update(item: Comment) {
await Update(item);
}
async count() {
return await Count();
}
}

View File

@@ -17,6 +17,7 @@ const pushOrRemove = (option: T) => {
} else { } else {
selected.value.push(option) selected.value.push(option)
} }
//setNullIds()
} }
const setNullIds = () => { const setNullIds = () => {
@@ -26,11 +27,12 @@ const setNullIds = () => {
}) })
} }
//onMounted(setNullIds)
</script> </script>
<template> <template>
<div class=""> <div class="">
<ul class="max-h-48 h-auto overflow-y-auto background rounded-md p-3 w-full native-border"> <ul class="max-h-48 h-auto overflow-y-auto background rounded-md p-3 w-full border-gray-500 border">
<li v-for="option in options" :key="option.Id" class="flex items-center gap-2"> <li v-for="option in options" :key="option.Id" class="flex items-center gap-2">
<Checkbox :checked="selected.some(item => item.Id == option.Id)" @click="pushOrRemove(option)" /> <Checkbox :checked="selected.some(item => item.Id == option.Id)" @click="pushOrRemove(option)" />
<label :for="option.Id.toString()">{{ structView(option, path) }}</label> <label :for="option.Id.toString()">{{ structView(option, path) }}</label>

View File

@@ -1,40 +1,34 @@
<script setup lang="ts">
import { Button } from 'primevue';
import NavCard from '../components/cards/NavCard.vue';
import { ExportAllEntities, ImportAllEntities } from '../../bindings/app/internal/services/excelmodule';
import { useErrorStore } from '../stores/error.store';
<!--<script setup lang="ts">--> const errorStore = useErrorStore()
<!--import { Button } from 'primevue';-->
<!--import NavCard from '../components/cards/NavCard.vue';-->
<!--import { ExportAllEntities, ImportAllEntities } from '../../bindings/app/internal/services/excelmodule';-->
<!--import { useErrorStore } from '../stores/error.store';-->
<!--const errorStore = useErrorStore()--> const importFromExcel = async () => {
try {
await ImportAllEntities()
} catch(e) {
errorStore.summon((e as Error).message)
}
}
<!--const importFromExcel = async () => {--> const exportFromExcel = async () => {
<!-- try {--> try {
<!-- await ImportAllEntities()--> await ExportAllEntities()
<!-- } catch(e) {--> } catch(e) {
<!-- errorStore.summon((e as Error).message)--> errorStore.summon((e as Error).message)
<!-- }--> }
<!--}--> }
</script>
<!--const exportFromExcel = async () => {-->
<!-- try {-->
<!-- await ExportAllEntities()-->
<!-- } catch(e) {-->
<!-- errorStore.summon((e as Error).message)-->
<!-- }-->
<!--}-->
<!--</script>-->
<!--<template>-->
<!-- <div class="flex w-screen h-screen items-center gap-5 justify-center">-->
<!-- <NavCard :title="'Пользователь'" :to="'/user'" :content="'Не может пользоваться туалетной бумагой'" />-->
<!-- </div>-->
<!-- <footer class="fixed w-full bottom-10 flex items-center justify-center gap-2">-->
<!-- <Button severity="secondary" @click="importFromExcel">Импортировать данные</Button>-->
<!-- <Button severity="secondary" @click="exportFromExcel">Экспортировать данные</Button>-->
<!-- </footer>-->
<!--</template>-->
<template> <template>
<div class="flex w-screen h-screen items-center gap-5 justify-center">
<NavCard :title="'Пользователь'" :to="'/user'" :content="'Не может пользоваться туалетной бумагой'" />
</div>
<footer class="fixed w-full bottom-10 flex items-center justify-center gap-2">
<Button severity="secondary" @click="importFromExcel">Импортировать данные</Button>
<Button severity="secondary" @click="exportFromExcel">Экспортировать данные</Button>
</footer>
</template> </template>
<script setup lang="ts">
</script>

View File

@@ -0,0 +1,3 @@
<template>
<h1>GREBEN</h1>
</template>

View File

@@ -0,0 +1,14 @@
<script setup lang="ts">
import { RouterView, useRoute, type RouteRecordRaw } from 'vue-router';
import NavModal, { type Route } from '../../components/modals/NavModal.vue';
import { routes } from '../../router/router';
const route = useRoute()
const lastRouteName = route.matched[route.matched.length - 2].path
</script>
<template>
<NavModal :routes="<Route[]>routes[routes.findIndex(r => r.path == lastRouteName)].children" />
<RouterView />
</template>

View File

@@ -0,0 +1,7 @@
<script setup lang="ts">
import PostScheme from '../../post/PostScheme.vue';
</script>
<template>
<PostScheme />
</template>

View File

@@ -0,0 +1,148 @@
<script setup lang="ts">
import Table from "../table/Table.vue";
import { onMounted, reactive } from "vue";
import { getDefaultValues } from "../utils/structs/defaults.util";
import Service from "./post.service.ts";
import type { Scheme } from "../types/scheme.type";
import { Author, Post } from "../../bindings/app/internal/services";
import { ref } from "vue";
import type { Validate } from "../types/validate.type";
import AuthorService from "../author/author.service.ts";
const authorService = new AuthorService();
import PosttypeService from "../posttype/posttype.service.ts";
const posttypeService = new PosttypeService();
import CommentService from "../comment/comment.service.ts";
import { SortedByOrder } from "../../bindings/app/internal/services/postservice.ts";
import type { Search } from "../types/search.type.ts";
const commentService = new CommentService();
const service = new Service();
const items = ref<Post[]>([]);
const load = async () => {
(scheme as any).Author.type!.nested!.values = await authorService.readAll();
(scheme as any).PostType.type!.nested!.values =
await posttypeService.readAll();
(scheme as any).Comments.type!.nested!.values =
await commentService.readAll();
items.value = await service.readAll();
return items.value;
};
onMounted(async () => {
await load();
console.log(await SortedByOrder({ "Author": "DESC", "Text": "ASC" }));
});
const scheme: Scheme<Post> = reactive({
entityId: "PostId",
Id: {
hidden: true,
russian: 'Id',
type: {
primitive: "number",
}
},
Text: {
russian: "Текст",
type: {
primitive: "string",
},
},
Deadline: {
russian: "Дедлайн",
date: true,
type: {
primitive: "date",
},
},
CreatedAt: {
russian: "Дата создания",
type: {
primitive: "date",
},
},
AuthorId: {
hidden: true,
type: {
primitive: "number",
},
},
Author: {
russian: "Автор",
type: {
nested: {
values: [],
field: ["Name"],
},
},
},
PostTypeId: {
hidden: true,
type: {
primitive: "number",
},
},
PostType: {
russian: "Тип",
type: {
nested: {
values: [],
field: ["Name"],
},
},
},
Comments: {
russian: "Комментарии",
many: true,
type: {
nested: {
values: [],
field: ["Text"],
},
},
customWindow: {
create: true,
}
},
});
const getDefaults = () => {
return ({ ...getDefaultValues(scheme), AuthorId: 1, PostTypeId: 1 });
};
const validate: Validate<Post> = (entity) => {
return {
status: "success",
};
};
const search: Search<Post> = (input) => {
}
</script>
<template>
<Table :scheme :service :get-defaults :load :items :validate @on-search="search">
<template #CommentsCreate="{ data }">
<p>{{ data }}</p>
</template>
</Table>
</template>

View File

@@ -0,0 +1,36 @@
import {
GetAll,
Create,
Delete,
GetById,
Update,
Count,
} from "../../bindings/app/internal/services/postservice.ts";
import type { Post } from "../../bindings/app/internal/services";
import type { IService } from "../types/service.type.ts";
export default class PostService implements IService<Post> {
async read(id: number) {
return (await GetById(id)) as Post;
}
async readAll() {
return (await GetAll()) as Post[];
}
async create(item: Post) {
await Create(item);
}
async delete(id: number) {
return await Delete(id);
}
async update(item: Post) {
await Update(item);
}
async count() {
return await Count();
}
}

View File

@@ -0,0 +1,62 @@
<script setup lang="ts">
import Table from "../table/Table.vue";
import { onMounted, reactive } from "vue";
import { getDefaultValues } from "../utils/structs/defaults.util";
import Service from "./posttype.service.ts";
import type { Scheme } from "../types/scheme.type";
import { PostType } from "../../bindings/app/internal/services";
import { ref } from "vue";
import type { Validate } from "../types/validate.type";
const service = new Service();
const items = ref<PostType[]>([]);
const load = async () => {
items.value = await service.readAll();
return items.value;
};
onMounted(async () => {
await load();
});
const scheme: Scheme<PostType> = reactive({
entityId: "PostTypeId",
Id: {
hidden: true,
type: {
primitive: "number",
},
},
Name: {
russian: "Название",
type: {
primitive: "string",
},
},
});
const getDefaults = () => getDefaultValues(scheme);
const validate: Validate<PostType> = (entity) => {
return {
status: "success",
};
};
const colorize = (data: PostType): string => {
if (data.Name === "test") {
return "red";
}
return ''
}
</script>
<template>
<main class="w-screen h-screen">
<Table :scheme :service :get-defaults :load :items :validate :colorize></Table>
</main>
</template>

View File

@@ -0,0 +1,36 @@
import {
GetAll,
Create,
Delete,
GetById,
Update,
Count,
} from "../../bindings/app/internal/services/posttypeservice.ts";
import type { PostType } from "../../bindings/app/internal/services";
import type { IService } from "../types/service.type.ts";
export default class PostTypeService implements IService<PostType> {
async read(id: number) {
return (await GetById(id)) as PostType;
}
async readAll() {
return (await GetAll()) as PostType[];
}
async create(item: PostType) {
await Create(item);
}
async delete(id: number) {
return await Delete(id);
}
async update(item: PostType) {
await Update(item);
}
async count() {
return await Count();
}
}

View File

@@ -1,32 +1,32 @@
import { createRouter, createWebHistory, type RouteRecordRaw } from "vue-router"; import { createRouter, createWebHistory, type RouteRecordRaw } from "vue-router";
import Index from "../pages/Index.vue"; import Index from "../pages/Index.vue";
import UserPage from "../pages/pages/UserPage.vue";
import PostTablePage from "../pages/tables/PostTablePage.vue";
import GrebenPage from "../pages/pages/GrebenPage.vue";
export const routes: RouteRecordRaw[] = [{ export const routes: RouteRecordRaw[] = [{
path: "/", path: "/",
component: Index, component: Index,
name: 'Главная' name: 'Главная'
}, ] as const }, {
path: '/user',
name: 'Пользователь',
component: UserPage,
redirect: '/user/post',
children: [
{
component: PostTablePage,
path: '/user/post',
name: 'Новости'
}, {
component: GrebenPage,
path: '/user/greben',
name: 'Страница гребня'
}
]
}] as const
export const router = createRouter({ export const router = createRouter({
history: createWebHistory(), history: createWebHistory(),
routes, routes,
}); });
// {
// path: '/user',
// name: 'Пользователь',
// component: UserPage,
// redirect: '/user/post',
// children: [
// {
// component: PostTablePage,
// path: '/user/post',
// name: 'Новости'
// }, {
// component: GrebenPage,
// path: '/user/greben',
// name: 'Страница гребня'
// }
// ]
// }

View File

@@ -5,25 +5,10 @@
html, body, .background { html, body, .background {
background: white; background: white;
color: rgb(51, 65, 85);
}
.secondary-background {
background: var(--p-content-background)
}
.native-border {
border: 1px solid var(--p-select-border-color);
} }
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
html, body, .background { html, body, .background {
background: #121212; background: #121212;
color: white;
} }
.native-border {
border: 1px solid rgb(100, 100, 109);
}
} }

View File

@@ -62,7 +62,7 @@ async function handleSave() {
</script> </script>
<template> <template>
<Dialog v-model:visible="showCreate" class="w-[500px]"> <Dialog v-model:visible="showCreate">
<template #header> <template #header>
<h1>{{ props.updateMode ? 'Изменить' : 'Создать' }} {{ props.name?.toLowerCase() }}</h1> <h1>{{ props.updateMode ? 'Изменить' : 'Создать' }} {{ props.name?.toLowerCase() }}</h1>
</template> </template>

View File

@@ -1,31 +0,0 @@
<script setup lang="ts" generic="T extends IEntity">
import type {IEntity} from "../types/entity.type.ts";
import type {SortOptions} from "../types/sort-options.type.ts";
import type {Scheme} from "../types/scheme.type.ts";
import {Select} from 'primevue'
import {watch} from "vue";
const options = defineModel<SortOptions<T>>()
const optionsKeys = Object.keys(options.value as T) as (keyof T)[]
defineProps<{
scheme: Scheme<T>
load: () => Promise<T[]>
}>()
</script>
<template>
<ul class="flex flex-col gap-2 native-border secondary-background p-3 rounded-md">
<li v-for="optionKey in optionsKeys" class="flex items-center justify-between w-full">
<h1>{{ scheme[optionKey].russian }}</h1>
<Select size="small" class="w-24" :options="['NONE', 'ASC', 'DESC']" v-model="options![optionKey]" @value-change="load">
<template #value="{ value }">
<span class="pi pi-sort-amount-up-alt" v-if="value == 'ASC'"></span>
<span class="pi pi-sort-amount-down" v-else-if="value == 'DESC'"></span>
<span v-else>-</span>
</template>
</Select>
</li>
</ul>
</template>

View File

@@ -1,18 +1,20 @@
<script setup lang="ts" generic="T extends IEntity"> <script setup lang="ts" generic="T extends IEntity">
import {onMounted, ref, watch, type UnwrapRef} from "vue"; import { onMounted, ref, watch, type UnwrapRef } from "vue";
import type {TableProps} from "../types/table-props.type"; import type { TableProps } from "../types/table-props.type";
import {DataTable, Column, Button, InputText} from "primevue"; import { DataTable, Column, Button, InputText } from "primevue";
import {manyStructsView} from "../utils/structs/structs-view.util"; import { manyStructsView } from "../utils/structs/structs-view.util";
import type {TableEmits} from "../types/table-emits.type"; import type { TableEmits } from "../types/table-emits.type";
import FloatingButton from "../components/buttons/FloatingButton.vue"; import FloatingButton from "../components/buttons/FloatingButton.vue";
import type {IEntity} from "../types/entity.type"; import type { IEntity } from "../types/entity.type";
import DialogWindow from "./DialogWindow.vue"; import DialogWindow from "./DialogWindow.vue";
import {viewDate} from "../utils/date/view.util"; import { viewDate } from "../utils/date/view.util";
import SortingOptions from "./SortingOptions.vue";
import type {SortOptions} from "../types/sort-options.type.ts";
const props = defineProps<TableProps<T>>(); const props = defineProps<TableProps<T>>();
onMounted(async () => {
props.load();
});
type Key = keyof T; type Key = keyof T;
const keys = Object.keys(props.scheme) as Key[]; const keys = Object.keys(props.scheme) as Key[];
const emits = defineEmits<TableEmits>(); const emits = defineEmits<TableEmits>();
@@ -24,148 +26,176 @@ const showUpdate = ref(false);
const updateItem = ref<null | T>(null); const updateItem = ref<null | T>(null);
watch(showUpdate, (value) => { watch(showUpdate, (value) => {
if (!value) { if (!value) {
updateItem.value = null; updateItem.value = null;
} }
}); });
watch(updateItem, (value) => { watch(updateItem, (value) => {
if (value) { if (value) {
showUpdate.value = true; showUpdate.value = true;
} else { } else {
showUpdate.value = false; showUpdate.value = false;
} }
}); });
watch(showCreate, (value) => { watch(showCreate, (value) => {
if (value) { if (value) {
createItem.value = props.getDefaults(); createItem.value = props.getDefaults();
} else { } else {
createItem.value = null; createItem.value = null;
} }
}); });
watch(createItem, (value) => { watch(createItem, (value) => {
if (value) { if (value) {
showCreate.value = true; showCreate.value = true;
} else { } else {
showCreate.value = false; showCreate.value = false;
} }
}); });
const handleFloatingButtonClick = () => { const handleFloatingButtonClick = () => {
emits('onCreateOpen') emits("onCreateOpen");
emits('onOpen') emits("onOpen");
showCreate.value = true; showCreate.value = true;
}; };
const slots = defineSlots(); const slots = defineSlots();
const createSlotName = (key: any) => key + "Create"; const createSlotName = (key: any) => key + "Create";
const updateSlotName = (key: any) => key + "Update"; const updateSlotName = (key: any) => key + "Update";
watch(() => props.items, () => { watch(
if (props.colorize) { () => props.items,
const trs = document.querySelectorAll("tr"); () => {
props.items.forEach(item => { if (props.colorize) {
const tr = trs[item.Id]; const trs = document.querySelectorAll("tr");
if (tr) { props.items.forEach((item) => {
tr.style.backgroundColor = props.colorize!(item); const tr = trs[item.Id];
} if (tr) {
}) tr.style.backgroundColor = props.colorize!(item);
} }
}) });
}
const input = ref('') },
const sortOptions = defineModel<SortOptions<T>>('sort-options'); );
const showSortOptions = ref(false)
const input = defineModel<string>("search", { default: "" });
</script> </script>
<template> <template>
<div class="m-2 flex flex-col items-center gap-2"> <div class="flex h-10 justify-center m-4 gap-2">
<div class="flex flex-col gap-2 relative"> <InputText v-model="input" placeholder="Поиск" class="h-full w-64" />
<div class="flex items-center justify-center gap-2 h-10"> <Button class="h-full aspect-square" severity="secondary">
<Button severity="secondary" class="h-full aspect-square" @click="showSortOptions = !showSortOptions"> <span class="pi pi-search"></span>
<span class="pi pi-sort"></span>
</Button> </Button>
<InputText class="h-full w-64" v-model="input"/>
<Button severity="secondary" class="h-full aspect-square" @click="emits('onSearch', input)">
<span class="pi pi-search"></span>
</Button>
</div>
<SortingOptions v-model="sortOptions as SortOptions<T>" :scheme v-if="showSortOptions"
class="absolute z-10 w-full top-full translate-y-2" :load="props.load"></SortingOptions>
</div> </div>
<DialogWindow
</div> :name
<DialogWindow :name :load :items :validate :scheme :service :get-defaults v-model:item="<T>createItem" :load
v-model:show="showCreate" @on-save="data => emits('onSave', data)" :items
@on-save-create="data => emits('onSaveCreate', data)"> :validate
<template v-for="key in keys" #[key]="{ data }"> :scheme
<slot :name="<string>key" :data="data"></slot> :service
</template> :get-defaults
<template v-for="key in keys" #[createSlotName(key)]="{ data }"> v-model:item="<T>createItem"
<slot :name="createSlotName(key)" :data="data"></slot> v-model:show="showCreate"
</template> @on-save="(data) => emits('onSave', data)"
</DialogWindow> @on-save-create="(data) => emits('onSaveCreate', data)"
<DialogWindow :name :load :items :validate :scheme update-mode :service :get-defaults v-model:item="<T>updateItem" >
v-model:show="showUpdate" @on-save="data => emits('onSave', data)" <template v-for="key in keys" #[key]="{ data }">
@on-save-update="data => emits('onSaveUpdate', data)"> <slot :name="<string>key" :data></slot>
<template v-for="key in keys" #[key]="{ data }">
<slot :name="<string>key" :data="data"></slot>
</template>
<template v-for="key in keys" #[updateSlotName(key)]="{ data }">
<slot :name="updateSlotName(key)" :data="data"></slot>
</template>
</DialogWindow>
<div>
<DataTable :value="<[]>items" paginator :rows="10">
<template #header v-if="props.name">
<p>{{ props.name }}</p>
</template>
<template v-for="key in keys">
<Column :header="props.scheme[key]?.russian" v-if="!props.scheme[key].hidden">
<template #body="{ data }">
<p class="truncate max-w-56" v-tooltip="viewDate(manyStructsView(
data[key],
props.scheme[key]?.type?.nested?.field,
), scheme[key]?.type?.primitive)">
{{
viewDate(manyStructsView(
data[key],
props.scheme[key]?.type?.nested?.field,
), scheme[key]?.type?.primitive)
}}
</p>
</template>
</Column>
</template>
<Column header="Действия">
<template #body="{ data }">
<div class="flex gap-2">
<Button severity="secondary" icon="pi pi-pencil" @click="async () => {
await emits('onUpdateOpen')
await emits('onOpen')
updateItem = data
}"></Button>
<Button severity="danger" icon="pi pi-trash" @click="async () => {
await emits('onDelete', data)
await props.service.delete(data.Id)
await load()
}"></Button>
</div>
</template> </template>
</Column> <template v-for="key in keys" #[createSlotName(key)]="{ data }">
<template #paginatorstart> <slot :name="createSlotName(key)" :data></slot>
<Button severity="secondary" @click="props.load"> </template>
<span class="pi pi-refresh"></span> </DialogWindow>
</Button> <DialogWindow
</template> :name
<template #paginatorend> :load
<span></span> :items
</template> :validate
</DataTable> :scheme
<FloatingButton @click="handleFloatingButtonClick"/> update-mode
</div> :service
:get-defaults
v-model:item="<T>updateItem"
v-model:show="showUpdate"
@on-save="(data) => emits('onSave', data)"
@on-save-update="(data) => emits('onSaveUpdate', data)"
>
<template v-for="key in keys" #[key]="{ data }">
<slot :name="<string>key" :data></slot>
</template>
<template v-for="key in keys" #[updateSlotName(key)]="{ data }">
<slot :name="updateSlotName(key)" :data></slot>
</template>
</DialogWindow>
<div>
<DataTable :value="<[]>items" paginator :rows="10">
<template #header v-if="props.name">
<p>{{ props.name }}</p>
</template>
<template v-for="key in keys">
<Column
:header="props.scheme[key]?.russian"
v-if="!props.scheme[key].hidden"
>
<template #body="{ data }">
<p
class="truncate max-w-56"
v-tooltip="
viewDate(
manyStructsView(
data[key],
props.scheme[key]?.type?.nested?.field,
),
scheme[key]?.type?.primitive,
)
"
>
{{
viewDate(
manyStructsView(
data[key],
props.scheme[key]?.type?.nested?.field,
),
scheme[key]?.type?.primitive,
)
}}
</p>
</template>
</Column>
</template>
<Column header="Действия">
<template #body="{ data }">
<div class="flex gap-2">
<Button
severity="secondary"
icon="pi pi-pencil"
@click="
async () => {
await emits('onUpdateOpen');
await emits('onOpen');
updateItem = data;
}
"
></Button>
<Button
severity="danger"
icon="pi pi-trash"
@click="
async () => {
await emits('onDelete', data);
await props.service.delete(data.Id);
await load();
}
"
></Button>
</div>
</template>
</Column>
</DataTable>
<FloatingButton @click="handleFloatingButtonClick" />
</div>
</template> </template>

View File

@@ -0,0 +1,3 @@
import type { IEntity } from "./entity.type";
export type Search<T extends IEntity> = (input: string) => T[]

View File

@@ -1,6 +0,0 @@
import type { IEntity } from "./entity.type";
import type { Sorting } from "./sorting.type";
export type SortOptions<T extends IEntity> = {
[key in keyof Partial<T>]: Sorting
}

View File

@@ -0,0 +1,7 @@
import type { IEntity } from "./entity.type";
export type SortOptions<T extends IEntity> = {
[key in keyof T]: "ASC" | "DESC"
}
export type Sort<T extends IEntity> = (options: SortOptions<T>) => T[]

View File

@@ -1 +0,0 @@
export type Sorting = "DESC" | "ASC" | "NONE"

View File

@@ -1,6 +1,8 @@
import type { IEntity } from "./entity.type"; import type { IEntity } from "./entity.type";
import type { Scheme } from "./scheme.type"; import type { Scheme } from "./scheme.type";
import type { Search } from "./search.type";
import type { IService } from "./service.type"; import type { IService } from "./service.type";
import type { Sort } from "./sort.type";
import type { Validate } from "./validate.type"; import type { Validate } from "./validate.type";
export interface TableProps<T extends IEntity> { export interface TableProps<T extends IEntity> {

View File

@@ -1,16 +0,0 @@
import type { IEntity } from "../../types/entity.type";
import type { Scheme } from "../../types/scheme.type";
import type { SortOptions } from "../../types/sort-options.type";
export const getDefaultSortOptions = <T extends IEntity>(scheme: Scheme<T>): SortOptions<T> => {
const keys = Object.keys(scheme) as (keyof T)[]
const result: Partial<SortOptions<T>> = {}
keys.forEach(key => {
if (!scheme[key].hidden && key !== 'entityId' && !scheme[key].many) {
result[key] = 'NONE'
}
})
return result as SortOptions<T>
}

View File

@@ -0,0 +1,6 @@
import type { IEntity } from "../../types/entity.type";
import type { Scheme } from "../../types/scheme.type";
export const defaultSort = <T extends IEntity>(scheme: Scheme<T>) => {
}

12
go.mod
View File

@@ -1,4 +1,4 @@
module git.gogacoder.ru/NTO/boilerplate module app
go 1.23.0 go 1.23.0
@@ -6,8 +6,6 @@ toolchain go1.24.1
require ( require (
github.com/kuzgoga/fogg v0.1.2 github.com/kuzgoga/fogg v0.1.2
github.com/ncruces/go-sqlite3 v0.24.1
github.com/ncruces/go-sqlite3/gormlite v0.24.0
github.com/wailsapp/wails/v3 v3.0.0-alpha.9 github.com/wailsapp/wails/v3 v3.0.0-alpha.9
github.com/xuri/excelize/v2 v2.9.0 github.com/xuri/excelize/v2 v2.9.0
golang.org/x/text v0.23.0 golang.org/x/text v0.23.0
@@ -46,9 +44,8 @@ require (
github.com/lmittmann/tint v1.0.4 // indirect github.com/lmittmann/tint v1.0.4 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-sqlite3 v1.14.24 // indirect github.com/mattn/go-sqlite3 v1.14.22 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/ncruces/julianday v1.0.0 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
github.com/richardlehane/mscfb v1.0.4 // indirect github.com/richardlehane/mscfb v1.0.4 // indirect
@@ -57,18 +54,17 @@ require (
github.com/samber/lo v1.38.1 // indirect github.com/samber/lo v1.38.1 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/skeema/knownhosts v1.2.2 // indirect github.com/skeema/knownhosts v1.2.2 // indirect
github.com/tetratelabs/wazero v1.9.0 // indirect
github.com/wailsapp/go-webview2 v1.0.19 // indirect github.com/wailsapp/go-webview2 v1.0.19 // indirect
github.com/wailsapp/mimetype v1.4.1 // indirect github.com/wailsapp/mimetype v1.4.1 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d // indirect github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d // indirect
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 // indirect github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 // indirect
golang.org/x/crypto v0.36.0 // indirect golang.org/x/crypto v0.30.0 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/mod v0.22.0 // indirect golang.org/x/mod v0.22.0 // indirect
golang.org/x/net v0.32.0 // indirect golang.org/x/net v0.32.0 // indirect
golang.org/x/sync v0.12.0 // indirect golang.org/x/sync v0.12.0 // indirect
golang.org/x/sys v0.31.0 // indirect golang.org/x/sys v0.28.0 // indirect
golang.org/x/tools v0.28.0 // indirect golang.org/x/tools v0.28.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect

24
go.sum
View File

@@ -99,18 +99,12 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA= github.com/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA=
github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA= github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/ncruces/go-sqlite3 v0.24.1 h1:qHlIz+dlH3Y0wCUErFZXon5hCvw1Kc9eEkZVYYi8p14=
github.com/ncruces/go-sqlite3 v0.24.1/go.mod h1:n6Z7036yFilJx04yV0mi5JWaF66rUmXn1It9Ux8dx68=
github.com/ncruces/go-sqlite3/gormlite v0.24.0 h1:81sHeq3CCdhjoqAB650n5wEdRlLO9VBvosArskcN3+c=
github.com/ncruces/go-sqlite3/gormlite v0.24.0/go.mod h1:vXfVWdBfg7qOgqQqHpzUWl9LLswD0h+8mK4oouaV2oc=
github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M=
github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
@@ -143,8 +137,6 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
github.com/wailsapp/go-webview2 v1.0.19 h1:7U3QcDj1PrBPaxJNCui2k1SkWml+Q5kvFUFyTImA6NU= github.com/wailsapp/go-webview2 v1.0.19 h1:7U3QcDj1PrBPaxJNCui2k1SkWml+Q5kvFUFyTImA6NU=
github.com/wailsapp/go-webview2 v1.0.19/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc= github.com/wailsapp/go-webview2 v1.0.19/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc=
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs= github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
@@ -165,8 +157,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s= golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s=
@@ -207,15 +199,15 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=

View File

@@ -1,9 +1,9 @@
package excel package excel
import ( import (
"app/internal/dialogs"
"errors" "errors"
"fmt" "fmt"
"git.gogacoder.ru/NTO/boilerplate/internal/dialogs"
"github.com/kuzgoga/fogg" "github.com/kuzgoga/fogg"
"github.com/xuri/excelize/v2" "github.com/xuri/excelize/v2"
"golang.org/x/text/language" "golang.org/x/text/language"

615
internal/dal/authors.gen.go Normal file
View File

@@ -0,0 +1,615 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package dal
import (
"app/internal/models"
"context"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gen"
"gorm.io/gen/field"
"gorm.io/plugin/dbresolver"
)
func newAuthor(db *gorm.DB, opts ...gen.DOOption) author {
_author := author{}
_author.authorDo.UseDB(db, opts...)
_author.authorDo.UseModel(&models.Author{})
tableName := _author.authorDo.TableName()
_author.ALL = field.NewAsterisk(tableName)
_author.Id = field.NewUint(tableName, "id")
_author.Name = field.NewString(tableName, "name")
_author.Posts = authorHasManyPosts{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Posts", "models.Post"),
Author: struct {
field.RelationField
Posts struct {
field.RelationField
}
Comments struct {
field.RelationField
Author struct {
field.RelationField
}
Posts struct {
field.RelationField
}
}
}{
RelationField: field.NewRelation("Posts.Author", "models.Author"),
Posts: struct {
field.RelationField
}{
RelationField: field.NewRelation("Posts.Author.Posts", "models.Post"),
},
Comments: struct {
field.RelationField
Author struct {
field.RelationField
}
Posts struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Posts.Author.Comments", "models.Comment"),
Author: struct {
field.RelationField
}{
RelationField: field.NewRelation("Posts.Author.Comments.Author", "models.Author"),
},
Posts: struct {
field.RelationField
}{
RelationField: field.NewRelation("Posts.Author.Comments.Posts", "models.Post"),
},
},
},
PostType: struct {
field.RelationField
}{
RelationField: field.NewRelation("Posts.PostType", "models.PostType"),
},
Comments: struct {
field.RelationField
}{
RelationField: field.NewRelation("Posts.Comments", "models.Comment"),
},
}
_author.Comments = authorHasManyComments{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Comments", "models.Comment"),
}
_author.fillFieldMap()
return _author
}
type author struct {
authorDo
ALL field.Asterisk
Id field.Uint
Name field.String
Posts authorHasManyPosts
Comments authorHasManyComments
fieldMap map[string]field.Expr
}
func (a author) Table(newTableName string) *author {
a.authorDo.UseTable(newTableName)
return a.updateTableName(newTableName)
}
func (a author) As(alias string) *author {
a.authorDo.DO = *(a.authorDo.As(alias).(*gen.DO))
return a.updateTableName(alias)
}
func (a *author) updateTableName(table string) *author {
a.ALL = field.NewAsterisk(table)
a.Id = field.NewUint(table, "id")
a.Name = field.NewString(table, "name")
a.fillFieldMap()
return a
}
func (a *author) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
_f, ok := a.fieldMap[fieldName]
if !ok || _f == nil {
return nil, false
}
_oe, ok := _f.(field.OrderExpr)
return _oe, ok
}
func (a *author) fillFieldMap() {
a.fieldMap = make(map[string]field.Expr, 4)
a.fieldMap["id"] = a.Id
a.fieldMap["name"] = a.Name
}
func (a author) clone(db *gorm.DB) author {
a.authorDo.ReplaceConnPool(db.Statement.ConnPool)
return a
}
func (a author) replaceDB(db *gorm.DB) author {
a.authorDo.ReplaceDB(db)
return a
}
type authorHasManyPosts struct {
db *gorm.DB
field.RelationField
Author struct {
field.RelationField
Posts struct {
field.RelationField
}
Comments struct {
field.RelationField
Author struct {
field.RelationField
}
Posts struct {
field.RelationField
}
}
}
PostType struct {
field.RelationField
}
Comments struct {
field.RelationField
}
}
func (a authorHasManyPosts) Where(conds ...field.Expr) *authorHasManyPosts {
if len(conds) == 0 {
return &a
}
exprs := make([]clause.Expression, 0, len(conds))
for _, cond := range conds {
exprs = append(exprs, cond.BeCond().(clause.Expression))
}
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
return &a
}
func (a authorHasManyPosts) WithContext(ctx context.Context) *authorHasManyPosts {
a.db = a.db.WithContext(ctx)
return &a
}
func (a authorHasManyPosts) Session(session *gorm.Session) *authorHasManyPosts {
a.db = a.db.Session(session)
return &a
}
func (a authorHasManyPosts) Model(m *models.Author) *authorHasManyPostsTx {
return &authorHasManyPostsTx{a.db.Model(m).Association(a.Name())}
}
type authorHasManyPostsTx struct{ tx *gorm.Association }
func (a authorHasManyPostsTx) Find() (result []*models.Post, err error) {
return result, a.tx.Find(&result)
}
func (a authorHasManyPostsTx) Append(values ...*models.Post) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a authorHasManyPostsTx) Replace(values ...*models.Post) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a authorHasManyPostsTx) Delete(values ...*models.Post) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a authorHasManyPostsTx) Clear() error {
return a.tx.Clear()
}
func (a authorHasManyPostsTx) Count() int64 {
return a.tx.Count()
}
type authorHasManyComments struct {
db *gorm.DB
field.RelationField
}
func (a authorHasManyComments) Where(conds ...field.Expr) *authorHasManyComments {
if len(conds) == 0 {
return &a
}
exprs := make([]clause.Expression, 0, len(conds))
for _, cond := range conds {
exprs = append(exprs, cond.BeCond().(clause.Expression))
}
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
return &a
}
func (a authorHasManyComments) WithContext(ctx context.Context) *authorHasManyComments {
a.db = a.db.WithContext(ctx)
return &a
}
func (a authorHasManyComments) Session(session *gorm.Session) *authorHasManyComments {
a.db = a.db.Session(session)
return &a
}
func (a authorHasManyComments) Model(m *models.Author) *authorHasManyCommentsTx {
return &authorHasManyCommentsTx{a.db.Model(m).Association(a.Name())}
}
type authorHasManyCommentsTx struct{ tx *gorm.Association }
func (a authorHasManyCommentsTx) Find() (result []*models.Comment, err error) {
return result, a.tx.Find(&result)
}
func (a authorHasManyCommentsTx) Append(values ...*models.Comment) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a authorHasManyCommentsTx) Replace(values ...*models.Comment) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a authorHasManyCommentsTx) Delete(values ...*models.Comment) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a authorHasManyCommentsTx) Clear() error {
return a.tx.Clear()
}
func (a authorHasManyCommentsTx) Count() int64 {
return a.tx.Count()
}
type authorDo struct{ gen.DO }
type IAuthorDo interface {
gen.SubQuery
Debug() IAuthorDo
WithContext(ctx context.Context) IAuthorDo
WithResult(fc func(tx gen.Dao)) gen.ResultInfo
ReplaceDB(db *gorm.DB)
ReadDB() IAuthorDo
WriteDB() IAuthorDo
As(alias string) gen.Dao
Session(config *gorm.Session) IAuthorDo
Columns(cols ...field.Expr) gen.Columns
Clauses(conds ...clause.Expression) IAuthorDo
Not(conds ...gen.Condition) IAuthorDo
Or(conds ...gen.Condition) IAuthorDo
Select(conds ...field.Expr) IAuthorDo
Where(conds ...gen.Condition) IAuthorDo
Order(conds ...field.Expr) IAuthorDo
Distinct(cols ...field.Expr) IAuthorDo
Omit(cols ...field.Expr) IAuthorDo
Join(table schema.Tabler, on ...field.Expr) IAuthorDo
LeftJoin(table schema.Tabler, on ...field.Expr) IAuthorDo
RightJoin(table schema.Tabler, on ...field.Expr) IAuthorDo
Group(cols ...field.Expr) IAuthorDo
Having(conds ...gen.Condition) IAuthorDo
Limit(limit int) IAuthorDo
Offset(offset int) IAuthorDo
Count() (count int64, err error)
Scopes(funcs ...func(gen.Dao) gen.Dao) IAuthorDo
Unscoped() IAuthorDo
Create(values ...*models.Author) error
CreateInBatches(values []*models.Author, batchSize int) error
Save(values ...*models.Author) error
First() (*models.Author, error)
Take() (*models.Author, error)
Last() (*models.Author, error)
Find() ([]*models.Author, error)
FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*models.Author, err error)
FindInBatches(result *[]*models.Author, batchSize int, fc func(tx gen.Dao, batch int) error) error
Pluck(column field.Expr, dest interface{}) error
Delete(...*models.Author) (info gen.ResultInfo, err error)
Update(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
Updates(value interface{}) (info gen.ResultInfo, err error)
UpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
UpdateColumns(value interface{}) (info gen.ResultInfo, err error)
UpdateFrom(q gen.SubQuery) gen.Dao
Attrs(attrs ...field.AssignExpr) IAuthorDo
Assign(attrs ...field.AssignExpr) IAuthorDo
Joins(fields ...field.RelationField) IAuthorDo
Preload(fields ...field.RelationField) IAuthorDo
FirstOrInit() (*models.Author, error)
FirstOrCreate() (*models.Author, error)
FindByPage(offset int, limit int) (result []*models.Author, count int64, err error)
ScanByPage(result interface{}, offset int, limit int) (count int64, err error)
Scan(result interface{}) (err error)
Returning(value interface{}, columns ...string) IAuthorDo
UnderlyingDB() *gorm.DB
schema.Tabler
}
func (a authorDo) Debug() IAuthorDo {
return a.withDO(a.DO.Debug())
}
func (a authorDo) WithContext(ctx context.Context) IAuthorDo {
return a.withDO(a.DO.WithContext(ctx))
}
func (a authorDo) ReadDB() IAuthorDo {
return a.Clauses(dbresolver.Read)
}
func (a authorDo) WriteDB() IAuthorDo {
return a.Clauses(dbresolver.Write)
}
func (a authorDo) Session(config *gorm.Session) IAuthorDo {
return a.withDO(a.DO.Session(config))
}
func (a authorDo) Clauses(conds ...clause.Expression) IAuthorDo {
return a.withDO(a.DO.Clauses(conds...))
}
func (a authorDo) Returning(value interface{}, columns ...string) IAuthorDo {
return a.withDO(a.DO.Returning(value, columns...))
}
func (a authorDo) Not(conds ...gen.Condition) IAuthorDo {
return a.withDO(a.DO.Not(conds...))
}
func (a authorDo) Or(conds ...gen.Condition) IAuthorDo {
return a.withDO(a.DO.Or(conds...))
}
func (a authorDo) Select(conds ...field.Expr) IAuthorDo {
return a.withDO(a.DO.Select(conds...))
}
func (a authorDo) Where(conds ...gen.Condition) IAuthorDo {
return a.withDO(a.DO.Where(conds...))
}
func (a authorDo) Order(conds ...field.Expr) IAuthorDo {
return a.withDO(a.DO.Order(conds...))
}
func (a authorDo) Distinct(cols ...field.Expr) IAuthorDo {
return a.withDO(a.DO.Distinct(cols...))
}
func (a authorDo) Omit(cols ...field.Expr) IAuthorDo {
return a.withDO(a.DO.Omit(cols...))
}
func (a authorDo) Join(table schema.Tabler, on ...field.Expr) IAuthorDo {
return a.withDO(a.DO.Join(table, on...))
}
func (a authorDo) LeftJoin(table schema.Tabler, on ...field.Expr) IAuthorDo {
return a.withDO(a.DO.LeftJoin(table, on...))
}
func (a authorDo) RightJoin(table schema.Tabler, on ...field.Expr) IAuthorDo {
return a.withDO(a.DO.RightJoin(table, on...))
}
func (a authorDo) Group(cols ...field.Expr) IAuthorDo {
return a.withDO(a.DO.Group(cols...))
}
func (a authorDo) Having(conds ...gen.Condition) IAuthorDo {
return a.withDO(a.DO.Having(conds...))
}
func (a authorDo) Limit(limit int) IAuthorDo {
return a.withDO(a.DO.Limit(limit))
}
func (a authorDo) Offset(offset int) IAuthorDo {
return a.withDO(a.DO.Offset(offset))
}
func (a authorDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IAuthorDo {
return a.withDO(a.DO.Scopes(funcs...))
}
func (a authorDo) Unscoped() IAuthorDo {
return a.withDO(a.DO.Unscoped())
}
func (a authorDo) Create(values ...*models.Author) error {
if len(values) == 0 {
return nil
}
return a.DO.Create(values)
}
func (a authorDo) CreateInBatches(values []*models.Author, batchSize int) error {
return a.DO.CreateInBatches(values, batchSize)
}
// Save : !!! underlying implementation is different with GORM
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
func (a authorDo) Save(values ...*models.Author) error {
if len(values) == 0 {
return nil
}
return a.DO.Save(values)
}
func (a authorDo) First() (*models.Author, error) {
if result, err := a.DO.First(); err != nil {
return nil, err
} else {
return result.(*models.Author), nil
}
}
func (a authorDo) Take() (*models.Author, error) {
if result, err := a.DO.Take(); err != nil {
return nil, err
} else {
return result.(*models.Author), nil
}
}
func (a authorDo) Last() (*models.Author, error) {
if result, err := a.DO.Last(); err != nil {
return nil, err
} else {
return result.(*models.Author), nil
}
}
func (a authorDo) Find() ([]*models.Author, error) {
result, err := a.DO.Find()
return result.([]*models.Author), err
}
func (a authorDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*models.Author, err error) {
buf := make([]*models.Author, 0, batchSize)
err = a.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
defer func() { results = append(results, buf...) }()
return fc(tx, batch)
})
return results, err
}
func (a authorDo) FindInBatches(result *[]*models.Author, batchSize int, fc func(tx gen.Dao, batch int) error) error {
return a.DO.FindInBatches(result, batchSize, fc)
}
func (a authorDo) Attrs(attrs ...field.AssignExpr) IAuthorDo {
return a.withDO(a.DO.Attrs(attrs...))
}
func (a authorDo) Assign(attrs ...field.AssignExpr) IAuthorDo {
return a.withDO(a.DO.Assign(attrs...))
}
func (a authorDo) Joins(fields ...field.RelationField) IAuthorDo {
for _, _f := range fields {
a = *a.withDO(a.DO.Joins(_f))
}
return &a
}
func (a authorDo) Preload(fields ...field.RelationField) IAuthorDo {
for _, _f := range fields {
a = *a.withDO(a.DO.Preload(_f))
}
return &a
}
func (a authorDo) FirstOrInit() (*models.Author, error) {
if result, err := a.DO.FirstOrInit(); err != nil {
return nil, err
} else {
return result.(*models.Author), nil
}
}
func (a authorDo) FirstOrCreate() (*models.Author, error) {
if result, err := a.DO.FirstOrCreate(); err != nil {
return nil, err
} else {
return result.(*models.Author), nil
}
}
func (a authorDo) FindByPage(offset int, limit int) (result []*models.Author, count int64, err error) {
result, err = a.Offset(offset).Limit(limit).Find()
if err != nil {
return
}
if size := len(result); 0 < limit && 0 < size && size < limit {
count = int64(size + offset)
return
}
count, err = a.Offset(-1).Limit(-1).Count()
return
}
func (a authorDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
count, err = a.Count()
if err != nil {
return
}
err = a.Offset(offset).Limit(limit).Scan(result)
return
}
func (a authorDo) Scan(result interface{}) (err error) {
return a.DO.Scan(result)
}
func (a authorDo) Delete(models ...*models.Author) (result gen.ResultInfo, err error) {
return a.DO.Delete(models)
}
func (a *authorDo) withDO(do gen.Dao) *authorDo {
a.DO = *do.(*gen.DO)
return a
}

View File

@@ -0,0 +1,145 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package dal
import (
"app/internal/models"
"context"
"fmt"
"testing"
"gorm.io/gen"
"gorm.io/gen/field"
"gorm.io/gorm/clause"
)
func init() {
InitializeDB()
err := _gen_test_db.AutoMigrate(&models.Author{})
if err != nil {
fmt.Printf("Error: AutoMigrate(&models.Author{}) fail: %s", err)
}
}
func Test_authorQuery(t *testing.T) {
author := newAuthor(_gen_test_db)
author = *author.As(author.TableName())
_do := author.WithContext(context.Background()).Debug()
primaryKey := field.NewString(author.TableName(), clause.PrimaryKey)
_, err := _do.Unscoped().Where(primaryKey.IsNotNull()).Delete()
if err != nil {
t.Error("clean table <authors> fail:", err)
return
}
_, ok := author.GetFieldByName("")
if ok {
t.Error("GetFieldByName(\"\") from author success")
}
err = _do.Create(&models.Author{})
if err != nil {
t.Error("create item in table <authors> fail:", err)
}
err = _do.Save(&models.Author{})
if err != nil {
t.Error("create item in table <authors> fail:", err)
}
err = _do.CreateInBatches([]*models.Author{{}, {}}, 10)
if err != nil {
t.Error("create item in table <authors> fail:", err)
}
_, err = _do.Select(author.ALL).Take()
if err != nil {
t.Error("Take() on table <authors> fail:", err)
}
_, err = _do.First()
if err != nil {
t.Error("First() on table <authors> fail:", err)
}
_, err = _do.Last()
if err != nil {
t.Error("First() on table <authors> fail:", err)
}
_, err = _do.Where(primaryKey.IsNotNull()).FindInBatch(10, func(tx gen.Dao, batch int) error { return nil })
if err != nil {
t.Error("FindInBatch() on table <authors> fail:", err)
}
err = _do.Where(primaryKey.IsNotNull()).FindInBatches(&[]*models.Author{}, 10, func(tx gen.Dao, batch int) error { return nil })
if err != nil {
t.Error("FindInBatches() on table <authors> fail:", err)
}
_, err = _do.Select(author.ALL).Where(primaryKey.IsNotNull()).Order(primaryKey.Desc()).Find()
if err != nil {
t.Error("Find() on table <authors> fail:", err)
}
_, err = _do.Distinct(primaryKey).Take()
if err != nil {
t.Error("select Distinct() on table <authors> fail:", err)
}
_, err = _do.Select(author.ALL).Omit(primaryKey).Take()
if err != nil {
t.Error("Omit() on table <authors> fail:", err)
}
_, err = _do.Group(primaryKey).Find()
if err != nil {
t.Error("Group() on table <authors> fail:", err)
}
_, err = _do.Scopes(func(dao gen.Dao) gen.Dao { return dao.Where(primaryKey.IsNotNull()) }).Find()
if err != nil {
t.Error("Scopes() on table <authors> fail:", err)
}
_, _, err = _do.FindByPage(0, 1)
if err != nil {
t.Error("FindByPage() on table <authors> fail:", err)
}
_, err = _do.ScanByPage(&models.Author{}, 0, 1)
if err != nil {
t.Error("ScanByPage() on table <authors> fail:", err)
}
_, err = _do.Attrs(primaryKey).Assign(primaryKey).FirstOrInit()
if err != nil {
t.Error("FirstOrInit() on table <authors> fail:", err)
}
_, err = _do.Attrs(primaryKey).Assign(primaryKey).FirstOrCreate()
if err != nil {
t.Error("FirstOrCreate() on table <authors> fail:", err)
}
var _a _another
var _aPK = field.NewString(_a.TableName(), "id")
err = _do.Join(&_a, primaryKey.EqCol(_aPK)).Scan(map[string]interface{}{})
if err != nil {
t.Error("Join() on table <authors> fail:", err)
}
err = _do.LeftJoin(&_a, primaryKey.EqCol(_aPK)).Scan(map[string]interface{}{})
if err != nil {
t.Error("LeftJoin() on table <authors> fail:", err)
}
_, err = _do.Not().Or().Clauses().Take()
if err != nil {
t.Error("Not/Or/Clauses on table <authors> fail:", err)
}
}

View File

@@ -0,0 +1,622 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package dal
import (
"app/internal/models"
"context"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gen"
"gorm.io/gen/field"
"gorm.io/plugin/dbresolver"
)
func newComment(db *gorm.DB, opts ...gen.DOOption) comment {
_comment := comment{}
_comment.commentDo.UseDB(db, opts...)
_comment.commentDo.UseModel(&models.Comment{})
tableName := _comment.commentDo.TableName()
_comment.ALL = field.NewAsterisk(tableName)
_comment.Id = field.NewUint(tableName, "id")
_comment.Text = field.NewString(tableName, "text")
_comment.AuthorId = field.NewUint(tableName, "author_id")
_comment.Author = commentBelongsToAuthor{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Author", "models.Author"),
Posts: struct {
field.RelationField
Author struct {
field.RelationField
}
PostType struct {
field.RelationField
}
Comments struct {
field.RelationField
Author struct {
field.RelationField
}
Posts struct {
field.RelationField
}
}
}{
RelationField: field.NewRelation("Author.Posts", "models.Post"),
Author: struct {
field.RelationField
}{
RelationField: field.NewRelation("Author.Posts.Author", "models.Author"),
},
PostType: struct {
field.RelationField
}{
RelationField: field.NewRelation("Author.Posts.PostType", "models.PostType"),
},
Comments: struct {
field.RelationField
Author struct {
field.RelationField
}
Posts struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Author.Posts.Comments", "models.Comment"),
Author: struct {
field.RelationField
}{
RelationField: field.NewRelation("Author.Posts.Comments.Author", "models.Author"),
},
Posts: struct {
field.RelationField
}{
RelationField: field.NewRelation("Author.Posts.Comments.Posts", "models.Post"),
},
},
},
Comments: struct {
field.RelationField
}{
RelationField: field.NewRelation("Author.Comments", "models.Comment"),
},
}
_comment.Posts = commentManyToManyPosts{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Posts", "models.Post"),
}
_comment.fillFieldMap()
return _comment
}
type comment struct {
commentDo
ALL field.Asterisk
Id field.Uint
Text field.String
AuthorId field.Uint
Author commentBelongsToAuthor
Posts commentManyToManyPosts
fieldMap map[string]field.Expr
}
func (c comment) Table(newTableName string) *comment {
c.commentDo.UseTable(newTableName)
return c.updateTableName(newTableName)
}
func (c comment) As(alias string) *comment {
c.commentDo.DO = *(c.commentDo.As(alias).(*gen.DO))
return c.updateTableName(alias)
}
func (c *comment) updateTableName(table string) *comment {
c.ALL = field.NewAsterisk(table)
c.Id = field.NewUint(table, "id")
c.Text = field.NewString(table, "text")
c.AuthorId = field.NewUint(table, "author_id")
c.fillFieldMap()
return c
}
func (c *comment) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
_f, ok := c.fieldMap[fieldName]
if !ok || _f == nil {
return nil, false
}
_oe, ok := _f.(field.OrderExpr)
return _oe, ok
}
func (c *comment) fillFieldMap() {
c.fieldMap = make(map[string]field.Expr, 5)
c.fieldMap["id"] = c.Id
c.fieldMap["text"] = c.Text
c.fieldMap["author_id"] = c.AuthorId
}
func (c comment) clone(db *gorm.DB) comment {
c.commentDo.ReplaceConnPool(db.Statement.ConnPool)
return c
}
func (c comment) replaceDB(db *gorm.DB) comment {
c.commentDo.ReplaceDB(db)
return c
}
type commentBelongsToAuthor struct {
db *gorm.DB
field.RelationField
Posts struct {
field.RelationField
Author struct {
field.RelationField
}
PostType struct {
field.RelationField
}
Comments struct {
field.RelationField
Author struct {
field.RelationField
}
Posts struct {
field.RelationField
}
}
}
Comments struct {
field.RelationField
}
}
func (a commentBelongsToAuthor) Where(conds ...field.Expr) *commentBelongsToAuthor {
if len(conds) == 0 {
return &a
}
exprs := make([]clause.Expression, 0, len(conds))
for _, cond := range conds {
exprs = append(exprs, cond.BeCond().(clause.Expression))
}
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
return &a
}
func (a commentBelongsToAuthor) WithContext(ctx context.Context) *commentBelongsToAuthor {
a.db = a.db.WithContext(ctx)
return &a
}
func (a commentBelongsToAuthor) Session(session *gorm.Session) *commentBelongsToAuthor {
a.db = a.db.Session(session)
return &a
}
func (a commentBelongsToAuthor) Model(m *models.Comment) *commentBelongsToAuthorTx {
return &commentBelongsToAuthorTx{a.db.Model(m).Association(a.Name())}
}
type commentBelongsToAuthorTx struct{ tx *gorm.Association }
func (a commentBelongsToAuthorTx) Find() (result *models.Author, err error) {
return result, a.tx.Find(&result)
}
func (a commentBelongsToAuthorTx) Append(values ...*models.Author) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a commentBelongsToAuthorTx) Replace(values ...*models.Author) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a commentBelongsToAuthorTx) Delete(values ...*models.Author) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a commentBelongsToAuthorTx) Clear() error {
return a.tx.Clear()
}
func (a commentBelongsToAuthorTx) Count() int64 {
return a.tx.Count()
}
type commentManyToManyPosts struct {
db *gorm.DB
field.RelationField
}
func (a commentManyToManyPosts) Where(conds ...field.Expr) *commentManyToManyPosts {
if len(conds) == 0 {
return &a
}
exprs := make([]clause.Expression, 0, len(conds))
for _, cond := range conds {
exprs = append(exprs, cond.BeCond().(clause.Expression))
}
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
return &a
}
func (a commentManyToManyPosts) WithContext(ctx context.Context) *commentManyToManyPosts {
a.db = a.db.WithContext(ctx)
return &a
}
func (a commentManyToManyPosts) Session(session *gorm.Session) *commentManyToManyPosts {
a.db = a.db.Session(session)
return &a
}
func (a commentManyToManyPosts) Model(m *models.Comment) *commentManyToManyPostsTx {
return &commentManyToManyPostsTx{a.db.Model(m).Association(a.Name())}
}
type commentManyToManyPostsTx struct{ tx *gorm.Association }
func (a commentManyToManyPostsTx) Find() (result []*models.Post, err error) {
return result, a.tx.Find(&result)
}
func (a commentManyToManyPostsTx) Append(values ...*models.Post) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a commentManyToManyPostsTx) Replace(values ...*models.Post) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a commentManyToManyPostsTx) Delete(values ...*models.Post) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a commentManyToManyPostsTx) Clear() error {
return a.tx.Clear()
}
func (a commentManyToManyPostsTx) Count() int64 {
return a.tx.Count()
}
type commentDo struct{ gen.DO }
type ICommentDo interface {
gen.SubQuery
Debug() ICommentDo
WithContext(ctx context.Context) ICommentDo
WithResult(fc func(tx gen.Dao)) gen.ResultInfo
ReplaceDB(db *gorm.DB)
ReadDB() ICommentDo
WriteDB() ICommentDo
As(alias string) gen.Dao
Session(config *gorm.Session) ICommentDo
Columns(cols ...field.Expr) gen.Columns
Clauses(conds ...clause.Expression) ICommentDo
Not(conds ...gen.Condition) ICommentDo
Or(conds ...gen.Condition) ICommentDo
Select(conds ...field.Expr) ICommentDo
Where(conds ...gen.Condition) ICommentDo
Order(conds ...field.Expr) ICommentDo
Distinct(cols ...field.Expr) ICommentDo
Omit(cols ...field.Expr) ICommentDo
Join(table schema.Tabler, on ...field.Expr) ICommentDo
LeftJoin(table schema.Tabler, on ...field.Expr) ICommentDo
RightJoin(table schema.Tabler, on ...field.Expr) ICommentDo
Group(cols ...field.Expr) ICommentDo
Having(conds ...gen.Condition) ICommentDo
Limit(limit int) ICommentDo
Offset(offset int) ICommentDo
Count() (count int64, err error)
Scopes(funcs ...func(gen.Dao) gen.Dao) ICommentDo
Unscoped() ICommentDo
Create(values ...*models.Comment) error
CreateInBatches(values []*models.Comment, batchSize int) error
Save(values ...*models.Comment) error
First() (*models.Comment, error)
Take() (*models.Comment, error)
Last() (*models.Comment, error)
Find() ([]*models.Comment, error)
FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*models.Comment, err error)
FindInBatches(result *[]*models.Comment, batchSize int, fc func(tx gen.Dao, batch int) error) error
Pluck(column field.Expr, dest interface{}) error
Delete(...*models.Comment) (info gen.ResultInfo, err error)
Update(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
Updates(value interface{}) (info gen.ResultInfo, err error)
UpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
UpdateColumns(value interface{}) (info gen.ResultInfo, err error)
UpdateFrom(q gen.SubQuery) gen.Dao
Attrs(attrs ...field.AssignExpr) ICommentDo
Assign(attrs ...field.AssignExpr) ICommentDo
Joins(fields ...field.RelationField) ICommentDo
Preload(fields ...field.RelationField) ICommentDo
FirstOrInit() (*models.Comment, error)
FirstOrCreate() (*models.Comment, error)
FindByPage(offset int, limit int) (result []*models.Comment, count int64, err error)
ScanByPage(result interface{}, offset int, limit int) (count int64, err error)
Scan(result interface{}) (err error)
Returning(value interface{}, columns ...string) ICommentDo
UnderlyingDB() *gorm.DB
schema.Tabler
}
func (c commentDo) Debug() ICommentDo {
return c.withDO(c.DO.Debug())
}
func (c commentDo) WithContext(ctx context.Context) ICommentDo {
return c.withDO(c.DO.WithContext(ctx))
}
func (c commentDo) ReadDB() ICommentDo {
return c.Clauses(dbresolver.Read)
}
func (c commentDo) WriteDB() ICommentDo {
return c.Clauses(dbresolver.Write)
}
func (c commentDo) Session(config *gorm.Session) ICommentDo {
return c.withDO(c.DO.Session(config))
}
func (c commentDo) Clauses(conds ...clause.Expression) ICommentDo {
return c.withDO(c.DO.Clauses(conds...))
}
func (c commentDo) Returning(value interface{}, columns ...string) ICommentDo {
return c.withDO(c.DO.Returning(value, columns...))
}
func (c commentDo) Not(conds ...gen.Condition) ICommentDo {
return c.withDO(c.DO.Not(conds...))
}
func (c commentDo) Or(conds ...gen.Condition) ICommentDo {
return c.withDO(c.DO.Or(conds...))
}
func (c commentDo) Select(conds ...field.Expr) ICommentDo {
return c.withDO(c.DO.Select(conds...))
}
func (c commentDo) Where(conds ...gen.Condition) ICommentDo {
return c.withDO(c.DO.Where(conds...))
}
func (c commentDo) Order(conds ...field.Expr) ICommentDo {
return c.withDO(c.DO.Order(conds...))
}
func (c commentDo) Distinct(cols ...field.Expr) ICommentDo {
return c.withDO(c.DO.Distinct(cols...))
}
func (c commentDo) Omit(cols ...field.Expr) ICommentDo {
return c.withDO(c.DO.Omit(cols...))
}
func (c commentDo) Join(table schema.Tabler, on ...field.Expr) ICommentDo {
return c.withDO(c.DO.Join(table, on...))
}
func (c commentDo) LeftJoin(table schema.Tabler, on ...field.Expr) ICommentDo {
return c.withDO(c.DO.LeftJoin(table, on...))
}
func (c commentDo) RightJoin(table schema.Tabler, on ...field.Expr) ICommentDo {
return c.withDO(c.DO.RightJoin(table, on...))
}
func (c commentDo) Group(cols ...field.Expr) ICommentDo {
return c.withDO(c.DO.Group(cols...))
}
func (c commentDo) Having(conds ...gen.Condition) ICommentDo {
return c.withDO(c.DO.Having(conds...))
}
func (c commentDo) Limit(limit int) ICommentDo {
return c.withDO(c.DO.Limit(limit))
}
func (c commentDo) Offset(offset int) ICommentDo {
return c.withDO(c.DO.Offset(offset))
}
func (c commentDo) Scopes(funcs ...func(gen.Dao) gen.Dao) ICommentDo {
return c.withDO(c.DO.Scopes(funcs...))
}
func (c commentDo) Unscoped() ICommentDo {
return c.withDO(c.DO.Unscoped())
}
func (c commentDo) Create(values ...*models.Comment) error {
if len(values) == 0 {
return nil
}
return c.DO.Create(values)
}
func (c commentDo) CreateInBatches(values []*models.Comment, batchSize int) error {
return c.DO.CreateInBatches(values, batchSize)
}
// Save : !!! underlying implementation is different with GORM
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
func (c commentDo) Save(values ...*models.Comment) error {
if len(values) == 0 {
return nil
}
return c.DO.Save(values)
}
func (c commentDo) First() (*models.Comment, error) {
if result, err := c.DO.First(); err != nil {
return nil, err
} else {
return result.(*models.Comment), nil
}
}
func (c commentDo) Take() (*models.Comment, error) {
if result, err := c.DO.Take(); err != nil {
return nil, err
} else {
return result.(*models.Comment), nil
}
}
func (c commentDo) Last() (*models.Comment, error) {
if result, err := c.DO.Last(); err != nil {
return nil, err
} else {
return result.(*models.Comment), nil
}
}
func (c commentDo) Find() ([]*models.Comment, error) {
result, err := c.DO.Find()
return result.([]*models.Comment), err
}
func (c commentDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*models.Comment, err error) {
buf := make([]*models.Comment, 0, batchSize)
err = c.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
defer func() { results = append(results, buf...) }()
return fc(tx, batch)
})
return results, err
}
func (c commentDo) FindInBatches(result *[]*models.Comment, batchSize int, fc func(tx gen.Dao, batch int) error) error {
return c.DO.FindInBatches(result, batchSize, fc)
}
func (c commentDo) Attrs(attrs ...field.AssignExpr) ICommentDo {
return c.withDO(c.DO.Attrs(attrs...))
}
func (c commentDo) Assign(attrs ...field.AssignExpr) ICommentDo {
return c.withDO(c.DO.Assign(attrs...))
}
func (c commentDo) Joins(fields ...field.RelationField) ICommentDo {
for _, _f := range fields {
c = *c.withDO(c.DO.Joins(_f))
}
return &c
}
func (c commentDo) Preload(fields ...field.RelationField) ICommentDo {
for _, _f := range fields {
c = *c.withDO(c.DO.Preload(_f))
}
return &c
}
func (c commentDo) FirstOrInit() (*models.Comment, error) {
if result, err := c.DO.FirstOrInit(); err != nil {
return nil, err
} else {
return result.(*models.Comment), nil
}
}
func (c commentDo) FirstOrCreate() (*models.Comment, error) {
if result, err := c.DO.FirstOrCreate(); err != nil {
return nil, err
} else {
return result.(*models.Comment), nil
}
}
func (c commentDo) FindByPage(offset int, limit int) (result []*models.Comment, count int64, err error) {
result, err = c.Offset(offset).Limit(limit).Find()
if err != nil {
return
}
if size := len(result); 0 < limit && 0 < size && size < limit {
count = int64(size + offset)
return
}
count, err = c.Offset(-1).Limit(-1).Count()
return
}
func (c commentDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
count, err = c.Count()
if err != nil {
return
}
err = c.Offset(offset).Limit(limit).Scan(result)
return
}
func (c commentDo) Scan(result interface{}) (err error) {
return c.DO.Scan(result)
}
func (c commentDo) Delete(models ...*models.Comment) (result gen.ResultInfo, err error) {
return c.DO.Delete(models)
}
func (c *commentDo) withDO(do gen.Dao) *commentDo {
c.DO = *do.(*gen.DO)
return c
}

View File

@@ -0,0 +1,145 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package dal
import (
"app/internal/models"
"context"
"fmt"
"testing"
"gorm.io/gen"
"gorm.io/gen/field"
"gorm.io/gorm/clause"
)
func init() {
InitializeDB()
err := _gen_test_db.AutoMigrate(&models.Comment{})
if err != nil {
fmt.Printf("Error: AutoMigrate(&models.Comment{}) fail: %s", err)
}
}
func Test_commentQuery(t *testing.T) {
comment := newComment(_gen_test_db)
comment = *comment.As(comment.TableName())
_do := comment.WithContext(context.Background()).Debug()
primaryKey := field.NewString(comment.TableName(), clause.PrimaryKey)
_, err := _do.Unscoped().Where(primaryKey.IsNotNull()).Delete()
if err != nil {
t.Error("clean table <comments> fail:", err)
return
}
_, ok := comment.GetFieldByName("")
if ok {
t.Error("GetFieldByName(\"\") from comment success")
}
err = _do.Create(&models.Comment{})
if err != nil {
t.Error("create item in table <comments> fail:", err)
}
err = _do.Save(&models.Comment{})
if err != nil {
t.Error("create item in table <comments> fail:", err)
}
err = _do.CreateInBatches([]*models.Comment{{}, {}}, 10)
if err != nil {
t.Error("create item in table <comments> fail:", err)
}
_, err = _do.Select(comment.ALL).Take()
if err != nil {
t.Error("Take() on table <comments> fail:", err)
}
_, err = _do.First()
if err != nil {
t.Error("First() on table <comments> fail:", err)
}
_, err = _do.Last()
if err != nil {
t.Error("First() on table <comments> fail:", err)
}
_, err = _do.Where(primaryKey.IsNotNull()).FindInBatch(10, func(tx gen.Dao, batch int) error { return nil })
if err != nil {
t.Error("FindInBatch() on table <comments> fail:", err)
}
err = _do.Where(primaryKey.IsNotNull()).FindInBatches(&[]*models.Comment{}, 10, func(tx gen.Dao, batch int) error { return nil })
if err != nil {
t.Error("FindInBatches() on table <comments> fail:", err)
}
_, err = _do.Select(comment.ALL).Where(primaryKey.IsNotNull()).Order(primaryKey.Desc()).Find()
if err != nil {
t.Error("Find() on table <comments> fail:", err)
}
_, err = _do.Distinct(primaryKey).Take()
if err != nil {
t.Error("select Distinct() on table <comments> fail:", err)
}
_, err = _do.Select(comment.ALL).Omit(primaryKey).Take()
if err != nil {
t.Error("Omit() on table <comments> fail:", err)
}
_, err = _do.Group(primaryKey).Find()
if err != nil {
t.Error("Group() on table <comments> fail:", err)
}
_, err = _do.Scopes(func(dao gen.Dao) gen.Dao { return dao.Where(primaryKey.IsNotNull()) }).Find()
if err != nil {
t.Error("Scopes() on table <comments> fail:", err)
}
_, _, err = _do.FindByPage(0, 1)
if err != nil {
t.Error("FindByPage() on table <comments> fail:", err)
}
_, err = _do.ScanByPage(&models.Comment{}, 0, 1)
if err != nil {
t.Error("ScanByPage() on table <comments> fail:", err)
}
_, err = _do.Attrs(primaryKey).Assign(primaryKey).FirstOrInit()
if err != nil {
t.Error("FirstOrInit() on table <comments> fail:", err)
}
_, err = _do.Attrs(primaryKey).Assign(primaryKey).FirstOrCreate()
if err != nil {
t.Error("FirstOrCreate() on table <comments> fail:", err)
}
var _a _another
var _aPK = field.NewString(_a.TableName(), "id")
err = _do.Join(&_a, primaryKey.EqCol(_aPK)).Scan(map[string]interface{}{})
if err != nil {
t.Error("Join() on table <comments> fail:", err)
}
err = _do.LeftJoin(&_a, primaryKey.EqCol(_aPK)).Scan(map[string]interface{}{})
if err != nil {
t.Error("LeftJoin() on table <comments> fail:", err)
}
_, err = _do.Not().Or().Clauses().Take()
if err != nil {
t.Error("Not/Or/Clauses on table <comments> fail:", err)
}
}

View File

@@ -16,34 +16,49 @@ import (
) )
var ( var (
Q = new(Query) Q = new(Query)
Post *post Author *author
Comment *comment
Post *post
PostType *postType
) )
func SetDefault(db *gorm.DB, opts ...gen.DOOption) { func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
*Q = *Use(db, opts...) *Q = *Use(db, opts...)
Author = &Q.Author
Comment = &Q.Comment
Post = &Q.Post Post = &Q.Post
PostType = &Q.PostType
} }
func Use(db *gorm.DB, opts ...gen.DOOption) *Query { func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
return &Query{ return &Query{
db: db, db: db,
Post: newPost(db, opts...), Author: newAuthor(db, opts...),
Comment: newComment(db, opts...),
Post: newPost(db, opts...),
PostType: newPostType(db, opts...),
} }
} }
type Query struct { type Query struct {
db *gorm.DB db *gorm.DB
Post post Author author
Comment comment
Post post
PostType postType
} }
func (q *Query) Available() bool { return q.db != nil } func (q *Query) Available() bool { return q.db != nil }
func (q *Query) clone(db *gorm.DB) *Query { func (q *Query) clone(db *gorm.DB) *Query {
return &Query{ return &Query{
db: db, db: db,
Post: q.Post.clone(db), Author: q.Author.clone(db),
Comment: q.Comment.clone(db),
Post: q.Post.clone(db),
PostType: q.PostType.clone(db),
} }
} }
@@ -57,18 +72,27 @@ func (q *Query) WriteDB() *Query {
func (q *Query) ReplaceDB(db *gorm.DB) *Query { func (q *Query) ReplaceDB(db *gorm.DB) *Query {
return &Query{ return &Query{
db: db, db: db,
Post: q.Post.replaceDB(db), Author: q.Author.replaceDB(db),
Comment: q.Comment.replaceDB(db),
Post: q.Post.replaceDB(db),
PostType: q.PostType.replaceDB(db),
} }
} }
type queryCtx struct { type queryCtx struct {
Post IPostDo Author IAuthorDo
Comment ICommentDo
Post IPostDo
PostType IPostTypeDo
} }
func (q *Query) WithContext(ctx context.Context) *queryCtx { func (q *Query) WithContext(ctx context.Context) *queryCtx {
return &queryCtx{ return &queryCtx{
Post: q.Post.WithContext(ctx), Author: q.Author.WithContext(ctx),
Comment: q.Comment.WithContext(ctx),
Post: q.Post.WithContext(ctx),
PostType: q.PostType.WithContext(ctx),
} }
} }

BIN
internal/dal/gen_test.db Normal file

Binary file not shown.

View File

@@ -77,7 +77,10 @@ func Test_WithContext(t *testing.T) {
qCtx := query.WithContext(context.WithValue(context.Background(), key, value)) qCtx := query.WithContext(context.WithValue(context.Background(), key, value))
for _, ctx := range []context.Context{ for _, ctx := range []context.Context{
qCtx.Author.UnderlyingDB().Statement.Context,
qCtx.Comment.UnderlyingDB().Statement.Context,
qCtx.Post.UnderlyingDB().Statement.Context, qCtx.Post.UnderlyingDB().Statement.Context,
qCtx.PostType.UnderlyingDB().Statement.Context,
} { } {
if v := ctx.Value(key); v != value { if v := ctx.Value(key); v != value {
t.Errorf("get value from context fail, expect %q, got %q", value, v) t.Errorf("get value from context fail, expect %q, got %q", value, v)

View File

@@ -0,0 +1,383 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package dal
import (
"app/internal/models"
"context"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gen"
"gorm.io/gen/field"
"gorm.io/plugin/dbresolver"
)
func newPostType(db *gorm.DB, opts ...gen.DOOption) postType {
_postType := postType{}
_postType.postTypeDo.UseDB(db, opts...)
_postType.postTypeDo.UseModel(&models.PostType{})
tableName := _postType.postTypeDo.TableName()
_postType.ALL = field.NewAsterisk(tableName)
_postType.Id = field.NewUint(tableName, "id")
_postType.Name = field.NewString(tableName, "name")
_postType.fillFieldMap()
return _postType
}
type postType struct {
postTypeDo
ALL field.Asterisk
Id field.Uint
Name field.String
fieldMap map[string]field.Expr
}
func (p postType) Table(newTableName string) *postType {
p.postTypeDo.UseTable(newTableName)
return p.updateTableName(newTableName)
}
func (p postType) As(alias string) *postType {
p.postTypeDo.DO = *(p.postTypeDo.As(alias).(*gen.DO))
return p.updateTableName(alias)
}
func (p *postType) updateTableName(table string) *postType {
p.ALL = field.NewAsterisk(table)
p.Id = field.NewUint(table, "id")
p.Name = field.NewString(table, "name")
p.fillFieldMap()
return p
}
func (p *postType) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
_f, ok := p.fieldMap[fieldName]
if !ok || _f == nil {
return nil, false
}
_oe, ok := _f.(field.OrderExpr)
return _oe, ok
}
func (p *postType) fillFieldMap() {
p.fieldMap = make(map[string]field.Expr, 2)
p.fieldMap["id"] = p.Id
p.fieldMap["name"] = p.Name
}
func (p postType) clone(db *gorm.DB) postType {
p.postTypeDo.ReplaceConnPool(db.Statement.ConnPool)
return p
}
func (p postType) replaceDB(db *gorm.DB) postType {
p.postTypeDo.ReplaceDB(db)
return p
}
type postTypeDo struct{ gen.DO }
type IPostTypeDo interface {
gen.SubQuery
Debug() IPostTypeDo
WithContext(ctx context.Context) IPostTypeDo
WithResult(fc func(tx gen.Dao)) gen.ResultInfo
ReplaceDB(db *gorm.DB)
ReadDB() IPostTypeDo
WriteDB() IPostTypeDo
As(alias string) gen.Dao
Session(config *gorm.Session) IPostTypeDo
Columns(cols ...field.Expr) gen.Columns
Clauses(conds ...clause.Expression) IPostTypeDo
Not(conds ...gen.Condition) IPostTypeDo
Or(conds ...gen.Condition) IPostTypeDo
Select(conds ...field.Expr) IPostTypeDo
Where(conds ...gen.Condition) IPostTypeDo
Order(conds ...field.Expr) IPostTypeDo
Distinct(cols ...field.Expr) IPostTypeDo
Omit(cols ...field.Expr) IPostTypeDo
Join(table schema.Tabler, on ...field.Expr) IPostTypeDo
LeftJoin(table schema.Tabler, on ...field.Expr) IPostTypeDo
RightJoin(table schema.Tabler, on ...field.Expr) IPostTypeDo
Group(cols ...field.Expr) IPostTypeDo
Having(conds ...gen.Condition) IPostTypeDo
Limit(limit int) IPostTypeDo
Offset(offset int) IPostTypeDo
Count() (count int64, err error)
Scopes(funcs ...func(gen.Dao) gen.Dao) IPostTypeDo
Unscoped() IPostTypeDo
Create(values ...*models.PostType) error
CreateInBatches(values []*models.PostType, batchSize int) error
Save(values ...*models.PostType) error
First() (*models.PostType, error)
Take() (*models.PostType, error)
Last() (*models.PostType, error)
Find() ([]*models.PostType, error)
FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*models.PostType, err error)
FindInBatches(result *[]*models.PostType, batchSize int, fc func(tx gen.Dao, batch int) error) error
Pluck(column field.Expr, dest interface{}) error
Delete(...*models.PostType) (info gen.ResultInfo, err error)
Update(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
Updates(value interface{}) (info gen.ResultInfo, err error)
UpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
UpdateColumns(value interface{}) (info gen.ResultInfo, err error)
UpdateFrom(q gen.SubQuery) gen.Dao
Attrs(attrs ...field.AssignExpr) IPostTypeDo
Assign(attrs ...field.AssignExpr) IPostTypeDo
Joins(fields ...field.RelationField) IPostTypeDo
Preload(fields ...field.RelationField) IPostTypeDo
FirstOrInit() (*models.PostType, error)
FirstOrCreate() (*models.PostType, error)
FindByPage(offset int, limit int) (result []*models.PostType, count int64, err error)
ScanByPage(result interface{}, offset int, limit int) (count int64, err error)
Scan(result interface{}) (err error)
Returning(value interface{}, columns ...string) IPostTypeDo
UnderlyingDB() *gorm.DB
schema.Tabler
}
func (p postTypeDo) Debug() IPostTypeDo {
return p.withDO(p.DO.Debug())
}
func (p postTypeDo) WithContext(ctx context.Context) IPostTypeDo {
return p.withDO(p.DO.WithContext(ctx))
}
func (p postTypeDo) ReadDB() IPostTypeDo {
return p.Clauses(dbresolver.Read)
}
func (p postTypeDo) WriteDB() IPostTypeDo {
return p.Clauses(dbresolver.Write)
}
func (p postTypeDo) Session(config *gorm.Session) IPostTypeDo {
return p.withDO(p.DO.Session(config))
}
func (p postTypeDo) Clauses(conds ...clause.Expression) IPostTypeDo {
return p.withDO(p.DO.Clauses(conds...))
}
func (p postTypeDo) Returning(value interface{}, columns ...string) IPostTypeDo {
return p.withDO(p.DO.Returning(value, columns...))
}
func (p postTypeDo) Not(conds ...gen.Condition) IPostTypeDo {
return p.withDO(p.DO.Not(conds...))
}
func (p postTypeDo) Or(conds ...gen.Condition) IPostTypeDo {
return p.withDO(p.DO.Or(conds...))
}
func (p postTypeDo) Select(conds ...field.Expr) IPostTypeDo {
return p.withDO(p.DO.Select(conds...))
}
func (p postTypeDo) Where(conds ...gen.Condition) IPostTypeDo {
return p.withDO(p.DO.Where(conds...))
}
func (p postTypeDo) Order(conds ...field.Expr) IPostTypeDo {
return p.withDO(p.DO.Order(conds...))
}
func (p postTypeDo) Distinct(cols ...field.Expr) IPostTypeDo {
return p.withDO(p.DO.Distinct(cols...))
}
func (p postTypeDo) Omit(cols ...field.Expr) IPostTypeDo {
return p.withDO(p.DO.Omit(cols...))
}
func (p postTypeDo) Join(table schema.Tabler, on ...field.Expr) IPostTypeDo {
return p.withDO(p.DO.Join(table, on...))
}
func (p postTypeDo) LeftJoin(table schema.Tabler, on ...field.Expr) IPostTypeDo {
return p.withDO(p.DO.LeftJoin(table, on...))
}
func (p postTypeDo) RightJoin(table schema.Tabler, on ...field.Expr) IPostTypeDo {
return p.withDO(p.DO.RightJoin(table, on...))
}
func (p postTypeDo) Group(cols ...field.Expr) IPostTypeDo {
return p.withDO(p.DO.Group(cols...))
}
func (p postTypeDo) Having(conds ...gen.Condition) IPostTypeDo {
return p.withDO(p.DO.Having(conds...))
}
func (p postTypeDo) Limit(limit int) IPostTypeDo {
return p.withDO(p.DO.Limit(limit))
}
func (p postTypeDo) Offset(offset int) IPostTypeDo {
return p.withDO(p.DO.Offset(offset))
}
func (p postTypeDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IPostTypeDo {
return p.withDO(p.DO.Scopes(funcs...))
}
func (p postTypeDo) Unscoped() IPostTypeDo {
return p.withDO(p.DO.Unscoped())
}
func (p postTypeDo) Create(values ...*models.PostType) error {
if len(values) == 0 {
return nil
}
return p.DO.Create(values)
}
func (p postTypeDo) CreateInBatches(values []*models.PostType, batchSize int) error {
return p.DO.CreateInBatches(values, batchSize)
}
// Save : !!! underlying implementation is different with GORM
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
func (p postTypeDo) Save(values ...*models.PostType) error {
if len(values) == 0 {
return nil
}
return p.DO.Save(values)
}
func (p postTypeDo) First() (*models.PostType, error) {
if result, err := p.DO.First(); err != nil {
return nil, err
} else {
return result.(*models.PostType), nil
}
}
func (p postTypeDo) Take() (*models.PostType, error) {
if result, err := p.DO.Take(); err != nil {
return nil, err
} else {
return result.(*models.PostType), nil
}
}
func (p postTypeDo) Last() (*models.PostType, error) {
if result, err := p.DO.Last(); err != nil {
return nil, err
} else {
return result.(*models.PostType), nil
}
}
func (p postTypeDo) Find() ([]*models.PostType, error) {
result, err := p.DO.Find()
return result.([]*models.PostType), err
}
func (p postTypeDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*models.PostType, err error) {
buf := make([]*models.PostType, 0, batchSize)
err = p.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
defer func() { results = append(results, buf...) }()
return fc(tx, batch)
})
return results, err
}
func (p postTypeDo) FindInBatches(result *[]*models.PostType, batchSize int, fc func(tx gen.Dao, batch int) error) error {
return p.DO.FindInBatches(result, batchSize, fc)
}
func (p postTypeDo) Attrs(attrs ...field.AssignExpr) IPostTypeDo {
return p.withDO(p.DO.Attrs(attrs...))
}
func (p postTypeDo) Assign(attrs ...field.AssignExpr) IPostTypeDo {
return p.withDO(p.DO.Assign(attrs...))
}
func (p postTypeDo) Joins(fields ...field.RelationField) IPostTypeDo {
for _, _f := range fields {
p = *p.withDO(p.DO.Joins(_f))
}
return &p
}
func (p postTypeDo) Preload(fields ...field.RelationField) IPostTypeDo {
for _, _f := range fields {
p = *p.withDO(p.DO.Preload(_f))
}
return &p
}
func (p postTypeDo) FirstOrInit() (*models.PostType, error) {
if result, err := p.DO.FirstOrInit(); err != nil {
return nil, err
} else {
return result.(*models.PostType), nil
}
}
func (p postTypeDo) FirstOrCreate() (*models.PostType, error) {
if result, err := p.DO.FirstOrCreate(); err != nil {
return nil, err
} else {
return result.(*models.PostType), nil
}
}
func (p postTypeDo) FindByPage(offset int, limit int) (result []*models.PostType, count int64, err error) {
result, err = p.Offset(offset).Limit(limit).Find()
if err != nil {
return
}
if size := len(result); 0 < limit && 0 < size && size < limit {
count = int64(size + offset)
return
}
count, err = p.Offset(-1).Limit(-1).Count()
return
}
func (p postTypeDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
count, err = p.Count()
if err != nil {
return
}
err = p.Offset(offset).Limit(limit).Scan(result)
return
}
func (p postTypeDo) Scan(result interface{}) (err error) {
return p.DO.Scan(result)
}
func (p postTypeDo) Delete(models ...*models.PostType) (result gen.ResultInfo, err error) {
return p.DO.Delete(models)
}
func (p *postTypeDo) withDO(do gen.Dao) *postTypeDo {
p.DO = *do.(*gen.DO)
return p
}

View File

@@ -0,0 +1,145 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package dal
import (
"app/internal/models"
"context"
"fmt"
"testing"
"gorm.io/gen"
"gorm.io/gen/field"
"gorm.io/gorm/clause"
)
func init() {
InitializeDB()
err := _gen_test_db.AutoMigrate(&models.PostType{})
if err != nil {
fmt.Printf("Error: AutoMigrate(&models.PostType{}) fail: %s", err)
}
}
func Test_postTypeQuery(t *testing.T) {
postType := newPostType(_gen_test_db)
postType = *postType.As(postType.TableName())
_do := postType.WithContext(context.Background()).Debug()
primaryKey := field.NewString(postType.TableName(), clause.PrimaryKey)
_, err := _do.Unscoped().Where(primaryKey.IsNotNull()).Delete()
if err != nil {
t.Error("clean table <post_types> fail:", err)
return
}
_, ok := postType.GetFieldByName("")
if ok {
t.Error("GetFieldByName(\"\") from postType success")
}
err = _do.Create(&models.PostType{})
if err != nil {
t.Error("create item in table <post_types> fail:", err)
}
err = _do.Save(&models.PostType{})
if err != nil {
t.Error("create item in table <post_types> fail:", err)
}
err = _do.CreateInBatches([]*models.PostType{{}, {}}, 10)
if err != nil {
t.Error("create item in table <post_types> fail:", err)
}
_, err = _do.Select(postType.ALL).Take()
if err != nil {
t.Error("Take() on table <post_types> fail:", err)
}
_, err = _do.First()
if err != nil {
t.Error("First() on table <post_types> fail:", err)
}
_, err = _do.Last()
if err != nil {
t.Error("First() on table <post_types> fail:", err)
}
_, err = _do.Where(primaryKey.IsNotNull()).FindInBatch(10, func(tx gen.Dao, batch int) error { return nil })
if err != nil {
t.Error("FindInBatch() on table <post_types> fail:", err)
}
err = _do.Where(primaryKey.IsNotNull()).FindInBatches(&[]*models.PostType{}, 10, func(tx gen.Dao, batch int) error { return nil })
if err != nil {
t.Error("FindInBatches() on table <post_types> fail:", err)
}
_, err = _do.Select(postType.ALL).Where(primaryKey.IsNotNull()).Order(primaryKey.Desc()).Find()
if err != nil {
t.Error("Find() on table <post_types> fail:", err)
}
_, err = _do.Distinct(primaryKey).Take()
if err != nil {
t.Error("select Distinct() on table <post_types> fail:", err)
}
_, err = _do.Select(postType.ALL).Omit(primaryKey).Take()
if err != nil {
t.Error("Omit() on table <post_types> fail:", err)
}
_, err = _do.Group(primaryKey).Find()
if err != nil {
t.Error("Group() on table <post_types> fail:", err)
}
_, err = _do.Scopes(func(dao gen.Dao) gen.Dao { return dao.Where(primaryKey.IsNotNull()) }).Find()
if err != nil {
t.Error("Scopes() on table <post_types> fail:", err)
}
_, _, err = _do.FindByPage(0, 1)
if err != nil {
t.Error("FindByPage() on table <post_types> fail:", err)
}
_, err = _do.ScanByPage(&models.PostType{}, 0, 1)
if err != nil {
t.Error("ScanByPage() on table <post_types> fail:", err)
}
_, err = _do.Attrs(primaryKey).Assign(primaryKey).FirstOrInit()
if err != nil {
t.Error("FirstOrInit() on table <post_types> fail:", err)
}
_, err = _do.Attrs(primaryKey).Assign(primaryKey).FirstOrCreate()
if err != nil {
t.Error("FirstOrCreate() on table <post_types> fail:", err)
}
var _a _another
var _aPK = field.NewString(_a.TableName(), "id")
err = _do.Join(&_a, primaryKey.EqCol(_aPK)).Scan(map[string]interface{}{})
if err != nil {
t.Error("Join() on table <post_types> fail:", err)
}
err = _do.LeftJoin(&_a, primaryKey.EqCol(_aPK)).Scan(map[string]interface{}{})
if err != nil {
t.Error("LeftJoin() on table <post_types> fail:", err)
}
_, err = _do.Not().Or().Clauses().Take()
if err != nil {
t.Error("Not/Or/Clauses on table <post_types> fail:", err)
}
}

View File

@@ -5,9 +5,9 @@
package dal package dal
import ( import (
"app/internal/models"
"context" "context"
"git.gogacoder.ru/NTO/boilerplate/internal/models"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/clause" "gorm.io/gorm/clause"
"gorm.io/gorm/schema" "gorm.io/gorm/schema"
@@ -28,7 +28,83 @@ func newPost(db *gorm.DB, opts ...gen.DOOption) post {
_post.ALL = field.NewAsterisk(tableName) _post.ALL = field.NewAsterisk(tableName)
_post.Id = field.NewUint(tableName, "id") _post.Id = field.NewUint(tableName, "id")
_post.Text = field.NewString(tableName, "text") _post.Text = field.NewString(tableName, "text")
_post.CreatedAt = field.NewUint(tableName, "created_at") _post.Deadline = field.NewInt64(tableName, "deadline")
_post.CreatedAt = field.NewInt64(tableName, "created_at")
_post.AuthorId = field.NewUint(tableName, "author_id")
_post.PostTypeId = field.NewUint(tableName, "post_type_id")
_post.Author = postBelongsToAuthor{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Author", "models.Author"),
Posts: struct {
field.RelationField
Author struct {
field.RelationField
}
PostType struct {
field.RelationField
}
Comments struct {
field.RelationField
Author struct {
field.RelationField
}
Posts struct {
field.RelationField
}
}
}{
RelationField: field.NewRelation("Author.Posts", "models.Post"),
Author: struct {
field.RelationField
}{
RelationField: field.NewRelation("Author.Posts.Author", "models.Author"),
},
PostType: struct {
field.RelationField
}{
RelationField: field.NewRelation("Author.Posts.PostType", "models.PostType"),
},
Comments: struct {
field.RelationField
Author struct {
field.RelationField
}
Posts struct {
field.RelationField
}
}{
RelationField: field.NewRelation("Author.Posts.Comments", "models.Comment"),
Author: struct {
field.RelationField
}{
RelationField: field.NewRelation("Author.Posts.Comments.Author", "models.Author"),
},
Posts: struct {
field.RelationField
}{
RelationField: field.NewRelation("Author.Posts.Comments.Posts", "models.Post"),
},
},
},
Comments: struct {
field.RelationField
}{
RelationField: field.NewRelation("Author.Comments", "models.Comment"),
},
}
_post.PostType = postBelongsToPostType{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("PostType", "models.PostType"),
}
_post.Comments = postManyToManyComments{
db: db.Session(&gorm.Session{}),
RelationField: field.NewRelation("Comments", "models.Comment"),
}
_post.fillFieldMap() _post.fillFieldMap()
@@ -38,10 +114,18 @@ func newPost(db *gorm.DB, opts ...gen.DOOption) post {
type post struct { type post struct {
postDo postDo
ALL field.Asterisk ALL field.Asterisk
Id field.Uint Id field.Uint
Text field.String Text field.String
CreatedAt field.Uint Deadline field.Int64
CreatedAt field.Int64
AuthorId field.Uint
PostTypeId field.Uint
Author postBelongsToAuthor
PostType postBelongsToPostType
Comments postManyToManyComments
fieldMap map[string]field.Expr fieldMap map[string]field.Expr
} }
@@ -60,7 +144,10 @@ func (p *post) updateTableName(table string) *post {
p.ALL = field.NewAsterisk(table) p.ALL = field.NewAsterisk(table)
p.Id = field.NewUint(table, "id") p.Id = field.NewUint(table, "id")
p.Text = field.NewString(table, "text") p.Text = field.NewString(table, "text")
p.CreatedAt = field.NewUint(table, "created_at") p.Deadline = field.NewInt64(table, "deadline")
p.CreatedAt = field.NewInt64(table, "created_at")
p.AuthorId = field.NewUint(table, "author_id")
p.PostTypeId = field.NewUint(table, "post_type_id")
p.fillFieldMap() p.fillFieldMap()
@@ -77,10 +164,14 @@ func (p *post) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
} }
func (p *post) fillFieldMap() { func (p *post) fillFieldMap() {
p.fieldMap = make(map[string]field.Expr, 3) p.fieldMap = make(map[string]field.Expr, 9)
p.fieldMap["id"] = p.Id p.fieldMap["id"] = p.Id
p.fieldMap["text"] = p.Text p.fieldMap["text"] = p.Text
p.fieldMap["deadline"] = p.Deadline
p.fieldMap["created_at"] = p.CreatedAt p.fieldMap["created_at"] = p.CreatedAt
p.fieldMap["author_id"] = p.AuthorId
p.fieldMap["post_type_id"] = p.PostTypeId
} }
func (p post) clone(db *gorm.DB) post { func (p post) clone(db *gorm.DB) post {
@@ -93,6 +184,241 @@ func (p post) replaceDB(db *gorm.DB) post {
return p return p
} }
type postBelongsToAuthor struct {
db *gorm.DB
field.RelationField
Posts struct {
field.RelationField
Author struct {
field.RelationField
}
PostType struct {
field.RelationField
}
Comments struct {
field.RelationField
Author struct {
field.RelationField
}
Posts struct {
field.RelationField
}
}
}
Comments struct {
field.RelationField
}
}
func (a postBelongsToAuthor) Where(conds ...field.Expr) *postBelongsToAuthor {
if len(conds) == 0 {
return &a
}
exprs := make([]clause.Expression, 0, len(conds))
for _, cond := range conds {
exprs = append(exprs, cond.BeCond().(clause.Expression))
}
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
return &a
}
func (a postBelongsToAuthor) WithContext(ctx context.Context) *postBelongsToAuthor {
a.db = a.db.WithContext(ctx)
return &a
}
func (a postBelongsToAuthor) Session(session *gorm.Session) *postBelongsToAuthor {
a.db = a.db.Session(session)
return &a
}
func (a postBelongsToAuthor) Model(m *models.Post) *postBelongsToAuthorTx {
return &postBelongsToAuthorTx{a.db.Model(m).Association(a.Name())}
}
type postBelongsToAuthorTx struct{ tx *gorm.Association }
func (a postBelongsToAuthorTx) Find() (result *models.Author, err error) {
return result, a.tx.Find(&result)
}
func (a postBelongsToAuthorTx) Append(values ...*models.Author) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a postBelongsToAuthorTx) Replace(values ...*models.Author) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a postBelongsToAuthorTx) Delete(values ...*models.Author) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a postBelongsToAuthorTx) Clear() error {
return a.tx.Clear()
}
func (a postBelongsToAuthorTx) Count() int64 {
return a.tx.Count()
}
type postBelongsToPostType struct {
db *gorm.DB
field.RelationField
}
func (a postBelongsToPostType) Where(conds ...field.Expr) *postBelongsToPostType {
if len(conds) == 0 {
return &a
}
exprs := make([]clause.Expression, 0, len(conds))
for _, cond := range conds {
exprs = append(exprs, cond.BeCond().(clause.Expression))
}
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
return &a
}
func (a postBelongsToPostType) WithContext(ctx context.Context) *postBelongsToPostType {
a.db = a.db.WithContext(ctx)
return &a
}
func (a postBelongsToPostType) Session(session *gorm.Session) *postBelongsToPostType {
a.db = a.db.Session(session)
return &a
}
func (a postBelongsToPostType) Model(m *models.Post) *postBelongsToPostTypeTx {
return &postBelongsToPostTypeTx{a.db.Model(m).Association(a.Name())}
}
type postBelongsToPostTypeTx struct{ tx *gorm.Association }
func (a postBelongsToPostTypeTx) Find() (result *models.PostType, err error) {
return result, a.tx.Find(&result)
}
func (a postBelongsToPostTypeTx) Append(values ...*models.PostType) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a postBelongsToPostTypeTx) Replace(values ...*models.PostType) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a postBelongsToPostTypeTx) Delete(values ...*models.PostType) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a postBelongsToPostTypeTx) Clear() error {
return a.tx.Clear()
}
func (a postBelongsToPostTypeTx) Count() int64 {
return a.tx.Count()
}
type postManyToManyComments struct {
db *gorm.DB
field.RelationField
}
func (a postManyToManyComments) Where(conds ...field.Expr) *postManyToManyComments {
if len(conds) == 0 {
return &a
}
exprs := make([]clause.Expression, 0, len(conds))
for _, cond := range conds {
exprs = append(exprs, cond.BeCond().(clause.Expression))
}
a.db = a.db.Clauses(clause.Where{Exprs: exprs})
return &a
}
func (a postManyToManyComments) WithContext(ctx context.Context) *postManyToManyComments {
a.db = a.db.WithContext(ctx)
return &a
}
func (a postManyToManyComments) Session(session *gorm.Session) *postManyToManyComments {
a.db = a.db.Session(session)
return &a
}
func (a postManyToManyComments) Model(m *models.Post) *postManyToManyCommentsTx {
return &postManyToManyCommentsTx{a.db.Model(m).Association(a.Name())}
}
type postManyToManyCommentsTx struct{ tx *gorm.Association }
func (a postManyToManyCommentsTx) Find() (result []*models.Comment, err error) {
return result, a.tx.Find(&result)
}
func (a postManyToManyCommentsTx) Append(values ...*models.Comment) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Append(targetValues...)
}
func (a postManyToManyCommentsTx) Replace(values ...*models.Comment) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Replace(targetValues...)
}
func (a postManyToManyCommentsTx) Delete(values ...*models.Comment) (err error) {
targetValues := make([]interface{}, len(values))
for i, v := range values {
targetValues[i] = v
}
return a.tx.Delete(targetValues...)
}
func (a postManyToManyCommentsTx) Clear() error {
return a.tx.Clear()
}
func (a postManyToManyCommentsTx) Count() int64 {
return a.tx.Count()
}
type postDo struct{ gen.DO } type postDo struct{ gen.DO }
type IPostDo interface { type IPostDo interface {

View File

@@ -5,11 +5,11 @@
package dal package dal
import ( import (
"app/internal/models"
"context" "context"
"fmt" "fmt"
"testing" "testing"
"git.gogacoder.ru/NTO/boilerplate/internal/models"
"gorm.io/gen" "gorm.io/gen"
"gorm.io/gen/field" "gorm.io/gen/field"
"gorm.io/gorm/clause" "gorm.io/gorm/clause"

View File

@@ -1,20 +1,13 @@
package database package database
import ( import (
"context" "app/internal/dal"
"database/sql"
"git.gogacoder.ru/NTO/boilerplate/internal/dal"
"github.com/ncruces/go-sqlite3"
"github.com/ncruces/go-sqlite3/driver"
"log" "log"
"log/slog"
"os" "os"
"sync" "sync"
"time" "time"
_ "github.com/ncruces/go-sqlite3/embed" "gorm.io/driver/sqlite"
"github.com/ncruces/go-sqlite3/ext/unicode"
"github.com/ncruces/go-sqlite3/gormlite"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/logger" "gorm.io/gorm/logger"
) )
@@ -38,14 +31,13 @@ func initialize() error {
Colorful: true, // Disable color Colorful: true, // Disable color
}, },
) )
db, err = gorm.Open(gormlite.Open("file:"+Path+"?_fk=1"), &gorm.Config{ db, err = gorm.Open(sqlite.Open("file:"+Path+"?_fk=1"), &gorm.Config{
Logger: newLogger, Logger: newLogger,
FullSaveAssociations: false, FullSaveAssociations: false,
}) })
if err != nil { if err != nil {
return err return err
} }
RegisterUnicodeExtension(db)
if res := db.Exec(`PRAGMA foreign_keys = ON`); res.Error != nil { if res := db.Exec(`PRAGMA foreign_keys = ON`); res.Error != nil {
return res.Error return res.Error
} }
@@ -93,59 +85,3 @@ func GetInstance() *gorm.DB {
}) })
return db return db
} }
func RegisterUnicodeExtension(db *gorm.DB) {
sqlDB, err := db.DB()
if err != nil {
panic(err)
}
ctx := context.Background()
conn, err := sqlDB.Conn(ctx)
if err != nil {
panic(err)
}
defer func(conn *sql.Conn) {
err := conn.Close()
if err != nil {
slog.Error(err.Error())
}
}(conn)
err = conn.Raw(func(driverConn any) error {
c := driverConn.(driver.Conn)
sqliteConn := c.Raw()
if err := unicode.Register(sqliteConn); err != nil {
return err
}
if err := sqliteConn.Exec(`SELECT icu_load_collation('ru-RU', 'russian')`); err != nil {
return err
}
if err := sqliteConn.Exec(`SELECT icu_load_collation('en-US', 'english')`); err != nil {
return err
}
stmt, _, err := sqliteConn.Prepare(`SELECT 'ы' LIKE 'Ы'`)
if err != nil {
return err
}
defer func(stmt *sqlite3.Stmt) {
err := stmt.Close()
if err != nil {
slog.Error(err.Error())
}
}(stmt)
if stmt.Step() {
slog.Info("ICU test result", "value", stmt.ColumnBool(0))
}
return stmt.Err()
})
if err != nil {
panic(err)
}
}

View File

@@ -1,7 +1,7 @@
package main package main
import ( import (
"git.gogacoder.ru/NTO/boilerplate/internal/models" "app/internal/models"
"gorm.io/gen" "gorm.io/gen"
) )

View File

@@ -1,9 +1,37 @@
package models package models
var Entities = []any{&Post{}} var Entities = []any{
&Post{}, &Author{}, &PostType{}, &Comment{},
}
type PostType struct {
Id uint `gorm:"primaryKey" ui:"hidden"`
Name string `ui:"label:Название;"`
}
type Post struct { type Post struct {
Id uint `gorm:"primaryKey" ui:"hidden"` Id uint `gorm:"primaryKey" ui:"hidden;label:\"Номер поста\""`
Text string `ui:"label:Текст;"` Text string `ui:"label:Текст"`
CreatedAt uint `gorm:"autoCreateTime" ui:"readonly"` Deadline int64 `ui:"label:Дедлайн;datatype:datetime;"`
CreatedAt int64 `gorm:"autoCreateTime" ui:"label:Время создания;readonly;datatype:datetime;"`
AuthorId uint `ui:"hidden" gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
Author Author `ui:"label:Автор; field:Name;"`
PostTypeId uint `ui:"hidden; excel:Номер типа поста;"`
PostType PostType `ui:"label:Тип поста; field:Name;" gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
Comments []Comment `ui:"label:Комментарии; field:Text;" gorm:"many2many:comments_post;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
}
type Author struct {
Id uint `gorm:"primaryKey" ui:"hidden"`
Name string `ui:"label:Имя;"`
Posts []Post `ui:"label:Посты; field:Text;" gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
Comments []Comment `ui:"label:Комментарии; field:Text;" gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
}
type Comment struct {
Id uint `gorm:"primaryKey" ui:"hidden"`
Text string `ui:"label:Текст;"`
AuthorId uint `ui:"hidden"`
Author Author `ui:"label:Автор; field:Name;" gorm:"foreignKey:AuthorId;references:Id;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
Posts []Post `ui:"label:Посты; field:Text;" gorm:"many2many:comments_post;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
} }

View File

@@ -0,0 +1,66 @@
package services
import (
"app/internal/dal"
"app/internal/database"
"app/internal/models"
"app/internal/utils"
"errors"
"gorm.io/gen/field"
"gorm.io/gorm"
)
type AuthorService struct {
}
type Author = models.Author
func (service *AuthorService) Create(item Author) (Author, error) {
utils.ReplaceEmptySlicesWithNil(&item)
err := dal.Author.Create(&item)
if err != nil {
return item, err
}
err = utils.AppendAssociations(database.GetInstance(), &item)
return item, err
}
func (service *AuthorService) GetAll() ([]*Author, error) {
var authors []*Author
authors, err := dal.Author.Preload(field.Associations).Find()
return authors, err
}
func (service *AuthorService) GetById(id uint) (*Author, error) {
item, err := dal.Author.Preload(field.Associations).Where(dal.Author.Id.Eq(id)).First()
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
} else {
return nil, err
}
}
return item, nil
}
func (service *AuthorService) Update(item Author) (Author, error) {
utils.ReplaceEmptySlicesWithNil(&item)
_, err := dal.Author.Updates(&item)
if err != nil {
return item, err
}
err = utils.UpdateAssociations(database.GetInstance(), &item)
if err != nil {
return item, err
}
return item, err
}
func (service *AuthorService) Delete(id uint) error {
_, err := dal.Author.Unscoped().Where(dal.Author.Id.Eq(id)).Delete()
return err
}
func (service *AuthorService) Count() (int64, error) {
amount, err := dal.Author.Count()
return amount, err
}
func (service *AuthorService) SortedByOrder(fieldsSortOrder map[string]string) ([]*Author, error) {
return utils.SortByOrder(fieldsSortOrder, Author{})
}

View File

@@ -0,0 +1,66 @@
package services
import (
"app/internal/dal"
"app/internal/database"
"app/internal/models"
"app/internal/utils"
"errors"
"gorm.io/gen/field"
"gorm.io/gorm"
)
type CommentService struct {
}
type Comment = models.Comment
func (service *CommentService) Create(item Comment) (Comment, error) {
utils.ReplaceEmptySlicesWithNil(&item)
err := dal.Comment.Create(&item)
if err != nil {
return item, err
}
err = utils.AppendAssociations(database.GetInstance(), &item)
return item, err
}
func (service *CommentService) GetAll() ([]*Comment, error) {
var comments []*Comment
comments, err := dal.Comment.Preload(field.Associations).Find()
return comments, err
}
func (service *CommentService) GetById(id uint) (*Comment, error) {
item, err := dal.Comment.Preload(field.Associations).Where(dal.Comment.Id.Eq(id)).First()
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
} else {
return nil, err
}
}
return item, nil
}
func (service *CommentService) Update(item Comment) (Comment, error) {
utils.ReplaceEmptySlicesWithNil(&item)
_, err := dal.Author.Updates(&item)
if err != nil {
return item, err
}
err = utils.UpdateAssociations(database.GetInstance(), &item)
if err != nil {
return item, err
}
return item, err
}
func (service *CommentService) Delete(id uint) error {
_, err := dal.Comment.Unscoped().Where(dal.Comment.Id.Eq(id)).Delete()
return err
}
func (service *CommentService) Count() (int64, error) {
amount, err := dal.Comment.Count()
return amount, err
}
func (service *CommentService) SortedByOrder(fieldsSortOrder map[string]string) ([]*Comment, error) {
return utils.SortByOrder(fieldsSortOrder, Comment{})
}

View File

@@ -1,31 +1,16 @@
package services package services
import ( import (
"app/internal/dialogs"
"fmt" "fmt"
"git.gogacoder.ru/NTO/boilerplate/internal/dialogs" "time"
"git.gogacoder.ru/NTO/boilerplate/internal/models"
) )
func InsertDefaultData() { func InsertDefaultData() {
insertPostTypes()
insertAuthors()
insertPosts() insertPosts()
} insertComments()
// Example of usage
//func insertProductTypes() {
// InsertDefaultEntityData(&ProductTypeService{}, []ProductType{
// {Id: 1, Name: "Сырые пиломатериалы"},
// {Id: 2, Name: "Сухие пиломатериалы"},
// {Id: 3, Name: "Строганные доски"},
// {Id: 4, Name: "Рейки"},
// {Id: 5, Name: "Брус"},
// {Id: 6, Name: "Пеллеты"},
// })
//}
func insertPosts() {
InsertDefaultEntityData(&PostService{}, []models.Post{
{Text: "В Кузбассе начали строить дома выше, чтобы их жители стали ближе к Богу."},
})
} }
func InsertDefaultEntityData[T any](service Service[T], entities []T) { func InsertDefaultEntityData[T any](service Service[T], entities []T) {
@@ -36,3 +21,86 @@ func InsertDefaultEntityData[T any](service Service[T], entities []T) {
} }
} }
} }
func insertPosts() {
InsertDefaultEntityData(&PostService{}, []Post{
{
Id: 1,
Text: "Жителям Кузбасса запретили болеть.",
Deadline: time.Now().Unix(),
AuthorId: 1,
PostTypeId: 1,
},
{
Id: 2,
Deadline: time.Now().Add(time.Hour * 24 * 5).Unix(),
Text: "⚡️⚡️⚡️Дома будут летать.",
AuthorId: 2,
PostTypeId: 2,
},
{
Id: 3,
Deadline: time.Now().Add(time.Hour * 24 * 6).Unix(),
Text: "В Кузбассе начали строить дома выше, чтобы жители были ближе к богу и солнцу.",
AuthorId: 3,
PostTypeId: 3,
},
})
}
func insertAuthors() {
InsertDefaultEntityData(&AuthorService{}, []Author{
{
Id: 1,
Name: "ИА Кузбасс",
},
{
Id: 2,
Name: "ASTRA",
},
{
Id: 3,
Name: "ЧТД",
},
})
}
func insertComments() {
InsertDefaultEntityData(&CommentService{}, []Comment{
{
Id: 1,
Text: "Это просто замечательно!",
AuthorId: 1,
Posts: []Post{{Id: 1}},
},
{
Id: 2,
Text: "Я тоже думаю, что это замечательно!",
AuthorId: 2,
Posts: []Post{{Id: 2}},
},
{
Id: 3,
Text: "Я тоже думаю, что это замечательно!",
AuthorId: 3,
Posts: []Post{{Id: 3}},
},
})
}
func insertPostTypes() {
InsertDefaultEntityData(&PostTypeService{}, []PostType{
{
Id: 1,
Name: "Общество",
},
{
Id: 2,
Name: "Политика",
},
{
Id: 3,
Name: "Экономика",
},
})
}

View File

@@ -1,7 +1,10 @@
package services package services
import ( import (
"app/internal/addons/excel"
"app/internal/dialogs"
"github.com/wailsapp/wails/v3/pkg/application" "github.com/wailsapp/wails/v3/pkg/application"
"strconv"
) )
type ExcelModule struct{} type ExcelModule struct{}
@@ -9,47 +12,45 @@ type ExcelModule struct{}
var ExcelService = application.NewService(&ExcelModule{}) var ExcelService = application.NewService(&ExcelModule{})
func (s *ExcelModule) ImportAllEntities() error { func (s *ExcelModule) ImportAllEntities() error {
// Example of usage: postTypeService := PostTypeService{}
//postTypeService := PostTypeService{} filepath, err := dialogs.OpenFileDialog("Импорт данных")
//filepath, err := dialogs.OpenFileDialog("Импорт данных") if err != nil {
//if err != nil { return err
// return err }
//} err = excel.ImportEntitiesFromSpreadsheet(filepath, excel.Importer{
//err = excel.ImportEntitiesFromSpreadsheet(filepath, excel.Importer{ SheetName: "Тип поста",
// SheetName: "Тип поста", Loader: func(rowIndex int, row []string) error {
// Loader: func(rowIndex int, row []string) error { id, err := strconv.Atoi(row[0])
// id, err := strconv.Atoi(row[0]) if err != nil {
// if err != nil { return err
// return err }
// }
// _, err = postTypeService.Create(PostType{
// _, err = postTypeService.Create(PostType{ Id: uint(id),
// Id: uint(id), Name: row[1],
// Name: row[1], })
// }) if err != nil {
// if err != nil { return err
// return err }
// } return nil
// return nil },
// }, })
//}) if err != nil {
//if err != nil { return err
// return err }
//}
return nil return nil
} }
func (s *ExcelModule) ExportAllEntities() error { func (s *ExcelModule) ExportAllEntities() error {
// Example of usage: postService := PostService{}
//postService := PostService{} exporter := excel.Exporter[Post]{
//exporter := excel.Exporter[Post]{ SheetName: "Посты",
// SheetName: "Посты", Entity: Post{},
// Entity: Post{}, Provider: postService.GetAll,
// Provider: postService.GetAll, }
//} err := excel.ExportEntitiesToSpreadsheet("report.xlsx", exporter)
//err := excel.ExportEntitiesToSpreadsheet("report.xlsx", exporter) if err != nil {
//if err != nil { return err
// return err }
//}
return nil return nil
} }

View File

@@ -1,11 +1,11 @@
package services package services
import ( import (
"app/internal/database"
"app/internal/models"
"context" "context"
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"git.gogacoder.ru/NTO/boilerplate/internal/database"
"git.gogacoder.ru/NTO/boilerplate/internal/models"
"io" "io"
"log/slog" "log/slog"
"os" "os"

View File

@@ -1,22 +1,21 @@
package services package services
import ( import (
"app/internal/dal"
"app/internal/database"
"app/internal/models"
"app/internal/utils"
"errors" "errors"
"git.gogacoder.ru/NTO/boilerplate/internal/dal"
"git.gogacoder.ru/NTO/boilerplate/internal/database"
"git.gogacoder.ru/NTO/boilerplate/internal/models"
"git.gogacoder.ru/NTO/boilerplate/internal/utils"
"gorm.io/gen/field" "gorm.io/gen/field"
"gorm.io/gorm" "gorm.io/gorm"
) )
type PostService struct { type PostService struct{}
}
type Post = models.Post type Post = models.Post
func (service *PostService) Create(item Post) (Post, error) { func (service *PostService) Create(item Post) (Post, error) {
utils.ReplaceEmptySlicesWithNil(&item) utils.ReplaceEmptySlicesWithNil(&item)
err := dal.Post.Create(&item) err := dal.Post.Preload(field.Associations).Create(&item)
if err != nil { if err != nil {
return item, err return item, err
} }
@@ -39,20 +38,20 @@ func (service *PostService) GetById(id uint) (*Post, error) {
} }
return item, nil return item, nil
} }
func (service *PostService) Update(item Post) (Post, error) { func (service *PostService) Update(item Post) (Post, error) {
utils.ReplaceEmptySlicesWithNil(&item) utils.ReplaceEmptySlicesWithNil(&item)
_, err := dal.Post.Updates(&item) err := dal.Post.Preload(field.Associations).Save(&item)
if err != nil { if err != nil {
return item, err return item, err
} }
err = utils.UpdateAssociations(database.GetInstance(), &item) err = utils.UpdateAssociations(database.GetInstance(), &item)
if err != nil { if err != nil {
return item, err return item, err
} }
return item, err return item, err
} }
func (service *PostService) Delete(id uint) error { func (service *PostService) Delete(id uint) error {
_, err := dal.Post.Unscoped().Where(dal.Post.Id.Eq(id)).Delete() _, err := dal.Post.Unscoped().Where(dal.Post.Id.Eq(id)).Delete()
return err return err
@@ -61,9 +60,7 @@ func (service *PostService) Count() (int64, error) {
amount, err := dal.Post.Count() amount, err := dal.Post.Count()
return amount, err return amount, err
} }
func (service *PostService) SortedByOrder(fieldsSortingOrder []utils.SortField) ([]*Post, error) {
return utils.SortByOrder(fieldsSortingOrder, Post{}) func (service *PostService) SortedByOrder(fieldsSortOrder map[string]string) ([]*Post, error) {
} return utils.SortByOrder(fieldsSortOrder, Post{})
func (service *PostService) SearchByAllTextFields(phrase string) ([]*Post, error) {
return utils.FindPhraseByStringFields[Post](phrase, Post{})
} }

View File

@@ -0,0 +1,65 @@
package services
import (
"app/internal/dal"
"app/internal/database"
"app/internal/models"
"app/internal/utils"
"errors"
"gorm.io/gen/field"
"gorm.io/gorm"
)
type PostTypeService struct {
}
type PostType = models.PostType
func (service *PostTypeService) Create(item PostType) (PostType, error) {
utils.ReplaceEmptySlicesWithNil(&item)
err := dal.PostType.Preload(field.Associations).Create(&item)
if err != nil {
return item, err
}
err = utils.AppendAssociations(database.GetInstance(), &item)
return item, err
}
func (service *PostTypeService) GetAll() ([]*PostType, error) {
var posttypes []*PostType
posttypes, err := dal.PostType.Preload(field.Associations).Find()
return posttypes, err
}
func (service *PostTypeService) GetById(id uint) (*PostType, error) {
item, err := dal.PostType.Preload(field.Associations).Where(dal.PostType.Id.Eq(id)).First()
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
} else {
return nil, err
}
}
return item, nil
}
func (service *PostTypeService) Update(item PostType) (PostType, error) {
utils.ReplaceEmptySlicesWithNil(&item)
err := dal.PostType.Preload(field.Associations).Save(&item)
if err != nil {
return item, err
}
err = utils.UpdateAssociations(database.GetInstance(), &item)
if err != nil {
return item, err
}
return item, err
}
func (service *PostTypeService) Delete(id uint) error {
_, err := dal.PostType.Unscoped().Where(dal.PostType.Id.Eq(id)).Delete()
return err
}
func (service *PostTypeService) Count() (int64, error) {
amount, err := dal.PostType.Count()
return amount, err
}
func (service *PostTypeService) SortedByOrder(fieldsSortOrder map[string]string) ([]*PostType, error) {
return utils.SortByOrder(fieldsSortOrder, PostType{})
}

View File

@@ -1,9 +1,10 @@
package services package services
import ( import "github.com/wailsapp/wails/v3/pkg/application"
"github.com/wailsapp/wails/v3/pkg/application"
)
var ExportedServices = []application.Service{ var ExportedServices = []application.Service{
application.NewService(&Post{}), application.NewService(&PostService{}),
application.NewService(&AuthorService{}),
application.NewService(&CommentService{}),
application.NewService(&PostTypeService{}),
} }

View File

@@ -1,29 +0,0 @@
package utils
import (
"fmt"
"git.gogacoder.ru/NTO/boilerplate/internal/database"
"gorm.io/gorm/clause"
"reflect"
)
func FindPhraseByStringFields[T any](phrase string, entity T) ([]*T, error) {
db := database.GetInstance().Preload(clause.Associations)
structType := reflect.TypeOf(entity)
for i := 0; i < structType.NumField(); i++ {
field := structType.Field(i)
if field.Type.Kind() == reflect.Pointer {
field.Type = field.Type.Elem()
}
if field.Type.Kind() == reflect.String {
db.Where(fmt.Sprintf("`%s` like ?", field.Name), "%"+phrase+"%")
}
}
var items []*T
db.Find(&items)
return items, nil
}

View File

@@ -1,114 +1,70 @@
package utils package utils
import ( import (
"app/internal/database"
"errors" "errors"
"fmt" "fmt"
"git.gogacoder.ru/NTO/boilerplate/internal/database"
"github.com/kuzgoga/fogg" "github.com/kuzgoga/fogg"
"golang.org/x/text/language" "golang.org/x/text/language"
"golang.org/x/text/message" "golang.org/x/text/message"
"gorm.io/gorm/clause" "gorm.io/gorm/clause"
"reflect" "reflect"
"slices"
"strings" "strings"
) )
import "gorm.io/gorm"
type SortField struct {
Name string
Order string
}
func GetTableName(model any, db *gorm.DB) (string, error) {
stmt := &gorm.Statement{DB: db}
if err := stmt.Parse(model); err != nil {
return "", err
}
return stmt.Schema.Table, nil
}
func GetColumnName(model any, fieldName string, db *gorm.DB) (string, error) {
stmt := &gorm.Statement{DB: db}
if err := stmt.Parse(model); err != nil {
return "", err
}
field := stmt.Schema.LookUpField(fieldName)
if field == nil {
return "", fmt.Errorf("field '%s' not found", fieldName)
}
return field.DBName, nil
}
var p = message.NewPrinter(language.Russian) var p = message.NewPrinter(language.Russian)
func SortByOrder[T any](fieldsSortingOrder []SortField, entity T) ([]*T, error) { func SortByOrder[T any](fieldsSortOrder map[string]string, entity T) ([]*T, error) {
var ( var (
orderQuery []string orderQuery []string
items []*T items []*T
joins []string joins []string
) )
for _, item := range fieldsSortingOrder { for name, order := range fieldsSortOrder {
structInfo := reflect.ValueOf(entity).Type() structInfo := reflect.ValueOf(entity).Type()
field, fieldExist := structInfo.FieldByName(item.Name) field, fieldExist := structInfo.FieldByName(name)
if !fieldExist { if !fieldExist {
return nil, errors.New(p.Sprintf("Field %s not found", item.Name)) return nil, errors.New(p.Sprintf("Field %s not found", name))
} }
if strings.ToUpper(item.Order) != "ASC" && strings.ToUpper(item.Order) != "DESC" { if strings.ToUpper(order) != "ASC" && strings.ToUpper(order) != "DESC" {
return nil, errors.New(p.Sprintf("Field `%s` can only be sorted by ASC or DESC", item.Name)) return nil, errors.New(p.Sprintf("Field `%s` can only be sorted by ASC or DESC", name))
} }
tag, err := fogg.Parse(string(field.Tag)) tag, err := fogg.Parse(string(field.Tag))
if err != nil { if err != nil {
return nil, errors.New(p.Sprintf("Failed to parse tag for `%s` failed: %s", item.Name, err)) return nil, errors.New(p.Sprintf("Failed to parse tag for `%s` failed: %s", name, err))
} }
if !tag.HasTag("ui") { if !tag.HasTag("ui") {
return nil, errors.New(p.Sprintf("Field `%s` doesn't have ui tag", item.Name)) return nil, errors.New(p.Sprintf("Field `%s` doesn't have ui tag", name))
} }
if field.Type.Kind() == reflect.Slice { if field.Type.Kind() == reflect.Slice {
return nil, errors.New(p.Sprintf("Field `%s` is array and cannot be used for sorting", item.Name)) return nil, errors.New(p.Sprintf("Field `%s` is array and cannot be used for sorting", name))
} }
fieldPath := tag.GetTag("ui").GetParamOr("field", "") fieldPath := tag.GetTag("ui").GetParamOr("field", "")
if fieldPath == "" { if fieldPath == "" {
tableName, err := GetTableName(entity, database.GetInstance()) orderQuery = append(orderQuery, fmt.Sprintf("%s %s", name, order))
if err != nil {
return nil, errors.New(p.Sprintf("Failed to get table name: %s", err))
}
columnName, err := GetColumnName(entity, field.Name, database.GetInstance())
if err != nil {
return nil, errors.New(p.Sprintf("Failed to get column name: %s", err))
}
orderQuery = append(orderQuery, fmt.Sprintf("`%s`.`%s` %s", tableName, columnName, item.Order))
} else { } else {
fieldsPathParts := strings.Split(fieldPath, ".") fieldsPathParts := strings.Split(fieldPath, ".")
if len(fieldsPathParts) > 1 { if len(fieldsPathParts) > 1 {
return nil, errors.New(p.Sprintf("Too complex fieldPath for structure `%s`", item.Name)) return nil, errors.New(p.Sprintf("Too complex fieldPath for structure `%s`", name))
} }
if len(fieldsPathParts) == 0 { if len(fieldsPathParts) == 0 {
return nil, errors.New(p.Sprintf("Invalid field path for `%s` field", item.Name)) return nil, errors.New(p.Sprintf("Invalid field path for `%s` field", name))
} }
joinPathParts := append([]string{field.Type.Name()}, fieldsPathParts...) joinPathParts := append([]string{field.Type.Name()}, fieldsPathParts...)
for i, part := range joinPathParts {
joinPathParts[i] = "`" + part + "`"
}
joinField := strings.Join(joinPathParts, ".") joinField := strings.Join(joinPathParts, ".")
joinTable := field.Type.Name() joinTable := field.Type.Name()
if !slices.Contains(joins, joinTable) { joins = append(joins, joinTable)
joins = append(joins, joinTable) orderQuery = append(orderQuery, fmt.Sprintf("%s %s", joinField, order))
}
orderQuery = append(orderQuery, fmt.Sprintf("%s %s", joinField, item.Order))
} }
} }

View File

@@ -1,8 +1,8 @@
package main package main
import ( import (
"app/internal/services"
"embed" "embed"
"git.gogacoder.ru/NTO/boilerplate/internal/services"
"github.com/wailsapp/wails/v3/pkg/application" "github.com/wailsapp/wails/v3/pkg/application"
"log" "log"
) )

18
package-lock.json generated Normal file
View File

@@ -0,0 +1,18 @@
{
"name": "boilerplate",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"dependencies": {
"@wailsio/runtime": "3.0.0-alpha.66"
}
},
"node_modules/@wailsio/runtime": {
"version": "3.0.0-alpha.66",
"resolved": "https://registry.npmjs.org/@wailsio/runtime/-/runtime-3.0.0-alpha.66.tgz",
"integrity": "sha512-ENLu8rn1griL1gFHJqkq1i+BVxrrA0JPJHYneUJYuf/s54kjuQViW0RKDEe/WTDo56ABpfykrd/T8OYpPUyXUw==",
"license": "MIT"
}
}
}

5
package.json Normal file
View File

@@ -0,0 +1,5 @@
{
"dependencies": {
"@wailsio/runtime": "3.0.0-alpha.66"
}
}

22
pnpm-lock.yaml generated Normal file
View File

@@ -0,0 +1,22 @@
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
dependencies:
'@wailsio/runtime':
specifier: 3.0.0-alpha.66
version: 3.0.0-alpha.66
packages:
'@wailsio/runtime@3.0.0-alpha.66':
resolution: {integrity: sha512-ENLu8rn1griL1gFHJqkq1i+BVxrrA0JPJHYneUJYuf/s54kjuQViW0RKDEe/WTDo56ABpfykrd/T8OYpPUyXUw==}
snapshots:
'@wailsio/runtime@3.0.0-alpha.66': {}