Compare commits
No commits in common. "main" and "1.1.10" have entirely different histories.
1138 changed files with 72154 additions and 127568 deletions
309
.github/instructions/ai.instructions.md
vendored
309
.github/instructions/ai.instructions.md
vendored
|
|
@ -14,25 +14,14 @@ Primary principle:
|
||||||
|
|
||||||
- Configuration first, code last.
|
- Configuration first, code last.
|
||||||
|
|
||||||
Companion documents (this file wins on conflict):
|
|
||||||
|
|
||||||
| Document | Scope |
|
|
||||||
| --- | --- |
|
|
||||||
| `lowcode.instructions.md` | **Authoring reference**: exact schemas, enum values and worked examples for producing wizard screens, editor scripts, custom components, SQL objects and endpoints. Read it before writing any artifact. |
|
|
||||||
| `lowcode-reference.instructions.md` | **Capability inventory**: field-by-field contract of every ListForm/ListFormField JSON column, chart and pivot options, SQL command hooks, Visual Designer toolbox, the scheduled-mail module and (§12) every ready-made screen under the Saas and Administration menus. Sweep its §0 checklist before producing a screen or component; a request whose answer already exists as a platform screen (§12.5) is configured there, not rebuilt. |
|
|
||||||
| `dotnet.instructions.md` | Binding code standard for everything under `api/` |
|
|
||||||
| `list.instructions.md` | Step-by-step procedure for adding a module / list through seeders |
|
|
||||||
| `../../README.md` | Technical map: what exists, where it lives, how it is wired |
|
|
||||||
| `../../CLAUDE.md` | Operating rules and standing defaults for AI agents in this repo |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. Platform Scope
|
## 2. Platform Scope
|
||||||
|
|
||||||
Core stack:
|
Core stack:
|
||||||
|
|
||||||
- Backend: C# .NET 10 (C# 14) + ABP 10.x — kod standardı: `dotnet.instructions.md`
|
- Backend: C# .NET 9 + ABP 9.x
|
||||||
- Frontend: React 19 + DevExtreme
|
- Frontend: React 18 + DevExtreme
|
||||||
- Database: SQL Server (dynamic datasource support)
|
- Database: SQL Server (dynamic datasource support)
|
||||||
- Cache: Redis
|
- Cache: Redis
|
||||||
- Background jobs: Hangfire + ABP Background Workers
|
- Background jobs: Hangfire + ABP Background Workers
|
||||||
|
|
@ -52,25 +41,11 @@ System nature:
|
||||||
3. Every proposal must include tenant and permission design.
|
3. Every proposal must include tenant and permission design.
|
||||||
4. Never bypass platform authorization patterns.
|
4. Never bypass platform authorization patterns.
|
||||||
5. Never hardcode secrets, tenant ids, or connection strings.
|
5. Never hardcode secrets, tenant ids, or connection strings.
|
||||||
6. **Stay inside the framework; ask before stepping out.** The platform may extend itself through
|
|
||||||
its own mechanisms (seed artifacts; adding a missing event, prop, or small backward-compatible
|
|
||||||
capability to an existing UI component — when Developer Kit tooling builds ~70% of the
|
|
||||||
requested component, the rest is completed with such small additions, never with a new
|
|
||||||
component; backward-compatible additions to script recipes / editor options / tokens).
|
|
||||||
Anything beyond that — a new React component or page, a new toolbox family, core engine changes (ListForm
|
|
||||||
runtime, seeders, code generator, auth, DataSourceManager, dispatcher/compiler), new ABP
|
|
||||||
entity/module/migration, platform table schema changes, new NuGet/npm dependencies, `Routes`
|
|
||||||
in `MenusData.json`, new external integrations — is **out of frame**: finish everything that
|
|
||||||
fits inside the frame first, then ask the user with a single question (why configuration is
|
|
||||||
insufficient, the smallest change that works, files touched, rollback) and proceed **only with
|
|
||||||
explicit approval**. Approval is per request and does not carry over. Full lists:
|
|
||||||
`lowcode.instructions.md` §0.8.
|
|
||||||
|
|
||||||
Exception policy:
|
Exception policy:
|
||||||
|
|
||||||
- Code-level React or backend development can be considered only if user explicitly requests code implementation and configuration path is insufficient.
|
- Code-level React or backend development can be considered only if user explicitly requests code implementation and configuration path is insufficient.
|
||||||
- If exception is required, AI must explain why configuration-based options are not enough.
|
- If exception is required, AI must explain why configuration-based options are not enough.
|
||||||
- Never say "cannot be done": the answer is either an in-frame build or an approval question.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -141,7 +116,7 @@ Capabilities:
|
||||||
- Query
|
- Query
|
||||||
- WebService
|
- WebService
|
||||||
- Cascading lookup parent-child behavior
|
- Cascading lookup parent-child behavior
|
||||||
- Dynamic validation, editor options, editor scripts (see 7.1)
|
- Dynamic validation, editor options, editor scripts
|
||||||
- Conditional formatting and style injection
|
- Conditional formatting and style injection
|
||||||
- Grid state save/load/reset
|
- Grid state save/load/reset
|
||||||
- User filter save/apply/delete flows
|
- User filter save/apply/delete flows
|
||||||
|
|
@ -171,34 +146,6 @@ Supported editor types:
|
||||||
- dxTextArea
|
- dxTextArea
|
||||||
- dxTextBox
|
- dxTextBox
|
||||||
|
|
||||||
### 7.1 Editor Script and Editor Options (builder-backed)
|
|
||||||
|
|
||||||
`EditorScript` and `EditorOptions` are produced from a shared vocabulary, in two places that
|
|
||||||
must stay identical:
|
|
||||||
|
|
||||||
| Surface | Location |
|
|
||||||
| --- | --- |
|
|
||||||
| Visual builder (UI) | `ui/src/views/admin/listForm/edit/json-row-operations/editor-script/scriptRecipes.ts` and `.../editor-options/optionSpecs.ts` + `presets.ts` |
|
|
||||||
| Seeder API (C#) | `api/src/Sozsoft.Platform.Domain.Shared/Editors/` — `EditorScript`, `EditorScriptRule`, `EditorScriptCondition`, `EditorOptions`, `EditorOptionsBuilder` |
|
|
||||||
|
|
||||||
Rules AI must follow:
|
|
||||||
|
|
||||||
- Prefer a recipe over hand-written JS. Recipes cover arithmetic (`Multiply`, `Subtract`,
|
|
||||||
`Percent`, `Sum`, `Formula`), dates (`Today`, `Days`, `Hours`), data movement (`Copy`,
|
|
||||||
`SetValue`, `Clear`, `ApiToField`), and view/notify actions (`ReadOnly`, `Notify`, `Ask`,
|
|
||||||
`OpenUrl`). Free-form JS is `Custom` and is the last option.
|
|
||||||
- Generated script carries a `// @builder {...}` header on the first line that stores the rules,
|
|
||||||
plus `// @runOnOpen` when any rule runs on form open. **Never edit the header by hand and never
|
|
||||||
emit a script that does not match what the builder would emit** — the dialog then treats the
|
|
||||||
script as manually edited and closes the rule editor.
|
|
||||||
- Rule triggers: `change` (default), `open` (default-value generation), `both`. Conditions join
|
|
||||||
with `and` / `or`.
|
|
||||||
- When a recipe changes on one side, change its port on the other side in the same task.
|
|
||||||
- In seeders write `EditorOptions = EditorOptions.Number(2).ShowClearButton()` style expressions
|
|
||||||
instead of raw JSON strings. Options marked `platform: true` in `optionSpecs.ts` deserialize
|
|
||||||
into typed DTOs (`GridBoxOptionsDto`, `TagBoxOptionsDto`, `ImageUploadOptionsDto`); wrong types
|
|
||||||
are silently ignored.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 8. Dynamic Form System
|
## 8. Dynamic Form System
|
||||||
|
|
@ -225,8 +172,6 @@ Supported component families:
|
||||||
- TreeList / Tree view
|
- TreeList / Tree view
|
||||||
- SubForm tabs (List, Tree, Gantt, Scheduler, Form, Chart)
|
- SubForm tabs (List, Tree, Gantt, Scheduler, Form, Chart)
|
||||||
- Widget Group (dashboard KPI cards)
|
- Widget Group (dashboard KPI cards)
|
||||||
- TodoBoard — a Kanban board: columns come from `statusExpr` values, cards change status by drag
|
|
||||||
and drop, new columns (statuses) can be added from the board
|
|
||||||
|
|
||||||
Runtime UI capabilities:
|
Runtime UI capabilities:
|
||||||
|
|
||||||
|
|
@ -267,54 +212,6 @@ If user requests custom code explicitly:
|
||||||
- Provide a warning that low-code path is preferred.
|
- Provide a warning that low-code path is preferred.
|
||||||
- Offer configuration-first alternative first.
|
- Offer configuration-first alternative first.
|
||||||
|
|
||||||
### 10.1 Custom Component path (still not hand-written React)
|
|
||||||
|
|
||||||
When a screen genuinely cannot be expressed as a ListForm, the next step is **not** a physical
|
|
||||||
React page. It is a Custom Component built in the Visual Designer and attached to a menu through
|
|
||||||
the Wizard's `Custom` path.
|
|
||||||
|
|
||||||
- The component is stored in the database (`Name`, `RoutePath`, `Code`, `Props`, `Dependencies`,
|
|
||||||
`DataSources`) and compiled in the browser with `@babel/standalone`.
|
|
||||||
- The designer document lives in `Props.visualDesigner` **and** in the
|
|
||||||
`/*__SOZSOFT_VISUAL_DESIGNER__…__*/` header of the generated code. `sourceMode` is `visual` or
|
|
||||||
`code`; switching to `code` is one-way — the canvas cannot be restored afterwards, so do not
|
|
||||||
propose it casually.
|
|
||||||
- Toolbox families: `layout` (`PageContainer`, `FlexRow`, `Spacer`), `data` (`Form` — owns the
|
|
||||||
four CRUD endpoints and acts as a container), `platform` (`ListView`, `DataGridView`,
|
|
||||||
`TreeView`, `GanttView`, `TodoBoard`, `CardView`, `SchedulerView`, `PivotView`, `ChartView` —
|
|
||||||
each embeds a ListForm screen through `listFormCode`), `html`/`ui`, and other custom
|
|
||||||
components as `custom`.
|
|
||||||
- **Reuse before drawing:** if the requirement is a list/tree/chart that a ListForm already
|
|
||||||
serves, drop the matching `platform` node with its `listFormCode` instead of rebuilding it out
|
|
||||||
of primitives.
|
|
||||||
- Data binding goes through CRUD endpoints. Filter operators map to the CrudEndpoint `GetList`
|
|
||||||
contract: `eq` is a bare query parameter, every other operator carries its name as a suffix
|
|
||||||
(`?Name.contains=…`).
|
|
||||||
- Permissions are two-layered and mandatory: node-level `designerPermission` for visibility, and
|
|
||||||
per-command Form permissions (`selectPermission`, `insertPermission`, `updatePermission`,
|
|
||||||
`deletePermission`). In `Otomatik` mode the base is the read permission the Wizard created for
|
|
||||||
the component and the commands get `''` / `.Create` / `.Update` / `.Delete` suffixes.
|
|
||||||
- List screen permissions: `App.DeveloperKit.CustomComponents{,.Create,.Update,.Delete,.Export,.Import}`.
|
|
||||||
- Every save/delete is mirrored into `configs/seeds/{scope}/data/App.DeveloperKit.CustomComponents.json`; the endpoints it uses are
|
|
||||||
referenced through `crud/{Entity}.json`. A component that is not seed-backed is not done.
|
|
||||||
|
|
||||||
### 10.2 SQL View Designer
|
|
||||||
|
|
||||||
For shaping data before a screen binds to it, prefer a designed view over an inline query:
|
|
||||||
`SqlViewDesignerDialog` produces T-SQL from a diagram (sources, joins, `CROSS/OUTER APPLY`,
|
|
||||||
derived subqueries) plus an SSMS-style criteria grid (Column / Alias / Output / Group By / Sort /
|
|
||||||
Filter / Or…). It is **one-way** — model to T-SQL. A view that does not parse back into the
|
|
||||||
canonical shape drops the dialog to raw SQL mode, so hand-editing a designed view costs the
|
|
||||||
designer. Aggregations available in the Group By column: `GroupBy`, `Where`, `SUM`, `COUNT`,
|
|
||||||
`COUNT_DISTINCT`, `AVG`, `MIN`, `MAX`.
|
|
||||||
|
|
||||||
Persistence: only the **table designer's** deploy writes a seed file automatically
|
|
||||||
(`{sql|postgres}/object/{TableName}.sql`, full CREATE script, via `save-table-script`). View
|
|
||||||
Designer deploys and any DDL run from the SQL editor (views, procedures, functions) reach the
|
|
||||||
database only — their seed files under `{sql|postgres}/object/` must be written explicitly.
|
|
||||||
Full mechanics and the SP/function production patterns: `lowcode.instructions.md` §8.1–§8.2.1
|
|
||||||
and §9.5.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 11. Integrations
|
## 11. Integrations
|
||||||
|
|
@ -445,8 +342,7 @@ When configuration cannot satisfy requirement:
|
||||||
1. Try SQL-based design
|
1. Try SQL-based design
|
||||||
2. Try Custom Endpoint
|
2. Try Custom Endpoint
|
||||||
3. Try Dynamic Service
|
3. Try Dynamic Service
|
||||||
4. Propose minimal code change with explicit justification — this step is out of frame:
|
4. Propose minimal code change with explicit justification
|
||||||
**ask and wait for approval before touching code** (rule 3.6).
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -644,39 +540,13 @@ When in doubt, AI must choose the path that preserves:
|
||||||
|
|
||||||
## 31. Seeder-Driven Low-Code Development Guide (Authoritative)
|
## 31. Seeder-Driven Low-Code Development Guide (Authoritative)
|
||||||
|
|
||||||
There are **two seed surfaces**. AI must not mix them.
|
AI must learn and teach implementation flow primarily from these seed assets:
|
||||||
|
|
||||||
**A. Repository seeds (shipped with code)** — `api/src/Sozsoft.Platform.DbMigrator/Migrations/`.
|
- api/src/Sozsoft.Platform.DbMigrator/Seeds/ListFormSeeder_Saas.cs
|
||||||
Platform-owned screens, menus, permissions and language texts. Changing them requires a build and
|
- api/src/Sozsoft.Platform.DbMigrator/Seeds/MenusData.json
|
||||||
a deploy. AI must learn and teach implementation flow primarily from these assets:
|
- api/src/Sozsoft.Platform.DbMigrator/Seeds/PermissionsData.json
|
||||||
|
- api/src/Sozsoft.Platform.DbMigrator/Seeds/HostData.json
|
||||||
- ListFormSeeder_Saas.cs / ListFormSeeder_Administration.cs / ListFormSeeder_{Modul}.cs
|
- api/src/Sozsoft.Platform.DbMigrator/Seeds/LanguagesData.json
|
||||||
- MenusData.json
|
|
||||||
- PermissionsData.json
|
|
||||||
- HostData.json
|
|
||||||
- LanguagesData.json
|
|
||||||
- WizardDataSeeder.cs / CrudDataSeeder.cs — the seeders that read
|
|
||||||
surface B below
|
|
||||||
- ListFormSeedDataApplier, CrudEndpointSeedApplier (Domain/Data/) — the single home of the apply
|
|
||||||
rules; both the migrate/seed flow and the wizard import's completion call the same services.
|
|
||||||
`data/` rows are inserted-only on migrate (a possibly stale file must not overwrite live data);
|
|
||||||
`crud/` entries are timestamp-guarded — an existing record is updated or deleted only when the
|
|
||||||
file's `GeneratedAt` is newer than the record's `LastModificationTime`/`CreationTime`. **Migrate
|
|
||||||
adds; it does not overwrite.** Follow this rule in any new seeder.
|
|
||||||
|
|
||||||
**B. Runtime seeds (written by the running application)** — `configs/seeds/`, resolved through
|
|
||||||
`SeedPathResolver`. The Wizard, the Custom Components screen / Visual Designer, the CRUD
|
|
||||||
Endpoint Manager and every ListForm screen with a `SeedFilePath` write here; the
|
|
||||||
seeders above restore them when the database is recreated. Scope folders mirror the CDN layout:
|
|
||||||
|
|
||||||
```
|
|
||||||
configs/seeds/host/{wizard,data,crud,sql/{object,execute},postgres/{object,execute}}
|
|
||||||
configs/seeds/tenants/{tenantId}/… same layout, tenant scope
|
|
||||||
configs/seeds/.imports/{importId}/… wizard import staging + backup (not scanned)
|
|
||||||
```
|
|
||||||
|
|
||||||
Rule: anything produced at runtime must land in surface B. **A feature that disappears when the
|
|
||||||
database is recreated is not delivered.**
|
|
||||||
|
|
||||||
If user asks "how to add a new module/screen", AI must answer with this exact operational sequence.
|
If user asks "how to add a new module/screen", AI must answer with this exact operational sequence.
|
||||||
|
|
||||||
|
|
@ -789,39 +659,6 @@ When user asks for a screen/module, AI must answer in this order:
|
||||||
|
|
||||||
AI should produce practical, copy-adaptable artifact definitions and avoid abstract-only explanations.
|
AI should produce practical, copy-adaptable artifact definitions and avoid abstract-only explanations.
|
||||||
|
|
||||||
### 31.9 Screen brief, discovery questions and image-based requests
|
|
||||||
|
|
||||||
A request arrives either as prose or as a **screenshot / mockup / photo** of the desired screen.
|
|
||||||
Both enter the same pipeline — and for a **new screen/component the pipeline starts with
|
|
||||||
questions, not files**:
|
|
||||||
|
|
||||||
1. **Run the discovery round** (`lowcode.instructions.md` §0.6). Ask in batches (max 4 questions
|
|
||||||
per round, each with 2–4 options and a recommended default), max three rounds. Round 1 always
|
|
||||||
covers: screen kind (List/Wizard vs Custom Component vs single-record form), data source,
|
|
||||||
owning module, and tenancy scope. Round 2 uses the path-specific question set (§0.6.3 for
|
|
||||||
List, §0.6.4 for Custom). Never ask what the repo already answers — search first, confirm
|
|
||||||
findings instead of re-asking. If the user says "you decide / don't ask", skip the round and
|
|
||||||
build on defaults.
|
|
||||||
2. Turn the answers into a single screen brief — purpose, data source, columns and editors,
|
|
||||||
layouts, actions, sub-screens, widgets, approval flow, menu, permissions, tenancy, language
|
|
||||||
texts. For an image, read it first (page regions → main region type → toolbar → columns →
|
|
||||||
filters → form controls → row actions); the image answers most of round 2, so ask only what
|
|
||||||
an image cannot show (§0.7.4).
|
|
||||||
3. **Get the brief confirmed before writing any seed file.** Mark defaulted lines `(varsayılan)`
|
|
||||||
so the user sees what they are approving.
|
|
||||||
4. Pick the tool: grid-shaped work → SQL Query Manager + Wizard; free layout / dashboard /
|
|
||||||
single-record custom form → Custom Component. **When in doubt, recommend the Wizard.**
|
|
||||||
5. Produce the seed files in dependency order, then list them and restate the defaulted choices.
|
|
||||||
|
|
||||||
An image is a reference, not a pixel contract: build the closest thing the platform's own
|
|
||||||
components give, and state at the end what had no equivalent. Three things are never assumed,
|
|
||||||
always asked: which table is written to, who approves (when there is an approval flow), and the
|
|
||||||
formula of any calculation/business rule. This question-first rule applies only to producing a
|
|
||||||
**new** screen/component; fixes, field additions, debugging and explanations proceed directly.
|
|
||||||
|
|
||||||
The concrete question sets, brief template, the image-element → artifact mapping and the
|
|
||||||
visual-control → `EditorType` mapping are in `lowcode.instructions.md` §0.6–§0.7.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 32. Mandatory Default Behaviors (Do Not Ask Repeatedly)
|
## 32. Mandatory Default Behaviors (Do Not Ask Repeatedly)
|
||||||
|
|
@ -864,131 +701,7 @@ Rules:
|
||||||
- Permission contract must be created/bound for both parent and child menu items.
|
- Permission contract must be created/bound for both parent and child menu items.
|
||||||
- Route/menu consistency remains mandatory (`Url` and screen contract must match).
|
- Route/menu consistency remains mandatory (`Url` and screen contract must match).
|
||||||
|
|
||||||
### 32.3 Wizard component kind and menu creation
|
### 32.3 AI enforcement requirement
|
||||||
|
|
||||||
`ListFormWizardDto.ComponentKind` is the wizard's fork and the first field in the seed file.
|
|
||||||
Default is `List`, so older seed files that lack it keep working.
|
|
||||||
|
|
||||||
| Kind | Produces | Menu URL | Child permissions |
|
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| `List` | ListForm + ListFormFields + Route + Menu + Permissions + language keys | `/admin/list/{ListFormCode}` | `.Create`, `.Update`, `.Delete`, `.Export`, `.Import`, `.Note` |
|
|
||||||
| `Custom` | Menu + Permissions + language keys; the screen is the selected Custom Component | the component's `RoutePath` | `.Create`, `.Update`, `.Delete` only |
|
|
||||||
|
|
||||||
`Export` / `Import` / `Note` exist only in the ListForm ecosystem. Never propose them on the
|
|
||||||
`Custom` path.
|
|
||||||
|
|
||||||
`CreateMenu = false` (menu-less wizard): no menu record — and no parent menu record — is created;
|
|
||||||
ListForm, permissions and language keys are still produced. Use it when the screen is meant to be
|
|
||||||
embedded elsewhere as a SubGrid/part. Default is `true`.
|
|
||||||
|
|
||||||
### 32.4 Permission group selection
|
|
||||||
|
|
||||||
The wizard's permission group comes from a select list backed by `GetWizardPermissionGroups`.
|
|
||||||
Display names are read from the language key whose name equals the group name.
|
|
||||||
|
|
||||||
- Selecting an existing group fills `PermissionGroupEn/Tr` (seed file: `Language.PermissionGroupEn/Tr`);
|
|
||||||
if left blank the server writes the database values back, so the seed file always carries both
|
|
||||||
languages.
|
|
||||||
- Defining a new group writes those texts to the language key with the same name as the group.
|
|
||||||
- AI must never propose a permission group without EN + TR display names.
|
|
||||||
|
|
||||||
### 32.5 Wizard export/import is a package, not a file
|
|
||||||
|
|
||||||
Exporting a wizard produces a zip whose paths are relative to the scope folder
|
|
||||||
(`host` / `tenants/{tenantId}`) and which carries every dependency:
|
|
||||||
|
|
||||||
The closure is **recursive** (`WizardExportCollector`): a sub-form's wizard file is queued and its
|
|
||||||
own dependencies are collected under the same rules, so the archive stands on its own.
|
|
||||||
|
|
||||||
- `wizard/{file}.json` — the screen itself plus the wizard files of the sub-forms it references
|
|
||||||
through `SubFormsDto[].Code`, recursively
|
|
||||||
- `data/App.DeveloperKit.CustomComponents.json` — `Custom` path; the file is filtered down to the
|
|
||||||
exported component and its dependency chain, never exported whole
|
|
||||||
- `data/{ListFormCode}.json` — the screen's own data mirror when `SeedFilePath` is set
|
|
||||||
- `crud/{entity}.json` — endpoints those components use
|
|
||||||
- `{sql|postgres}/{object|execute}/{object}.sql` — the object behind `SelectCommand`, the objects
|
|
||||||
a lookup query (`LookupDto.LookupQuery`) references through `FROM`/`JOIN`, and on the `Custom`
|
|
||||||
path the tables the CRUD endpoints sit on
|
|
||||||
|
|
||||||
Script files are never guessed from a fixed provider/folder combination: the scope's script folders
|
|
||||||
are scanned from disk, and every file matching the object name is added. A missing script is **not**
|
|
||||||
a warning — not every table is seed-managed (ABP's own tables come from EF migrations); whether the
|
|
||||||
target has it is decided by the import analysis against the real database, never assumed here.
|
|
||||||
|
|
||||||
An unresolved **recorded reference** (a sub-form, a `SeedFilePath` mirror, a `DataSources[].SeedFile`,
|
|
||||||
a component row named in a seed file but with no counterpart) does not block the download but is
|
|
||||||
never silent: `ExportAnalysis` returns the full archive contents plus what is missing, and the File
|
|
||||||
Manager shows that list before downloading.
|
|
||||||
|
|
||||||
The import screen is a four-step wizard (`Steps`): **Files** (resolve conflicts) → **Checks**
|
|
||||||
(findings; a schema finding opens the existing `SqlTableDesignerDialog`, and the analysis re-runs
|
|
||||||
after a deploy) → **Plan** (the full list of what confirming will do, in the server's completion
|
|
||||||
order) → **Apply**. Going back is always allowed; going forward is gated — unresolved conflicts
|
|
||||||
block Checks, a blocking finding blocks Plan.
|
|
||||||
|
|
||||||
Server-side it is two phases: `AnalyzeImport` (unzip to `.imports/{id}/staged`, classify each entry
|
|
||||||
as `New` / `Identical` / `Conflict`) then `ApplyImport` (file-by-file write, previous content backed
|
|
||||||
up so `RollbackImport` can undo the whole batch), closed by `CompleteImport`.
|
|
||||||
|
|
||||||
`AnalyzeImport` also **validates** before anything is written (`WizardImportValidator`). With at
|
|
||||||
least one `Error` finding the session is marked `blocked` on the server and `ApplyImport` refuses,
|
|
||||||
so the check cannot be bypassed by calling the endpoint directly. Errors: malformed JSON; a wizard
|
|
||||||
file with no `Wizard`/`ListForm` section; a screen's or a `crud/{Entity}.json`'s table missing in
|
|
||||||
the target **and** absent from the archive's scripts; a `data/{code}.json` whose ListForm exists
|
|
||||||
neither in the archive nor in the target; an unresolvable custom-component `Dependencies` /
|
|
||||||
`DataSources[].SeedFile`; an empty component name or route on the `Custom` path; a sub-form
|
|
||||||
reference with no counterpart. Warnings: object existence could not be queried, provider mismatch
|
|
||||||
(`sql` ↔ `postgres`), a missing `SeedFilePath` data file, an empty file.
|
|
||||||
|
|
||||||
The analysis also **compares the table schema**. Generated table scripts are wrapped in
|
|
||||||
`IF OBJECT_ID(...) IS NULL BEGIN CREATE TABLE ... END`, so when the target already has the table the
|
|
||||||
`CREATE` block is skipped entirely and a new column never arrives from the script itself — the screen
|
|
||||||
would then fail with an invalid-column error. The incoming `CREATE TABLE` declaration (`SqlCreateTableParser`) is
|
|
||||||
compared against the live `INFORMATION_SCHEMA.COLUMNS` shape. The split follows the risk of loss:
|
|
||||||
**a missing column is added** — adding is lossless, so it is reported as a Warning with the statement
|
|
||||||
it will run (`WizardImportIssueDto.Statement`) and applied at completion; the one exception is a
|
|
||||||
`NOT NULL` column with no default on a non-empty table, which the database itself would reject and
|
|
||||||
where the value to write is a business decision — that blocks. **An existing column is never
|
|
||||||
changed**: a length / precision / scale / nullability difference is an Error and stops the import,
|
|
||||||
because narrowing truncates and a type conversion can fail. An extra column in the target is a
|
|
||||||
Warning and is never dropped; no path emits `DROP TABLE`, `DROP COLUMN` or `ALTER COLUMN`. The
|
|
||||||
comparison lives in one place (`SqlTableSchemaComparer`) and both the analysis and the completion
|
|
||||||
use it. Type **names** are deliberately not compared: the provider catalog reports its own
|
|
||||||
canonical name (`VARCHAR` → `character varying` on PostgreSQL), so a textual comparison would raise
|
|
||||||
a false difference on every `VARCHAR` column.
|
|
||||||
|
|
||||||
A `data/*.json` entry is compared and merged **by row, not by file**: the diff shows only the
|
|
||||||
rows whose keys the archive carries (file-level `GeneratedAt`/`Order` are excluded), so unrelated
|
|
||||||
records never surface as a difference — no matching row means `New` (insert), a matching identical
|
|
||||||
row means `Identical` (nothing is transferred), a matching different row means `Conflict` (update).
|
|
||||||
The file itself is never overwritten: rows are merged into the target by key (incoming row wins,
|
|
||||||
target-only rows are kept). `CompleteImport` then applies exactly the
|
|
||||||
`data/` files it wrote to the database through `IListFormSeedDataApplier` with
|
|
||||||
`overwriteExisting: true` — a missing row is inserted, an existing one is updated from the file
|
|
||||||
(unlike migrate/seed, where a possibly stale file must not overwrite live data), and a row that was
|
|
||||||
soft deleted is revived by clearing the columns the screen's `DeleteCommand` sets
|
|
||||||
(`IsDeleted`/`DeletionTime`/`DeleterId`), otherwise the default filter would keep hiding it — and
|
|
||||||
reports the inserted/updated/skipped counts. Data files that were already identical to the target
|
|
||||||
are applied too: there was nothing to write, but the rows may be missing or soft deleted in the
|
|
||||||
database. A file the user chose to keep (different, not written) is left alone. This step is not
|
|
||||||
covered by `RollbackImport`, which only restores files and must be run before it.
|
|
||||||
|
|
||||||
`CompleteImport` runs in **dependency order and the order is fixed**: (a) the `.sql` scripts under
|
|
||||||
`{sql|postgres}/{object,execute}` (`object` first) — a missing table is created here, otherwise the
|
|
||||||
later steps would fail on it; (b) columns missing from existing tables are added; (c) `crud/*.json`
|
|
||||||
through `ICrudEndpointSeedApplier`; (d) the `data/*.json` rows; (e) `wizard/*.json` — the screens
|
|
||||||
are installed through the same path the wizard screen uses (`DeployWizardAsync`), reusing the file's
|
|
||||||
`ListForm`/`Fields` contract as-is instead of rebuilding it from the wizard answers. A screen that
|
|
||||||
already exists is left untouched, and each screen is installed in its own unit of work. No migrate
|
|
||||||
run is needed: the screen works as soon as the import finishes.
|
|
||||||
|
|
||||||
Limits: allowed root folders `wizard`, `crud`, `data`, `sql`, `postgres` (and only `object` /
|
|
||||||
`execute` under the SQL provider folders); 5 MB per entry, 50 MB per archive, 500 entries.
|
|
||||||
Permissions: `App.Listforms.Wizard.Export` / `.Import`.
|
|
||||||
|
|
||||||
AI must not propose moving a screen between environments by copying a single json file.
|
|
||||||
|
|
||||||
### 32.6 AI enforcement requirement
|
|
||||||
|
|
||||||
AI must proactively apply these defaults in:
|
AI must proactively apply these defaults in:
|
||||||
|
|
||||||
|
|
|
||||||
150
.github/instructions/dotnet.instructions.md
vendored
150
.github/instructions/dotnet.instructions.md
vendored
|
|
@ -1,150 +0,0 @@
|
||||||
# .NET / ABP Kod Standardı
|
|
||||||
|
|
||||||
Bu doküman `api/` altındaki tüm projeler için bağlayıcıdır. Platform davranışına dair
|
|
||||||
kurallar için `ai.instructions.md` esastır; çelişki olursa o dosya kazanır.
|
|
||||||
|
|
||||||
Standart derlemede zorlanır: `api/Directory.Build.props` analizörleri açar,
|
|
||||||
`api/.editorconfig` kural şiddetlerini tanımlar. Bu iki dosya standardın tek
|
|
||||||
kaynağıdır; proje dosyalarında tekrar edilmez.
|
|
||||||
|
|
||||||
## 1. Hedef sürümler
|
|
||||||
|
|
||||||
| Katman | Sürüm | Nerede tanımlı |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| SDK | .NET 10 (`10.0.300`) | `api/global.json` |
|
|
||||||
| TargetFramework | `net10.0` | proje dosyaları |
|
|
||||||
| Dil | C# 14 (`LangVersion=latest`) | `api/common.props` |
|
|
||||||
| ABP | 10.0.0 | `Volo.Abp.*` paket referansları |
|
|
||||||
|
|
||||||
Sürüm yükseltmesi tek noktadan yapılır: `global.json` + `common.props` + paket sürümleri.
|
|
||||||
Proje dosyalarına TFM/LangVersion tekrar yazılmaz.
|
|
||||||
|
|
||||||
## 2. Nullable
|
|
||||||
|
|
||||||
`Directory.Build.props` içinde **çözüm geneli `Nullable` ayarı yapılmaz.** Sebebi
|
|
||||||
EF Core: kolonun zorunlu olup olmadığını, entity assembly'sinin nullable annotation
|
|
||||||
bağlamından türetir. Global bir `enable`/`annotations` anahtarı, bir sonraki
|
|
||||||
`dotnet ef migrations add` çağrısında tüm `string` özelliklerini sessizce NOT NULL
|
|
||||||
kolona çevirir — seeder'ın doldurmadığı her alan da o anda kırılır.
|
|
||||||
|
|
||||||
Kural:
|
|
||||||
|
|
||||||
- Nullable, proje bazında ve bilinçli olarak açılır (`<Nullable>enable</Nullable>`).
|
|
||||||
Şu an açık olanlar: `Sozsoft.Platform.Application`, `Sozsoft.Notifications.*`,
|
|
||||||
test projeleri.
|
|
||||||
- **Entity taşıyan projelerde** (`*.Domain`) nullable açılacaksa, aynı PR'da
|
|
||||||
`dotnet ef migrations add` çıktısı gözden geçirilir; şema değişikliği isteniyorsa
|
|
||||||
kabul edilir, istenmiyorsa kolon zorunluluğu `PlatformDbContext` içinde
|
|
||||||
`.IsRequired(false)` ile açıkça sabitlenir.
|
|
||||||
- Kolon zorunluluğu tercihen NRT'ye değil, `PlatformDbContext` yapılandırmasına
|
|
||||||
yazılır; böylece derleyici anahtarından bağımsız olur.
|
|
||||||
|
|
||||||
## 3. Ölü kod politikası
|
|
||||||
|
|
||||||
Aşağıdaki kurallar ihlal edilmiş kod merge edilmez:
|
|
||||||
|
|
||||||
| Kural | Anlamı |
|
|
||||||
| --- | --- |
|
|
||||||
| IDE0005 | Kullanılmayan `using` |
|
|
||||||
| IDE0051 | Çağrılmayan private üye |
|
|
||||||
| IDE0052 | Yazılıp hiç okunmayan private alan (kullanılmayan enjekte bağımlılık) |
|
|
||||||
| IDE0059 | Gereksiz değer ataması |
|
|
||||||
| IDE0161 | `namespace X;` (file-scoped) |
|
|
||||||
|
|
||||||
Tarama ve otomatik düzeltme (kalıcı yapılandırma gerektirmez):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Rapor
|
|
||||||
dotnet build api/Sozsoft.Platform.sln --no-incremental \
|
|
||||||
-p:EnforceCodeStyleInBuild=true -p:GenerateDocumentationFile=true
|
|
||||||
|
|
||||||
# Düzeltme
|
|
||||||
dotnet format api/Sozsoft.Platform.sln --diagnostics IDE0005,IDE0161 --severity warn
|
|
||||||
```
|
|
||||||
|
|
||||||
> Kurallar `.editorconfig`'te `warning` seviyesindedir ve `EnforceCodeStyleInBuild`
|
|
||||||
> açık olduğu için normal `dotnet build` de bunları raporlar.
|
|
||||||
|
|
||||||
IDE0052 düzeltilirken alan ile birlikte **constructor parametresi de** silinir; yoksa
|
|
||||||
DI hâlâ gereksiz servisi çözer.
|
|
||||||
|
|
||||||
`CS0162` (erişilemez kod) `Directory.Build.props` içinde gerekçesiyle susturulmuştur:
|
|
||||||
veritabanı sağlayıcısı derleme zamanı sabiti olduğu için diğer sağlayıcının dalları
|
|
||||||
erişilemez görünür. Bu durum dışında erişilemez kod bırakılmaz.
|
|
||||||
|
|
||||||
Ek kurallar:
|
|
||||||
|
|
||||||
- Bir tip/dosya silinmeden önce **repo genelinde** (api + ui + configs) referans
|
|
||||||
taraması yapılır. DI ile çözülen tipler (AppService, Manager, Provider, Repository,
|
|
||||||
Seeder, Localizer, Notification sağlayıcıları) düz metin aramasında "referanssız"
|
|
||||||
görünür — bunlar yalnızca modül/DI kaydı incelendikten sonra silinir.
|
|
||||||
- Yorum satırına alınmış kod bloğu bırakılmaz; ihtiyaç varsa git geçmişinden alınır.
|
|
||||||
- "İleride lazım olur" gerekçesiyle kullanılmayan DTO/Input/Consts tutulmaz.
|
|
||||||
- `Migrations/` ve `*.g.cs` üretilmiş koddur, bu kuralların dışındadır.
|
|
||||||
|
|
||||||
## 4. Modern C# kullanımı (yeni kod)
|
|
||||||
|
|
||||||
- File-scoped namespace: `namespace Sozsoft.Platform.ListForms;`
|
|
||||||
- Primary constructor: `public class XAppService(IRepository<X, Guid> repo) : PlatformAppService`
|
|
||||||
— parametre doğrudan kullanılır, `private readonly ... = param;` şeklinde
|
|
||||||
tekrar tanımlanmaz.
|
|
||||||
- Koleksiyon ifadeleri (`[]`, `[.. items]`), `is not null`, `switch` ifadesi,
|
|
||||||
desen eşleme, `Index/Range`, hedefi belli `new()`.
|
|
||||||
- `out var`, tuple deconstruction; kullanılmayan çıktı için `_`.
|
|
||||||
- `async` metotlar `Async` ile biter ve `Task`/`Task<T>` döner; `async void` yasak.
|
|
||||||
ABP'de `ConfigureAwait` kullanılmaz.
|
|
||||||
- Biçim: `api/.editorconfig` geçerlidir (4 boşluk girinti, `using` blokları namespace
|
|
||||||
dışında ve System önce, dosya sonunda satır sonu, UTF-8 BOM).
|
|
||||||
|
|
||||||
## 5. ABP kuralları
|
|
||||||
|
|
||||||
- **Katman sınırı**: Domain.Shared → Domain → Application.Contracts → Application →
|
|
||||||
HttpApi → HttpApi.Host. Ters yönde referans verilmez; `EntityFrameworkCore` yalnızca
|
|
||||||
Domain'e bakar. `DbMigrator` yalnızca `Application.Contracts` + `EntityFrameworkCore`
|
|
||||||
referansı taşır; **Application'a referans verilmez** — seeder'ın uygulama servislerine
|
|
||||||
ihtiyaç duyması, kodun yanlış katmanda olduğunun işaretidir.
|
|
||||||
- **Hem seeder'ın hem AppService'in ihtiyaç duyduğu dönüşüm**: ortak kod
|
|
||||||
`Application.Contracts`'a konur (DTO'nun kendi metodu olarak; yeni yardımcı sınıf
|
|
||||||
üretilmez). Dönüşümün entity'ye yazması gerekiyorsa entity'nin **yazılabilir kolon
|
|
||||||
sözleşmesi** Domain.Shared'de bir arayüz olarak tanımlanır (`IListFormArtifact`,
|
|
||||||
`IListFormFieldArtifact`), entity bu arayüzü uygular ve dönüşüm arayüz üzerinden yazar.
|
|
||||||
Böylece Contracts, Domain'i tanımadan entity doldurabilir ve aynı dönüşüm iki yerde
|
|
||||||
kopyalanmaz.
|
|
||||||
- **AppService**: `PlatformAppService`'ten türer, arayüzü Application.Contracts'ta
|
|
||||||
tanımlanır. Controller yazılmaz — ABP otomatik API üretir. Controller sadece ABP
|
|
||||||
konvansiyonunun karşılamadığı durumda (dosya indirme, harici webhook) eklenir.
|
|
||||||
- **Yetki**: her AppService/metot `[Authorize(PlatformConsts.AppCodes....)]` ile
|
|
||||||
korunur; izin adları `PermissionDefinitionProvider` içinde tanımlanır. Yetki
|
|
||||||
kontrolü koda gömülmez, izin adı sabitten okunur.
|
|
||||||
- **Tenant izolasyonu**: sorgular `ICurrentTenant` bağlamında çalışır; `IMultiTenant`
|
|
||||||
entity'lerde manuel `TenantId` filtresi yazılmaz, veri filtreleri devre dışı
|
|
||||||
bırakılacaksa (`ICurrentTenant.Change`, `IDataFilter`) gerekçe yorumla yazılır.
|
|
||||||
- **Repository**: özel sorgu gerekiyorsa Domain'de arayüz + EntityFrameworkCore'da
|
|
||||||
implementasyon. AppService içinde `DbContext` doğrudan kullanılmaz.
|
|
||||||
- **Dinamik SQL**: parametreli çalıştırılır; string birleştirme ile sorgu kurulmaz.
|
|
||||||
- **Mapping**: Mapperly (`[Mapper]` partial class) kullanılır; AutoMapper eklenmez.
|
|
||||||
RMG012 uyarısı "hedefte karşılığı olmayan alan" demektir; yeni mapper eklerken bu
|
|
||||||
uyarı bırakılmaz.
|
|
||||||
- **Localization**: kullanıcıya görünen metin koda gömülmez, `PlatformResource`
|
|
||||||
anahtarı üzerinden verilir.
|
|
||||||
- **Çok değerli alan taşıyan girdi DTO'su**: ekranda `dxTagBox` (ya da çoklu seçimli
|
|
||||||
`dxGridBox`) ile doldurulan bir alan tipli bir DTO'ya bağlanıyorsa property `string`
|
|
||||||
kalır ve `[JsonConverter(typeof(MultiValueStringJsonConverter))]` ile işaretlenir —
|
|
||||||
UI diziyi olduğu gibi yollar, converter `PlatformConsts.MultiValueDelimiter` ile tek
|
|
||||||
string üretir. Varsayılan `list-form-data/*` ucundan geçen ekranlarda bu iş
|
|
||||||
`QueryHelper` tarafından yapılır, DTO yoktur; okuma DTO'larına öznitelik eklenmez.
|
|
||||||
- **Sabitler**: bağlantı dizesi, tenant id, anahtar/secret koda yazılmaz; konfigürasyon
|
|
||||||
veya `SettingDefinition` üzerinden okunur.
|
|
||||||
|
|
||||||
## 6. Değişiklik öncesi kontrol listesi
|
|
||||||
|
|
||||||
```bash
|
|
||||||
dotnet build api/Sozsoft.Platform.sln --no-incremental \
|
|
||||||
-p:EnforceCodeStyleInBuild=true -p:GenerateDocumentationFile=true # 0 hata, yeni IDE00xx yok
|
|
||||||
dotnet format api/Sozsoft.Platform.sln --verify-no-changes --diagnostics IDE0005,IDE0161 --severity warn
|
|
||||||
dotnet test api/test/Sozsoft.Platform.EntityFrameworkCore.Tests
|
|
||||||
```
|
|
||||||
|
|
||||||
> Not: EntityFrameworkCore testleri şu an SQLite şema oluşturmada
|
|
||||||
> (`nvarchar(max)` → `SQLite Error 1`) kırık; bu kırıklık bu standarttan önce de vardı
|
|
||||||
> ve ayrı bir iş olarak ele alınmalıdır.
|
|
||||||
8
.github/instructions/list.instructions.md
vendored
8
.github/instructions/list.instructions.md
vendored
|
|
@ -7,10 +7,6 @@ This document summarizes the rules, standards, and step-by-step instructions for
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
> **Bu doküman depo içi seeder'larla (DbMigrator/Migrations) modül eklemeyi anlatır.**
|
|
||||||
> Çalışma zamanında Wizard / Custom Components / CRUD Endpoint Manager ile üretim yapacaksan
|
|
||||||
> `lowcode.instructions.md` esastır; oradaki seed dosyaları `configs/seeds` altına düşer.
|
|
||||||
|
|
||||||
## General Principles
|
## General Principles
|
||||||
- **Configuration First:** Always prefer platform configuration (menus, permissions, forms, localization) over custom code.
|
- **Configuration First:** Always prefer platform configuration (menus, permissions, forms, localization) over custom code.
|
||||||
- **Modularization:** Each module (e.g., {Modul}) must have its own seeder, permission group, and localization entries.
|
- **Modularization:** Each module (e.g., {Modul}) must have its own seeder, permission group, and localization entries.
|
||||||
|
|
@ -28,7 +24,7 @@ This document summarizes the rules, standards, and step-by-step instructions for
|
||||||
## Special File Handling Rules
|
## Special File Handling Rules
|
||||||
|
|
||||||
- **MenusData.json:**
|
- **MenusData.json:**
|
||||||
- Never modify the `Routes` section directly. Only `Modules` and `Menus` sections can be changed for menu additions or updates. All menu additions must use the platform's configuration mechanisms or be added to `Modules` and `Menus` as required by platform design.
|
- Never modify the `Routes` section directly. Only `MenuGroups` and `Menus` sections can be changed for menu additions or updates. All menu additions must use the platform's configuration mechanisms or be added to `MenuGroups` and `Menus` as required by platform design.
|
||||||
|
|
||||||
- **LanguagesData.json:**
|
- **LanguagesData.json:**
|
||||||
- When adding a new key, always check if the key already exists. Only add the key if it does not exist to prevent duplicates.
|
- When adding a new key, always check if the key already exists. Only add the key if it does not exist to prevent duplicates.
|
||||||
|
|
@ -39,7 +35,7 @@ This document summarizes the rules, standards, and step-by-step instructions for
|
||||||
|
|
||||||
|
|
||||||
### 1. MenusData.json
|
### 1. MenusData.json
|
||||||
- **{Modul} menu** added as a top-level menu with `ModuleId: {Modul}`.
|
- **{Modul} menu** added as a top-level menu with `ShortName: {Modul}`.
|
||||||
- **{Liste}** added as a child menu under {Modul}.
|
- **{Liste}** added as a child menu under {Modul}.
|
||||||
|
|
||||||
### 2. PermissionsData.json
|
### 2. PermissionsData.json
|
||||||
|
|
|
||||||
1393
.github/instructions/lowcode-reference.instructions.md
vendored
1393
.github/instructions/lowcode-reference.instructions.md
vendored
File diff suppressed because it is too large
Load diff
3746
.github/instructions/lowcode.instructions.md
vendored
3746
.github/instructions/lowcode.instructions.md
vendored
File diff suppressed because it is too large
Load diff
9
.gitignore
vendored
9
.gitignore
vendored
|
|
@ -1,14 +1,5 @@
|
||||||
# Konteyner veri klasorleri (mssql/postgres/redis vb.); seed veri dosyalari haric tutulur.
|
|
||||||
configs/**/data/**
|
configs/**/data/**
|
||||||
!configs/seeds/**/data/**
|
|
||||||
*.bak
|
*.bak
|
||||||
**/node_modules
|
**/node_modules
|
||||||
**/.DS_Store
|
**/.DS_Store
|
||||||
logs/
|
logs/
|
||||||
|
|
||||||
# Deploy sırasında git tag'lerinden üretilir (configs/change-logs/generate-change-logs.sh)
|
|
||||||
# ve API konteynerine mount edilir. Takip edilirse deploy'daki `git pull` yerel
|
|
||||||
# değişiklikle çakışır ve dosya eskir.
|
|
||||||
configs/change-logs/change-logs.json
|
|
||||||
# Wizard import staging klasoru (gecici; oturum bitince silinir)
|
|
||||||
configs/seeds/.imports/
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,5 @@
|
||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<NoWarn>$(NoWarn);CS0162;CS8321;CS8618;CS8632;CS1591;CS1573;CS1572;CS1570;CS1574</NoWarn>
|
<NoWarn>$(NoWarn);CS0162;CS8321;CS8618;CS8632</NoWarn>
|
||||||
<LangVersion>latest</LangVersion>
|
|
||||||
<AnalysisLevel>latest</AnalysisLevel>
|
|
||||||
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
|
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
using Volo.Abp.Application;
|
using Volo.Abp.Application;
|
||||||
using Volo.Abp.Authorization;
|
|
||||||
using Volo.Abp.Modularity;
|
using Volo.Abp.Modularity;
|
||||||
|
using Volo.Abp.Authorization;
|
||||||
|
|
||||||
namespace Sozsoft.Languages;
|
namespace Sozsoft.Languages;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Sozsoft.Languages;
|
||||||
|
|
||||||
|
public class LanguagesRemoteServiceConsts
|
||||||
|
{
|
||||||
|
public const string RemoteServiceName = "Languages";
|
||||||
|
|
||||||
|
public const string ModuleName = "languages";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Volo.Abp.Authorization.Permissions;
|
using Volo.Abp.Authorization.Permissions;
|
||||||
|
|
||||||
namespace Sozsoft.Languages.Permissions;
|
namespace Sozsoft.Languages.Permissions;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
using System;
|
using Sozsoft.Languages.Entities;
|
||||||
|
using Sozsoft.Languages.Localization;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Sozsoft.Languages.Entities;
|
|
||||||
using Sozsoft.Languages.Localization;
|
|
||||||
using Volo.Abp.Application.Dtos;
|
using Volo.Abp.Application.Dtos;
|
||||||
using Volo.Abp.Application.Services;
|
using Volo.Abp.Application.Services;
|
||||||
using Volo.Abp.Domain.Entities;
|
using Volo.Abp.Domain.Entities;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
using System;
|
using Sozsoft.Languages.Entities;
|
||||||
|
using Sozsoft.Languages.Localization;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Sozsoft.Languages.Entities;
|
|
||||||
using Sozsoft.Languages.Localization;
|
|
||||||
using Volo.Abp.Application.Dtos;
|
using Volo.Abp.Application.Dtos;
|
||||||
using Volo.Abp.Application.Services;
|
using Volo.Abp.Application.Services;
|
||||||
using Volo.Abp.Domain.Repositories;
|
using Volo.Abp.Domain.Repositories;
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
using System;
|
using Sozsoft.Languages.Entities;
|
||||||
|
using Sozsoft.Languages.Localization;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using StackExchange.Redis;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Dynamic.Core;
|
using System.Linq.Dynamic.Core;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Sozsoft.Languages.Entities;
|
|
||||||
using Sozsoft.Languages.Localization;
|
|
||||||
using StackExchange.Redis;
|
|
||||||
using Volo.Abp;
|
using Volo.Abp;
|
||||||
using Volo.Abp.Application.Dtos;
|
using Volo.Abp.Application.Dtos;
|
||||||
using Volo.Abp.Application.Services;
|
using Volo.Abp.Application.Services;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
using Riok.Mapperly.Abstractions;
|
|
||||||
using Sozsoft.Languages.Entities;
|
using Sozsoft.Languages.Entities;
|
||||||
using Volo.Abp.Localization;
|
using Volo.Abp.Localization;
|
||||||
|
using Riok.Mapperly.Abstractions;
|
||||||
using Volo.Abp.Mapperly;
|
using Volo.Abp.Mapperly;
|
||||||
|
|
||||||
namespace Sozsoft.Languages;
|
namespace Sozsoft.Languages;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
namespace Sozsoft.Languages;
|
||||||
|
|
||||||
|
public static class LanguagesErrorCodes
|
||||||
|
{
|
||||||
|
//Add your business exception error codes here...
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
namespace Sozsoft.Languages.Languages;
|
namespace Sozsoft.Languages.Languages
|
||||||
|
|
||||||
public static class LanguageCodes
|
|
||||||
{
|
{
|
||||||
|
public static class LanguageCodes
|
||||||
|
{
|
||||||
public const string Ar = "ar";
|
public const string Ar = "ar";
|
||||||
public const string Cs = "cs";
|
public const string Cs = "cs";
|
||||||
public const string De = "de-DE";
|
public const string De = "de-DE";
|
||||||
|
|
@ -19,5 +19,27 @@ public static class LanguageCodes
|
||||||
public const string Sl = "sl";
|
public const string Sl = "sl";
|
||||||
public const string Tr = "tr";
|
public const string Tr = "tr";
|
||||||
public const string Zh = "zh-Hans";
|
public const string Zh = "zh-Hans";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class LanguageNames
|
||||||
|
{
|
||||||
|
public const string Ar = "العربية";
|
||||||
|
public const string Cs = "Čeština";
|
||||||
|
public const string De = "Deutsch";
|
||||||
|
public const string En = "English";
|
||||||
|
public const string Es = "Español";
|
||||||
|
public const string Fi = "Finnish";
|
||||||
|
public const string Fr = "Français";
|
||||||
|
public const string Hi = "Hindi";
|
||||||
|
public const string Hr = "Croatian";
|
||||||
|
public const string Hu = "Magyar";
|
||||||
|
public const string It = "Italiano";
|
||||||
|
public const string Pt = "Português";
|
||||||
|
public const string Ru = "Русский";
|
||||||
|
public const string Sk = "Slovak";
|
||||||
|
public const string Sl = "Slovenščina";
|
||||||
|
public const string Tr = "Türkçe";
|
||||||
|
public const string Zh = "繁體中文";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
using System.Threading.Tasks;
|
using Sozsoft.Languages.Entities;
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Volo.Abp.Caching;
|
using Volo.Abp.Caching;
|
||||||
using Volo.Abp.DependencyInjection;
|
using Volo.Abp.DependencyInjection;
|
||||||
using Volo.Abp.Domain.Entities.Events.Distributed;
|
using Volo.Abp.Domain.Entities.Events.Distributed;
|
||||||
|
using Volo.Abp.Domain.Repositories;
|
||||||
using Volo.Abp.EventBus.Distributed;
|
using Volo.Abp.EventBus.Distributed;
|
||||||
|
|
||||||
namespace Sozsoft.Languages;
|
namespace Sozsoft.Languages;
|
||||||
|
|
@ -12,11 +15,14 @@ public class LanguageTextCacheItemInvalidatorDistributed :
|
||||||
IDistributedEventHandler<EntityDeletedEto<LanguageTextEto>>,
|
IDistributedEventHandler<EntityDeletedEto<LanguageTextEto>>,
|
||||||
ITransientDependency
|
ITransientDependency
|
||||||
{
|
{
|
||||||
|
private readonly IRepository<LanguageText, Guid> languageTextRepository;
|
||||||
private readonly IDistributedCache<LanguageTextCacheItem> cache;
|
private readonly IDistributedCache<LanguageTextCacheItem> cache;
|
||||||
|
|
||||||
public LanguageTextCacheItemInvalidatorDistributed(
|
public LanguageTextCacheItemInvalidatorDistributed(
|
||||||
|
IRepository<LanguageText, Guid> languageTextRepository,
|
||||||
IDistributedCache<LanguageTextCacheItem> cache)
|
IDistributedCache<LanguageTextCacheItem> cache)
|
||||||
{
|
{
|
||||||
|
this.languageTextRepository = languageTextRepository;
|
||||||
this.cache = cache;
|
this.cache = cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
using System;
|
using Sozsoft.Languages.Entities;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Extensions.Localization;
|
|
||||||
using Sozsoft.Languages.Entities;
|
|
||||||
using Volo.Abp.Caching;
|
using Volo.Abp.Caching;
|
||||||
using Volo.Abp.DependencyInjection;
|
using Volo.Abp.DependencyInjection;
|
||||||
using Volo.Abp.Domain.Repositories;
|
using Volo.Abp.Domain.Repositories;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
using System.Collections.Generic;
|
using Microsoft.Extensions.Localization;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Extensions.Localization;
|
|
||||||
using Volo.Abp.Localization;
|
using Volo.Abp.Localization;
|
||||||
|
|
||||||
namespace Sozsoft.Languages.Localization;
|
namespace Sozsoft.Languages.Localization;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Sozsoft.Languages.Entities;
|
||||||
using Sozsoft.Languages.Entities;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Volo.Abp.Data;
|
using Volo.Abp.Data;
|
||||||
using Volo.Abp.EntityFrameworkCore;
|
using Volo.Abp.EntityFrameworkCore;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Sozsoft.Languages.Entities;
|
||||||
using Sozsoft.Languages.Entities;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Volo.Abp;
|
using Volo.Abp;
|
||||||
using Volo.Abp.EntityFrameworkCore.Modeling;
|
using Volo.Abp.EntityFrameworkCore.Modeling;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
namespace Sozsoft.MailQueue.Application;
|
namespace Sozsoft.MailQueue.Application;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
using Microsoft.Extensions.Configuration;
|
using Sozsoft.MailQueue.FileGeneration;
|
||||||
using Sozsoft.MailQueue.FileGeneration;
|
|
||||||
using Sozsoft.MailQueue.MailGeneration.Models;
|
using Sozsoft.MailQueue.MailGeneration.Models;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
using Volo.Abp.DependencyInjection;
|
using Volo.Abp.DependencyInjection;
|
||||||
using Volo.Abp.Guids;
|
using Volo.Abp.Guids;
|
||||||
using Volo.Abp.TextTemplating;
|
using Volo.Abp.TextTemplating;
|
||||||
|
|
@ -141,53 +141,53 @@ public class AttachmentGenerator : IAttachmentGenerator, ITransientDependency
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
// if (model.Filtreler.ContainsKey("QUERY") && model.Filtreler.ContainsKey("QUERYSQL"))
|
// if (model.Filtreler.ContainsKey("QUERY") && model.Filtreler.ContainsKey("QUERYSQL"))
|
||||||
// {
|
// {
|
||||||
// /*
|
// /*
|
||||||
//Parametrelerden asagidaki sekilde gelen queryleri en alttaki formata donusturuyoruz
|
//Parametrelerden asagidaki sekilde gelen queryleri en alttaki formata donusturuyoruz
|
||||||
//params
|
//params
|
||||||
//0 =>
|
//0 =>
|
||||||
// QUERY -> qryDepo_Cikis_CekiListesi_Detayli
|
// QUERY -> qryDepo_Cikis_CekiListesi_Detayli
|
||||||
// QUERYSQL -> Ksp_Depo_Cikis_CekiListesi_Detayli 252993
|
// QUERYSQL -> Ksp_Depo_Cikis_CekiListesi_Detayli 252993
|
||||||
// QUERY2 -> qryDepo_Cikis_CekiListesi_Sub
|
// QUERY2 -> qryDepo_Cikis_CekiListesi_Sub
|
||||||
// QUERY2SQL -> KSp_Depo_Cikis_CekiListesi_Sub 252993
|
// QUERY2SQL -> KSp_Depo_Cikis_CekiListesi_Sub 252993
|
||||||
|
|
||||||
//SQL olan keyvalue'lari cikar ve sadece querylerde foreach yap
|
//SQL olan keyvalue'lari cikar ve sadece querylerde foreach yap
|
||||||
// QUERY -> qryDepo_Cikis_CekiListesi_Detayli
|
// QUERY -> qryDepo_Cikis_CekiListesi_Detayli
|
||||||
// dict.add(item.Value, params[item.Key + "SQL"].Value)
|
// dict.add(item.Value, params[item.Key + "SQL"].Value)
|
||||||
// QUERY2 -> qryDepo_Cikis_CekiListesi_Sub
|
// QUERY2 -> qryDepo_Cikis_CekiListesi_Sub
|
||||||
// dict.add(item.Value, params[item.Key + "SQL"].Value)
|
// dict.add(item.Value, params[item.Key + "SQL"].Value)
|
||||||
|
|
||||||
//Bunlari yeni dict'e ekle
|
//Bunlari yeni dict'e ekle
|
||||||
//dict
|
//dict
|
||||||
//qryDepo_Cikis_CekiListesi_Detayli -> Ksp_Depo_Cikis_CekiListesi_Detayli 252993
|
//qryDepo_Cikis_CekiListesi_Detayli -> Ksp_Depo_Cikis_CekiListesi_Detayli 252993
|
||||||
//qryDepo_Cikis_CekiListesi_Sub -> KSp_Depo_Cikis_CekiListesi_Sub 252993
|
//qryDepo_Cikis_CekiListesi_Sub -> KSp_Depo_Cikis_CekiListesi_Sub 252993
|
||||||
// */
|
// */
|
||||||
// //dict -> key (name), value (sql)
|
// //dict -> key (name), value (sql)
|
||||||
// var queries = model.Filtreler
|
// var queries = model.Filtreler
|
||||||
// .Where(a => a.Key.StartsWith("QUERY"))
|
// .Where(a => a.Key.StartsWith("QUERY"))
|
||||||
// .Where(a => !a.Key.EndsWith("SQL"))
|
// .Where(a => !a.Key.EndsWith("SQL"))
|
||||||
// .Where(a => !a.Value.IsNullOrWhiteSpace())
|
// .Where(a => !a.Value.IsNullOrWhiteSpace())
|
||||||
// .ToDictionary(x => x.Value, x => model.Filtreler.GetValueOrDefault(x.Key + "SQL"));
|
// .ToDictionary(x => x.Value, x => model.Filtreler.GetValueOrDefault(x.Key + "SQL"));
|
||||||
|
|
||||||
// return MsAccessReportManager.GenerateAccessReport(
|
// return MsAccessReportManager.GenerateAccessReport(
|
||||||
// model.DbAdi,
|
// model.DbAdi,
|
||||||
// model.Index,
|
// model.Index,
|
||||||
// model.RaporAdi,
|
// model.RaporAdi,
|
||||||
// model.ExportDosyaTipi,
|
// model.ExportDosyaTipi,
|
||||||
// queries,
|
// queries,
|
||||||
// attachmentFolder);
|
// attachmentFolder);
|
||||||
// }
|
// }
|
||||||
// else
|
// else
|
||||||
// {
|
// {
|
||||||
// return MsAccessReportManager.GenerateAccessReport(
|
// return MsAccessReportManager.GenerateAccessReport(
|
||||||
// model.DbAdi,
|
// model.DbAdi,
|
||||||
// model.Index,
|
// model.Index,
|
||||||
// model.RaporAdi,
|
// model.RaporAdi,
|
||||||
// string.Join(" AND ", model.Filtreler.Select(a => $"{a.Key}={a.Value}")),
|
// string.Join(" AND ", model.Filtreler.Select(a => $"{a.Key}={a.Value}")),
|
||||||
// model.ExportDosyaTipi,
|
// model.ExportDosyaTipi,
|
||||||
// attachmentFolder);
|
// attachmentFolder);
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string> ConvertFileToBase64Async(string filePath)
|
private async Task<string> ConvertFileToBase64Async(string filePath)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
using Microsoft.Extensions.Configuration;
|
using Sozsoft.MailQueue.MailGeneration.Models;
|
||||||
using Sozsoft.MailQueue.MailGeneration.Models;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Volo.Abp.DependencyInjection;
|
using Volo.Abp.DependencyInjection;
|
||||||
using Volo.Abp.TextTemplating;
|
using Volo.Abp.TextTemplating;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Sozsoft.MailQueue.Domain.Entities;
|
using Sozsoft.MailQueue.Domain.Entities;
|
||||||
using Sozsoft.MailQueue.EntityFrameworkCore;
|
using Sozsoft.MailQueue.EntityFrameworkCore;
|
||||||
using Sozsoft.MailQueue.MailGeneration.Models;
|
using Sozsoft.MailQueue.MailGeneration.Models;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Volo.Abp.DependencyInjection;
|
using Volo.Abp.DependencyInjection;
|
||||||
using Volo.Abp.Domain.Repositories;
|
using Volo.Abp.Domain.Repositories;
|
||||||
using Volo.Abp.TextTemplating;
|
using Volo.Abp.TextTemplating;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
using Microsoft.Extensions.Configuration;
|
using Sozsoft.Sender.Mail;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Sozsoft.MailQueue.Domain.MailGeneration;
|
using Sozsoft.MailQueue.Domain.MailGeneration;
|
||||||
using Sozsoft.MailQueue.Domain.Shared;
|
using Sozsoft.MailQueue.Domain.Shared;
|
||||||
using Sozsoft.Sender.Mail;
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Volo.Abp.BackgroundWorkers;
|
using Volo.Abp.BackgroundWorkers;
|
||||||
using Volo.Abp.Domain.Repositories;
|
using Volo.Abp.Domain.Repositories;
|
||||||
using Volo.Abp.Guids;
|
using Volo.Abp.Guids;
|
||||||
|
|
@ -182,7 +182,7 @@ public class MailQueueWorker : BackgroundWorkerBase
|
||||||
}
|
}
|
||||||
queue.SendStatus = result.Success;
|
queue.SendStatus = result.Success;
|
||||||
queue.SendTime = Clock.Now;
|
queue.SendTime = Clock.Now;
|
||||||
await Repository.UpdateAsync(queue, cancellationToken: cancellationToken);
|
await Repository.UpdateAsync(queue);
|
||||||
|
|
||||||
// Attachmentlari silebiliriz
|
// Attachmentlari silebiliriz
|
||||||
try
|
try
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
using Amazon;
|
using Amazon;
|
||||||
using Amazon.SQS;
|
using Amazon.SQS;
|
||||||
using Amazon.SQS.Model;
|
using Amazon.SQS.Model;
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Sozsoft.MailQueue.Domain.Entities;
|
|
||||||
using Sozsoft.Sender.Mail.AmazonSes;
|
using Sozsoft.Sender.Mail.AmazonSes;
|
||||||
|
using Sozsoft.MailQueue.Domain.Entities;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
using Volo.Abp.Domain.Repositories;
|
using Volo.Abp.Domain.Repositories;
|
||||||
using Volo.Abp.Domain.Services;
|
using Volo.Abp.Domain.Services;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Sozsoft.MailQueue.Domain;
|
||||||
using Sozsoft.MailQueue.Domain;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Volo.Abp.Data;
|
using Volo.Abp.Data;
|
||||||
using Volo.Abp.EntityFrameworkCore;
|
using Volo.Abp.EntityFrameworkCore;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Sozsoft.MailQueue.Domain;
|
||||||
using Sozsoft.MailQueue.Domain;
|
|
||||||
using Sozsoft.MailQueue.Domain.Entities;
|
using Sozsoft.MailQueue.Domain.Entities;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Volo.Abp;
|
using Volo.Abp;
|
||||||
using Volo.Abp.EntityFrameworkCore.Modeling;
|
using Volo.Abp.EntityFrameworkCore.Modeling;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Sozsoft.MailQueue.Domain.Entities;
|
using Sozsoft.MailQueue.Domain.Entities;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Volo.Abp.DependencyInjection;
|
using Volo.Abp.DependencyInjection;
|
||||||
using Volo.Abp.Domain.Repositories;
|
using Volo.Abp.Domain.Repositories;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using ClosedXML.Excel;
|
using ClosedXML.Excel;
|
||||||
|
using DocumentFormat.OpenXml.Spreadsheet;
|
||||||
using Sozsoft.MailQueue.MailGeneration.Models;
|
using Sozsoft.MailQueue.MailGeneration.Models;
|
||||||
using Volo.Abp.DependencyInjection;
|
using Volo.Abp.DependencyInjection;
|
||||||
|
|
||||||
|
|
@ -79,11 +80,8 @@ public class XLSFileGenerator : IFileGenerator, ITransientDependency
|
||||||
var col = table.TabloSutunlari[j];
|
var col = table.TabloSutunlari[j];
|
||||||
var cell = sheet.Cell(i + 2, j + 1);
|
var cell = sheet.Cell(i + 2, j + 1);
|
||||||
var value = row.GetValueOrDefault(col.SutunAdi);
|
var value = row.GetValueOrDefault(col.SutunAdi);
|
||||||
if (!Enum.TryParse(col.VeriTipi, out XLDataType dataType))
|
var dataType = XLDataType.Text;
|
||||||
{
|
Enum.TryParse(col.VeriTipi, out dataType);
|
||||||
dataType = XLDataType.Text;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (dataType)
|
switch (dataType)
|
||||||
{
|
{
|
||||||
case XLDataType.Number:
|
case XLDataType.Number:
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Microsoft.CodeAnalysis;
|
using Sozsoft.Sender;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Sozsoft.MailQueue.Domain.Shared;
|
using Sozsoft.MailQueue.Domain.Shared;
|
||||||
using Sozsoft.MailQueue.EntityFrameworkCore;
|
using Sozsoft.MailQueue.EntityFrameworkCore;
|
||||||
using Sozsoft.Sender;
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Volo.Abp.Application;
|
using Volo.Abp.Application;
|
||||||
using Volo.Abp.Authorization;
|
using Volo.Abp.Authorization;
|
||||||
|
using Volo.Abp.Mapperly;
|
||||||
using Volo.Abp.Domain;
|
using Volo.Abp.Domain;
|
||||||
using Volo.Abp.EntityFrameworkCore;
|
using Volo.Abp.EntityFrameworkCore;
|
||||||
using Volo.Abp.Localization;
|
using Volo.Abp.Localization;
|
||||||
using Volo.Abp.Localization.ExceptionHandling;
|
using Volo.Abp.Localization.ExceptionHandling;
|
||||||
using Volo.Abp.Mapperly;
|
|
||||||
using Volo.Abp.Modularity;
|
using Volo.Abp.Modularity;
|
||||||
using Volo.Abp.TextTemplating.Razor;
|
using Volo.Abp.TextTemplating.Razor;
|
||||||
using Volo.Abp.VirtualFileSystem;
|
using Volo.Abp.VirtualFileSystem;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Sozsoft.Notifications.Application;
|
||||||
|
|
||||||
|
public class NotificationRemoteServiceConsts
|
||||||
|
{
|
||||||
|
public const string RemoteServiceName = "Notifications";
|
||||||
|
|
||||||
|
public const string ModuleName = "notifications";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Volo.Abp.Authorization.Permissions;
|
using Volo.Abp.Authorization.Permissions;
|
||||||
|
|
||||||
namespace Sozsoft.Notifications.Permissions;
|
namespace Sozsoft.Notifications.Permissions;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,11 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Sozsoft.Notifications.Domain;
|
using Sozsoft.Notifications.Domain;
|
||||||
using Sozsoft.Notifications.Entities;
|
using Sozsoft.Notifications.Entities;
|
||||||
using Sozsoft.Notifications.Enums;
|
using Sozsoft.Notifications.Enums;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Volo.Abp;
|
using Volo.Abp;
|
||||||
using Volo.Abp.Application.Services;
|
using Volo.Abp.Application.Services;
|
||||||
using Volo.Abp.Domain.Entities;
|
using Volo.Abp.Domain.Entities;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
using Riok.Mapperly.Abstractions;
|
|
||||||
using Sozsoft.Notifications.Domain;
|
|
||||||
using Sozsoft.Notifications.Entities;
|
using Sozsoft.Notifications.Entities;
|
||||||
|
using Sozsoft.Notifications.Domain;
|
||||||
using Sozsoft.Notifications.NotificationRules;
|
using Sozsoft.Notifications.NotificationRules;
|
||||||
|
using Riok.Mapperly.Abstractions;
|
||||||
using Volo.Abp.Mapperly;
|
using Volo.Abp.Mapperly;
|
||||||
|
|
||||||
namespace Sozsoft.Notifications.Application;
|
namespace Sozsoft.Notifications.Application;
|
||||||
|
|
@ -9,7 +9,7 @@ namespace Sozsoft.Notifications.Application;
|
||||||
[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)]
|
[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)]
|
||||||
public partial class NotificationTypeToNotificationTypeDtoMapper : MapperBase<NotificationType, NotificationTypeDto>
|
public partial class NotificationTypeToNotificationTypeDtoMapper : MapperBase<NotificationType, NotificationTypeDto>
|
||||||
{
|
{
|
||||||
public override partial NotificationTypeDto Map(NotificationType source);
|
public override partial NotificationTypeDto Map(NotificationType source);
|
||||||
|
|
||||||
public override partial void Map(NotificationType source, NotificationTypeDto destination);
|
public override partial void Map(NotificationType source, NotificationTypeDto destination);
|
||||||
|
|
||||||
|
|
@ -25,7 +25,7 @@ public partial class NotificationTypeToNotificationTypeDtoMapper : MapperBase<No
|
||||||
[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)]
|
[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)]
|
||||||
public partial class NotificationRuleToNotificationRuleDtoMapper : MapperBase<NotificationRule, NotificationRuleDto>
|
public partial class NotificationRuleToNotificationRuleDtoMapper : MapperBase<NotificationRule, NotificationRuleDto>
|
||||||
{
|
{
|
||||||
public override partial NotificationRuleDto Map(NotificationRule source);
|
public override partial NotificationRuleDto Map(NotificationRule source);
|
||||||
|
|
||||||
public override partial void Map(NotificationRule source, NotificationRuleDto destination);
|
public override partial void Map(NotificationRule source, NotificationRuleDto destination);
|
||||||
|
|
||||||
|
|
@ -41,15 +41,10 @@ public partial class NotificationRuleToNotificationRuleDtoMapper : MapperBase<No
|
||||||
[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)]
|
[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)]
|
||||||
public partial class NotificationToNotificationDtoMapper : MapperBase<Notification, NotificationDto>
|
public partial class NotificationToNotificationDtoMapper : MapperBase<Notification, NotificationDto>
|
||||||
{
|
{
|
||||||
// NotificationType hedefte string. Kaynaktaki navigation entity'si doğrudan
|
[MapperIgnoreTarget(nameof(NotificationDto.CreatorFullname))]
|
||||||
// eşlenirse ToString() devreye girip "[ENTITY: NotificationType] Id = ..."
|
|
||||||
// yazısı UI'a düşüyor; bu yüzden açıkça Name alanından beslenir.
|
|
||||||
[MapProperty("NotificationType.Name", nameof(NotificationDto.NotificationType))]
|
|
||||||
[MapperIgnoreTarget(nameof(NotificationDto.CreatorFullname))]
|
|
||||||
[MapperIgnoreTarget(nameof(NotificationDto.TenantId))]
|
[MapperIgnoreTarget(nameof(NotificationDto.TenantId))]
|
||||||
public override partial NotificationDto Map(Notification source);
|
public override partial NotificationDto Map(Notification source);
|
||||||
|
|
||||||
[MapProperty("NotificationType.Name", nameof(NotificationDto.NotificationType))]
|
|
||||||
[MapperIgnoreTarget(nameof(NotificationDto.CreatorFullname))]
|
[MapperIgnoreTarget(nameof(NotificationDto.CreatorFullname))]
|
||||||
[MapperIgnoreTarget(nameof(NotificationDto.TenantId))]
|
[MapperIgnoreTarget(nameof(NotificationDto.TenantId))]
|
||||||
public override partial void Map(Notification source, NotificationDto destination);
|
public override partial void Map(Notification source, NotificationDto destination);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Sozsoft.Notifications.Domain;
|
||||||
using Sozsoft.Notifications.Domain;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Volo.Abp.Application;
|
using Volo.Abp.Application;
|
||||||
using Volo.Abp.Mapperly;
|
using Volo.Abp.Mapperly;
|
||||||
using Volo.Abp.Modularity;
|
using Volo.Abp.Modularity;
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,10 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Sozsoft.Notifications.Domain;
|
using Sozsoft.Notifications.Domain;
|
||||||
using Sozsoft.Notifications.Entities;
|
using Sozsoft.Notifications.Entities;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Volo.Abp.Application.Dtos;
|
using Volo.Abp.Application.Dtos;
|
||||||
using Volo.Abp.Application.Services;
|
using Volo.Abp.Application.Services;
|
||||||
using Volo.Abp.Domain.Repositories;
|
using Volo.Abp.Domain.Repositories;
|
||||||
|
|
@ -23,16 +23,22 @@ public class NotificationRuleAppService : CrudAppService<
|
||||||
INotificationRuleAppService
|
INotificationRuleAppService
|
||||||
{
|
{
|
||||||
private readonly IRepository<NotificationType, Guid> repositoryType;
|
private readonly IRepository<NotificationType, Guid> repositoryType;
|
||||||
|
private readonly IOrganizationUnitRepository repositoryOrganizationUnit;
|
||||||
|
private readonly IdentityUserManager userManager;
|
||||||
private readonly IIdentityUserRepository repositoryUser;
|
private readonly IIdentityUserRepository repositoryUser;
|
||||||
|
|
||||||
public NotificationRuleAppService(
|
public NotificationRuleAppService(
|
||||||
IRepository<NotificationType, Guid> repositoryType,
|
IRepository<NotificationType, Guid> repositoryType,
|
||||||
IRepository<NotificationRule, Guid> repository,
|
IRepository<NotificationRule, Guid> repository,
|
||||||
IIdentityUserRepository repositoryUser
|
IdentityUserManager userManager,
|
||||||
|
IIdentityUserRepository repositoryUser,
|
||||||
|
IOrganizationUnitRepository repositoryOrganizationUnit
|
||||||
) : base(repository)
|
) : base(repository)
|
||||||
{
|
{
|
||||||
this.repositoryType = repositoryType;
|
this.repositoryType = repositoryType;
|
||||||
|
this.userManager = userManager;
|
||||||
this.repositoryUser = repositoryUser;
|
this.repositoryUser = repositoryUser;
|
||||||
|
this.repositoryOrganizationUnit = repositoryOrganizationUnit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string[]> GetNotificationTypes()
|
public async Task<string[]> GetNotificationTypes()
|
||||||
|
|
|
||||||
|
|
@ -9,5 +9,5 @@ public static class NotificationChannels
|
||||||
public const string UiActivity = "UiActivity"; //UI Activity (ABP UI Activity)
|
public const string UiActivity = "UiActivity"; //UI Activity (ABP UI Activity)
|
||||||
public const string UiToast = "UiToast"; //UI Toast (ABP UI Toast) Ayarlar/Sistem/Bildirimler/Chrome açık olması gerekiyor.
|
public const string UiToast = "UiToast"; //UI Toast (ABP UI Toast) Ayarlar/Sistem/Bildirimler/Chrome açık olması gerekiyor.
|
||||||
public const string WhatsApp = "WhatsApp"; //WhatsApp (HTTP API, template-based)
|
public const string WhatsApp = "WhatsApp"; //WhatsApp (HTTP API, template-based)
|
||||||
public const string Telegram = "Telegram"; //Telegram (Bot API, chat based)
|
// public const string Telegram = "Telegram";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
namespace Sozsoft.Notifications.Domain;
|
||||||
|
|
||||||
|
public static class NotificationErrorCodes
|
||||||
|
{
|
||||||
|
//Add your business exception error codes here...
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using Volo.Abp.Domain.Entities.Auditing;
|
using Volo.Abp.Domain.Entities.Auditing;
|
||||||
|
|
||||||
namespace Sozsoft.Notifications.Entities;
|
namespace Sozsoft.Notifications.Entities;
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ public class NotificationIdentifierProvider : INotificationIdentifierProvider
|
||||||
NotificationChannels.UiActivity => user.Id.ToString(),
|
NotificationChannels.UiActivity => user.Id.ToString(),
|
||||||
NotificationChannels.UiToast => user.Id.ToString(),
|
NotificationChannels.UiToast => user.Id.ToString(),
|
||||||
NotificationChannels.WhatsApp => user.PhoneNumber,
|
NotificationChannels.WhatsApp => user.PhoneNumber,
|
||||||
NotificationChannels.Telegram => user.GetProperty<string?>("TelegramChatId"),
|
// NotificationChannels.Telegram => user.PhoneNumber,
|
||||||
_ => null,
|
_ => null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Sozsoft.Notifications.Entities;
|
using Sozsoft.Notifications.Entities;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Volo.Abp.DependencyInjection;
|
using Volo.Abp.DependencyInjection;
|
||||||
using Volo.Abp.Domain.Repositories;
|
using Volo.Abp.Domain.Repositories;
|
||||||
using Volo.Abp.Identity;
|
using Volo.Abp.Identity;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Sozsoft.Notifications.Domain;
|
||||||
using Sozsoft.Notifications.Domain;
|
|
||||||
using Sozsoft.Notifications.Entities;
|
using Sozsoft.Notifications.Entities;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Volo.Abp.Data;
|
using Volo.Abp.Data;
|
||||||
using Volo.Abp.EntityFrameworkCore;
|
using Volo.Abp.EntityFrameworkCore;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Sozsoft.Notifications.Domain;
|
||||||
using Sozsoft.Notifications.Domain;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Volo.Abp.EntityFrameworkCore;
|
using Volo.Abp.EntityFrameworkCore;
|
||||||
using Volo.Abp.Modularity;
|
using Volo.Abp.Modularity;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
using Volo.Abp.Localization;
|
using Volo.Abp.Localization;
|
||||||
|
|
||||||
namespace Sozsoft.Sender.Localization;
|
namespace Sozsoft.Sender.Localization
|
||||||
|
|
||||||
[LocalizationResourceName("SozsoftSender")]
|
|
||||||
public class ErpSenderResource
|
|
||||||
{
|
{
|
||||||
|
[LocalizationResourceName("SozsoftSender")]
|
||||||
|
public class ErpSenderResource
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Volo.Abp.BackgroundJobs;
|
using Volo.Abp.BackgroundJobs;
|
||||||
using Volo.Abp.DependencyInjection;
|
using Volo.Abp.DependencyInjection;
|
||||||
using Volo.Abp.MultiTenancy;
|
using Volo.Abp.MultiTenancy;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Volo.Abp.MultiTenancy;
|
using Volo.Abp.MultiTenancy;
|
||||||
|
|
||||||
namespace Sozsoft.Sender.Mail;
|
namespace Sozsoft.Sender.Mail;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Volo.Abp.Settings;
|
using Volo.Abp.Settings;
|
||||||
|
|
||||||
namespace Sozsoft.Sender.Rocket;
|
namespace Sozsoft.Sender.Rocket;
|
||||||
|
|
@ -11,13 +12,16 @@ public interface IRocketHttpClient
|
||||||
public sealed class RocketHttpClient : IRocketHttpClient, IDisposable
|
public sealed class RocketHttpClient : IRocketHttpClient, IDisposable
|
||||||
{
|
{
|
||||||
private readonly HttpClient httpClient;
|
private readonly HttpClient httpClient;
|
||||||
|
private readonly ILogger<RocketHttpClient> logger;
|
||||||
private readonly ISettingProvider settingProvider;
|
private readonly ISettingProvider settingProvider;
|
||||||
|
|
||||||
public RocketHttpClient(
|
public RocketHttpClient(
|
||||||
HttpClient httpClient,
|
HttpClient httpClient,
|
||||||
|
ILogger<RocketHttpClient> logger,
|
||||||
ISettingProvider settingProvider)
|
ISettingProvider settingProvider)
|
||||||
{
|
{
|
||||||
this.httpClient = httpClient;
|
this.httpClient = httpClient;
|
||||||
|
this.logger = logger;
|
||||||
this.settingProvider = settingProvider;
|
this.settingProvider = settingProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using Volo.Abp.Settings;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Volo.Abp.Settings;
|
||||||
|
|
||||||
namespace Sozsoft.Sender.Sms.PostaGuvercini;
|
namespace Sozsoft.Sender.Sms.PostaGuvercini;
|
||||||
|
|
||||||
|
|
@ -10,13 +11,16 @@ public interface IPostaGuverciniHttpClient
|
||||||
public sealed class PostaGuverciniHttpClient : IPostaGuverciniHttpClient, IDisposable
|
public sealed class PostaGuverciniHttpClient : IPostaGuverciniHttpClient, IDisposable
|
||||||
{
|
{
|
||||||
private readonly HttpClient httpClient;
|
private readonly HttpClient httpClient;
|
||||||
|
private readonly ILogger<PostaGuverciniHttpClient> logger;
|
||||||
private readonly ISettingProvider settingProvider;
|
private readonly ISettingProvider settingProvider;
|
||||||
|
|
||||||
public PostaGuverciniHttpClient(
|
public PostaGuverciniHttpClient(
|
||||||
HttpClient httpClient,
|
HttpClient httpClient,
|
||||||
|
ILogger<PostaGuverciniHttpClient> logger,
|
||||||
ISettingProvider settingProvider)
|
ISettingProvider settingProvider)
|
||||||
{
|
{
|
||||||
this.httpClient = httpClient;
|
this.httpClient = httpClient;
|
||||||
|
this.logger = logger;
|
||||||
this.settingProvider = settingProvider;
|
this.settingProvider = settingProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Sozsoft.Sender.Localization;
|
||||||
using Sozsoft.Sender.Localization;
|
|
||||||
using Sozsoft.Sender.Mail;
|
using Sozsoft.Sender.Mail;
|
||||||
using Sozsoft.Sender.Mail.AmazonSes;
|
using Sozsoft.Sender.Mail.AmazonSes;
|
||||||
using Sozsoft.Sender.Rocket;
|
using Sozsoft.Sender.Rocket;
|
||||||
using Sozsoft.Sender.Sms;
|
using Sozsoft.Sender.Sms;
|
||||||
using Sozsoft.Sender.Sms.PostaGuvercini;
|
using Sozsoft.Sender.Sms.PostaGuvercini;
|
||||||
using Sozsoft.Sender.Telegram;
|
|
||||||
using Sozsoft.Sender.WhatsApp;
|
using Sozsoft.Sender.WhatsApp;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Volo.Abp.BackgroundJobs;
|
using Volo.Abp.BackgroundJobs;
|
||||||
using Volo.Abp.Emailing;
|
using Volo.Abp.Emailing;
|
||||||
using Volo.Abp.Localization;
|
using Volo.Abp.Localization;
|
||||||
|
|
@ -37,7 +36,6 @@ public class ErpSenderModule : AbpModule
|
||||||
context.Services.AddScoped<ISozsoftSmsSender>(sp => sp.GetRequiredService<PostaGuverciniSmsSender>());
|
context.Services.AddScoped<ISozsoftSmsSender>(sp => sp.GetRequiredService<PostaGuverciniSmsSender>());
|
||||||
context.Services.AddScoped<ISozsoftRocketSender>(sp => sp.GetRequiredService<RocketSender>());
|
context.Services.AddScoped<ISozsoftRocketSender>(sp => sp.GetRequiredService<RocketSender>());
|
||||||
context.Services.AddScoped<ISozsoftWhatsAppSender>(sp => sp.GetRequiredService<SozsoftWhatsAppSender>());
|
context.Services.AddScoped<ISozsoftWhatsAppSender>(sp => sp.GetRequiredService<SozsoftWhatsAppSender>());
|
||||||
context.Services.AddScoped<ISozsoftTelegramSender>(sp => sp.GetRequiredService<SozsoftTelegramSender>());
|
|
||||||
|
|
||||||
context.Services.AddHttpClient<PostaGuverciniHttpClient>();
|
context.Services.AddHttpClient<PostaGuverciniHttpClient>();
|
||||||
context.Services.AddScoped<IPostaGuverciniHttpClient>(sp => sp.GetRequiredService<PostaGuverciniHttpClient>());
|
context.Services.AddScoped<IPostaGuverciniHttpClient>(sp => sp.GetRequiredService<PostaGuverciniHttpClient>());
|
||||||
|
|
@ -48,9 +46,6 @@ public class ErpSenderModule : AbpModule
|
||||||
context.Services.AddHttpClient<WhatsAppHttpClient>();
|
context.Services.AddHttpClient<WhatsAppHttpClient>();
|
||||||
context.Services.AddScoped<IWhatsAppHttpClient>(sp => sp.GetRequiredService<WhatsAppHttpClient>());
|
context.Services.AddScoped<IWhatsAppHttpClient>(sp => sp.GetRequiredService<WhatsAppHttpClient>());
|
||||||
|
|
||||||
context.Services.AddHttpClient<TelegramHttpClient>();
|
|
||||||
context.Services.AddScoped<ITelegramHttpClient>(sp => sp.GetRequiredService<TelegramHttpClient>());
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Volo.Abp.DependencyInjection;
|
|
||||||
|
|
||||||
namespace Sozsoft.Sender.Telegram;
|
|
||||||
|
|
||||||
public interface ISozsoftTelegramSender : ITransientDependency
|
|
||||||
{
|
|
||||||
Task<TelegramSendResult> SendAsync(string ChatId, string Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SozsoftTelegramSender : ISozsoftTelegramSender
|
|
||||||
{
|
|
||||||
private readonly ITelegramHttpClient httpClient;
|
|
||||||
private readonly ILogger<SozsoftTelegramSender> logger;
|
|
||||||
|
|
||||||
public SozsoftTelegramSender(
|
|
||||||
ITelegramHttpClient httpClient,
|
|
||||||
ILogger<SozsoftTelegramSender> logger)
|
|
||||||
{
|
|
||||||
this.httpClient = httpClient;
|
|
||||||
this.logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<TelegramSendResult> SendAsync(string ChatId, string Message)
|
|
||||||
{
|
|
||||||
logger.LogInformation("Sending Telegram message. {ChatId}, {Message}", ChatId, Message);
|
|
||||||
var result = await httpClient.SendMessageAsync(ChatId, Message);
|
|
||||||
var success = result?.ok == true;
|
|
||||||
if (success)
|
|
||||||
{
|
|
||||||
logger.LogInformation("Telegram sent successfully. {ChatId}, {Message}", ChatId, Message);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logger.LogWarning("Failed to send Telegram message. {ChatId}, {Message}, {ErrorCode}, {Description}",
|
|
||||||
ChatId, Message, result?.error_code, result?.description);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new TelegramSendResult
|
|
||||||
{
|
|
||||||
Success = success,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
using System.Net.Http.Json;
|
|
||||||
using Volo.Abp.Settings;
|
|
||||||
|
|
||||||
namespace Sozsoft.Sender.Telegram;
|
|
||||||
|
|
||||||
public interface ITelegramHttpClient
|
|
||||||
{
|
|
||||||
Task<TelegramSendResultDto> SendMessageAsync(string ChatId, string Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class TelegramHttpClient : ITelegramHttpClient, IDisposable
|
|
||||||
{
|
|
||||||
private readonly HttpClient httpClient;
|
|
||||||
private readonly ISettingProvider settingProvider;
|
|
||||||
|
|
||||||
public TelegramHttpClient(
|
|
||||||
HttpClient httpClient,
|
|
||||||
ISettingProvider settingProvider)
|
|
||||||
{
|
|
||||||
this.httpClient = httpClient;
|
|
||||||
this.settingProvider = settingProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<TelegramSendResultDto> SendMessageAsync(string ChatId, string Message)
|
|
||||||
{
|
|
||||||
var url = await settingProvider.GetOrNullAsync(TelegramSettingNames.Url);
|
|
||||||
var botToken = await settingProvider.GetOrNullAsync(TelegramSettingNames.BotToken);
|
|
||||||
var parseMode = await settingProvider.GetOrNullAsync(TelegramSettingNames.ParseMode);
|
|
||||||
|
|
||||||
var response = await httpClient.PostAsJsonAsync($"{url.TrimEnd('/')}/bot{botToken}/sendMessage",
|
|
||||||
new TelegramPostMessageInput
|
|
||||||
{
|
|
||||||
chat_id = ChatId,
|
|
||||||
text = Message,
|
|
||||||
parse_mode = parseMode.IsNullOrWhiteSpace() ? null : parseMode,
|
|
||||||
disable_web_page_preview = true
|
|
||||||
});
|
|
||||||
|
|
||||||
// Bot API hatayı 4xx gövdesinde açıklar; durum kodunu fırlatmak açıklamayı kaybettirir.
|
|
||||||
return await response.Content.ReadFromJsonAsync<TelegramSendResultDto>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose() => httpClient?.Dispose();
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
namespace Sozsoft.Sender.Telegram;
|
|
||||||
|
|
||||||
public class TelegramPostMessageInput
|
|
||||||
{
|
|
||||||
public string chat_id { get; set; }
|
|
||||||
public string text { get; set; }
|
|
||||||
public string parse_mode { get; set; }
|
|
||||||
public bool disable_web_page_preview { get; set; }
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
namespace Sozsoft.Sender.Telegram;
|
|
||||||
|
|
||||||
public class TelegramSendResult
|
|
||||||
{
|
|
||||||
public bool Success { get; set; }
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
namespace Sozsoft.Sender.Telegram;
|
|
||||||
|
|
||||||
public class TelegramSendResultDto
|
|
||||||
{
|
|
||||||
public bool ok { get; set; }
|
|
||||||
public int? error_code { get; set; }
|
|
||||||
public string description { get; set; }
|
|
||||||
public Message result { get; set; }
|
|
||||||
|
|
||||||
public class Message
|
|
||||||
{
|
|
||||||
public long message_id { get; set; }
|
|
||||||
public int date { get; set; }
|
|
||||||
public Chat chat { get; set; }
|
|
||||||
|
|
||||||
public class Chat
|
|
||||||
{
|
|
||||||
public long id { get; set; }
|
|
||||||
public string type { get; set; }
|
|
||||||
public string username { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
namespace Sozsoft.Sender.Telegram;
|
|
||||||
|
|
||||||
public static class TelegramSettingNames
|
|
||||||
{
|
|
||||||
public const string Default = "App.Sender.Telegram.";
|
|
||||||
public const string Url = Default + "Url"; // https://api.telegram.org
|
|
||||||
public const string BotToken = Default + "BotToken"; // BotFather token: 123456:ABC-DEF...
|
|
||||||
public const string ParseMode = Default + "ParseMode"; // HTML | Markdown | MarkdownV2 | (bos)
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Volo.Abp.Settings;
|
using Volo.Abp.Settings;
|
||||||
|
|
||||||
namespace Sozsoft.Sender.WhatsApp;
|
namespace Sozsoft.Sender.WhatsApp;
|
||||||
|
|
@ -12,13 +13,16 @@ public interface IWhatsAppHttpClient
|
||||||
public sealed class WhatsAppHttpClient : IWhatsAppHttpClient, IDisposable
|
public sealed class WhatsAppHttpClient : IWhatsAppHttpClient, IDisposable
|
||||||
{
|
{
|
||||||
private readonly HttpClient httpClient;
|
private readonly HttpClient httpClient;
|
||||||
|
private readonly ILogger<WhatsAppHttpClient> logger;
|
||||||
private readonly ISettingProvider settingProvider;
|
private readonly ISettingProvider settingProvider;
|
||||||
|
|
||||||
public WhatsAppHttpClient(
|
public WhatsAppHttpClient(
|
||||||
HttpClient httpClient,
|
HttpClient httpClient,
|
||||||
|
ILogger<WhatsAppHttpClient> logger,
|
||||||
ISettingProvider settingProvider)
|
ISettingProvider settingProvider)
|
||||||
{
|
{
|
||||||
this.httpClient = httpClient;
|
this.httpClient = httpClient;
|
||||||
|
this.logger = logger;
|
||||||
this.settingProvider = settingProvider;
|
this.settingProvider = settingProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Volo.Abp.Authorization.Permissions;
|
using Volo.Abp.Authorization.Permissions;
|
||||||
|
|
||||||
namespace Sozsoft.Settings.Permissions;
|
namespace Sozsoft.Settings.Permissions;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Sozsoft.Settings;
|
||||||
|
|
||||||
|
public class SettingsRemoteServiceConsts
|
||||||
|
{
|
||||||
|
public const string RemoteServiceName = "Settings";
|
||||||
|
|
||||||
|
public const string ModuleName = "settings";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Sozsoft.Languages;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Volo.Abp.Application.Services;
|
using Volo.Abp.Application.Services;
|
||||||
using Volo.Abp.MultiTenancy;
|
using Volo.Abp.MultiTenancy;
|
||||||
|
|
@ -14,17 +15,20 @@ namespace Sozsoft.Settings;
|
||||||
public class SettingUiAppService : ApplicationService, ISettingUiAppService
|
public class SettingUiAppService : ApplicationService, ISettingUiAppService
|
||||||
{
|
{
|
||||||
//private readonly IRepository<SettingDefinition, Guid> repositorySettingDefinition;
|
//private readonly IRepository<SettingDefinition, Guid> repositorySettingDefinition;
|
||||||
|
private readonly ILanguageKeyIntegrationService languageKeyIntegrationService;
|
||||||
private readonly ISettingDefinitionManager settingDefinitionManager;
|
private readonly ISettingDefinitionManager settingDefinitionManager;
|
||||||
private readonly ISettingManager settingManager;
|
private readonly ISettingManager settingManager;
|
||||||
private readonly ErpSettingDefinitionManager ErpSettingDefinitionManager;
|
private readonly ErpSettingDefinitionManager ErpSettingDefinitionManager;
|
||||||
private readonly ICurrentTenant currentTenant;
|
private readonly ICurrentTenant currentTenant;
|
||||||
|
|
||||||
public SettingUiAppService(
|
public SettingUiAppService(
|
||||||
|
ILanguageKeyIntegrationService languageKeyIntegrationService,
|
||||||
ISettingDefinitionManager settingDefinitionManager,
|
ISettingDefinitionManager settingDefinitionManager,
|
||||||
ISettingManager settingManager,
|
ISettingManager settingManager,
|
||||||
ErpSettingDefinitionManager ErpSettingDefinitionManager,
|
ErpSettingDefinitionManager ErpSettingDefinitionManager,
|
||||||
ICurrentTenant currentTenant)
|
ICurrentTenant currentTenant)
|
||||||
{
|
{
|
||||||
|
this.languageKeyIntegrationService = languageKeyIntegrationService;
|
||||||
this.settingDefinitionManager = settingDefinitionManager;
|
this.settingDefinitionManager = settingDefinitionManager;
|
||||||
this.settingManager = settingManager;
|
this.settingManager = settingManager;
|
||||||
this.ErpSettingDefinitionManager = ErpSettingDefinitionManager;
|
this.ErpSettingDefinitionManager = ErpSettingDefinitionManager;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
using Riok.Mapperly.Abstractions;
|
|
||||||
using Sozsoft.Settings.Entities;
|
using Sozsoft.Settings.Entities;
|
||||||
|
using Riok.Mapperly.Abstractions;
|
||||||
using Volo.Abp.Mapperly;
|
using Volo.Abp.Mapperly;
|
||||||
|
|
||||||
namespace Sozsoft.Settings;
|
namespace Sozsoft.Settings;
|
||||||
|
|
@ -7,7 +7,7 @@ namespace Sozsoft.Settings;
|
||||||
[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)]
|
[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)]
|
||||||
public partial class SettingDefinitionToSettingDefinitionDtoMapper : TwoWayMapperBase<SettingDefinition, SettingDefinitionDto>
|
public partial class SettingDefinitionToSettingDefinitionDtoMapper : TwoWayMapperBase<SettingDefinition, SettingDefinitionDto>
|
||||||
{
|
{
|
||||||
public override partial SettingDefinitionDto Map(SettingDefinition source);
|
public override partial SettingDefinitionDto Map(SettingDefinition source);
|
||||||
|
|
||||||
public override partial void Map(SettingDefinition source, SettingDefinitionDto destination);
|
public override partial void Map(SettingDefinition source, SettingDefinitionDto destination);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
namespace Sozsoft.Settings;
|
||||||
|
|
||||||
|
public static class SettingsErrorCodes
|
||||||
|
{
|
||||||
|
//Add your business exception error codes here...
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Sozsoft.Settings.Localization;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
using Sozsoft.Settings.Localization;
|
|
||||||
using Volo.Abp.Domain.Repositories;
|
using Volo.Abp.Domain.Repositories;
|
||||||
using Volo.Abp.Domain.Services;
|
using Volo.Abp.Domain.Services;
|
||||||
using Volo.Abp.Settings;
|
using Volo.Abp.Settings;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Volo.Abp.Modularity;
|
using Volo.Abp.Modularity;
|
||||||
using Volo.Abp.SettingManagement;
|
using Volo.Abp.SettingManagement;
|
||||||
using Volo.Abp.Settings;
|
using Volo.Abp.Settings;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Sozsoft.Settings.Entities;
|
||||||
using Sozsoft.Settings.Entities;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Volo.Abp.Data;
|
using Volo.Abp.Data;
|
||||||
using Volo.Abp.EntityFrameworkCore;
|
using Volo.Abp.EntityFrameworkCore;
|
||||||
using Volo.Abp.SettingManagement.EntityFrameworkCore;
|
using Volo.Abp.SettingManagement.EntityFrameworkCore;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using Sozsoft.Settings.Entities;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||||
using Sozsoft.Settings.Entities;
|
|
||||||
using Volo.Abp;
|
using Volo.Abp;
|
||||||
using Volo.Abp.EntityFrameworkCore.Modeling;
|
using Volo.Abp.EntityFrameworkCore.Modeling;
|
||||||
using Volo.Abp.SettingManagement.EntityFrameworkCore;
|
using Volo.Abp.SettingManagement.EntityFrameworkCore;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Sozsoft.SqlQueryManager.Application.Contracts;
|
namespace Sozsoft.SqlQueryManager.Application.Contracts;
|
||||||
|
|
||||||
public class DatabaseTableDto
|
public class DatabaseTableDto
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Volo.Abp.Application.Services;
|
using Volo.Abp.Application.Services;
|
||||||
|
|
||||||
|
|
@ -30,16 +30,29 @@ public interface ISqlObjectManagerAppService : IApplicationService
|
||||||
Task<string> GetNativeObjectDefinitionAsync(string dataSourceCode, string schemaName, string objectName);
|
Task<string> GetNativeObjectDefinitionAsync(string dataSourceCode, string schemaName, string objectName);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Saves the T-SQL script to {SeedsRoot}/{host|tenants/{tenantId}}/{sql|postgres}/object/{fileName}.sql
|
/// Saves the T-SQL script to Seeds/SqlData/{fileName}.sql in the DbMigrator project.
|
||||||
/// (repo: configs/seeds). The scope folder follows the current tenant: host context writes to host/.
|
|
||||||
/// Called automatically after a successful SqlTableDesigner deploy so the script can be re-seeded.
|
/// Called automatically after a successful SqlTableDesigner deploy so the script can be re-seeded.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task SaveTableScriptAsync(SaveTableScriptDto input);
|
Task SaveTableScriptAsync(SaveTableScriptDto input);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deletes matching SQL seed files from the current scope's object folder when objects are
|
/// Deletes matching SQL seed files from Seeds/SqlData when objects are dropped from the UI.
|
||||||
/// dropped from the UI.
|
|
||||||
/// Non-existing files are ignored.
|
/// Non-existing files are ignored.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task DeleteSqlDataFilesAsync(DeleteSqlDataFilesDto input);
|
Task DeleteSqlDataFilesAsync(DeleteSqlDataFilesDto input);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Lists .sql files currently available under DbMigrator Seeds/SqlData.
|
||||||
|
/// </summary>
|
||||||
|
Task<List<SqlDataFileDto>> GetSqlDataFilesAsync(string dataDirectoryName = "SqlData", string relativePath = "");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a .sql seed file content from the selected data directory.
|
||||||
|
/// </summary>
|
||||||
|
Task<string> GetSqlDataFileContentAsync(string dataDirectoryName = "SqlData", string relativePath = "");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Moves a SQL seed file between the selected data directory root and HostData.
|
||||||
|
/// </summary>
|
||||||
|
Task MoveSqlDataFileAsync(MoveSqlDataFileDto input);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Sozsoft.SqlQueryManager.Application.Contracts;
|
||||||
|
|
||||||
|
public class SqlDataFileDto
|
||||||
|
{
|
||||||
|
public string FileName { get; set; } = string.Empty;
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
public string RelativePath { get; set; } = string.Empty;
|
||||||
|
public bool IsDirectory { get; set; }
|
||||||
|
public DateTime CreatedAt { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Sozsoft.SqlQueryManager.Application.Contracts;
|
namespace Sozsoft.SqlQueryManager.Application.Contracts;
|
||||||
|
|
||||||
|
|
@ -20,8 +20,7 @@ public class SqlQueryExecutionResultDto
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Input for saving a T-SQL script file to the seeds folder
|
/// Input for saving a T-SQL script file to DbMigrator Seeds/SqlData.
|
||||||
/// ({App:SeedsPath}/{host|tenants/{tenantId}}/{sql|postgres}/object).
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class SaveTableScriptDto
|
public class SaveTableScriptDto
|
||||||
{
|
{
|
||||||
|
|
@ -37,8 +36,7 @@ public class SaveTableScriptDto
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Input for deleting seed files from the seeds folder
|
/// Input for deleting seed files from DbMigrator Seeds/SqlData.
|
||||||
/// ({App:SeedsPath}/{host|tenants/{tenantId}}/{sql|postgres}/object).
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class DeleteSqlDataFilesDto
|
public class DeleteSqlDataFilesDto
|
||||||
{
|
{
|
||||||
|
|
@ -48,3 +46,10 @@ public class DeleteSqlDataFilesDto
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<string> FileNames { get; set; } = new();
|
public List<string> FileNames { get; set; } = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class MoveSqlDataFileDto
|
||||||
|
{
|
||||||
|
public string DataDirectoryName { get; set; } = string.Empty;
|
||||||
|
public string SourceRelativePath { get; set; } = string.Empty;
|
||||||
|
public string TargetRelativePath { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Sozsoft.SqlQueryManager.Application.Contracts;
|
namespace Sozsoft.SqlQueryManager.Application.Contracts;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Volo.Abp.Application;
|
using Volo.Abp.Application;
|
||||||
using Volo.Abp.Authorization;
|
using Volo.Abp.Authorization;
|
||||||
using Volo.Abp.Modularity;
|
using Volo.Abp.Modularity;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
namespace Sozsoft.SqlQueryManager.Application.Contracts;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Sozsoft.SqlQueryManager.Application.Contracts;
|
||||||
|
|
||||||
public class SqlTemplateDto
|
public class SqlTemplateDto
|
||||||
{
|
{
|
||||||
|
|
@ -7,3 +9,10 @@ public class SqlTemplateDto
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
public string Template { get; set; }
|
public string Template { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class GetTemplateInput
|
||||||
|
{
|
||||||
|
public string TemplateType { get; set; }
|
||||||
|
public string ObjectName { get; set; }
|
||||||
|
public string SchemaName { get; set; }
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,21 +2,19 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Sozsoft.SqlQueryManager.Application.Contracts;
|
||||||
|
using Sozsoft.SqlQueryManager.Domain.Services;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Sozsoft.Platform.Data.Seeds;
|
|
||||||
using Sozsoft.Platform.Enums;
|
|
||||||
using Sozsoft.Platform.Queries;
|
|
||||||
using Sozsoft.Settings;
|
|
||||||
using Sozsoft.SqlQueryManager.Application.Contracts;
|
|
||||||
using Sozsoft.SqlQueryManager.Domain.Services;
|
|
||||||
using Volo.Abp.Application.Services;
|
using Volo.Abp.Application.Services;
|
||||||
using Volo.Abp.MultiTenancy;
|
using Volo.Abp.MultiTenancy;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Sozsoft.Platform.Enums;
|
||||||
|
using Sozsoft.Platform.Queries;
|
||||||
|
|
||||||
namespace Sozsoft.SqlQueryManager.Application;
|
namespace Sozsoft.SqlQueryManager.Application;
|
||||||
|
|
||||||
|
|
@ -50,7 +48,7 @@ public class SqlObjectManagerAppService : ApplicationService, ISqlObjectManagerA
|
||||||
private readonly IDataSourceManager _dataSourceManager;
|
private readonly IDataSourceManager _dataSourceManager;
|
||||||
private readonly ICurrentTenant _currentTenant;
|
private readonly ICurrentTenant _currentTenant;
|
||||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||||
private readonly IConfiguration _configuration;
|
private readonly IHostEnvironment _hostEnvironment;
|
||||||
private readonly ILogger<SqlObjectManagerAppService> _logger;
|
private readonly ILogger<SqlObjectManagerAppService> _logger;
|
||||||
|
|
||||||
public SqlObjectManagerAppService(
|
public SqlObjectManagerAppService(
|
||||||
|
|
@ -59,7 +57,7 @@ public class SqlObjectManagerAppService : ApplicationService, ISqlObjectManagerA
|
||||||
IDataSourceManager dataSourceManager,
|
IDataSourceManager dataSourceManager,
|
||||||
ICurrentTenant currentTenant,
|
ICurrentTenant currentTenant,
|
||||||
IHttpContextAccessor httpContextAccessor,
|
IHttpContextAccessor httpContextAccessor,
|
||||||
IConfiguration configuration,
|
IHostEnvironment hostEnvironment,
|
||||||
ILogger<SqlObjectManagerAppService> logger)
|
ILogger<SqlObjectManagerAppService> logger)
|
||||||
{
|
{
|
||||||
_sqlExecutorService = sqlExecutorService;
|
_sqlExecutorService = sqlExecutorService;
|
||||||
|
|
@ -67,7 +65,7 @@ public class SqlObjectManagerAppService : ApplicationService, ISqlObjectManagerA
|
||||||
_dataSourceManager = dataSourceManager;
|
_dataSourceManager = dataSourceManager;
|
||||||
_currentTenant = currentTenant;
|
_currentTenant = currentTenant;
|
||||||
_httpContextAccessor = httpContextAccessor;
|
_httpContextAccessor = httpContextAccessor;
|
||||||
_configuration = configuration;
|
_hostEnvironment = hostEnvironment;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -956,27 +954,191 @@ FROM (
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("api/app/sql-object-manager/sql-data-files")]
|
||||||
/// <summary>
|
public Task<List<SqlDataFileDto>> GetSqlDataFilesAsync(
|
||||||
/// Script dosyalarinin yazilacagi klasor. SqlDataSeeder ile ayni cozumleyiciyi kullanir
|
[FromQuery] string dataDirectoryName = "SqlData",
|
||||||
/// (App:SeedsPath -> configs/seeds), aksi halde deploy edilen nesne seed olarak geri okunmaz.
|
[FromQuery] string relativePath = "")
|
||||||
/// <para>
|
|
||||||
/// Yol, islemin yapildigi veritabani kapsamini izler: host (varsayilan) baglaminda
|
|
||||||
/// <c>host/{sql|postgres}/object</c>, bir tenant secilmisken
|
|
||||||
/// <c>tenants/{tenantId}/{sql|postgres}/object</c>. Boylece nesne, seed sirasinda
|
|
||||||
/// olusturuldugu veritabaninda yeniden olusturulur.
|
|
||||||
/// </para>
|
|
||||||
/// </summary>
|
|
||||||
private string ResolveSqlDataOutputPath()
|
|
||||||
{
|
{
|
||||||
var providerDirectoryName = SeedPathResolver.GetProviderFolderName(
|
ValidateTenantAccess();
|
||||||
SettingsConsts.DefaultDatabaseProvider == SettingsConsts.DatabaseProvider.PostgreSql);
|
|
||||||
|
|
||||||
return SeedPathResolver.GetScopePath(
|
try
|
||||||
_configuration,
|
{
|
||||||
_currentTenant.Id,
|
var rootPath = ResolveSqlDataOutputPath(dataDirectoryName);
|
||||||
providerDirectoryName,
|
var outputPath = ResolveSqlDataChildPath(rootPath, relativePath);
|
||||||
SeedPathResolver.ObjectFolder);
|
if (!Directory.Exists(outputPath))
|
||||||
|
return Task.FromResult(new List<SqlDataFileDto>());
|
||||||
|
|
||||||
|
var directories = Directory.GetDirectories(outputPath, "*", SearchOption.TopDirectoryOnly)
|
||||||
|
.Where(d => string.Equals(Path.GetFileName(d), "HostData", StringComparison.OrdinalIgnoreCase))
|
||||||
|
.Select(d => new SqlDataFileDto
|
||||||
|
{
|
||||||
|
FileName = Path.GetFileName(d)!,
|
||||||
|
Name = Path.GetFileName(d)!,
|
||||||
|
RelativePath = BuildSqlDataRelativePath(relativePath, Path.GetFileName(d)!),
|
||||||
|
IsDirectory = true,
|
||||||
|
CreatedAt = Directory.GetCreationTime(d)
|
||||||
|
});
|
||||||
|
|
||||||
|
var files = Directory.GetFiles(outputPath, "*.sql", SearchOption.TopDirectoryOnly)
|
||||||
|
.Select(f => new SqlDataFileDto
|
||||||
|
{
|
||||||
|
FileName = Path.GetFileName(f)!,
|
||||||
|
Name = Path.GetFileName(f)!,
|
||||||
|
RelativePath = BuildSqlDataRelativePath(relativePath, Path.GetFileName(f)!),
|
||||||
|
IsDirectory = false,
|
||||||
|
CreatedAt = File.GetCreationTime(f)
|
||||||
|
})
|
||||||
|
.Where(x => !string.IsNullOrWhiteSpace(x.Name));
|
||||||
|
|
||||||
|
var entries = directories
|
||||||
|
.Concat(files)
|
||||||
|
.OrderByDescending(x => x.IsDirectory)
|
||||||
|
.ThenBy(x => x.Name, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
return Task.FromResult(entries);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Failed to list SQL seed files: {Message}", ex.Message);
|
||||||
|
return Task.FromResult(new List<SqlDataFileDto>());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("api/app/sql-object-manager/sql-data-file-content")]
|
||||||
|
public async Task<string> GetSqlDataFileContentAsync(
|
||||||
|
[FromQuery] string dataDirectoryName = "SqlData",
|
||||||
|
[FromQuery] string relativePath = "")
|
||||||
|
{
|
||||||
|
ValidateTenantAccess();
|
||||||
|
|
||||||
|
var rootPath = ResolveSqlDataOutputPath(dataDirectoryName);
|
||||||
|
var filePath = ResolveSqlDataChildPath(rootPath, relativePath);
|
||||||
|
|
||||||
|
if (!File.Exists(filePath))
|
||||||
|
throw new Volo.Abp.UserFriendlyException("SQL seed file was not found.");
|
||||||
|
|
||||||
|
if (!string.Equals(Path.GetExtension(filePath), ".sql", StringComparison.OrdinalIgnoreCase))
|
||||||
|
throw new Volo.Abp.UserFriendlyException("Only .sql files can be previewed.");
|
||||||
|
|
||||||
|
return await File.ReadAllTextAsync(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("api/app/sql-object-manager/move-sql-data-file")]
|
||||||
|
public Task MoveSqlDataFileAsync(MoveSqlDataFileDto input)
|
||||||
|
{
|
||||||
|
ValidateTenantAccess();
|
||||||
|
|
||||||
|
if (input == null)
|
||||||
|
throw new Volo.Abp.UserFriendlyException("Invalid move request.");
|
||||||
|
|
||||||
|
var rootPath = ResolveSqlDataOutputPath(input.DataDirectoryName);
|
||||||
|
var sourcePath = ResolveSqlDataChildPath(rootPath, input.SourceRelativePath);
|
||||||
|
var targetPath = ResolveSqlDataChildPath(rootPath, input.TargetRelativePath);
|
||||||
|
|
||||||
|
if (!File.Exists(sourcePath))
|
||||||
|
throw new Volo.Abp.UserFriendlyException("Source file was not found.");
|
||||||
|
|
||||||
|
if (!string.Equals(Path.GetExtension(sourcePath), ".sql", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
!string.Equals(Path.GetExtension(targetPath), ".sql", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
throw new Volo.Abp.UserFriendlyException("Only .sql files can be moved.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Directory.CreateDirectory(Path.GetDirectoryName(targetPath)!);
|
||||||
|
|
||||||
|
if (File.Exists(targetPath))
|
||||||
|
throw new Volo.Abp.UserFriendlyException("A file with the same name already exists in the target folder.");
|
||||||
|
|
||||||
|
File.Move(sourcePath, targetPath);
|
||||||
|
_logger.LogInformation("SQL seed file moved from {SourcePath} to {TargetPath}", sourcePath, targetPath);
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ResolveSqlDataOutputPath()
|
||||||
|
{
|
||||||
|
return ResolveSqlDataOutputPath("SqlData");
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ResolveSqlDataOutputPath(string dataDirectoryName)
|
||||||
|
{
|
||||||
|
const string dbMigratorName = "Sozsoft.Platform.DbMigrator";
|
||||||
|
var safeDirectoryName = NormalizeSqlDataDirectoryName(dataDirectoryName);
|
||||||
|
var dir = new DirectoryInfo(_hostEnvironment.ContentRootPath);
|
||||||
|
|
||||||
|
while (dir != null)
|
||||||
|
{
|
||||||
|
var candidate = Path.Combine(dir.FullName, "src", dbMigratorName, "Seeds");
|
||||||
|
if (Directory.Exists(candidate))
|
||||||
|
return Path.Combine(candidate, safeDirectoryName);
|
||||||
|
|
||||||
|
candidate = Path.Combine(dir.FullName, dbMigratorName, "Seeds");
|
||||||
|
if (Directory.Exists(candidate))
|
||||||
|
return Path.Combine(candidate, safeDirectoryName);
|
||||||
|
|
||||||
|
dir = dir.Parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Path.Combine(_hostEnvironment.ContentRootPath, "Seeds", safeDirectoryName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeSqlDataDirectoryName(string dataDirectoryName)
|
||||||
|
{
|
||||||
|
return string.Equals(dataDirectoryName, "PostgresData", StringComparison.OrdinalIgnoreCase)
|
||||||
|
? "PostgresData"
|
||||||
|
: "SqlData";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ResolveSqlDataChildPath(string rootPath, string relativePath)
|
||||||
|
{
|
||||||
|
var normalized = NormalizeSqlDataRelativePath(relativePath);
|
||||||
|
var fullPath = Path.GetFullPath(Path.Combine(rootPath, normalized));
|
||||||
|
var fullRoot = Path.GetFullPath(rootPath);
|
||||||
|
var fullRootWithSeparator = fullRoot.EndsWith(Path.DirectorySeparatorChar)
|
||||||
|
? fullRoot
|
||||||
|
: fullRoot + Path.DirectorySeparatorChar;
|
||||||
|
|
||||||
|
if (!string.Equals(fullPath, fullRoot, StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
!fullPath.StartsWith(fullRootWithSeparator, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
throw new Volo.Abp.UserFriendlyException("Invalid path.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return fullPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeSqlDataRelativePath(string relativePath)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(relativePath))
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
var normalized = relativePath.Replace('\\', '/').Trim('/');
|
||||||
|
var parts = normalized.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
if (parts.Length == 0)
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
if (parts.Any(p => p == "." || p == ".." || p.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0))
|
||||||
|
throw new Volo.Abp.UserFriendlyException("Invalid path.");
|
||||||
|
|
||||||
|
var isRootFile = parts.Length == 1 && parts[0].EndsWith(".sql", StringComparison.OrdinalIgnoreCase);
|
||||||
|
var isHostDataFolder = parts.Length == 1 && string.Equals(parts[0], "HostData", StringComparison.OrdinalIgnoreCase);
|
||||||
|
var isHostDataFile = parts.Length == 2 &&
|
||||||
|
string.Equals(parts[0], "HostData", StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
parts[1].EndsWith(".sql", StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
if (!isRootFile && !isHostDataFolder && !isHostDataFile)
|
||||||
|
throw new Volo.Abp.UserFriendlyException("Invalid path.");
|
||||||
|
|
||||||
|
return Path.Combine(parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildSqlDataRelativePath(string parentRelativePath, string name)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(parentRelativePath))
|
||||||
|
return name;
|
||||||
|
|
||||||
|
return $"{parentRelativePath.Trim('/', '\\')}/{name}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
namespace Sozsoft.SqlQueryManager.Application;
|
namespace Sozsoft.SqlQueryManager.Application;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Volo.Abp.Localization;
|
using Volo.Abp.Localization;
|
||||||
|
|
||||||
namespace Sozsoft.SqlQueryManager.Domain.Shared;
|
namespace Sozsoft.SqlQueryManager.Domain.Shared;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Sozsoft.SqlQueryManager.Domain.Shared;
|
namespace Sozsoft.SqlQueryManager.Domain.Shared;
|
||||||
|
|
||||||
// SqlObjectType, SqlFunctionType and SqlQueryStatus enums removed:
|
// SqlObjectType, SqlFunctionType and SqlQueryStatus enums removed:
|
||||||
// The module no longer persists SQL objects and therefore does not need these enums.
|
// The module no longer persists SQL objects and therefore does not need these enums.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Sozsoft.SqlQueryManager.Domain.Shared;
|
using Sozsoft.SqlQueryManager.Domain.Shared;
|
||||||
using Volo.Abp.Domain;
|
using Volo.Abp.Domain;
|
||||||
using Volo.Abp.Localization;
|
using Volo.Abp.Localization;
|
||||||
using Volo.Abp.Localization.ExceptionHandling;
|
using Volo.Abp.Localization.ExceptionHandling;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
namespace Sozsoft.SqlQueryManager.Domain.Shared;
|
||||||
|
|
||||||
|
public static class SqlQueryManagerErrorCodes
|
||||||
|
{
|
||||||
|
public const string InvalidSqlQuery = "SqlQueryManager:InvalidSqlQuery";
|
||||||
|
public const string DataSourceNotFound = "SqlQueryManager:DataSourceNotFound";
|
||||||
|
public const string ExecutionFailed = "SqlQueryManager:ExecutionFailed";
|
||||||
|
public const string PermissionDenied = "SqlQueryManager:PermissionDenied";
|
||||||
|
public const string InvalidObjectName = "SqlQueryManager:InvalidObjectName";
|
||||||
|
public const string ObjectAlreadyExists = "SqlQueryManager:ObjectAlreadyExists";
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Volo.Abp.Reflection;
|
using Volo.Abp.Reflection;
|
||||||
|
|
||||||
namespace Sozsoft.SqlQueryManager.Domain.Shared;
|
namespace Sozsoft.SqlQueryManager.Domain.Shared;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Sozsoft.SqlQueryManager.Domain.Services;
|
namespace Sozsoft.SqlQueryManager.Domain.Services;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Sozsoft.Platform.DynamicData;
|
using Sozsoft.Platform.DynamicData;
|
||||||
using Sozsoft.Platform.Enums;
|
using Sozsoft.Platform.Enums;
|
||||||
using Sozsoft.Platform.Queries;
|
using Sozsoft.Platform.Queries;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Volo.Abp;
|
using Volo.Abp;
|
||||||
using Volo.Abp.Domain.Services;
|
using Volo.Abp.Domain.Services;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
using Volo.Abp.DependencyInjection;
|
using Volo.Abp.DependencyInjection;
|
||||||
|
|
||||||
namespace Sozsoft.SqlQueryManager.Domain.Services;
|
namespace Sozsoft.SqlQueryManager.Domain.Services;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using Volo.Abp.Domain;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Volo.Abp.Domain;
|
||||||
using Volo.Abp.Modularity;
|
using Volo.Abp.Modularity;
|
||||||
|
|
||||||
namespace Sozsoft.SqlQueryManager;
|
namespace Sozsoft.SqlQueryManager;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Volo.Abp.Data;
|
using Volo.Abp.Data;
|
||||||
using Volo.Abp.EntityFrameworkCore;
|
using Volo.Abp.EntityFrameworkCore;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Volo.Abp.EntityFrameworkCore;
|
using Volo.Abp.EntityFrameworkCore;
|
||||||
using Volo.Abp.Modularity;
|
using Volo.Abp.Modularity;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Volo.Abp.Application.Dtos;
|
using Volo.Abp.Application.Dtos;
|
||||||
|
|
||||||
namespace Sozsoft.Platform.AuditLogs;
|
namespace Sozsoft.Platform.AuditLogs;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
using System;
|
|
||||||
using Volo.Abp.Application.Dtos;
|
|
||||||
|
|
||||||
namespace Sozsoft.Platform.ChangeLogs;
|
|
||||||
|
|
||||||
public class ChangeLogDto : EntityDto<Guid>
|
|
||||||
{
|
|
||||||
public string Version { get; set; }
|
|
||||||
public DateTime ReleaseDate { get; set; }
|
|
||||||
|
|
||||||
/// <summary>Sürüm notu maddeleri (entity'de satır satır saklanır).</summary>
|
|
||||||
public string[] ChangeLog { get; set; }
|
|
||||||
}
|
|
||||||
|
|
@ -7,31 +7,21 @@ namespace Sozsoft.Platform.DeveloperKit;
|
||||||
public class CustomComponentDto : FullAuditedEntityDto<Guid>
|
public class CustomComponentDto : FullAuditedEntityDto<Guid>
|
||||||
{
|
{
|
||||||
public string Name { get; set; } = string.Empty;
|
public string Name { get; set; } = string.Empty;
|
||||||
/// <summary>Rotasi olmayan bilesende (ust bilgi, alt bilgi) bostur.</summary>
|
public string RoutePath { get; set; } = string.Empty;
|
||||||
public string? RoutePath { get; set; }
|
|
||||||
public string Code { get; set; } = string.Empty;
|
public string Code { get; set; } = string.Empty;
|
||||||
public string? Props { get; set; }
|
public string? Props { get; set; }
|
||||||
public string? Description { get; set; }
|
public string? Description { get; set; }
|
||||||
public bool IsActive { get; set; } = true;
|
public bool IsActive { get; set; } = true;
|
||||||
public string? Dependencies { get; set; } // JSON string of component names
|
public string? Dependencies { get; set; } // JSON string of component names
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Data sekmesinde kullanilan API'ler; JSON dizisi. Props icindeki designer dokumanindan
|
|
||||||
/// kaydetme sirasinda sunucu tarafinda uretilir, istemciden gonderilmez.
|
|
||||||
/// </summary>
|
|
||||||
public string? DataSources { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CreateUpdateCustomComponentDto
|
public class CreateUpdateCustomComponentDto
|
||||||
{
|
{
|
||||||
public string Name { get; set; } = string.Empty;
|
public string Name { get; set; } = string.Empty;
|
||||||
/// <summary>
|
[Required]
|
||||||
/// Bilesenin kendi rotasi. Bos birakilabilir: rotasi olmayan bir bilesen (ornegin
|
|
||||||
/// baska bir ekranin icinde cizilen alt bilgi) hicbir adrese baglanmaz.
|
|
||||||
/// </summary>
|
|
||||||
[StringLength(512)]
|
[StringLength(512)]
|
||||||
[RegularExpression(@"^(/.*)?$", ErrorMessage = "RoutePath must start with '/'.")]
|
[RegularExpression(@"^/.*", ErrorMessage = "RoutePath must start with '/'.")]
|
||||||
public string? RoutePath { get; set; }
|
public string RoutePath { get; set; } = string.Empty;
|
||||||
public string Code { get; set; } = string.Empty;
|
public string Code { get; set; } = string.Empty;
|
||||||
public string? Props { get; set; }
|
public string? Props { get; set; }
|
||||||
public string? Description { get; set; }
|
public string? Description { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -12,17 +12,9 @@ public interface ICrudEndpointAppService : ICrudAppService<
|
||||||
PagedAndSortedResultRequestDto,
|
PagedAndSortedResultRequestDto,
|
||||||
CreateUpdateCrudEndpointDto>
|
CreateUpdateCrudEndpointDto>
|
||||||
{
|
{
|
||||||
|
Task<List<CrudEndpointDto>> GetActiveEndpointsAsync();
|
||||||
Task<List<CrudEndpointDto>> GetEndpointsByEntityAsync(string entityName);
|
Task<List<CrudEndpointDto>> GetEndpointsByEntityAsync(string entityName);
|
||||||
Task<CrudEndpointDto> ToggleAsync(Guid id);
|
Task<CrudEndpointDto> ToggleAsync(Guid id);
|
||||||
/// <param name="operationTypes">
|
Task<PagedResultDto<CrudEndpointDto>> GenerateCrudEndpointsAsync(string entityName);
|
||||||
/// Aktif baslatilacak operasyonlarin virgulle ayrilmis listesi
|
|
||||||
/// (GetList, GetById, Create, Update, Delete). Bos birakilirsa hepsi aktif olur.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="objectType">
|
|
||||||
/// Veritabani nesnesinin turu (table, view, procedure, function). Tablo disindaki
|
|
||||||
/// nesneler icin yalnizca okuma endpoint'leri (GetList, GetById) uretilir.
|
|
||||||
/// </param>
|
|
||||||
Task<PagedResultDto<CrudEndpointDto>> GenerateCrudEndpointsAsync(string entityName, string operationTypes = null, string objectType = null);
|
|
||||||
Task DeleteByEntityAsync(string entityName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Volo.Abp.Application.Dtos;
|
using Volo.Abp.Application.Dtos;
|
||||||
using Volo.Abp.Application.Services;
|
using Volo.Abp.Application.Services;
|
||||||
|
|
||||||
|
|
@ -10,4 +12,7 @@ public interface ICustomComponentAppService : ICrudAppService<
|
||||||
PagedAndSortedResultRequestDto,
|
PagedAndSortedResultRequestDto,
|
||||||
CreateUpdateCustomComponentDto>
|
CreateUpdateCustomComponentDto>
|
||||||
{
|
{
|
||||||
|
Task<List<CustomComponentDto>> GetActiveComponentsAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Collections.Generic;
|
||||||
using Volo.Abp.Content;
|
using Volo.Abp.Content;
|
||||||
|
|
||||||
namespace Sozsoft.Platform.FileManagement;
|
namespace Sozsoft.Platform.FileManagement;
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,34 @@ public class ForumCategoryDto : FullAuditedEntityDto<Guid>
|
||||||
public Guid? LastPostUserId { get; set; }
|
public Guid? LastPostUserId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class CreateForumCategoryDto
|
||||||
|
{
|
||||||
|
public Guid? TenantId { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[StringLength(100)]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[StringLength(100)]
|
||||||
|
public string Slug { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[StringLength(500)]
|
||||||
|
public string Description { get; set; }
|
||||||
|
|
||||||
|
[StringLength(10)]
|
||||||
|
public string Icon { get; set; }
|
||||||
|
|
||||||
|
public int DisplayOrder { get; set; }
|
||||||
|
public bool IsActive { get; set; } = true;
|
||||||
|
public bool IsLocked { get; set; } = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UpdateForumCategoryDto : CreateForumCategoryDto
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public class GetCategoriesInput : PagedAndSortedResultRequestDto
|
public class GetCategoriesInput : PagedAndSortedResultRequestDto
|
||||||
{
|
{
|
||||||
public bool? IsActive { get; set; }
|
public bool? IsActive { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,9 @@ public interface IForumAppService : IApplicationService
|
||||||
Task<PagedResultDto<ForumCategoryDto>> GetCategoriesAsync(GetCategoriesInput input);
|
Task<PagedResultDto<ForumCategoryDto>> GetCategoriesAsync(GetCategoriesInput input);
|
||||||
Task<ForumCategoryDto> GetCategoryAsync(Guid id);
|
Task<ForumCategoryDto> GetCategoryAsync(Guid id);
|
||||||
Task<ForumCategoryDto> GetCategoryBySlugAsync(string slug);
|
Task<ForumCategoryDto> GetCategoryBySlugAsync(string slug);
|
||||||
|
Task<ForumCategoryDto> CreateCategoryAsync(CreateForumCategoryDto input);
|
||||||
|
Task<ForumCategoryDto> UpdateCategoryAsync(Guid id, UpdateForumCategoryDto input);
|
||||||
|
Task DeleteCategoryAsync(Guid id);
|
||||||
|
|
||||||
// Topics
|
// Topics
|
||||||
Task<PagedResultDto<ForumTopicDto>> GetTopicsAsync(GetTopicsInput input);
|
Task<PagedResultDto<ForumTopicDto>> GetTopicsAsync(GetTopicsInput input);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Sozsoft.Platform.Identity.Dto;
|
namespace Sozsoft.Platform.Identity.Dto;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using Volo.Abp.Content;
|
using Volo.Abp.Content;
|
||||||
|
|
||||||
namespace Sozsoft.Platform.Identity.Dto;
|
namespace Sozsoft.Platform.Identity.Dto;
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,6 @@ public class UserInfoViewModel : ExtensibleObject
|
||||||
public bool ShouldChangePasswordOnNextLogin { get; set; }
|
public bool ShouldChangePasswordOnNextLogin { get; set; }
|
||||||
public string Avatar { get; set; }
|
public string Avatar { get; set; }
|
||||||
public string RocketUsername { get; set; }
|
public string RocketUsername { get; set; }
|
||||||
public string TelegramChatId { get; set; }
|
|
||||||
public DateTimeOffset? CreationTime { get; set; }
|
public DateTimeOffset? CreationTime { get; set; }
|
||||||
public DateTimeOffset? LastModificationTime { get; set; }
|
public DateTimeOffset? LastModificationTime { get; set; }
|
||||||
public string WorkHour { get; set; }
|
public string WorkHour { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Sozsoft.Platform.Intranet;
|
namespace Sozsoft.Platform.Intranet;
|
||||||
|
|
||||||
|
|
@ -6,6 +6,11 @@ public class CreateSocialPostInput
|
||||||
{
|
{
|
||||||
public string Content { get; set; } = string.Empty;
|
public string Content { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// JSON string containing location data (name, address, lat, lng, placeId).
|
||||||
|
/// </summary>
|
||||||
|
public string? LocationJson { get; set; }
|
||||||
|
|
||||||
public CreateSocialPostMediaInput? Media { get; set; }
|
public CreateSocialPostMediaInput? Media { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue