style: format the repository with prettier
These files were already out of step with the project's Prettier config before this branch of work; running `pnpm format` finally swept them up. No semantic changes. Kept separate from the functional commits so the reformatting does not bury them in diff noise.
This commit is contained in:
48 files changed
+641
-676
No files matched your search
@@ -49,8 +49,8 @@ Minimal setup:
|
||||
|
||||
```tsx
|
||||
// theme/index.tsx
|
||||
import './index.css'; // optional
|
||||
export * from '@rspress/core/theme-original';
|
||||
import "./index.css" // optional
|
||||
export * from "@rspress/core/theme-original"
|
||||
```
|
||||
|
||||
**Critical import rule**: Inside `theme/` files, always import from `@rspress/core/theme-original`. The path `@rspress/core/theme` resolves to your own `theme/index.tsx`, which causes circular imports. (In `docs/` MDX files, `@rspress/core/theme` is fine — it correctly points to your custom theme.)
|
||||
@@ -80,8 +80,8 @@ Override CSS custom properties for brand colors, backgrounds, text, code blocks,
|
||||
```ts
|
||||
// rspress.config.ts
|
||||
export default defineConfig({
|
||||
globalStyles: path.join(__dirname, 'styles/custom.css'),
|
||||
});
|
||||
globalStyles: path.join(__dirname, "styles/custom.css"),
|
||||
})
|
||||
```
|
||||
|
||||
> **Full variable list**: Read `references/css-variables.md` for all available CSS variables with light/dark defaults.
|
||||
@@ -104,13 +104,11 @@ Inject content at specific positions in the layout without replacing built-in co
|
||||
|
||||
```tsx
|
||||
// theme/index.tsx
|
||||
import { Layout as OriginalLayout } from '@rspress/core/theme-original';
|
||||
export * from '@rspress/core/theme-original';
|
||||
import { Layout as OriginalLayout } from "@rspress/core/theme-original"
|
||||
export * from "@rspress/core/theme-original"
|
||||
|
||||
export function Layout() {
|
||||
return (
|
||||
<OriginalLayout beforeNavTitle={<MyLogo />} bottom={<CustomFooter />} />
|
||||
);
|
||||
return <OriginalLayout beforeNavTitle={<MyLogo />} bottom={<CustomFooter />} />
|
||||
}
|
||||
```
|
||||
|
||||
@@ -132,8 +130,8 @@ rspress eject DocFooter # eject to theme/components/DocFooter/
|
||||
Then re-export in `theme/index.tsx` (named export takes precedence over the wildcard):
|
||||
|
||||
```tsx
|
||||
export * from '@rspress/core/theme-original';
|
||||
export { DocFooter } from './components/DocFooter';
|
||||
export * from "@rspress/core/theme-original"
|
||||
export { DocFooter } from "./components/DocFooter"
|
||||
```
|
||||
|
||||
> **Component list & patterns**: Read `references/eject-components.md` for available components, workflow, and common patterns.
|
||||
@@ -147,38 +145,38 @@ Rspress has 27 built-in icons used across the UI. You can replace any of them by
|
||||
**Icon type**: Each icon is a React component or a URL string:
|
||||
|
||||
```ts
|
||||
import type { FC, SVGProps } from 'react';
|
||||
type Icon = FC<SVGProps<SVGSVGElement>> | string;
|
||||
import type { FC, SVGProps } from "react"
|
||||
type Icon = FC<SVGProps<SVGSVGElement>> | string
|
||||
```
|
||||
|
||||
**Example 1** — Replace an icon with a custom SVG component:
|
||||
|
||||
```tsx
|
||||
// theme/index.tsx
|
||||
export * from '@rspress/core/theme-original';
|
||||
export * from "@rspress/core/theme-original"
|
||||
|
||||
// Named export overrides the wildcard — replaces the GitHub icon site-wide
|
||||
export const IconGithub = (props: React.SVGProps<SVGSVGElement>) => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" {...props}>
|
||||
<path d="M12 2C6.477 2 2 6.484 2 12.017c0 ..." fill="currentColor" />
|
||||
</svg>
|
||||
);
|
||||
)
|
||||
```
|
||||
|
||||
**Example 2** — Use an SVGR import:
|
||||
|
||||
```tsx
|
||||
// theme/index.tsx
|
||||
export * from '@rspress/core/theme-original';
|
||||
export * from "@rspress/core/theme-original"
|
||||
|
||||
import CustomGithubIcon from './icons/github.svg?react';
|
||||
export const IconGithub = CustomGithubIcon;
|
||||
import CustomGithubIcon from "./icons/github.svg?react"
|
||||
export const IconGithub = CustomGithubIcon
|
||||
```
|
||||
|
||||
**Using `SvgWrapper` in MDX or custom components**:
|
||||
|
||||
```mdx
|
||||
import { SvgWrapper, IconGithub } from '@rspress/core/theme';
|
||||
import { SvgWrapper, IconGithub } from "@rspress/core/theme"
|
||||
|
||||
<SvgWrapper icon={IconGithub} width={24} height={24} />
|
||||
```
|
||||
@@ -197,13 +195,10 @@ For components that should render on every page without theme overrides:
|
||||
// rspress.config.ts
|
||||
export default defineConfig({
|
||||
globalUIComponents: [
|
||||
path.join(__dirname, 'components', 'BackToTop.tsx'),
|
||||
[
|
||||
path.join(__dirname, 'components', 'Analytics.tsx'),
|
||||
{ trackingId: '...' },
|
||||
],
|
||||
path.join(__dirname, "components", "BackToTop.tsx"),
|
||||
[path.join(__dirname, "components", "Analytics.tsx"), { trackingId: "..." }],
|
||||
],
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -50,8 +50,8 @@ Ejected source is placed in `theme/components/<ComponentName>/`.
|
||||
|
||||
```tsx
|
||||
// theme/index.tsx
|
||||
export * from '@rspress/core/theme-original';
|
||||
export { DocFooter } from './components/DocFooter';
|
||||
export * from "@rspress/core/theme-original"
|
||||
export { DocFooter } from "./components/DocFooter"
|
||||
```
|
||||
|
||||
The named export takes precedence over the wildcard re-export, so Rspress uses your custom version.
|
||||
@@ -64,21 +64,21 @@ The most common eject use case is wrapping the entire app in a context provider
|
||||
|
||||
```tsx
|
||||
// theme/components/Root/index.tsx
|
||||
import type { RootProps } from '@rspress/core/theme';
|
||||
import type { RootProps } from "@rspress/core/theme"
|
||||
|
||||
export function Root({ children }: RootProps) {
|
||||
return (
|
||||
<ThemeProvider>
|
||||
<AnalyticsProvider>{children}</AnalyticsProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
```tsx
|
||||
// theme/index.tsx
|
||||
export * from '@rspress/core/theme-original';
|
||||
export { Root } from './components/Root';
|
||||
export * from "@rspress/core/theme-original"
|
||||
export { Root } from "./components/Root"
|
||||
```
|
||||
|
||||
## Common Pattern: Custom Home Page (HomeLayout)
|
||||
@@ -87,12 +87,12 @@ When the default home page structure (Hero + Features) doesn't meet the design r
|
||||
|
||||
```tsx
|
||||
// theme/components/HomeLayout/index.tsx
|
||||
import { useSite, useLang } from '@rspress/core/runtime';
|
||||
import { useSite, useLang } from "@rspress/core/runtime"
|
||||
|
||||
export function HomeLayout() {
|
||||
const site = useSite();
|
||||
const lang = useLang();
|
||||
const { title, description } = site.siteData;
|
||||
const site = useSite()
|
||||
const lang = useLang()
|
||||
const { title, description } = site.siteData
|
||||
|
||||
return (
|
||||
<div className="custom-home">
|
||||
@@ -100,10 +100,7 @@ export function HomeLayout() {
|
||||
<h1>{title}</h1>
|
||||
<p>{description}</p>
|
||||
<div className="hero-actions">
|
||||
<a
|
||||
href={lang === 'zh' ? '/zh/guide/start' : '/guide/start'}
|
||||
className="primary-btn"
|
||||
>
|
||||
<a href={lang === "zh" ? "/zh/guide/start" : "/guide/start"} className="primary-btn">
|
||||
Get Started
|
||||
</a>
|
||||
<a href="https://github.com/..." className="secondary-btn">
|
||||
@@ -116,14 +113,14 @@ export function HomeLayout() {
|
||||
{/* Custom content: testimonials, stats, demos, etc. */}
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
```tsx
|
||||
// theme/index.tsx
|
||||
export * from '@rspress/core/theme-original';
|
||||
export { HomeLayout } from './components/HomeLayout';
|
||||
export * from "@rspress/core/theme-original"
|
||||
export { HomeLayout } from "./components/HomeLayout"
|
||||
```
|
||||
|
||||
The named export overrides the built-in `HomeLayout` from the wildcard re-export — no need to eject first.
|
||||
@@ -134,16 +131,16 @@ If you only need to add content before/after the Hero or Features sections (with
|
||||
|
||||
```tsx
|
||||
// theme/components/DocFooter/index.tsx
|
||||
import { useFrontmatter } from '@rspress/core/runtime';
|
||||
import { useFrontmatter } from "@rspress/core/runtime"
|
||||
|
||||
export function DocFooter() {
|
||||
const frontmatter = useFrontmatter();
|
||||
const frontmatter = useFrontmatter()
|
||||
return (
|
||||
<footer className="custom-doc-footer">
|
||||
{frontmatter.author && <span>Author: {frontmatter.author}</span>}
|
||||
<a href="https://github.com/...">Edit this page</a>
|
||||
</footer>
|
||||
);
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -71,11 +71,11 @@ All examples below follow the same structure in `theme/index.tsx`. The key parts
|
||||
|
||||
```tsx
|
||||
// theme/index.tsx
|
||||
import { Layout as OriginalLayout } from '@rspress/core/theme-original';
|
||||
export * from '@rspress/core/theme-original';
|
||||
import { Layout as OriginalLayout } from "@rspress/core/theme-original"
|
||||
export * from "@rspress/core/theme-original"
|
||||
|
||||
export function Layout() {
|
||||
return <OriginalLayout beforeNavTitle={<MyLogo />} />;
|
||||
return <OriginalLayout beforeNavTitle={<MyLogo />} />
|
||||
}
|
||||
```
|
||||
|
||||
@@ -83,8 +83,8 @@ export function Layout() {
|
||||
|
||||
```tsx
|
||||
// theme/index.tsx
|
||||
import { Layout as OriginalLayout } from '@rspress/core/theme-original';
|
||||
export * from '@rspress/core/theme-original';
|
||||
import { Layout as OriginalLayout } from "@rspress/core/theme-original"
|
||||
export * from "@rspress/core/theme-original"
|
||||
|
||||
export function Layout() {
|
||||
return (
|
||||
@@ -93,7 +93,7 @@ export function Layout() {
|
||||
bottom={<footer>© 2025 My Company</footer>}
|
||||
afterOutline={<div>Related resources</div>}
|
||||
/>
|
||||
);
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
@@ -101,17 +101,17 @@ export function Layout() {
|
||||
|
||||
```tsx
|
||||
// theme/index.tsx
|
||||
import { Layout as OriginalLayout } from '@rspress/core/theme-original';
|
||||
import { useLang } from '@rspress/core/runtime';
|
||||
export * from '@rspress/core/theme-original';
|
||||
import { Layout as OriginalLayout } from "@rspress/core/theme-original"
|
||||
import { useLang } from "@rspress/core/runtime"
|
||||
export * from "@rspress/core/theme-original"
|
||||
|
||||
function LocalizedBanner() {
|
||||
const lang = useLang();
|
||||
return <div>{lang === 'zh' ? '欢迎' : 'Welcome'}</div>;
|
||||
const lang = useLang()
|
||||
return <div>{lang === "zh" ? "欢迎" : "Welcome"}</div>
|
||||
}
|
||||
|
||||
export function Layout() {
|
||||
return <OriginalLayout top={<LocalizedBanner />} />;
|
||||
return <OriginalLayout top={<LocalizedBanner />} />
|
||||
}
|
||||
```
|
||||
|
||||
@@ -121,17 +121,15 @@ The `components` slot accepts a `Record<string, React.ComponentType>` to overrid
|
||||
|
||||
```tsx
|
||||
// theme/index.tsx
|
||||
import { Layout as OriginalLayout } from '@rspress/core/theme-original';
|
||||
export * from '@rspress/core/theme-original';
|
||||
import { Layout as OriginalLayout } from "@rspress/core/theme-original"
|
||||
export * from "@rspress/core/theme-original"
|
||||
|
||||
function CustomH1({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<h1 style={{ borderBottom: '2px solid var(--rp-c-brand)' }}>{children}</h1>
|
||||
);
|
||||
return <h1 style={{ borderBottom: "2px solid var(--rp-c-brand)" }}>{children}</h1>
|
||||
}
|
||||
|
||||
export function Layout() {
|
||||
return <OriginalLayout components={{ h1: CustomH1 }} />;
|
||||
return <OriginalLayout components={{ h1: CustomH1 }} />
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ This project incorporates the following third-party open source software.
|
||||
## Direct Dependencies
|
||||
|
||||
| Package | License |
|
||||
|-------------------|---------|
|
||||
| ----------------- | ------- |
|
||||
| @rspress/core | MIT |
|
||||
| react | MIT |
|
||||
| react-dom | MIT |
|
||||
|
||||
@@ -11,7 +11,7 @@ author:
|
||||
## When to Add Seasonings
|
||||
|
||||
| Seasoning Type | Timing | Suitable Dishes | Effect |
|
||||
|----------------------------------------|----------------------------------------|------------------------------------------------------|-------------------------------------------------------------------------------|
|
||||
| -------------------------------------- | -------------------------------------- | ---------------------------------------------------- | ----------------------------------------------------------------------------- |
|
||||
| **Salt** | Last | Leafy greens | Prevents leaves from wilting and releasing water, keeps them crisp and tender |
|
||||
| | Mid-way | Shredded potatoes, green beans, garlic shoots | Better flavour absorption |
|
||||
| **MSG, Light Soy Sauce, Oyster Sauce** | Towards the end | Dishes needing umami and aroma enhancement | Adds umami and aroma; high heat destroys freshness and fragrance |
|
||||
@@ -24,7 +24,7 @@ author:
|
||||
## Quick Reference
|
||||
|
||||
| Seasoning | Timing | Effect | Example |
|
||||
|-----------------------------------------------|------------------------------|-----------------------------------------------------------------------------|---------------------------------------------------------------------------|
|
||||
| --------------------------------------------- | ---------------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
|
||||
| **Salt** | 10–15 seconds before serving | Prevents premature water loss and wilting of leafy greens; keeps them crisp | Sprinkle salt and toss before turning off the heat for lettuce or spinach |
|
||||
| **Minced Garlic / Ginger** | First, after oil is hot | Releases fragrance and enhances flavour | Sauté minced garlic in hot oil, then add greens |
|
||||
| **Light Soy Sauce / Oyster Sauce** (optional) | After salt | Adds umami; use sparingly to avoid overpowering the vegetables | Add 1–2 drops of light soy sauce after salting Shanghai greens |
|
||||
|
||||
@@ -17,13 +17,13 @@ author:
|
||||
```javascript
|
||||
function processResponse(response) {
|
||||
if (response.status === 200 && response.data) {
|
||||
return { success: true, data: response.data };
|
||||
return { success: true, data: response.data }
|
||||
} else if (response.status === 404) {
|
||||
return { success: false, error: 'Not found' };
|
||||
return { success: false, error: "Not found" }
|
||||
} else if (response.status >= 500) {
|
||||
return { success: false, error: 'Server error' };
|
||||
return { success: false, error: "Server error" }
|
||||
} else {
|
||||
return { success: false, error: 'Unknown error' };
|
||||
return { success: false, error: "Unknown error" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -19,11 +19,11 @@ sound more confident and professional.
|
||||
|
||||
## Scenario Cheatsheet
|
||||
|
||||
| Scenario | ❌ Don't Use (sounds…) | ✅ Use Instead (sounds…) | Why It Works |
|
||||
|----------------------------|----------------------------------------------|-----------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Scenario | ❌ Don't Use (sounds…) | ✅ Use Instead (sounds…) | Why It Works |
|
||||
| -------------------------- | -------------------------------------------- | --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Late reply | **Sorry for the delay** | **Thanks for your patience** | Swaps an apology for gratitude — the focus shifts from "I was wrong" to "you were generous," acknowledging the delay while making the recipient feel respected |
|
||||
| Scheduling | **What works best for you?** | **Could you do …?** | The former throws the decision entirely back to the other person; the latter offers a concrete option, cutting down on back-and-forth |
|
||||
| After helping someone | **No problem / No worries** | **Always happy to help** | The former implies the task *could* have been a problem; the latter signals you enjoyed it and would happily do it again |
|
||||
| After helping someone | **No problem / No worries** | **Always happy to help** | The former implies the task _could_ have been a problem; the latter signals you enjoyed it and would happily do it again |
|
||||
| Making a suggestion | **I think maybe we should …** | **It'd be best if we …** | The former oozes hesitation and self-doubt; the latter delivers a clear judgment — like someone with experience making a decision |
|
||||
| Text isn't working | **_Spending 30 minutes rewriting an email_** | **It'd be easier to discuss in person** | Recognising the medium itself is the bottleneck and switching channels can be the most efficient move |
|
||||
| Checking for understanding | **Hopefully that makes sense?** | **Let me know if you have questions** | The former betrays doubt about your own clarity; the latter calmly shares responsibility — the reader now has an action item too |
|
||||
|
||||
@@ -8,7 +8,7 @@ tags:
|
||||
---
|
||||
|
||||
> This article was originally written by **落格博客
|
||||
**: [落格博客](https://www.logcg.com/) » [Frequent Wake-from-Sleep Issues After Upgrading to macOS Monterey](https://www.logcg.com/archives/3528.html)
|
||||
> **: [落格博客](https://www.logcg.com/) » [Frequent Wake-from-Sleep Issues After Upgrading to macOS Monterey](https://www.logcg.com/archives/3528.html)
|
||||
|
||||
After upgrading to macOS Monterey, my screen kept lighting up in the middle of the night for no apparent reason. It had
|
||||
happened before, but only when notifications came in. Now the screen lights up on its own with no trigger — same
|
||||
@@ -33,8 +33,8 @@ So for Power Nap, we have to go through the command line. First, check the curre
|
||||
|
||||
Also check **`tcpkeepalive`** — it likely isn't 0 either and should also be turned off. It controls whether your Mac
|
||||
maintains TCP connections while sleeping. Run **`sudo pmset -a tcpkeepalive 0`** — you'll see a terminal warning:
|
||||
***Warning: This option disables TCP Keep Alive mechanism when system is sleeping. This will result in some critical
|
||||
features like 'Find My Mac' not to function properly.***
|
||||
**_Warning: This option disables TCP Keep Alive mechanism when system is sleeping. This will result in some critical
|
||||
features like 'Find My Mac' not to function properly._**
|
||||
|
||||
Essentially, turning it off limits some features — the system simply won't connect to the network while asleep. I'm
|
||||
fairly confident that if someone actually steals your Mac, they won't be getting it online anyway.
|
||||
@@ -10,8 +10,8 @@ author:
|
||||
email: real@zihluwang.me
|
||||
---
|
||||
|
||||
| Chinese Size Name | English Point Size (pt) | mm | px |
|
||||
|--------------------|-------------------------|-------|------|
|
||||
| Chinese Size Name | English Point Size (pt) | mm | px |
|
||||
| -------------------- | ----------------------- | ----- | ---- |
|
||||
| 初号 (Primary) | 42 | 14.82 | 56 |
|
||||
| 小初 (Small Primary) | 36 | 12.7 | 48 |
|
||||
| 一号 (No. 1) | 26 | 9.17 | 34.7 |
|
||||
|
||||
@@ -23,9 +23,9 @@ author:
|
||||
- **Axios Instance**: Create a centralised Axios instance for API calls to apply common configuration (base URL,
|
||||
headers, interceptors).
|
||||
- **Interceptors**: Use Axios interceptors to:
|
||||
- Add authentication tokens to outgoing requests.
|
||||
- Handle global error responses (e.g., display a notification for `401 Unauthorized`).
|
||||
- Log requests/responses in development environments.
|
||||
- Add authentication tokens to outgoing requests.
|
||||
- Handle global error responses (e.g., display a notification for `401 Unauthorized`).
|
||||
- Log requests/responses in development environments.
|
||||
- **Error Handling**: Centralise API error handling in Axios interceptors or custom utility functions to provide
|
||||
consistent user feedback.
|
||||
|
||||
@@ -34,14 +34,14 @@ author:
|
||||
- **Function Components & Hooks**: Prefer function components with React Hooks over class components for new
|
||||
development.
|
||||
- **Props**:
|
||||
- Define `interface` or `type` for component props to ensure type safety.
|
||||
- Destructure props at the component entry point for clarity.
|
||||
- Define `interface` or `type` for component props to ensure type safety.
|
||||
- Destructure props at the component entry point for clarity.
|
||||
- **State Management (Redux)**:
|
||||
- Use Redux Toolkit for efficient, boilerplate-reduced Redux development.
|
||||
- Use `createSlice` to organise Redux logic into "slices" (feature-specific reducers, actions, and selectors).
|
||||
- Follow the "ducks" pattern or "slices" approach to co-locate Redux logic.
|
||||
- Use Redux Toolkit for efficient, boilerplate-reduced Redux development.
|
||||
- Use `createSlice` to organise Redux logic into "slices" (feature-specific reducers, actions, and selectors).
|
||||
- Follow the "ducks" pattern or "slices" approach to co-locate Redux logic.
|
||||
- **Component Composition**: Break down complex UIs into smaller, reusable, single-responsibility components.
|
||||
- **Ant Design**:
|
||||
- Leverage Ant Design components for consistent UI/UX.
|
||||
- Use CSS-in-JS solutions to consistently customise Ant Design themes and styles across the application if needed.
|
||||
- Leverage Ant Design components for consistent UI/UX.
|
||||
- Use CSS-in-JS solutions to consistently customise Ant Design themes and styles across the application if needed.
|
||||
- **Accessibility**: Design and implement components with web accessibility (a11y) in mind from the start.
|
||||
@@ -105,7 +105,7 @@ The N+1 problem occurs when JPA executes one query for the parent entity, then N
|
||||
**Fixes:**
|
||||
|
||||
| Approach | When to use |
|
||||
|--------------------------|---------------------------------------------------|
|
||||
| ------------------------ | ------------------------------------------------- |
|
||||
| `@EntityGraph` | Declarative, good for entity-specific fetch plans |
|
||||
| `JOIN FETCH` in `@Query` | Fine-grained control per query |
|
||||
| `@BatchSize` | Reduces N+1 to N/k+1 by batching |
|
||||
@@ -144,6 +144,7 @@ This happens when you access a lazily-loaded association outside the persistence
|
||||
@Query("SELECT new com.example.UserDto(u.id, u.name) FROM User u WHERE u.id = :id")
|
||||
UserDto findUserDtoById(@Param("id") long id);
|
||||
```
|
||||
|
||||
3. **`@Transactional(readOnly = true)`** on the service method — keep the session open for the entire method scope.
|
||||
|
||||
### When should I use `@Transactional(readOnly = true)`?
|
||||
@@ -275,7 +276,7 @@ Note that `@JsonAnyGetter` must be placed on the **getter method**, not the fiel
|
||||
|
||||
### How to choose
|
||||
|
||||
| Scenario | Annotation |
|
||||
|-------------------------------------------------------|--------------------|
|
||||
| Flatten nested objects with fixed fields (e.g. address, config) | `@JsonUnwrapped` |
|
||||
| Flatten dynamic key-value pairs with unknown keys | `@JsonAnyGetter` |
|
||||
| Scenario | Annotation |
|
||||
| --------------------------------------------------------------- | ---------------- |
|
||||
| Flatten nested objects with fixed fields (e.g. address, config) | `@JsonUnwrapped` |
|
||||
| Flatten dynamic key-value pairs with unknown keys | `@JsonAnyGetter` |
|
||||
@@ -98,7 +98,6 @@ author:
|
||||
## Spring Boot Best Practices & Layered Architecture
|
||||
|
||||
- **Layered Architecture (MVC with Manager Layer)**: Our backend applications follow a strict multi-layered architecture, ensuring clear separation of responsibilities and improving maintainability and testability. The layers and their responsibilities are:
|
||||
|
||||
- **Controller Layer**: Located in the `controller` package. Responsible for exposing RESTful APIs, handling HTTP requests, and mapping request parameters/bodies to service layer calls. Controllers should remain lightweight, focusing primarily on input validation (using DTOs) and coordinating calls to the `Service` layer.
|
||||
- **Service Layer**: Located in the `service` package. This layer encapsulates core business logic. Services expose a directly consumable API to the `Controller` layer, abstracting business processes and transaction management. Services coordinate calls to the `Manager` layer to execute business operations.
|
||||
- **Manager Layer**: Located in the `manager` package. This layer provides atomic business operations that can be composed by the `Service` layer. Managers typically handle more complex business logic and may involve interacting with multiple repositories or other external systems at a finer granularity.
|
||||
@@ -120,7 +119,7 @@ author:
|
||||
- Data operation conventions:
|
||||
- Use JPA for simple database operations.
|
||||
- Use MyBatis for complex database operations.
|
||||
- When performing paginated queries, page numbers should start from 0 (*compatible with Spring Data JPA*).
|
||||
- When performing paginated queries, page numbers should start from 0 (_compatible with Spring Data JPA_).
|
||||
- **Data Access Method Naming Conventions**:
|
||||
- For **querying data lists**: methods **must** start with `selectListBy`, followed by the filter criteria (e.g., `selectListByUserId`, `selectListByDepartmentIdAndStatus`). These methods must also include a `PageRequest` parameter for pagination.
|
||||
- For querying **single data records**: methods **must** start with `selectOne` (e.g., `selectOneById`, `selectOneByUsername`).
|
||||
|
||||
@@ -5,8 +5,8 @@ tags:
|
||||
- json
|
||||
- database
|
||||
author:
|
||||
name: Zihlu Wang
|
||||
email: real@zihluwang.me
|
||||
name: Zihlu Wang
|
||||
email: real@zihluwang.me
|
||||
---
|
||||
|
||||
MySQL (since version 5.7) **does not directly support a data type called `jsonb`**. `jsonb` is a data type specific to PostgreSQL, which stores JSON data in a binary format with pre-parsing for faster access and manipulation during queries.
|
||||
|
||||
@@ -26,11 +26,11 @@ https://gravatar.com/avatar/<hash>
|
||||
### Node.js
|
||||
|
||||
```js
|
||||
import { createHash } from "node:crypto";
|
||||
import { createHash } from "node:crypto"
|
||||
|
||||
const email = "user@example.com".trim().toLowerCase();
|
||||
const hash = createHash("sha256").update(email).digest("hex");
|
||||
const url = `https://gravatar.com/avatar/${hash}`;
|
||||
const email = "user@example.com".trim().toLowerCase()
|
||||
const hash = createHash("sha256").update(email).digest("hex")
|
||||
const url = `https://gravatar.com/avatar/${hash}`
|
||||
```
|
||||
|
||||
### Python
|
||||
@@ -54,7 +54,7 @@ echo -n "user@example.com" | tr '[:upper:]' '[:lower:]' | sha256sum | cut -d ' '
|
||||
The `/avatar/` endpoint accepts several query parameters to customise the result:
|
||||
|
||||
| Parameter | Description | Example |
|
||||
|-----------|-----------------------------------------|----------------|
|
||||
| --------- | --------------------------------------- | -------------- |
|
||||
| `s` | Size in pixels (default: 80) | `?s=200` |
|
||||
| `d` | Default image when no Gravatar is found | `?d=identicon` |
|
||||
| `r` | Content rating (`g`, `pg`, `r`, `x`) | `?r=g` |
|
||||
@@ -77,8 +77,7 @@ The `/avatar/` endpoint accepts several query parameters to customise the result
|
||||
src="https://gravatar.com/avatar/a3b4c5d6e7f8?s=160&d=robohash&r=g"
|
||||
alt="User avatar"
|
||||
width="160"
|
||||
height="160"
|
||||
/>
|
||||
height="160" />
|
||||
```
|
||||
|
||||
Always pass the `d` parameter to avoid broken images for users who have not set up a Gravatar. `identicon` and
|
||||
|
||||
@@ -7,7 +7,7 @@ title: Calendar Toolbox API
|
||||
The library is organised under the base package `com.onixbyte.calendar` with the following structure:
|
||||
|
||||
| Package | Description |
|
||||
|--------------------------------------------|--------------------------------------------|
|
||||
| ------------------------------------------ | ------------------------------------------ |
|
||||
| `com.onixbyte.calendar` | Root calendar object |
|
||||
| `com.onixbyte.calendar.component` | iCalendar components (VEVENT, VTODO, etc.) |
|
||||
| `com.onixbyte.calendar.component.property` | Component-level properties |
|
||||
@@ -47,7 +47,7 @@ String icsContent = calendar.formatted();
|
||||
All calendar-level properties live in `com.onixbyte.calendar.property` and implement `CalendarProperty`.
|
||||
|
||||
| Class | iCalendar Property | Description |
|
||||
|--------------------------|----------------------|-------------------------------------------|
|
||||
| ------------------------ | -------------------- | ----------------------------------------- |
|
||||
| `CalendarScale` | `CALSCALE` | Calendar system (e.g., GREGORIAN) |
|
||||
| `Method` | `METHOD` | iCalendar method (e.g., PUBLISH, REQUEST) |
|
||||
| `ProductIdentifier` | `PRODID` | Product identifier of the calendar |
|
||||
@@ -277,7 +277,7 @@ Alarm emailAlarm = Alarm.builder()
|
||||
Component properties live in `com.onixbyte.calendar.component.property` and implement `ComponentProperty`.
|
||||
|
||||
| Class | iCalendar Property | Description |
|
||||
|---------------------------|--------------------|----------------------------------------------------------|
|
||||
| ------------------------- | ------------------ | -------------------------------------------------------- |
|
||||
| `Action` | `ACTION` | Alarm action type (AUDIO, DISPLAY, EMAIL) |
|
||||
| `Attachment` | `ATTACH` | Document attachment |
|
||||
| `Attendee` | `ATTENDEE` | Event/task attendee |
|
||||
@@ -328,7 +328,7 @@ Component properties live in `com.onixbyte.calendar.component.property` and impl
|
||||
Parameters in `com.onixbyte.calendar.parameter` provide additional qualifiers on component properties.
|
||||
|
||||
| Class | iCalendar Parameter | Description |
|
||||
|-------------------------------|---------------------|--------------------------------------------------------|
|
||||
| ----------------------------- | ------------------- | ------------------------------------------------------ |
|
||||
| `AlarmTriggerRelationship` | `RELATED` | Trigger relationship (START, END) |
|
||||
| `AlternateTextRepresentation` | `ALTREP` | Alternate text URI |
|
||||
| `CalendarUserType` | `CUTYPE` | Calendar user type (INDIVIDUAL, GROUP, RESOURCE, etc.) |
|
||||
@@ -357,7 +357,7 @@ Parameters in `com.onixbyte.calendar.parameter` provide additional qualifiers on
|
||||
Located in `com.onixbyte.calendar.recurrence`.
|
||||
|
||||
| Class | Description |
|
||||
|--------------|---------------------------------------------------------------------------|
|
||||
| ------------ | ------------------------------------------------------------------------- |
|
||||
| `Frequency` | Frequency constants (DAILY, WEEKLY, MONTHLY, YEARLY) for recurrence rules |
|
||||
| `WeekdayNum` | Weekday number for recurrence rules (e.g., 2nd Monday) |
|
||||
|
||||
@@ -368,7 +368,7 @@ Located in `com.onixbyte.calendar.recurrence`.
|
||||
Located in `com.onixbyte.calendar.value`.
|
||||
|
||||
| Class | Description |
|
||||
|---------------------|------------------------------------------------|
|
||||
| ------------------- | ---------------------------------------------- |
|
||||
| `FreeBusyTimeValue` | Represents free/busy time value |
|
||||
| `PeriodOfTime` | Represents a period of time with start and end |
|
||||
| `PropertyValue` | Base value type for properties |
|
||||
|
||||
@@ -22,10 +22,8 @@ With Calendar Toolbox, you can effortlessly create, customise, and export calend
|
||||
|
||||
<Tabs>
|
||||
<Tab label="Gradle with Kotlin DSL">
|
||||
```kotlin title="build.gradle.kts"
|
||||
dependencies {
|
||||
implementation("com.onixbyte:calendar-toolbox:1.1.0")
|
||||
}
|
||||
```kotlin title="build.gradle.kts" dependencies{" "}
|
||||
{implementation("com.onixbyte:calendar-toolbox:1.1.0")}
|
||||
```
|
||||
</Tab>
|
||||
<Tab label="Maven">
|
||||
|
||||
@@ -6,19 +6,19 @@ title: Captcha API
|
||||
|
||||
The library is organised under the base package `com.onixbyte.captcha` with the following structure:
|
||||
|
||||
| Package | Description |
|
||||
|---------------------------------------------|---------------------------------------|
|
||||
| `com.onixbyte.captcha` | Core interfaces |
|
||||
| `com.onixbyte.captcha.impl` | Default implementations |
|
||||
| `com.onixbyte.captcha.text` | Text producer and renderer interfaces |
|
||||
| `com.onixbyte.captcha.text.impl` | Text producer and renderer impls |
|
||||
| `com.onixbyte.captcha.text.enums` | Text-related enums |
|
||||
| `com.onixbyte.captcha.background` | Background producer interface |
|
||||
| `com.onixbyte.captcha.background.impl` | Background producer implementations |
|
||||
| `com.onixbyte.captcha.noise` | Noise producer interface |
|
||||
| `com.onixbyte.captcha.noise.impl` | Noise producer implementations |
|
||||
| `com.onixbyte.captcha.gimpy` | Distortion engine interface |
|
||||
| `com.onixbyte.captcha.gimpy.impl` | Distortion engine implementations |
|
||||
| Package | Description |
|
||||
| -------------------------------------- | ------------------------------------- |
|
||||
| `com.onixbyte.captcha` | Core interfaces |
|
||||
| `com.onixbyte.captcha.impl` | Default implementations |
|
||||
| `com.onixbyte.captcha.text` | Text producer and renderer interfaces |
|
||||
| `com.onixbyte.captcha.text.impl` | Text producer and renderer impls |
|
||||
| `com.onixbyte.captcha.text.enums` | Text-related enums |
|
||||
| `com.onixbyte.captcha.background` | Background producer interface |
|
||||
| `com.onixbyte.captcha.background.impl` | Background producer implementations |
|
||||
| `com.onixbyte.captcha.noise` | Noise producer interface |
|
||||
| `com.onixbyte.captcha.noise.impl` | Noise producer implementations |
|
||||
| `com.onixbyte.captcha.gimpy` | Distortion engine interface |
|
||||
| `com.onixbyte.captcha.gimpy.impl` | Distortion engine implementations |
|
||||
|
||||
---
|
||||
|
||||
@@ -45,17 +45,17 @@ BufferedImage image = captcha.createImage(text);
|
||||
|
||||
### DefaultCaptchaProducer Builder
|
||||
|
||||
| Method | Default Value | Description |
|
||||
|---------------------|---------------------------------------------|---------------------------------------|
|
||||
| `textProducer` | `DefaultTextProducer.builder().build()` | The text producer to use |
|
||||
| `wordRenderer` | `DefaultWordRenderer.builder().build()` | The word renderer to use |
|
||||
| `gimpyEngine` | `WaterRipple.builder().build()` | The distortion engine to use |
|
||||
| `backgroundProducer`| `DefaultBackgroundProducer.builder().build()`| The background producer to use |
|
||||
| `width` | `200` | Width of the CAPTCHA image |
|
||||
| `height` | `50` | Height of the CAPTCHA image |
|
||||
| `borderDrawn` | `true` | Whether a border is drawn |
|
||||
| `borderColour` | `Color.BLACK` | The colour of the border |
|
||||
| `borderThickness` | `1` | The thickness of the border |
|
||||
| Method | Default Value | Description |
|
||||
| -------------------- | --------------------------------------------- | ------------------------------ |
|
||||
| `textProducer` | `DefaultTextProducer.builder().build()` | The text producer to use |
|
||||
| `wordRenderer` | `DefaultWordRenderer.builder().build()` | The word renderer to use |
|
||||
| `gimpyEngine` | `WaterRipple.builder().build()` | The distortion engine to use |
|
||||
| `backgroundProducer` | `DefaultBackgroundProducer.builder().build()` | The background producer to use |
|
||||
| `width` | `200` | Width of the CAPTCHA image |
|
||||
| `height` | `50` | Height of the CAPTCHA image |
|
||||
| `borderDrawn` | `true` | Whether a border is drawn |
|
||||
| `borderColour` | `Color.BLACK` | The colour of the border |
|
||||
| `borderThickness` | `1` | The thickness of the border |
|
||||
|
||||
---
|
||||
|
||||
@@ -82,10 +82,10 @@ DefaultTextProducer textProducer = DefaultTextProducer.builder()
|
||||
.build();
|
||||
```
|
||||
|
||||
| Method | Default Value | Description |
|
||||
|----------|-----------------------------------------------------------|--------------------------------|
|
||||
| `length` | `6` | Length of generated text |
|
||||
| `chars` | `a-z`, `A-Z`, `0-9` (62 characters) | Characters used for generation |
|
||||
| Method | Default Value | Description |
|
||||
| -------- | ----------------------------------- | ------------------------------ |
|
||||
| `length` | `6` | Length of generated text |
|
||||
| `chars` | `a-z`, `A-Z`, `0-9` (62 characters) | Characters used for generation |
|
||||
|
||||
### WordRenderer
|
||||
|
||||
@@ -111,24 +111,24 @@ DefaultWordRenderer wordRenderer = DefaultWordRenderer.builder()
|
||||
.build();
|
||||
```
|
||||
|
||||
| Method | Default Value | Description |
|
||||
|-------------|-----------------------------------|-----------------------------------------|
|
||||
| `fontSize` | `40` | Font size in pixels |
|
||||
| `fonts` | `["Arial", "Courier"]` | Font families to use (randomly chosen) |
|
||||
| `fontColour`| `Color.BLACK` | Font colour |
|
||||
| `charSpace` | `2` | Space between characters in pixels |
|
||||
| `fontStyle` | `FontStyle.BOLD` | Font style to apply |
|
||||
| Method | Default Value | Description |
|
||||
| ------------ | ---------------------- | -------------------------------------- |
|
||||
| `fontSize` | `40` | Font size in pixels |
|
||||
| `fonts` | `["Arial", "Courier"]` | Font families to use (randomly chosen) |
|
||||
| `fontColour` | `Color.BLACK` | Font colour |
|
||||
| `charSpace` | `2` | Space between characters in pixels |
|
||||
| `fontStyle` | `FontStyle.BOLD` | Font style to apply |
|
||||
|
||||
### FontStyle
|
||||
|
||||
Defines the supported font styles for rendering CAPTCHA text.
|
||||
|
||||
| Value | Corresponding AWT Constant | Description |
|
||||
|---------------|-------------------------------------|------------------------------|
|
||||
| `PLAIN` | `java.awt.Font.PLAIN` | Plain style |
|
||||
| `BOLD` | `java.awt.Font.BOLD` | Bold style |
|
||||
| `ITALIC` | `java.awt.Font.ITALIC` | Italic style |
|
||||
| `BOLD_ITALIC` | `java.awt.Font.BOLD | Font.ITALIC` | Bold and italic combined |
|
||||
| Value | Corresponding AWT Constant | Description |
|
||||
| ------------- | -------------------------- | ------------ | ------------------------ |
|
||||
| `PLAIN` | `java.awt.Font.PLAIN` | Plain style |
|
||||
| `BOLD` | `java.awt.Font.BOLD` | Bold style |
|
||||
| `ITALIC` | `java.awt.Font.ITALIC` | Italic style |
|
||||
| `BOLD_ITALIC` | `java.awt.Font.BOLD | Font.ITALIC` | Bold and italic combined |
|
||||
|
||||
---
|
||||
|
||||
@@ -152,9 +152,9 @@ WaterRipple gimpy = WaterRipple.builder()
|
||||
.build();
|
||||
```
|
||||
|
||||
| Method | Default Value | Description |
|
||||
|-----------------|---------------------------------------------|------------------------------|
|
||||
| `noiseProducer` | `DefaultNoiseProducer.builder().build()` | The noise producer to use |
|
||||
| Method | Default Value | Description |
|
||||
| --------------- | ---------------------------------------- | ------------------------- |
|
||||
| `noiseProducer` | `DefaultNoiseProducer.builder().build()` | The noise producer to use |
|
||||
|
||||
### FishEyeGimpy
|
||||
|
||||
@@ -175,9 +175,9 @@ ShadowGimpy gimpy = ShadowGimpy.builder()
|
||||
.build();
|
||||
```
|
||||
|
||||
| Method | Default Value | Description |
|
||||
|-----------------|---------------------------------------------|------------------------------|
|
||||
| `noiseProducer` | `DefaultNoiseProducer.builder().build()` | The noise producer to use |
|
||||
| Method | Default Value | Description |
|
||||
| --------------- | ---------------------------------------- | ------------------------- |
|
||||
| `noiseProducer` | `DefaultNoiseProducer.builder().build()` | The noise producer to use |
|
||||
|
||||
---
|
||||
|
||||
@@ -204,9 +204,9 @@ DefaultNoiseProducer noise = DefaultNoiseProducer.builder()
|
||||
.build();
|
||||
```
|
||||
|
||||
| Method | Default Value | Description |
|
||||
|---------------|---------------|---------------------|
|
||||
| `noiseColour` | `Color.BLACK` | The noise colour |
|
||||
| Method | Default Value | Description |
|
||||
| ------------- | ------------- | ---------------- |
|
||||
| `noiseColour` | `Color.BLACK` | The noise colour |
|
||||
|
||||
#### NoNoiseProducer
|
||||
|
||||
@@ -243,6 +243,6 @@ DefaultBackgroundProducer background = DefaultBackgroundProducer.builder()
|
||||
```
|
||||
|
||||
| Method | Default Value | Description |
|
||||
|--------------|--------------------|---------------------------------|
|
||||
| ------------ | ------------------ | ------------------------------- |
|
||||
| `colourFrom` | `Color.LIGHT_GRAY` | The starting colour of gradient |
|
||||
| `colourTo` | `Color.WHITE` | The ending colour of gradient |
|
||||
@@ -23,10 +23,7 @@ With Captcha, you can easily generate customisable CAPTCHA images with various d
|
||||
|
||||
<Tabs>
|
||||
<Tab label="Gradle with Kotlin DSL">
|
||||
```kotlin title="build.gradle.kts"
|
||||
dependencies {
|
||||
implementation("com.onixbyte:captcha:1.0.0")
|
||||
}
|
||||
```kotlin title="build.gradle.kts" dependencies {implementation("com.onixbyte:captcha:1.0.0")}
|
||||
```
|
||||
</Tab>
|
||||
<Tab label="Maven">
|
||||
|
||||
@@ -23,6 +23,7 @@ Skills are deployed directly into Claude Code's own skills directory (`~/.claude
|
||||
| Windows (amd64) | `ccsm-windows-amd64.exe` |
|
||||
|
||||
Place the binary somewhere on your `PATH` and rename it to `ccsm` (or `ccsm.exe` on Windows).
|
||||
|
||||
</Tab>
|
||||
<Tab label="Cargo install">
|
||||
```bash
|
||||
@@ -109,10 +110,10 @@ ccsm marketplace remove my-marketplace
|
||||
|
||||
CCSM supports two deploy modes:
|
||||
|
||||
| Mode | Behaviour | Use case |
|
||||
|------|-----------|----------|
|
||||
| `copy` (default) | Files are physically copied to `~/.claude/skills/<name>/` | Normal use — safe and self-contained |
|
||||
| `link` | A symlink is created pointing to the local store | Skill development — changes to source files are picked up immediately |
|
||||
| Mode | Behaviour | Use case |
|
||||
| ---------------- | --------------------------------------------------------- | --------------------------------------------------------------------- |
|
||||
| `copy` (default) | Files are physically copied to `~/.claude/skills/<name>/` | Normal use — safe and self-contained |
|
||||
| `link` | A symlink is created pointing to the local store | Skill development — changes to source files are picked up immediately |
|
||||
|
||||
Set the default mode in your config, or override per install:
|
||||
|
||||
@@ -158,21 +159,21 @@ your-marketplace-repo/
|
||||
|
||||
**Top-level fields:**
|
||||
|
||||
| Field | Required | Description |
|
||||
|-------|----------|-------------|
|
||||
| `name` | Yes | Marketplace display name |
|
||||
| `version` | No | Arbitrary version string for your own tracking |
|
||||
| `skills` | No | Array of skill entries (defaults to `[]`) |
|
||||
| Field | Required | Description |
|
||||
| --------- | -------- | ---------------------------------------------- |
|
||||
| `name` | Yes | Marketplace display name |
|
||||
| `version` | No | Arbitrary version string for your own tracking |
|
||||
| `skills` | No | Array of skill entries (defaults to `[]`) |
|
||||
|
||||
**Skill entry fields:**
|
||||
|
||||
| Field | Required | Description |
|
||||
|-------|----------|-------------|
|
||||
| `name` | Yes | Unique skill identifier (used with `ccsm install <name>`) |
|
||||
| `description` | Yes | One-line summary shown in `ccsm search` output |
|
||||
| `repository` | Yes | Public Git repository URL for the skill |
|
||||
| `branch` | No | Git branch to install from (tries `main` first, falls back to `master`) |
|
||||
| `path` | No | Subdirectory within the repository where the skill files live — enables monorepos hosting multiple skills |
|
||||
| Field | Required | Description |
|
||||
| ------------- | -------- | --------------------------------------------------------------------------------------------------------- |
|
||||
| `name` | Yes | Unique skill identifier (used with `ccsm install <name>`) |
|
||||
| `description` | Yes | One-line summary shown in `ccsm search` output |
|
||||
| `repository` | Yes | Public Git repository URL for the skill |
|
||||
| `branch` | No | Git branch to install from (tries `main` first, falls back to `master`) |
|
||||
| `path` | No | Subdirectory within the repository where the skill files live — enables monorepos hosting multiple skills |
|
||||
|
||||
### How it works
|
||||
|
||||
@@ -202,12 +203,12 @@ my-skill-repo/
|
||||
|
||||
## How CCSM Stores Data
|
||||
|
||||
| Path | Purpose |
|
||||
|------|---------|
|
||||
| `~/.claude/skills/` | Claude Code's skills directory — where skills are deployed |
|
||||
| `~/.config/ccsm/config.json` | CCSM configuration (registered marketplaces, default mode) |
|
||||
| `~/.config/ccsm/marketplaces/` | Cached marketplace manifests |
|
||||
| `~/.config/ccsm/skills/` | Downloaded skill files (local store) |
|
||||
| Path | Purpose |
|
||||
| ------------------------------ | ---------------------------------------------------------- |
|
||||
| `~/.claude/skills/` | Claude Code's skills directory — where skills are deployed |
|
||||
| `~/.config/ccsm/config.json` | CCSM configuration (registered marketplaces, default mode) |
|
||||
| `~/.config/ccsm/marketplaces/` | Cached marketplace manifests |
|
||||
| `~/.config/ccsm/skills/` | Downloaded skill files (local store) |
|
||||
|
||||
On Windows, `~/.config/` resolves to `C:\Users\<user>\AppData\Roaming\`.
|
||||
|
||||
|
||||
@@ -6,64 +6,64 @@ title: Delta Force Guide API
|
||||
|
||||
The backend is a Spring Boot 3 application organised under the base package `com.onixbyte.deltaforceguide`:
|
||||
|
||||
| Package | Description |
|
||||
|---------------------------------------------|-----------------------------------|
|
||||
| `controller` | REST API endpoint controllers |
|
||||
| `service` | Business logic layer |
|
||||
| `repository` | MyBatis data access layer |
|
||||
| `domain` | JPA entity and domain objects |
|
||||
| `config` | Spring configuration (security, cache, etc.) |
|
||||
| `properties` | Configuration property classes |
|
||||
| `enumeration` | Enumerated types |
|
||||
| `shared` | Shared utilities |
|
||||
| Package | Description |
|
||||
| ------------- | -------------------------------------------- |
|
||||
| `controller` | REST API endpoint controllers |
|
||||
| `service` | Business logic layer |
|
||||
| `repository` | MyBatis data access layer |
|
||||
| `domain` | JPA entity and domain objects |
|
||||
| `config` | Spring configuration (security, cache, etc.) |
|
||||
| `properties` | Configuration property classes |
|
||||
| `enumeration` | Enumerated types |
|
||||
| `shared` | Shared utilities |
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Authentication
|
||||
|
||||
| Method | Path | Description | Auth |
|
||||
|--------|-----------------------|--------------------------|------|
|
||||
| `POST` | `/api/auth/login` | Log in with credentials | No |
|
||||
| `POST` | `/api/auth/refresh` | Refresh access token | No |
|
||||
| `GET` | `/api/auth/profile` | Get current user profile | Yes |
|
||||
| Method | Path | Description | Auth |
|
||||
| ------ | ------------------- | ------------------------ | ---- |
|
||||
| `POST` | `/api/auth/login` | Log in with credentials | No |
|
||||
| `POST` | `/api/auth/refresh` | Refresh access token | No |
|
||||
| `GET` | `/api/auth/profile` | Get current user profile | Yes |
|
||||
|
||||
### Firearms
|
||||
|
||||
| Method | Path | Description |
|
||||
|----------|-----------------------|--------------------------|
|
||||
| `GET` | `/api/firearms` | List firearms (paginated, filterable by type) |
|
||||
| `GET` | `/api/firearms/:id` | Get firearm by ID |
|
||||
| `POST` | `/api/firearms` | Create a new firearm |
|
||||
| `PUT` | `/api/firearms/:id` | Update an existing firearm |
|
||||
| `DELETE` | `/api/firearms/:id` | Remove a firearm |
|
||||
| Method | Path | Description |
|
||||
| -------- | ------------------- | --------------------------------------------- |
|
||||
| `GET` | `/api/firearms` | List firearms (paginated, filterable by type) |
|
||||
| `GET` | `/api/firearms/:id` | Get firearm by ID |
|
||||
| `POST` | `/api/firearms` | Create a new firearm |
|
||||
| `PUT` | `/api/firearms/:id` | Update an existing firearm |
|
||||
| `DELETE` | `/api/firearms/:id` | Remove a firearm |
|
||||
|
||||
### Modifications
|
||||
|
||||
| Method | Path | Description |
|
||||
|----------|-----------------------------------|--------------------------------|
|
||||
| Method | Path | Description |
|
||||
| -------- | --------------------------------- | -------------------------------------------------------------- |
|
||||
| `GET` | `/api/modifications` | List modifications (paginated, filterable by firearm and tags) |
|
||||
| `GET` | `/api/modifications/:id` | Get modification by ID |
|
||||
| `POST` | `/api/modifications` | Create a modification |
|
||||
| `POST` | `/api/modifications/batch` | Batch create modifications |
|
||||
| `PUT` | `/api/modifications/:id` | Update a modification |
|
||||
| `DELETE` | `/api/modifications/:id` | Remove a modification |
|
||||
| `DELETE` | `/api/modifications/batch-delete` | Batch remove modifications |
|
||||
| `GET` | `/api/modifications/:id` | Get modification by ID |
|
||||
| `POST` | `/api/modifications` | Create a modification |
|
||||
| `POST` | `/api/modifications/batch` | Batch create modifications |
|
||||
| `PUT` | `/api/modifications/:id` | Update a modification |
|
||||
| `DELETE` | `/api/modifications/:id` | Remove a modification |
|
||||
| `DELETE` | `/api/modifications/batch-delete` | Batch remove modifications |
|
||||
|
||||
### Tags
|
||||
|
||||
| Method | Path | Description |
|
||||
|--------|-----------------------|-----------------------|
|
||||
| `GET` | `/api/tags` | List all tags |
|
||||
| Method | Path | Description |
|
||||
| ------ | ----------- | ------------- |
|
||||
| `GET` | `/api/tags` | List all tags |
|
||||
|
||||
## Frontend Routes
|
||||
|
||||
| Path | Layout | Page | Description |
|
||||
|---------------|-------------|-------------------|---------------------------------|
|
||||
| `/` | Hero Layout | Firearms Browser | Home page — firearm list |
|
||||
| `/firearms` | Hero Layout | Firearms Browser | Firearm detail and search |
|
||||
| `/mod-codes` | Hero Layout | Modification Codes| Modification code library |
|
||||
| `/legal` | Hero Layout | Legal | Legal and privacy information |
|
||||
| `/login` | Empty Layout| Login | User authentication page |
|
||||
| Path | Layout | Page | Description |
|
||||
| ------------ | ------------ | ------------------ | ----------------------------- |
|
||||
| `/` | Hero Layout | Firearms Browser | Home page — firearm list |
|
||||
| `/firearms` | Hero Layout | Firearms Browser | Firearm detail and search |
|
||||
| `/mod-codes` | Hero Layout | Modification Codes | Modification code library |
|
||||
| `/legal` | Hero Layout | Legal | Legal and privacy information |
|
||||
| `/login` | Empty Layout | Login | User authentication page |
|
||||
|
||||
## Frontend State Management
|
||||
|
||||
@@ -82,10 +82,10 @@ The frontend uses a shared `WebClient` wrapper around Axios that handles:
|
||||
|
||||
## Infrastructure
|
||||
|
||||
| Component | Technology |
|
||||
|---------------|-----------------------------|
|
||||
| Database | PostgreSQL (with Flyway migrations) |
|
||||
| Cache | Redis (session/token storage) |
|
||||
| File Storage | AWS S3 (firearm images) |
|
||||
| Containerisation | Docker |
|
||||
| Deployment | Docker Compose behind Nginx |
|
||||
| Component | Technology |
|
||||
| ---------------- | ----------------------------------- |
|
||||
| Database | PostgreSQL (with Flyway migrations) |
|
||||
| Cache | Redis (session/token storage) |
|
||||
| File Storage | AWS S3 (firearm images) |
|
||||
| Containerisation | Docker |
|
||||
| Deployment | Docker Compose behind Nginx |
|
||||
@@ -23,12 +23,12 @@ The live site is available at **[dfguide.onixbyte.cn](https://dfguide.onixbyte.c
|
||||
|
||||
## Architecture
|
||||
|
||||
| Component | Tech Stack |
|
||||
|---------------|---------------------------------------------------------------|
|
||||
| **Frontend** | React 19, TypeScript, Tailwind CSS 4, Ant Design 6, React Router 7, Redux Toolkit, TanStack Virtual |
|
||||
| **Backend** | Spring Boot 3, Java 21, PostgreSQL, MyBatis, Flyway, Redis |
|
||||
| **Auth** | JWT (access + refresh tokens), CAPTCHA integration |
|
||||
| **Storage** | AWS S3 for image uploads |
|
||||
| Component | Tech Stack |
|
||||
| ------------ | --------------------------------------------------------------------------------------------------- |
|
||||
| **Frontend** | React 19, TypeScript, Tailwind CSS 4, Ant Design 6, React Router 7, Redux Toolkit, TanStack Virtual |
|
||||
| **Backend** | Spring Boot 3, Java 21, PostgreSQL, MyBatis, Flyway, Redis |
|
||||
| **Auth** | JWT (access + refresh tokens), CAPTCHA integration |
|
||||
| **Storage** | AWS S3 for image uploads |
|
||||
|
||||
The frontend is a static site served by Vite, communicating with the Spring Boot REST API backend. Authentication uses JWT access and refresh tokens with Redis-backed refresh token management.
|
||||
|
||||
|
||||
@@ -6,13 +6,13 @@ title: OnixByte Toolbox API
|
||||
|
||||
All modules are published under the `com.onixbyte` group ID. Each module has its own base package:
|
||||
|
||||
| Module | Maven Artifact | Base Package |
|
||||
|----------------------|------------------------|---------------------------------------|
|
||||
| Common Toolbox | `common-toolbox` | `com.onixbyte.common` |
|
||||
| Crypto Toolbox | `crypto-toolbox` | `com.onixbyte.crypto` |
|
||||
| Math Toolbox | `math-toolbox` | `com.onixbyte.math` |
|
||||
| Tuple | `tuple` | `com.onixbyte.tuple` |
|
||||
| Identity Generator | `identity-generator` | `com.onixbyte.identitygenerator` |
|
||||
| Module | Maven Artifact | Base Package |
|
||||
| ------------------ | -------------------- | -------------------------------- |
|
||||
| Common Toolbox | `common-toolbox` | `com.onixbyte.common` |
|
||||
| Crypto Toolbox | `crypto-toolbox` | `com.onixbyte.crypto` |
|
||||
| Math Toolbox | `math-toolbox` | `com.onixbyte.math` |
|
||||
| Tuple | `tuple` | `com.onixbyte.tuple` |
|
||||
| Identity Generator | `identity-generator` | `com.onixbyte.identitygenerator` |
|
||||
|
||||
---
|
||||
|
||||
@@ -28,16 +28,16 @@ import com.onixbyte.common.adapter.ObjectMapAdapter;
|
||||
|
||||
### Utility Classes
|
||||
|
||||
| Class | Description |
|
||||
|--------------------|----------------------------------------------------------|
|
||||
| `AesUtil` | AES symmetric encryption/decryption with key generation |
|
||||
| `Base64Util` | Base64 encoding and decoding with multiple charset support|
|
||||
| `BoolUtil` | Boolean parsing and conversion utilities |
|
||||
| `BranchUtil` | Conditional branching helpers with functional-style chains|
|
||||
| `CollectionUtil` | Collection manipulation — grouping, partitioning, merging |
|
||||
| `HashUtil` | Hashing utilities supporting MD5, SHA-1, SHA-256, SHA-512 |
|
||||
| `MapUtil` | Map builder, merge, and transformation helpers |
|
||||
| `RangeUtil` | Numeric range operations with boundary handling |
|
||||
| Class | Description |
|
||||
| ---------------- | ---------------------------------------------------------- |
|
||||
| `AesUtil` | AES symmetric encryption/decryption with key generation |
|
||||
| `Base64Util` | Base64 encoding and decoding with multiple charset support |
|
||||
| `BoolUtil` | Boolean parsing and conversion utilities |
|
||||
| `BranchUtil` | Conditional branching helpers with functional-style chains |
|
||||
| `CollectionUtil` | Collection manipulation — grouping, partitioning, merging |
|
||||
| `HashUtil` | Hashing utilities supporting MD5, SHA-1, SHA-256, SHA-512 |
|
||||
| `MapUtil` | Map builder, merge, and transformation helpers |
|
||||
| `RangeUtil` | Numeric range operations with boundary handling |
|
||||
|
||||
**AesUtil Example:**
|
||||
|
||||
@@ -70,12 +70,12 @@ Provides a clean abstraction for loading cryptographic keys in PEM format. Suppo
|
||||
|
||||
### Key Loaders
|
||||
|
||||
| Class | Description |
|
||||
|---------------------------|--------------------------------------------|
|
||||
| `RSAPrivateKeyLoader` | Load RSA private keys from PEM strings |
|
||||
| `RSAPublicKeyLoader` | Load RSA public keys from PEM strings |
|
||||
| `ECPrivateKeyLoader` | Load ECDSA private keys from PEM strings |
|
||||
| `ECPublicKeyLoader` | Load ECDSA public keys from PEM strings |
|
||||
| Class | Description |
|
||||
| --------------------- | ---------------------------------------- |
|
||||
| `RSAPrivateKeyLoader` | Load RSA private keys from PEM strings |
|
||||
| `RSAPublicKeyLoader` | Load RSA public keys from PEM strings |
|
||||
| `ECPrivateKeyLoader` | Load ECDSA private keys from PEM strings |
|
||||
| `ECPublicKeyLoader` | Load ECDSA public keys from PEM strings |
|
||||
|
||||
All key loaders implement either `PrivateKeyLoader` or `PublicKeyLoader` interfaces.
|
||||
|
||||
@@ -99,9 +99,9 @@ var ecPublicKey = new ECPublicKeyLoader().load(pemString);
|
||||
|
||||
### Exceptions
|
||||
|
||||
| Class | Description |
|
||||
|------------------------|---------------------------------------------------|
|
||||
| `KeyLoadingException` | Thrown when a PEM-formatted key fails to parse |
|
||||
| Class | Description |
|
||||
| --------------------- | ---------------------------------------------- |
|
||||
| `KeyLoadingException` | Thrown when a PEM-formatted key fails to parse |
|
||||
|
||||
---
|
||||
|
||||
@@ -111,10 +111,10 @@ Statistical computation utilities for working with numeric datasets.
|
||||
|
||||
### Core Classes
|
||||
|
||||
| Class | Description |
|
||||
|-------------------------|----------------------------------------------------------|
|
||||
| `Calculator` | Statistical calculator for mean, median, variance, standard deviation, sum, min, max |
|
||||
| `PercentileCalculator` | Percentile computation with configurable interpolation strategies |
|
||||
| Class | Description |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------ |
|
||||
| `Calculator` | Statistical calculator for mean, median, variance, standard deviation, sum, min, max |
|
||||
| `PercentileCalculator` | Percentile computation with configurable interpolation strategies |
|
||||
|
||||
**Calculator Example:**
|
||||
|
||||
@@ -135,9 +135,9 @@ double p99 = calculator.calculate(99);
|
||||
|
||||
### Model
|
||||
|
||||
| Class | Description |
|
||||
|-------------------|----------------------------------------------------------------|
|
||||
| `QuartileBounds` | Holds Q1, median (Q2), Q3 values along with IQR and whisker bounds |
|
||||
| Class | Description |
|
||||
| ---------------- | ------------------------------------------------------------------ |
|
||||
| `QuartileBounds` | Holds Q1, median (Q2), Q3 values along with IQR and whisker bounds |
|
||||
|
||||
---
|
||||
|
||||
@@ -147,12 +147,12 @@ Lightweight generic tuple types for when you need to return multiple values with
|
||||
|
||||
### Classes
|
||||
|
||||
| Class | Type Parameters | Mutability |
|
||||
|--------------------|-----------------|------------|
|
||||
| `Tuple<A, B>` | Two elements | Mutable |
|
||||
| `ImmutableTuple<A, B>` | Two elements | Immutable |
|
||||
| `Triple<A, B, C>` | Three elements | Mutable |
|
||||
| `ImmutableTriple<A, B, C>` | Three elements | Immutable |
|
||||
| Class | Type Parameters | Mutability |
|
||||
| -------------------------- | --------------- | ---------- |
|
||||
| `Tuple<A, B>` | Two elements | Mutable |
|
||||
| `ImmutableTuple<A, B>` | Two elements | Immutable |
|
||||
| `Triple<A, B, C>` | Three elements | Mutable |
|
||||
| `ImmutableTriple<A, B, C>` | Three elements | Immutable |
|
||||
|
||||
**Example:**
|
||||
|
||||
@@ -183,10 +183,10 @@ Generates unique identifiers suitable for use as database primary keys or distri
|
||||
|
||||
### Implementations
|
||||
|
||||
| Class | Description |
|
||||
|------------------------------|-----------------------------------------------------------------|
|
||||
| Class | Description |
|
||||
| ---------------------------- | ------------------------------------------------------------------- |
|
||||
| `SequentialUuidGenerator` | Generates time-ordered, sequential UUIDs (optimised for DB indexes) |
|
||||
| `SnowflakeIdentityGenerator` | Snowflake-style distributed unique ID generation |
|
||||
| `SnowflakeIdentityGenerator` | Snowflake-style distributed unique ID generation |
|
||||
|
||||
**Sequential UUID:**
|
||||
|
||||
@@ -204,6 +204,6 @@ long id = generator.nextId(); // 64-bit snowflake ID
|
||||
|
||||
### Exceptions
|
||||
|
||||
| Class | Description |
|
||||
|-------------------|----------------------------------------------------------------|
|
||||
| Class | Description |
|
||||
| ----------------- | ------------------------------------------------------------------------ |
|
||||
| `TimingException` | Thrown when the system clock moves backwards, compromising ID uniqueness |
|
||||
@@ -13,7 +13,7 @@ The toolkit is available on Maven Central under the `com.onixbyte` group ID. Eac
|
||||
## Modules
|
||||
|
||||
| Module | Artifact ID | Description |
|
||||
|--------------------|----------------------|---------------------------------------------------------------------------|
|
||||
| ------------------ | -------------------- | ------------------------------------------------------------------------- |
|
||||
| Common Toolbox | `common-toolbox` | Everyday utilities — AES, hashing, Base64, collections, branching, ranges |
|
||||
| Crypto Toolbox | `crypto-toolbox` | Public/private key loading for RSA and ECDSA algorithms |
|
||||
| Math Toolbox | `math-toolbox` | Statistical calculation, percentile analysis, quartile computation |
|
||||
|
||||
@@ -21,12 +21,12 @@ The library models every ISO 3166-1 country and territory as an enum constant, g
|
||||
|
||||
Each region exposes four attributes:
|
||||
|
||||
| Attribute | Type | Description | Example |
|
||||
|----------------|--------|---------------------------------|------------------------------------|
|
||||
| `abbreviation` | String | ISO 3166-1 alpha-2 abbreviation | `HK`, `US`, `GB`, `RU` |
|
||||
| `code` | String | ISO 3166-1 alpha-3 code | `HKG`, `USA`, `GBR`, `RUS` |
|
||||
| Attribute | Type | Description | Example |
|
||||
| -------------- | ------ | ----------------------------------- | ---------------------------------- |
|
||||
| `abbreviation` | String | ISO 3166-1 alpha-2 abbreviation | `HK`, `US`, `GB`, `RU` |
|
||||
| `code` | String | ISO 3166-1 alpha-3 code | `HKG`, `USA`, `GBR`, `RUS` |
|
||||
| `callingCode` | String | E.164 international dialling prefix | `852`, `1`, `44`, `7` |
|
||||
| `locale` | String | Default language and region tag | `zh_HK`, `en_US`, `en_GB`, `ru_RU` |
|
||||
| `locale` | String | Default language and region tag | `zh_HK`, `en_US`, `en_GB`, `ru_RU` |
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
@@ -20,16 +20,8 @@ This plugin detects the situation at startup and prints a clear, colour-coded wa
|
||||
## Installation
|
||||
|
||||
<Tabs>
|
||||
<Tab label="npm">
|
||||
```bash
|
||||
npm install vite-plugin-port-checker -D
|
||||
```
|
||||
</Tab>
|
||||
<Tab label="pnpm">
|
||||
```bash
|
||||
pnpm add vite-plugin-port-checker -D
|
||||
```
|
||||
</Tab>
|
||||
<Tab label="npm">```bash npm install vite-plugin-port-checker -D ```</Tab>
|
||||
<Tab label="pnpm">```bash pnpm add vite-plugin-port-checker -D ```</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -10,22 +10,22 @@ author:
|
||||
|
||||
## 调料投放时机
|
||||
|
||||
| 调料类型 | 放调料时机 | 适用菜品 | 作用 |
|
||||
|--------------|----------|----------------|---------------------|
|
||||
| **盐** | 最后放 | 菜叶子 | 防止菜叶变软出水,保证口感干爽脆嫩 |
|
||||
| | 中间放 | 土豆丝、豆角、蒜苔等 | 更好入味 |
|
||||
| **味精、生抽、蚝油** | 偏后放 | 各类需要增鲜提香的菜品 | 增鲜提香,避免高温破坏鲜味和香味 |
|
||||
| **料酒** | 和食材一起放 | 需要去腥的食材 | 焯水时去腥效果更好 |
|
||||
| | 锅温最高的时候放 | 需要炒菜去腥的食材 | 炒菜时高温促进酒气挥发,炝锅去腥效果好 |
|
||||
| **醋** | 提前放 | 酸辣土豆丝、酸辣大白菜 | 使土豆丝变脆更入味 |
|
||||
| | 出锅前锅边醋 | 辣椒炒肉等需要猛火爆炒类菜品 | 大火炝锅增香 |
|
||||
| **老抽** | 炒半熟后放 | 需要上色的菜品 | 达到最佳上色效果 |
|
||||
| 调料类型 | 放调料时机 | 适用菜品 | 作用 |
|
||||
| -------------------- | ---------------- | ---------------------------- | -------------------------------------- |
|
||||
| **盐** | 最后放 | 菜叶子 | 防止菜叶变软出水,保证口感干爽脆嫩 |
|
||||
| | 中间放 | 土豆丝、豆角、蒜苔等 | 更好入味 |
|
||||
| **味精、生抽、蚝油** | 偏后放 | 各类需要增鲜提香的菜品 | 增鲜提香,避免高温破坏鲜味和香味 |
|
||||
| **料酒** | 和食材一起放 | 需要去腥的食材 | 焯水时去腥效果更好 |
|
||||
| | 锅温最高的时候放 | 需要炒菜去腥的食材 | 炒菜时高温促进酒气挥发,炝锅去腥效果好 |
|
||||
| **醋** | 提前放 | 酸辣土豆丝、酸辣大白菜 | 使土豆丝变脆更入味 |
|
||||
| | 出锅前锅边醋 | 辣椒炒肉等需要猛火爆炒类菜品 | 大火炝锅增香 |
|
||||
| **老抽** | 炒半熟后放 | 需要上色的菜品 | 达到最佳上色效果 |
|
||||
|
||||
## 快速参考
|
||||
|
||||
| 调料 | 投放时机 | 作用 | 示例 |
|
||||
|-----------------|----------------|------------------------|------------------------|
|
||||
| **盐** | 出锅前 10 - 15 秒放 | 避免提前放盐导致菜叶出水、变软,保持脆嫩口感 | 炒生菜、菠菜时,关火前撒盐翻炒均匀 |
|
||||
| **蒜末 / 姜末** | 热油后先放 | 爆香提味,激发香味 | 油热后下蒜末炝锅,再放青菜翻炒 |
|
||||
| **生抽 / 蚝油**(可选) | 盐之后放 | 增鲜提味,少量即可,避免盖过蔬菜原味 | 炒上海青时,加盐后滴 1 - 2 滴生抽翻炒 |
|
||||
| **食用油** | 热锅热油 | 油热后快速炒,锁住水分 | 大火快炒时,油稍多一点,油温要高 |
|
||||
| 调料 | 投放时机 | 作用 | 示例 |
|
||||
| ----------------------- | ------------------- | -------------------------------------------- | ------------------------------------- |
|
||||
| **盐** | 出锅前 10 - 15 秒放 | 避免提前放盐导致菜叶出水、变软,保持脆嫩口感 | 炒生菜、菠菜时,关火前撒盐翻炒均匀 |
|
||||
| **蒜末 / 姜末** | 热油后先放 | 爆香提味,激发香味 | 油热后下蒜末炝锅,再放青菜翻炒 |
|
||||
| **生抽 / 蚝油**(可选) | 盐之后放 | 增鲜提味,少量即可,避免盖过蔬菜原味 | 炒上海青时,加盐后滴 1 - 2 滴生抽翻炒 |
|
||||
| **食用油** | 热锅热油 | 油热后快速炒,锁住水分 | 大火快炒时,油稍多一点,油温要高 |
|
||||
@@ -17,13 +17,13 @@ author:
|
||||
```javascript
|
||||
function processResponse(response) {
|
||||
if (response.status === 200 && response.data) {
|
||||
return { success: true, data: response.data };
|
||||
return { success: true, data: response.data }
|
||||
} else if (response.status === 404) {
|
||||
return { success: false, error: 'Not found' };
|
||||
return { success: false, error: "Not found" }
|
||||
} else if (response.status >= 500) {
|
||||
return { success: false, error: 'Server error' };
|
||||
return { success: false, error: "Server error" }
|
||||
} else {
|
||||
return { success: false, error: 'Unknown error' };
|
||||
return { success: false, error: "Unknown error" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -17,17 +17,17 @@ author:
|
||||
|
||||
## 场景速查表
|
||||
|
||||
| 场景 | ❌ 别用(听起来……) | ✅ 改用(听起来……) | 为什么有效 |
|
||||
|----------|--------------------------------------------------------------|----------------------------------------------------------|-----------------------------------------------|
|
||||
| 回复晚了 | **Sorry for the delay**<br/>(抱歉回复晚了) | **Thanks for your patience**<br/>(感谢你的耐心等待) | 前者在道歉,后者在感谢——从「我做错了」转变为「你太好了」,既表达了歉意又让对方感到被尊重 |
|
||||
| 约时间 | **What works best for you?**<br/>(你什么时候方便?) | **Could you do …?**<br/>(你可以在某某时间吗?) | 前者把决定权完全推给对方,后者主动给出选项,减少来回沟通的成本 |
|
||||
| 帮助了别人之后 | **No problem / No worries**<br/>(没问题 / 别担心) | **Always happy to help**<br/>(乐于效劳) | 前者暗示这件事"本来可能是个问题",后者传递出你乐在其中、下次还愿意帮 |
|
||||
| 提出建议 | **I think maybe we should …**<br/>(我觉得也许我们应该……) | **It'd be best if we …**<br/>(最好是……) | 前者透着犹豫和不自信,后者直接给出判断——像有经验的人在做决策 |
|
||||
| 文字沟通费劲 | **_花 30 分钟反复修改一封邮件_** | **It'd be easier to discuss in person**<br/>(当面/电话聊会更轻松) | 意识到沟通渠道本身就是问题,果断切换方式反而是效率最高的选择 |
|
||||
| 确认对方是否理解 | **Hopefully that makes sense?**<br/>(希望我说清楚了?) | **Let me know if you have questions**<br/>(有问题随时问我) | 前者在怀疑自己的表达能力,后者把责任合理地分担给对方,姿态更稳 |
|
||||
| 催进度 | **Just wanted to check in**<br/>(只是想确认一下) | **When can I expect an update?**<br/>(我什么时候可以收到更新?) | 前者显得小心翼翼不敢打扰,后者直接问时间节点——清楚、礼貌、专业 |
|
||||
| 犯了小错 | **Ahh sorry my bad totally missed that**<br/>(啊啊抱歉我的错,完全漏掉了) | **Thanks for letting me know**<br/>(感谢提醒) | 前者过度道歉反而让对方尴尬,后者承认了问题但把焦点放在了"解决"上 |
|
||||
| 需要早退 | **Could I possibly leave early?**<br/>(我能提前走吗?) | **I will need to leave at …**<br/>(我需要在某某时间离开) | 前者在请求批准,后者在陈述安排——你是专业人士,不需要为合理需求道歉 |
|
||||
| 场景 | ❌ 别用(听起来……) | ✅ 改用(听起来……) | 为什么有效 |
|
||||
| ---------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
|
||||
| 回复晚了 | **Sorry for the delay**<br/>(抱歉回复晚了) | **Thanks for your patience**<br/>(感谢你的耐心等待) | 前者在道歉,后者在感谢——从「我做错了」转变为「你太好了」,既表达了歉意又让对方感到被尊重 |
|
||||
| 约时间 | **What works best for you?**<br/>(你什么时候方便?) | **Could you do …?**<br/>(你可以在某某时间吗?) | 前者把决定权完全推给对方,后者主动给出选项,减少来回沟通的成本 |
|
||||
| 帮助了别人之后 | **No problem / No worries**<br/>(没问题 / 别担心) | **Always happy to help**<br/>(乐于效劳) | 前者暗示这件事"本来可能是个问题",后者传递出你乐在其中、下次还愿意帮 |
|
||||
| 提出建议 | **I think maybe we should …**<br/>(我觉得也许我们应该……) | **It'd be best if we …**<br/>(最好是……) | 前者透着犹豫和不自信,后者直接给出判断——像有经验的人在做决策 |
|
||||
| 文字沟通费劲 | **_花 30 分钟反复修改一封邮件_** | **It'd be easier to discuss in person**<br/>(当面/电话聊会更轻松) | 意识到沟通渠道本身就是问题,果断切换方式反而是效率最高的选择 |
|
||||
| 确认对方是否理解 | **Hopefully that makes sense?**<br/>(希望我说清楚了?) | **Let me know if you have questions**<br/>(有问题随时问我) | 前者在怀疑自己的表达能力,后者把责任合理地分担给对方,姿态更稳 |
|
||||
| 催进度 | **Just wanted to check in**<br/>(只是想确认一下) | **When can I expect an update?**<br/>(我什么时候可以收到更新?) | 前者显得小心翼翼不敢打扰,后者直接问时间节点——清楚、礼貌、专业 |
|
||||
| 犯了小错 | **Ahh sorry my bad totally missed that**<br/>(啊啊抱歉我的错,完全漏掉了) | **Thanks for letting me know**<br/>(感谢提醒) | 前者过度道歉反而让对方尴尬,后者承认了问题但把焦点放在了"解决"上 |
|
||||
| 需要早退 | **Could I possibly leave early?**<br/>(我能提前走吗?) | **I will need to leave at …**<br/>(我需要在某某时间离开) | 前者在请求批准,后者在陈述安排——你是专业人士,不需要为合理需求道歉 |
|
||||
|
||||
## 核心原则
|
||||
|
||||
|
||||
@@ -21,6 +21,6 @@ tags:
|
||||
|
||||
于是对于 PowerNap 这个功能来说,我们只能从命令行下手,首先使用 **`pmset -g`** 命令查看当前状态,找到 **`powernap`** 的值,如果不是 0 ,说明是启用的状态,使用命令 **`sudo pmset -a powernap 0`** 关掉它。
|
||||
|
||||
同时,还有另外一个 **`tcpkeepalive`** ,这个默认应该也不是 0 ,也要关掉,它决定了你的 rmbp 在休眠时是否要保持 tcp 连接。 **`sudo pmset -a tcpkeepalive 0`** ,执行这条命令会导致终端提示:***Warning: This option disables TCP Keep Alive mechanism when system is sleeping. This will result in some critical features like 'Find My Mac' not to function properly.***
|
||||
同时,还有另外一个 **`tcpkeepalive`** ,这个默认应该也不是 0 ,也要关掉,它决定了你的 rmbp 在休眠时是否要保持 tcp 连接。 **`sudo pmset -a tcpkeepalive 0`** ,执行这条命令会导致终端提示:**_Warning: This option disables TCP Keep Alive mechanism when system is sleeping. This will result in some critical features like 'Find My Mac' not to function properly._**
|
||||
|
||||
大概是说关了的话某些功能会受到限制,其实就是系统功能不能休眠时联网了,我相信真有人偷了你 Mac,它也连不上网的。
|
||||
@@ -11,20 +11,20 @@ author:
|
||||
---
|
||||
|
||||
| 中文字号 | 英文字号(磅)/pt | 毫米/mm | 像素/px |
|
||||
|------|------------|-------|-------|
|
||||
| 初号 | 42 | 14.82 | 56 |
|
||||
| 小初 | 36 | 12.7 | 48 |
|
||||
| 一号 | 26 | 9.17 | 34.7 |
|
||||
| 小一 | 24 | 8.47 | 32 |
|
||||
| 二号 | 22 | 7.76 | 29.3 |
|
||||
| 小二 | 18 | 6.35 | 24 |
|
||||
| 三号 | 16 | 5.64 | 21.3 |
|
||||
| 小三 | 15 | 5.29 | 20 |
|
||||
| 四号 | 14 | 4.94 | 18.7 |
|
||||
| 小四 | 12 | 4.23 | 16 |
|
||||
| 五号 | 10.5 | 3.7 | 14 |
|
||||
| 小五 | 9 | 3.18 | 12 |
|
||||
| 六号 | 7.5 | 2.56 | 10 |
|
||||
| 小六 | 6.5 | 2.29 | 8.7 |
|
||||
| 七号 | 5.5 | 1.94 | 7.3 |
|
||||
| 八号 | 5 | 1.76 | 6.7 |
|
||||
| -------- | ----------------- | ------- | ------- |
|
||||
| 初号 | 42 | 14.82 | 56 |
|
||||
| 小初 | 36 | 12.7 | 48 |
|
||||
| 一号 | 26 | 9.17 | 34.7 |
|
||||
| 小一 | 24 | 8.47 | 32 |
|
||||
| 二号 | 22 | 7.76 | 29.3 |
|
||||
| 小二 | 18 | 6.35 | 24 |
|
||||
| 三号 | 16 | 5.64 | 21.3 |
|
||||
| 小三 | 15 | 5.29 | 20 |
|
||||
| 四号 | 14 | 4.94 | 18.7 |
|
||||
| 小四 | 12 | 4.23 | 16 |
|
||||
| 五号 | 10.5 | 3.7 | 14 |
|
||||
| 小五 | 9 | 3.18 | 12 |
|
||||
| 六号 | 7.5 | 2.56 | 10 |
|
||||
| 小六 | 6.5 | 2.29 | 8.7 |
|
||||
| 七号 | 5.5 | 1.94 | 7.3 |
|
||||
| 八号 | 5 | 1.76 | 6.7 |
|
||||
@@ -68,7 +68,7 @@ createRoot(document.getElementById("root")!).render(
|
||||
<StyleProvider>
|
||||
<ConfigProvider>
|
||||
<App>
|
||||
<RouterProvider/>
|
||||
<RouterProvider />
|
||||
</App>
|
||||
</ConfigProvider>
|
||||
</StyleProvider>
|
||||
|
||||
@@ -99,11 +99,11 @@ N+1 问题是指 JPA 先执行 1 条查询获取父实体,再为每条父实
|
||||
|
||||
**修复方案:**
|
||||
|
||||
| 方式 | 适用场景 |
|
||||
|---------------------------|----------------------|
|
||||
| `@EntityGraph` | 声明式,适合为特定实体定制抓取计划 |
|
||||
| `@Query` 中使用 `JOIN FETCH` | 按查询精细控制 |
|
||||
| `@BatchSize` | 将 N+1 降低为 N/k+1,批量加载 |
|
||||
| 方式 | 适用场景 |
|
||||
| ---------------------------- | ---------------------------------- |
|
||||
| `@EntityGraph` | 声明式,适合为特定实体定制抓取计划 |
|
||||
| `@Query` 中使用 `JOIN FETCH` | 按查询精细控制 |
|
||||
| `@BatchSize` | 将 N+1 降低为 N/k+1,批量加载 |
|
||||
|
||||
```java
|
||||
// 方式一:EntityGraph
|
||||
@@ -139,6 +139,7 @@ post.setAuthor(userRepository.getReferenceById(userId));
|
||||
@Query("SELECT new com.example.UserDto(u.id, u.name) FROM User u WHERE u.id = :id")
|
||||
UserDto findUserDtoById(@Param("id") long id);
|
||||
```
|
||||
|
||||
3. **在 Service 方法上使用 `@Transactional(readOnly = true)`** — 使 Session 在整个方法作用域内保持打开。
|
||||
|
||||
### 什么时候该用 `@Transactional(readOnly = true)`?
|
||||
@@ -270,7 +271,7 @@ public class Product {
|
||||
|
||||
### 如何选择
|
||||
|
||||
| 场景 | 注解 |
|
||||
|----------------------------------------|--------------------|
|
||||
| 平铺字段固定的嵌套对象(如地址、配置) | `@JsonUnwrapped` |
|
||||
| 平铺 key 不固定的动态键值对 | `@JsonAnyGetter` |
|
||||
| 场景 | 注解 |
|
||||
| -------------------------------------- | ---------------- |
|
||||
| 平铺字段固定的嵌套对象(如地址、配置) | `@JsonUnwrapped` |
|
||||
| 平铺 key 不固定的动态键值对 | `@JsonAnyGetter` |
|
||||
@@ -119,7 +119,7 @@ author:
|
||||
- 数据操作约定:
|
||||
- 对于简单数据库操作,使用 JPA。
|
||||
- 对于复杂数据库操作,使用 MyBatis。
|
||||
- 当进行分页查询时,页码应从 0 开始(*兼容 Spring Data JPA*)。
|
||||
- 当进行分页查询时,页码应从 0 开始(_兼容 Spring Data JPA_)。
|
||||
- **数据操作方法命名约定**:
|
||||
- 对于**查询数据列表**:方法**必须**以 `selectListBy` 开头,紧随其后是筛选条件(例如,`selectListByUserId`、`selectListByDepartmentIdAndStatus`)。这些方法也必须包含一个 `PageRequest` 参数用于分页。
|
||||
- 对于查询**单个数据记录**:方法**必须**以 `selectOne` 开头(例如,`selectOneById`、`selectOneByUsername`)。
|
||||
|
||||
@@ -5,8 +5,8 @@ tags:
|
||||
- json
|
||||
- database
|
||||
author:
|
||||
name: Zihlu Wang
|
||||
email: real@zihluwang.me
|
||||
name: Zihlu Wang
|
||||
email: real@zihluwang.me
|
||||
---
|
||||
|
||||
MySQL (自 5.7 版本开始) **不直接支持名为 `jsonb` 的数据类型**。`jsonb` 是 PostgreSQL 数据库特有的数据类型,它以二进制格式存储 JSON 数据,并对其进行预解析,以便在查询时更快地访问和操作。
|
||||
|
||||
@@ -25,11 +25,11 @@ https://gravatar.com/avatar/<hash>
|
||||
### Node.js
|
||||
|
||||
```js
|
||||
import { createHash } from "node:crypto";
|
||||
import { createHash } from "node:crypto"
|
||||
|
||||
const email = "user@example.com".trim().toLowerCase();
|
||||
const hash = createHash("sha256").update(email).digest("hex");
|
||||
const url = `https://gravatar.com/avatar/${hash}`;
|
||||
const email = "user@example.com".trim().toLowerCase()
|
||||
const hash = createHash("sha256").update(email).digest("hex")
|
||||
const url = `https://gravatar.com/avatar/${hash}`
|
||||
```
|
||||
|
||||
### Python
|
||||
@@ -52,11 +52,11 @@ echo -n "user@example.com" | tr '[:upper:]' '[:lower:]' | sha256sum | cut -d ' '
|
||||
|
||||
`/avatar/` 端点接受以下查询参数来定制结果:
|
||||
|
||||
| 参数 | 描述 | 示例 |
|
||||
|-----|------------------------|----------------|
|
||||
| `s` | 图片尺寸(像素,默认 80) | `?s=200` |
|
||||
| `d` | 未找到 Gravatar 时的默认图片 | `?d=identicon` |
|
||||
| `r` | 内容分级(`g`、`pg`、`r`、`x`) | `?r=g` |
|
||||
| 参数 | 描述 | 示例 |
|
||||
| ---- | ------------------------------- | -------------- |
|
||||
| `s` | 图片尺寸(像素,默认 80) | `?s=200` |
|
||||
| `d` | 未找到 Gravatar 时的默认图片 | `?d=identicon` |
|
||||
| `r` | 内容分级(`g`、`pg`、`r`、`x`) | `?r=g` |
|
||||
|
||||
### 默认图片选项(`d`)
|
||||
|
||||
@@ -76,8 +76,7 @@ echo -n "user@example.com" | tr '[:upper:]' '[:lower:]' | sha256sum | cut -d ' '
|
||||
src="https://gravatar.com/avatar/a3b4c5d6e7f8?s=160&d=robohash&r=g"
|
||||
alt="用户头像"
|
||||
width="160"
|
||||
height="160"
|
||||
/>
|
||||
height="160" />
|
||||
```
|
||||
|
||||
始终传入 `d` 参数以避免未设置 Gravatar 的用户出现裂图。`identicon` 和 `robohash` 是常用的选择,因为它们能为每个哈希值生成唯一且易于辨识的图片。
|
||||
@@ -6,16 +6,16 @@ title: Calendar Toolbox API
|
||||
|
||||
库的代码组织在基础包 `com.onixbyte.calendar` 下,结构如下:
|
||||
|
||||
| 包 | 描述 |
|
||||
|---|---|
|
||||
| `com.onixbyte.calendar` | 根日历对象 |
|
||||
| `com.onixbyte.calendar.component` | iCalendar 组件(VEVENT、VTODO 等) |
|
||||
| `com.onixbyte.calendar.component.property` | 组件级属性 |
|
||||
| `com.onixbyte.calendar.property` | 日历级属性 |
|
||||
| `com.onixbyte.calendar.parameter` | iCalendar 属性参数 |
|
||||
| `com.onixbyte.calendar.recurrence` | 重复规则类型 |
|
||||
| `com.onixbyte.calendar.value` | 值类型 |
|
||||
| `com.onixbyte.calendar.util` | 格式化工具类 |
|
||||
| 包 | 描述 |
|
||||
| ------------------------------------------ | ---------------------------------- |
|
||||
| `com.onixbyte.calendar` | 根日历对象 |
|
||||
| `com.onixbyte.calendar.component` | iCalendar 组件(VEVENT、VTODO 等) |
|
||||
| `com.onixbyte.calendar.component.property` | 组件级属性 |
|
||||
| `com.onixbyte.calendar.property` | 日历级属性 |
|
||||
| `com.onixbyte.calendar.parameter` | iCalendar 属性参数 |
|
||||
| `com.onixbyte.calendar.recurrence` | 重复规则类型 |
|
||||
| `com.onixbyte.calendar.value` | 值类型 |
|
||||
| `com.onixbyte.calendar.util` | 格式化工具类 |
|
||||
|
||||
---
|
||||
|
||||
@@ -46,19 +46,19 @@ String icsContent = calendar.formatted();
|
||||
|
||||
所有日历级属性位于 `com.onixbyte.calendar.property`,实现 `CalendarProperty` 接口。
|
||||
|
||||
| 类 | iCalendar 属性 | 描述 |
|
||||
|---|---|---|
|
||||
| `CalendarScale` | `CALSCALE` | 日历系统(例如 GREGORIAN) |
|
||||
| `Method` | `METHOD` | iCalendar 方法(例如 PUBLISH、REQUEST) |
|
||||
| `ProductIdentifier` | `PRODID` | 日历的产品标识符 |
|
||||
| `Version` | `VERSION` | iCalendar 版本(例如 2.0) |
|
||||
| `Owner` | `X-OWNER` | 日历所有者 |
|
||||
| `PrimaryCalendar` | `X-PRIMARY-CALENDAR` | 是否为主日历 |
|
||||
| `PublishedTTL` | `X-PUBLISHED-TTL` | 已发布日历的生存时间 |
|
||||
| `CalendarDescription` | `X-CALENDAR-DESC` | 日历描述 |
|
||||
| `CalendarName` | `X-CALENDAR-NAME` | 日历名称 |
|
||||
| `CalendarId` | `X-CALENDAR-ID` | 日历标识符 |
|
||||
| `CustomCalendarProperty` | `X-*` | 自定义日历属性 |
|
||||
| 类 | iCalendar 属性 | 描述 |
|
||||
| ------------------------ | -------------------- | --------------------------------------- |
|
||||
| `CalendarScale` | `CALSCALE` | 日历系统(例如 GREGORIAN) |
|
||||
| `Method` | `METHOD` | iCalendar 方法(例如 PUBLISH、REQUEST) |
|
||||
| `ProductIdentifier` | `PRODID` | 日历的产品标识符 |
|
||||
| `Version` | `VERSION` | iCalendar 版本(例如 2.0) |
|
||||
| `Owner` | `X-OWNER` | 日历所有者 |
|
||||
| `PrimaryCalendar` | `X-PRIMARY-CALENDAR` | 是否为主日历 |
|
||||
| `PublishedTTL` | `X-PUBLISHED-TTL` | 已发布日历的生存时间 |
|
||||
| `CalendarDescription` | `X-CALENDAR-DESC` | 日历描述 |
|
||||
| `CalendarName` | `X-CALENDAR-NAME` | 日历名称 |
|
||||
| `CalendarId` | `X-CALENDAR-ID` | 日历标识符 |
|
||||
| `CustomCalendarProperty` | `X-*` | 自定义日历属性 |
|
||||
|
||||
自定义属性使用 Builder 创建:
|
||||
|
||||
@@ -276,50 +276,50 @@ Alarm emailAlarm = Alarm.builder()
|
||||
|
||||
组件属性位于 `com.onixbyte.calendar.component.property`,实现 `ComponentProperty` 接口。
|
||||
|
||||
| 类 | iCalendar 属性 | 描述 |
|
||||
|---|---|---|
|
||||
| `Action` | `ACTION` | 闹钟操作类型(AUDIO、DISPLAY、EMAIL) |
|
||||
| `Attachment` | `ATTACH` | 文档附件 |
|
||||
| `Attendee` | `ATTENDEE` | 事件/任务参与者 |
|
||||
| `Categories` | `CATEGORIES` | 类别或标签 |
|
||||
| `Classification` | `CLASS` | 访问分类(PUBLIC、PRIVATE、CONFIDENTIAL) |
|
||||
| `Comment` | `COMMENT` | 评论 |
|
||||
| `Contact` | `CONTACT` | 联系信息 |
|
||||
| `DateTimeCompleted` | `COMPLETED` | 完成日期时间 |
|
||||
| `DateTimeCreated` | `CREATED` | 创建日期时间 |
|
||||
| `DateTimeDue` | `DUE` | 截止日期时间 |
|
||||
| `DateTimeEnd` | `DTEND` | 结束日期时间 |
|
||||
| `DateTimeStamp` | `DTSTAMP` | 日期时间戳 |
|
||||
| `DateTimeStart` | `DTSTART` | 开始日期时间 |
|
||||
| `Description` | `DESCRIPTION` | 描述 |
|
||||
| `ExceptionDateTimes` | `EXDATE` | 例外日期时间 |
|
||||
| `FreeBusyTime` | `FREEBUSY` | 空闲/忙碌时间段 |
|
||||
| `GeographicPosition` | `GEO` | 地理位置(纬度/经度) |
|
||||
| `LastModified` | `LAST-MODIFIED` | 最后修改日期时间 |
|
||||
| `Location` | `LOCATION` | 地点 |
|
||||
| `Organiser` | `ORGANIZER` | 组织者 |
|
||||
| `PercentComplete` | `PERCENT-COMPLETE` | 完成百分比 |
|
||||
| `Priority` | `PRIORITY` | 优先级 |
|
||||
| `RecurrenceDateTimes` | `RDATE` | 重复日期时间 |
|
||||
| `RecurrenceId` | `RECURRENCE-ID` | 重复实例标识符 |
|
||||
| `RecurrenceRule` | `RRULE` | 重复规则 |
|
||||
| `RelatedTo` | `RELATED-TO` | 相关组件引用 |
|
||||
| `RepeatCount` | `REPEAT` | 闹钟重复次数 |
|
||||
| `RequestStatus` | `REQUEST-STATUS` | 请求状态 |
|
||||
| `Resources` | `RESOURCES` | 资源 |
|
||||
| `SequenceNumber` | `SEQUENCE` | 序列号(修订版本) |
|
||||
| `Status` | `STATUS` | 组件状态(TENTATIVE、CONFIRMED、CANCELLED 等) |
|
||||
| `Summary` | `SUMMARY` | 标题或摘要 |
|
||||
| `TimeTransparency` | `TRANSP` | 时间透明度(OPAQUE、TRANSPARENT) |
|
||||
| `TimeZoneIdentifier` | `TZID` | 时区标识符 |
|
||||
| `TimeZoneName` | `TZNAME` | 时区名称 |
|
||||
| `TimeZoneOffsetFrom` | `TZOFFSETFROM` | 相对于 UTC 的时区偏移(原) |
|
||||
| `TimeZoneOffsetTo` | `TZOFFSETTO` | 相对于 UTC 的时区偏移(目标) |
|
||||
| `TimeZoneUrl` | `TZURL` | 时区 URL |
|
||||
| `Trigger` | `TRIGGER` | 闹钟触发器 |
|
||||
| `UniformResourceLocator` | `URL` | 关联 URL |
|
||||
| `UniqueIdentifier` | `UID` | 唯一标识符 |
|
||||
| `CustomComponentProperty` | `X-*` | 自定义组件属性 |
|
||||
| 类 | iCalendar 属性 | 描述 |
|
||||
| ------------------------- | ------------------ | ---------------------------------------------- |
|
||||
| `Action` | `ACTION` | 闹钟操作类型(AUDIO、DISPLAY、EMAIL) |
|
||||
| `Attachment` | `ATTACH` | 文档附件 |
|
||||
| `Attendee` | `ATTENDEE` | 事件/任务参与者 |
|
||||
| `Categories` | `CATEGORIES` | 类别或标签 |
|
||||
| `Classification` | `CLASS` | 访问分类(PUBLIC、PRIVATE、CONFIDENTIAL) |
|
||||
| `Comment` | `COMMENT` | 评论 |
|
||||
| `Contact` | `CONTACT` | 联系信息 |
|
||||
| `DateTimeCompleted` | `COMPLETED` | 完成日期时间 |
|
||||
| `DateTimeCreated` | `CREATED` | 创建日期时间 |
|
||||
| `DateTimeDue` | `DUE` | 截止日期时间 |
|
||||
| `DateTimeEnd` | `DTEND` | 结束日期时间 |
|
||||
| `DateTimeStamp` | `DTSTAMP` | 日期时间戳 |
|
||||
| `DateTimeStart` | `DTSTART` | 开始日期时间 |
|
||||
| `Description` | `DESCRIPTION` | 描述 |
|
||||
| `ExceptionDateTimes` | `EXDATE` | 例外日期时间 |
|
||||
| `FreeBusyTime` | `FREEBUSY` | 空闲/忙碌时间段 |
|
||||
| `GeographicPosition` | `GEO` | 地理位置(纬度/经度) |
|
||||
| `LastModified` | `LAST-MODIFIED` | 最后修改日期时间 |
|
||||
| `Location` | `LOCATION` | 地点 |
|
||||
| `Organiser` | `ORGANIZER` | 组织者 |
|
||||
| `PercentComplete` | `PERCENT-COMPLETE` | 完成百分比 |
|
||||
| `Priority` | `PRIORITY` | 优先级 |
|
||||
| `RecurrenceDateTimes` | `RDATE` | 重复日期时间 |
|
||||
| `RecurrenceId` | `RECURRENCE-ID` | 重复实例标识符 |
|
||||
| `RecurrenceRule` | `RRULE` | 重复规则 |
|
||||
| `RelatedTo` | `RELATED-TO` | 相关组件引用 |
|
||||
| `RepeatCount` | `REPEAT` | 闹钟重复次数 |
|
||||
| `RequestStatus` | `REQUEST-STATUS` | 请求状态 |
|
||||
| `Resources` | `RESOURCES` | 资源 |
|
||||
| `SequenceNumber` | `SEQUENCE` | 序列号(修订版本) |
|
||||
| `Status` | `STATUS` | 组件状态(TENTATIVE、CONFIRMED、CANCELLED 等) |
|
||||
| `Summary` | `SUMMARY` | 标题或摘要 |
|
||||
| `TimeTransparency` | `TRANSP` | 时间透明度(OPAQUE、TRANSPARENT) |
|
||||
| `TimeZoneIdentifier` | `TZID` | 时区标识符 |
|
||||
| `TimeZoneName` | `TZNAME` | 时区名称 |
|
||||
| `TimeZoneOffsetFrom` | `TZOFFSETFROM` | 相对于 UTC 的时区偏移(原) |
|
||||
| `TimeZoneOffsetTo` | `TZOFFSETTO` | 相对于 UTC 的时区偏移(目标) |
|
||||
| `TimeZoneUrl` | `TZURL` | 时区 URL |
|
||||
| `Trigger` | `TRIGGER` | 闹钟触发器 |
|
||||
| `UniformResourceLocator` | `URL` | 关联 URL |
|
||||
| `UniqueIdentifier` | `UID` | 唯一标识符 |
|
||||
| `CustomComponentProperty` | `X-*` | 自定义组件属性 |
|
||||
|
||||
---
|
||||
|
||||
@@ -327,28 +327,28 @@ Alarm emailAlarm = Alarm.builder()
|
||||
|
||||
参数位于 `com.onixbyte.calendar.parameter`,为组件属性提供额外限定。
|
||||
|
||||
| 类 | iCalendar 参数 | 描述 |
|
||||
|---|---|---|
|
||||
| `AlarmTriggerRelationship` | `RELATED` | 触发器关联(START、END) |
|
||||
| `AlternateTextRepresentation` | `ALTREP` | 替代文本 URI |
|
||||
| `CalendarUserType` | `CUTYPE` | 日历用户类型(INDIVIDUAL、GROUP、RESOURCE 等) |
|
||||
| `CommonName` | `CN` | 通用名称 |
|
||||
| `Delegatees` | `DELEGATED-TO` | 受托人 |
|
||||
| `Delegators` | `DELEGATED-FROM` | 委托人 |
|
||||
| `DirectoryEntryReference` | `DIR` | 目录条目 URI |
|
||||
| `FormatType` | `FMTTYPE` | 格式类型(MIME 类型) |
|
||||
| `FreeBusyTimeType` | `FBTYPE` | 空闲/忙碌时间类型 |
|
||||
| `InlineEncoding` | `ENCODING` | 内联编码(BASE64) |
|
||||
| `Language` | `LANGUAGE` | 语言 |
|
||||
| `Membership` | `MEMBER` | 组成员 |
|
||||
| `ParticipationRole` | `ROLE` | 参与角色(CHAIR、REQ-PARTICIPANT 等) |
|
||||
| `ParticipationStatus` | `PARTSTAT` | 参与状态(ACCEPTED、DECLINED 等) |
|
||||
| `RecurrenceIdentifierRange` | `RANGE` | 重复范围(THISANDPRIOR、THISANDFUTURE) |
|
||||
| `RelationshipType` | `RELTYPE` | 关系类型(PARENT、CHILD、SIBLING) |
|
||||
| `RsvpExpectation` | `RSVP` | RSVP 期望(TRUE、FALSE) |
|
||||
| `SentBy` | `SENT-BY` | 发送者 |
|
||||
| `TimeZoneIdentifier` | `TZID` | 时区标识符 |
|
||||
| `ValueDataType` | `VALUE` | 值数据类型(DATE、DATE-TIME 等) |
|
||||
| 类 | iCalendar 参数 | 描述 |
|
||||
| ----------------------------- | ---------------- | ---------------------------------------------- |
|
||||
| `AlarmTriggerRelationship` | `RELATED` | 触发器关联(START、END) |
|
||||
| `AlternateTextRepresentation` | `ALTREP` | 替代文本 URI |
|
||||
| `CalendarUserType` | `CUTYPE` | 日历用户类型(INDIVIDUAL、GROUP、RESOURCE 等) |
|
||||
| `CommonName` | `CN` | 通用名称 |
|
||||
| `Delegatees` | `DELEGATED-TO` | 受托人 |
|
||||
| `Delegators` | `DELEGATED-FROM` | 委托人 |
|
||||
| `DirectoryEntryReference` | `DIR` | 目录条目 URI |
|
||||
| `FormatType` | `FMTTYPE` | 格式类型(MIME 类型) |
|
||||
| `FreeBusyTimeType` | `FBTYPE` | 空闲/忙碌时间类型 |
|
||||
| `InlineEncoding` | `ENCODING` | 内联编码(BASE64) |
|
||||
| `Language` | `LANGUAGE` | 语言 |
|
||||
| `Membership` | `MEMBER` | 组成员 |
|
||||
| `ParticipationRole` | `ROLE` | 参与角色(CHAIR、REQ-PARTICIPANT 等) |
|
||||
| `ParticipationStatus` | `PARTSTAT` | 参与状态(ACCEPTED、DECLINED 等) |
|
||||
| `RecurrenceIdentifierRange` | `RANGE` | 重复范围(THISANDPRIOR、THISANDFUTURE) |
|
||||
| `RelationshipType` | `RELTYPE` | 关系类型(PARENT、CHILD、SIBLING) |
|
||||
| `RsvpExpectation` | `RSVP` | RSVP 期望(TRUE、FALSE) |
|
||||
| `SentBy` | `SENT-BY` | 发送者 |
|
||||
| `TimeZoneIdentifier` | `TZID` | 时区标识符 |
|
||||
| `ValueDataType` | `VALUE` | 值数据类型(DATE、DATE-TIME 等) |
|
||||
|
||||
---
|
||||
|
||||
@@ -356,10 +356,10 @@ Alarm emailAlarm = Alarm.builder()
|
||||
|
||||
位于 `com.onixbyte.calendar.recurrence`。
|
||||
|
||||
| 类 | 描述 |
|
||||
|---|---|
|
||||
| `Frequency` | 重复频率常量(DAILY、WEEKLY、MONTHLY、YEARLY),用于重复规则 |
|
||||
| `WeekdayNum` | 重复规则的星期序号(例如第 2 个星期一) |
|
||||
| 类 | 描述 |
|
||||
| ------------ | ------------------------------------------------------------ |
|
||||
| `Frequency` | 重复频率常量(DAILY、WEEKLY、MONTHLY、YEARLY),用于重复规则 |
|
||||
| `WeekdayNum` | 重复规则的星期序号(例如第 2 个星期一) |
|
||||
|
||||
---
|
||||
|
||||
@@ -367,12 +367,12 @@ Alarm emailAlarm = Alarm.builder()
|
||||
|
||||
位于 `com.onixbyte.calendar.value`。
|
||||
|
||||
| 类 | 描述 |
|
||||
|---|---|
|
||||
| `FreeBusyTimeValue` | 表示空闲/忙碌时间值 |
|
||||
| `PeriodOfTime` | 表示一个时间段,包含开始和结束 |
|
||||
| `PropertyValue` | 属性的基础值类型 |
|
||||
| `UtcOffset` | UTC 偏移值(例如 `-05:00`、`+01:00`) |
|
||||
| 类 | 描述 |
|
||||
| ------------------- | ------------------------------------- |
|
||||
| `FreeBusyTimeValue` | 表示空闲/忙碌时间值 |
|
||||
| `PeriodOfTime` | 表示一个时间段,包含开始和结束 |
|
||||
| `PropertyValue` | 属性的基础值类型 |
|
||||
| `UtcOffset` | UTC 偏移值(例如 `-05:00`、`+01:00`) |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -22,10 +22,8 @@ Calendar Toolbox 是一个 Java 库,旨在简化遵循 iCalendar 规范(RFC
|
||||
|
||||
<Tabs>
|
||||
<Tab label="Gradle with Kotlin DSL">
|
||||
```kotlin title="build.gradle.kts"
|
||||
dependencies {
|
||||
implementation("com.onixbyte:calendar-toolbox:1.1.0")
|
||||
}
|
||||
```kotlin title="build.gradle.kts" dependencies{" "}
|
||||
{implementation("com.onixbyte:calendar-toolbox:1.1.0")}
|
||||
```
|
||||
</Tab>
|
||||
<Tab label="Maven">
|
||||
|
||||
@@ -6,19 +6,19 @@ title: Captcha API
|
||||
|
||||
库的代码组织在基础包 `com.onixbyte.captcha` 下,结构如下:
|
||||
|
||||
| 包 | 描述 |
|
||||
|---|---|
|
||||
| `com.onixbyte.captcha` | 核心接口 |
|
||||
| `com.onixbyte.captcha.impl` | 默认实现 |
|
||||
| `com.onixbyte.captcha.text` | 文本生成器和渲染器接口 |
|
||||
| `com.onixbyte.captcha.text.impl` | 文本生成器和渲染器实现 |
|
||||
| `com.onixbyte.captcha.text.enums` | 文本相关枚举 |
|
||||
| `com.onixbyte.captcha.background` | 背景生成器接口 |
|
||||
| `com.onixbyte.captcha.background.impl` | 背景生成器实现 |
|
||||
| `com.onixbyte.captcha.noise` | 噪点生成器接口 |
|
||||
| `com.onixbyte.captcha.noise.impl` | 噪点生成器实现 |
|
||||
| `com.onixbyte.captcha.gimpy` | 扭曲引擎接口 |
|
||||
| `com.onixbyte.captcha.gimpy.impl` | 扭曲引擎实现 |
|
||||
| 包 | 描述 |
|
||||
| -------------------------------------- | ---------------------- |
|
||||
| `com.onixbyte.captcha` | 核心接口 |
|
||||
| `com.onixbyte.captcha.impl` | 默认实现 |
|
||||
| `com.onixbyte.captcha.text` | 文本生成器和渲染器接口 |
|
||||
| `com.onixbyte.captcha.text.impl` | 文本生成器和渲染器实现 |
|
||||
| `com.onixbyte.captcha.text.enums` | 文本相关枚举 |
|
||||
| `com.onixbyte.captcha.background` | 背景生成器接口 |
|
||||
| `com.onixbyte.captcha.background.impl` | 背景生成器实现 |
|
||||
| `com.onixbyte.captcha.noise` | 噪点生成器接口 |
|
||||
| `com.onixbyte.captcha.noise.impl` | 噪点生成器实现 |
|
||||
| `com.onixbyte.captcha.gimpy` | 扭曲引擎接口 |
|
||||
| `com.onixbyte.captcha.gimpy.impl` | 扭曲引擎实现 |
|
||||
|
||||
---
|
||||
|
||||
@@ -45,17 +45,17 @@ BufferedImage image = captcha.createImage(text);
|
||||
|
||||
### DefaultCaptchaProducer Builder
|
||||
|
||||
| 方法 | 默认值 | 描述 |
|
||||
|---|---|---|
|
||||
| `textProducer` | `DefaultTextProducer.builder().build()` | 文本生成器 |
|
||||
| `wordRenderer` | `DefaultWordRenderer.builder().build()` | 文字渲染器 |
|
||||
| `gimpyEngine` | `WaterRipple.builder().build()` | 扭曲引擎 |
|
||||
| `backgroundProducer` | `DefaultBackgroundProducer.builder().build()` | 背景生成器 |
|
||||
| `width` | `200` | 验证码图片宽度 |
|
||||
| `height` | `50` | 验证码图片高度 |
|
||||
| `borderDrawn` | `true` | 是否绘制边框 |
|
||||
| `borderColour` | `Color.BLACK` | 边框颜色 |
|
||||
| `borderThickness` | `1` | 边框厚度 |
|
||||
| 方法 | 默认值 | 描述 |
|
||||
| -------------------- | --------------------------------------------- | -------------- |
|
||||
| `textProducer` | `DefaultTextProducer.builder().build()` | 文本生成器 |
|
||||
| `wordRenderer` | `DefaultWordRenderer.builder().build()` | 文字渲染器 |
|
||||
| `gimpyEngine` | `WaterRipple.builder().build()` | 扭曲引擎 |
|
||||
| `backgroundProducer` | `DefaultBackgroundProducer.builder().build()` | 背景生成器 |
|
||||
| `width` | `200` | 验证码图片宽度 |
|
||||
| `height` | `50` | 验证码图片高度 |
|
||||
| `borderDrawn` | `true` | 是否绘制边框 |
|
||||
| `borderColour` | `Color.BLACK` | 边框颜色 |
|
||||
| `borderThickness` | `1` | 边框厚度 |
|
||||
|
||||
---
|
||||
|
||||
@@ -82,10 +82,10 @@ DefaultTextProducer textProducer = DefaultTextProducer.builder()
|
||||
.build();
|
||||
```
|
||||
|
||||
| 方法 | 默认值 | 描述 |
|
||||
|---|---|---|
|
||||
| `length` | `6` | 生成文本的长度 |
|
||||
| `chars` | `a-z`、`A-Z`、`0-9`(共 62 个字符) | 生成文本使用的字符集 |
|
||||
| 方法 | 默认值 | 描述 |
|
||||
| -------- | ----------------------------------- | -------------------- |
|
||||
| `length` | `6` | 生成文本的长度 |
|
||||
| `chars` | `a-z`、`A-Z`、`0-9`(共 62 个字符) | 生成文本使用的字符集 |
|
||||
|
||||
### WordRenderer
|
||||
|
||||
@@ -111,24 +111,24 @@ DefaultWordRenderer wordRenderer = DefaultWordRenderer.builder()
|
||||
.build();
|
||||
```
|
||||
|
||||
| 方法 | 默认值 | 描述 |
|
||||
|---|---|---|
|
||||
| `fontSize` | `40` | 字号(像素) |
|
||||
| `fonts` | `["Arial", "Courier"]` | 字体族(随机选择) |
|
||||
| `fontColour` | `Color.BLACK` | 字体颜色 |
|
||||
| `charSpace` | `2` | 字符间距(像素) |
|
||||
| `fontStyle` | `FontStyle.BOLD` | 字体样式 |
|
||||
| 方法 | 默认值 | 描述 |
|
||||
| ------------ | ---------------------- | ------------------ |
|
||||
| `fontSize` | `40` | 字号(像素) |
|
||||
| `fonts` | `["Arial", "Courier"]` | 字体族(随机选择) |
|
||||
| `fontColour` | `Color.BLACK` | 字体颜色 |
|
||||
| `charSpace` | `2` | 字符间距(像素) |
|
||||
| `fontStyle` | `FontStyle.BOLD` | 字体样式 |
|
||||
|
||||
### FontStyle
|
||||
|
||||
定义 CAPTCHA 文字渲染支持的字体样式。
|
||||
|
||||
| 值 | 对应的 AWT 常量 | 描述 |
|
||||
|---|---|---|
|
||||
| `PLAIN` | `java.awt.Font.PLAIN` | 普通样式 |
|
||||
| `BOLD` | `java.awt.Font.BOLD` | 粗体 |
|
||||
| `ITALIC` | `java.awt.Font.ITALIC` | 斜体 |
|
||||
| `BOLD_ITALIC` | `java.awt.Font.BOLD | Font.ITALIC` | 粗斜体 |
|
||||
| 值 | 对应的 AWT 常量 | 描述 |
|
||||
| ------------- | ---------------------- | ------------ | ------ |
|
||||
| `PLAIN` | `java.awt.Font.PLAIN` | 普通样式 |
|
||||
| `BOLD` | `java.awt.Font.BOLD` | 粗体 |
|
||||
| `ITALIC` | `java.awt.Font.ITALIC` | 斜体 |
|
||||
| `BOLD_ITALIC` | `java.awt.Font.BOLD | Font.ITALIC` | 粗斜体 |
|
||||
|
||||
---
|
||||
|
||||
@@ -152,8 +152,8 @@ WaterRipple gimpy = WaterRipple.builder()
|
||||
.build();
|
||||
```
|
||||
|
||||
| 方法 | 默认值 | 描述 |
|
||||
|---|---|---|
|
||||
| 方法 | 默认值 | 描述 |
|
||||
| --------------- | ---------------------------------------- | ---------- |
|
||||
| `noiseProducer` | `DefaultNoiseProducer.builder().build()` | 噪点生成器 |
|
||||
|
||||
### FishEyeGimpy
|
||||
@@ -175,8 +175,8 @@ ShadowGimpy gimpy = ShadowGimpy.builder()
|
||||
.build();
|
||||
```
|
||||
|
||||
| 方法 | 默认值 | 描述 |
|
||||
|---|---|---|
|
||||
| 方法 | 默认值 | 描述 |
|
||||
| --------------- | ---------------------------------------- | ---------- |
|
||||
| `noiseProducer` | `DefaultNoiseProducer.builder().build()` | 噪点生成器 |
|
||||
|
||||
---
|
||||
@@ -204,8 +204,8 @@ DefaultNoiseProducer noise = DefaultNoiseProducer.builder()
|
||||
.build();
|
||||
```
|
||||
|
||||
| 方法 | 默认值 | 描述 |
|
||||
|---|---|---|
|
||||
| 方法 | 默认值 | 描述 |
|
||||
| ------------- | ------------- | -------- |
|
||||
| `noiseColour` | `Color.BLACK` | 噪点颜色 |
|
||||
|
||||
#### NoNoiseProducer
|
||||
@@ -242,7 +242,7 @@ DefaultBackgroundProducer background = DefaultBackgroundProducer.builder()
|
||||
.build();
|
||||
```
|
||||
|
||||
| 方法 | 默认值 | 描述 |
|
||||
|---|---|---|
|
||||
| 方法 | 默认值 | 描述 |
|
||||
| ------------ | ------------------ | ------------ |
|
||||
| `colourFrom` | `Color.LIGHT_GRAY` | 渐变起始颜色 |
|
||||
| `colourTo` | `Color.WHITE` | 渐变结束颜色 |
|
||||
| `colourTo` | `Color.WHITE` | 渐变结束颜色 |
|
||||
@@ -23,10 +23,7 @@ Captcha 是一个用于生成 CAPTCHA 验证码图片的 Java 库。本项目是
|
||||
|
||||
<Tabs>
|
||||
<Tab label="Gradle with Kotlin DSL">
|
||||
```kotlin title="build.gradle.kts"
|
||||
dependencies {
|
||||
implementation("com.onixbyte:captcha:1.0.0")
|
||||
}
|
||||
```kotlin title="build.gradle.kts" dependencies {implementation("com.onixbyte:captcha:1.0.0")}
|
||||
```
|
||||
</Tab>
|
||||
<Tab label="Maven">
|
||||
|
||||
@@ -23,6 +23,7 @@ Skills 会被直接部署到 Claude Code 自身的 Skills 目录(`~/.claude/sk
|
||||
| Windows (amd64) | `ccsm-windows-amd64.exe` |
|
||||
|
||||
将二进制文件放到 `PATH` 中的某个目录,并重命名为 `ccsm`(Windows 上为 `ccsm.exe`)。
|
||||
|
||||
</Tab>
|
||||
<Tab label="Cargo 安装">
|
||||
```bash
|
||||
@@ -109,10 +110,10 @@ ccsm marketplace remove my-marketplace
|
||||
|
||||
CCSM 支持两种部署模式:
|
||||
|
||||
| 模式 | 行为 | 适用场景 |
|
||||
|------|------|----------|
|
||||
| `copy`(默认) | 将文件物理复制到 `~/.claude/skills/<name>/` | 日常使用 — 安全且自包含 |
|
||||
| `link` | 创建指向本地存储的符号链接 | Skill 开发 — 对源文件的修改会立即生效 |
|
||||
| 模式 | 行为 | 适用场景 |
|
||||
| -------------- | ------------------------------------------- | ------------------------------------- |
|
||||
| `copy`(默认) | 将文件物理复制到 `~/.claude/skills/<name>/` | 日常使用 — 安全且自包含 |
|
||||
| `link` | 创建指向本地存储的符号链接 | Skill 开发 — 对源文件的修改会立即生效 |
|
||||
|
||||
可在配置中设置默认模式,也可在安装时单独指定:
|
||||
|
||||
@@ -158,21 +159,21 @@ your-marketplace-repo/
|
||||
|
||||
**顶层字段:**
|
||||
|
||||
| 字段 | 必需 | 说明 |
|
||||
|------|------|------|
|
||||
| `name` | 是 | Marketplace 的显示名称 |
|
||||
| `version` | 否 | 用于自行追踪的任意版本号 |
|
||||
| `skills` | 否 | Skill 条目数组(默认为 `[]`) |
|
||||
| 字段 | 必需 | 说明 |
|
||||
| --------- | ---- | ----------------------------- |
|
||||
| `name` | 是 | Marketplace 的显示名称 |
|
||||
| `version` | 否 | 用于自行追踪的任意版本号 |
|
||||
| `skills` | 否 | Skill 条目数组(默认为 `[]`) |
|
||||
|
||||
**Skill 条目字段:**
|
||||
|
||||
| 字段 | 必需 | 说明 |
|
||||
|------|------|------|
|
||||
| `name` | 是 | Skill 的唯一标识符(用于 `ccsm install <name>`) |
|
||||
| `description` | 是 | 在 `ccsm search` 输出中显示的一行摘要 |
|
||||
| `repository` | 是 | Skill 的公开 Git 仓库 URL |
|
||||
| `branch` | 否 | 安装时使用的 Git 分支(先尝试 `main`,再回退到 `master`) |
|
||||
| `path` | 否 | 仓库中 Skill 文件所在的子目录 — 支持单个仓库托管多个 Skills |
|
||||
| 字段 | 必需 | 说明 |
|
||||
| ------------- | ---- | ----------------------------------------------------------- |
|
||||
| `name` | 是 | Skill 的唯一标识符(用于 `ccsm install <name>`) |
|
||||
| `description` | 是 | 在 `ccsm search` 输出中显示的一行摘要 |
|
||||
| `repository` | 是 | Skill 的公开 Git 仓库 URL |
|
||||
| `branch` | 否 | 安装时使用的 Git 分支(先尝试 `main`,再回退到 `master`) |
|
||||
| `path` | 否 | 仓库中 Skill 文件所在的子目录 — 支持单个仓库托管多个 Skills |
|
||||
|
||||
### 工作原理
|
||||
|
||||
@@ -202,12 +203,12 @@ my-skill-repo/
|
||||
|
||||
## 数据存储位置
|
||||
|
||||
| 路径 | 用途 |
|
||||
|------|------|
|
||||
| `~/.claude/skills/` | Claude Code 的 Skills 目录 —— Skills 的部署目标 |
|
||||
| `~/.config/ccsm/config.json` | CCSM 配置文件(已注册的 Marketplace、默认模式) |
|
||||
| `~/.config/ccsm/marketplaces/` | 缓存的 Marketplace 清单 |
|
||||
| `~/.config/ccsm/skills/` | 下载的 Skill 文件(本地存储) |
|
||||
| 路径 | 用途 |
|
||||
| ------------------------------ | ----------------------------------------------- |
|
||||
| `~/.claude/skills/` | Claude Code 的 Skills 目录 —— Skills 的部署目标 |
|
||||
| `~/.config/ccsm/config.json` | CCSM 配置文件(已注册的 Marketplace、默认模式) |
|
||||
| `~/.config/ccsm/marketplaces/` | 缓存的 Marketplace 清单 |
|
||||
| `~/.config/ccsm/skills/` | 下载的 Skill 文件(本地存储) |
|
||||
|
||||
在 Windows 上,`~/.config/` 对应 `C:\Users\<user>\AppData\Roaming\`。
|
||||
|
||||
|
||||
@@ -6,64 +6,64 @@ title: Delta Force Guide API
|
||||
|
||||
后端是基于 Spring Boot 3 的应用程序,代码组织在基础包 `com.onixbyte.deltaforceguide` 下:
|
||||
|
||||
| 包 | 描述 |
|
||||
|---|---|
|
||||
| `controller` | REST API 端点控制器 |
|
||||
| `service` | 业务逻辑层 |
|
||||
| `repository` | MyBatis 数据访问层 |
|
||||
| `domain` | JPA 实体与领域对象 |
|
||||
| `config` | Spring 配置(安全、缓存等) |
|
||||
| `properties` | 配置属性类 |
|
||||
| `enumeration` | 枚举类型 |
|
||||
| `shared` | 共享工具类 |
|
||||
| 包 | 描述 |
|
||||
| ------------- | --------------------------- |
|
||||
| `controller` | REST API 端点控制器 |
|
||||
| `service` | 业务逻辑层 |
|
||||
| `repository` | MyBatis 数据访问层 |
|
||||
| `domain` | JPA 实体与领域对象 |
|
||||
| `config` | Spring 配置(安全、缓存等) |
|
||||
| `properties` | 配置属性类 |
|
||||
| `enumeration` | 枚举类型 |
|
||||
| `shared` | 共享工具类 |
|
||||
|
||||
## API 端点
|
||||
|
||||
### 认证
|
||||
|
||||
| 方法 | 路径 | 描述 | 认证 |
|
||||
|---|---|---|---|
|
||||
| `POST` | `/api/auth/login` | 使用凭证登录 | 否 |
|
||||
| `POST` | `/api/auth/refresh` | 刷新 access token | 否 |
|
||||
| `GET` | `/api/auth/profile` | 获取当前用户信息 | 是 |
|
||||
| 方法 | 路径 | 描述 | 认证 |
|
||||
| ------ | ------------------- | ----------------- | ---- |
|
||||
| `POST` | `/api/auth/login` | 使用凭证登录 | 否 |
|
||||
| `POST` | `/api/auth/refresh` | 刷新 access token | 否 |
|
||||
| `GET` | `/api/auth/profile` | 获取当前用户信息 | 是 |
|
||||
|
||||
### 枪械
|
||||
|
||||
| 方法 | 路径 | 描述 |
|
||||
|---|---|---|
|
||||
| `GET` | `/api/firearms` | 枪械列表(分页,可按类型筛选) |
|
||||
| `GET` | `/api/firearms/:id` | 按 ID 获取枪械详情 |
|
||||
| `POST` | `/api/firearms` | 创建新枪械 |
|
||||
| `PUT` | `/api/firearms/:id` | 更新枪械信息 |
|
||||
| `DELETE` | `/api/firearms/:id` | 删除枪械 |
|
||||
| 方法 | 路径 | 描述 |
|
||||
| -------- | ------------------- | ------------------------------ |
|
||||
| `GET` | `/api/firearms` | 枪械列表(分页,可按类型筛选) |
|
||||
| `GET` | `/api/firearms/:id` | 按 ID 获取枪械详情 |
|
||||
| `POST` | `/api/firearms` | 创建新枪械 |
|
||||
| `PUT` | `/api/firearms/:id` | 更新枪械信息 |
|
||||
| `DELETE` | `/api/firearms/:id` | 删除枪械 |
|
||||
|
||||
### 改装
|
||||
|
||||
| 方法 | 路径 | 描述 |
|
||||
|---|---|---|
|
||||
| `GET` | `/api/modifications` | 改装列表(分页,可按枪械和标签筛选) |
|
||||
| `GET` | `/api/modifications/:id` | 按 ID 获取改装详情 |
|
||||
| `POST` | `/api/modifications` | 创建改装 |
|
||||
| `POST` | `/api/modifications/batch` | 批量创建改装 |
|
||||
| `PUT` | `/api/modifications/:id` | 更新改装 |
|
||||
| `DELETE` | `/api/modifications/:id` | 删除改装 |
|
||||
| `DELETE` | `/api/modifications/batch-delete` | 批量删除改装 |
|
||||
| 方法 | 路径 | 描述 |
|
||||
| -------- | --------------------------------- | ------------------------------------ |
|
||||
| `GET` | `/api/modifications` | 改装列表(分页,可按枪械和标签筛选) |
|
||||
| `GET` | `/api/modifications/:id` | 按 ID 获取改装详情 |
|
||||
| `POST` | `/api/modifications` | 创建改装 |
|
||||
| `POST` | `/api/modifications/batch` | 批量创建改装 |
|
||||
| `PUT` | `/api/modifications/:id` | 更新改装 |
|
||||
| `DELETE` | `/api/modifications/:id` | 删除改装 |
|
||||
| `DELETE` | `/api/modifications/batch-delete` | 批量删除改装 |
|
||||
|
||||
### 标签
|
||||
|
||||
| 方法 | 路径 | 描述 |
|
||||
|---|---|---|
|
||||
| 方法 | 路径 | 描述 |
|
||||
| ----- | ----------- | ------------ |
|
||||
| `GET` | `/api/tags` | 获取所有标签 |
|
||||
|
||||
## 前端路由
|
||||
|
||||
| 路径 | 布局 | 页面 | 描述 |
|
||||
|---|---|---|---|
|
||||
| `/` | 主布局 | 枪械浏览 | 首页 — 枪械列表 |
|
||||
| `/firearms` | 主布局 | 枪械浏览 | 枪械详情与搜索 |
|
||||
| `/mod-codes` | 主布局 | 改装代码 | 改装代码库 |
|
||||
| `/legal` | 主布局 | 法律信息 | 法律与隐私信息 |
|
||||
| `/login` | 空白布局 | 登录 | 用户认证页面 |
|
||||
| 路径 | 布局 | 页面 | 描述 |
|
||||
| ------------ | -------- | -------- | --------------- |
|
||||
| `/` | 主布局 | 枪械浏览 | 首页 — 枪械列表 |
|
||||
| `/firearms` | 主布局 | 枪械浏览 | 枪械详情与搜索 |
|
||||
| `/mod-codes` | 主布局 | 改装代码 | 改装代码库 |
|
||||
| `/legal` | 主布局 | 法律信息 | 法律与隐私信息 |
|
||||
| `/login` | 空白布局 | 登录 | 用户认证页面 |
|
||||
|
||||
## 前端状态管理
|
||||
|
||||
@@ -82,10 +82,10 @@ Redux Toolkit 结合 Redux Persist 管理以下状态:
|
||||
|
||||
## 基础设施
|
||||
|
||||
| 组件 | 技术 |
|
||||
|---|---|
|
||||
| 数据库 | PostgreSQL(使用 Flyway 进行数据库迁移) |
|
||||
| 缓存 | Redis(会话和 token 存储) |
|
||||
| 文件存储 | AWS S3(枪械图片) |
|
||||
| 容器化 | Docker |
|
||||
| 部署 | Docker Compose + Nginx |
|
||||
| 组件 | 技术 |
|
||||
| -------- | ---------------------------------------- |
|
||||
| 数据库 | PostgreSQL(使用 Flyway 进行数据库迁移) |
|
||||
| 缓存 | Redis(会话和 token 存储) |
|
||||
| 文件存储 | AWS S3(枪械图片) |
|
||||
| 容器化 | Docker |
|
||||
| 部署 | Docker Compose + Nginx |
|
||||
@@ -25,12 +25,12 @@ Delta Force Guide 是一个全栈 Web 应用,为游戏 Delta Force(三角洲
|
||||
|
||||
## 架构
|
||||
|
||||
| 组件 | 技术栈 |
|
||||
|---|---|
|
||||
| 组件 | 技术栈 |
|
||||
| -------- | --------------------------------------------------------------------------------------------------- |
|
||||
| **前端** | React 19、TypeScript、Tailwind CSS 4、Ant Design 6、React Router 7、Redux Toolkit、TanStack Virtual |
|
||||
| **后端** | Spring Boot 3、Java 21、PostgreSQL、MyBatis、Flyway、Redis |
|
||||
| **认证** | JWT(access 和 refresh token)、验证码集成 |
|
||||
| **存储** | AWS S3(图片上传) |
|
||||
| **后端** | Spring Boot 3、Java 21、PostgreSQL、MyBatis、Flyway、Redis |
|
||||
| **认证** | JWT(access 和 refresh token)、验证码集成 |
|
||||
| **存储** | AWS S3(图片上传) |
|
||||
|
||||
前端是由 Vite 构建的静态站点,通过 API 与 Spring Boot 后端通信。认证采用 JWT access 和 refresh token 机制,refresh token 通过 Redis 管理。
|
||||
|
||||
|
||||
@@ -6,12 +6,12 @@ title: OnixByte Toolbox API
|
||||
|
||||
所有模块均在 `com.onixbyte` group ID 下发布。每个模块有自己独立的基础包:
|
||||
|
||||
| 模块 | Maven Artifact | 基础包 |
|
||||
|---|---|---|
|
||||
| Common Toolbox | `common-toolbox` | `com.onixbyte.common` |
|
||||
| Crypto Toolbox | `crypto-toolbox` | `com.onixbyte.crypto` |
|
||||
| Math Toolbox | `math-toolbox` | `com.onixbyte.math` |
|
||||
| Tuple | `tuple` | `com.onixbyte.tuple` |
|
||||
| 模块 | Maven Artifact | 基础包 |
|
||||
| ------------------ | -------------------- | -------------------------------- |
|
||||
| Common Toolbox | `common-toolbox` | `com.onixbyte.common` |
|
||||
| Crypto Toolbox | `crypto-toolbox` | `com.onixbyte.crypto` |
|
||||
| Math Toolbox | `math-toolbox` | `com.onixbyte.math` |
|
||||
| Tuple | `tuple` | `com.onixbyte.tuple` |
|
||||
| Identity Generator | `identity-generator` | `com.onixbyte.identitygenerator` |
|
||||
|
||||
---
|
||||
@@ -28,16 +28,16 @@ import com.onixbyte.common.adapter.ObjectMapAdapter;
|
||||
|
||||
### 工具类
|
||||
|
||||
| 类 | 描述 |
|
||||
|---|---|
|
||||
| `AesUtil` | AES 对称加密/解密,支持密钥生成 |
|
||||
| `Base64Util` | Base64 编解码,支持多种字符集 |
|
||||
| `BoolUtil` | 布尔值解析与转换工具 |
|
||||
| `BranchUtil` | 条件分支辅助,支持函数式链式调用 |
|
||||
| `CollectionUtil` | 集合操作 — 分组、分区、合并 |
|
||||
| `HashUtil` | 哈希工具,支持 MD5、SHA-1、SHA-256、SHA-512 |
|
||||
| `MapUtil` | Map 构建、合并和转换辅助 |
|
||||
| `RangeUtil` | 数值范围操作,支持边界处理 |
|
||||
| 类 | 描述 |
|
||||
| ---------------- | ------------------------------------------- |
|
||||
| `AesUtil` | AES 对称加密/解密,支持密钥生成 |
|
||||
| `Base64Util` | Base64 编解码,支持多种字符集 |
|
||||
| `BoolUtil` | 布尔值解析与转换工具 |
|
||||
| `BranchUtil` | 条件分支辅助,支持函数式链式调用 |
|
||||
| `CollectionUtil` | 集合操作 — 分组、分区、合并 |
|
||||
| `HashUtil` | 哈希工具,支持 MD5、SHA-1、SHA-256、SHA-512 |
|
||||
| `MapUtil` | Map 构建、合并和转换辅助 |
|
||||
| `RangeUtil` | 数值范围操作,支持边界处理 |
|
||||
|
||||
**AesUtil 示例:**
|
||||
|
||||
@@ -70,12 +70,12 @@ var grouped = CollectionUtil.groupBy(users, User::getDepartment);
|
||||
|
||||
### 密钥加载器
|
||||
|
||||
| 类 | 描述 |
|
||||
|---|---|
|
||||
| `RSAPrivateKeyLoader` | 从 PEM 字符串加载 RSA 私钥 |
|
||||
| `RSAPublicKeyLoader` | 从 PEM 字符串加载 RSA 公钥 |
|
||||
| `ECPrivateKeyLoader` | 从 PEM 字符串加载 ECDSA 私钥 |
|
||||
| `ECPublicKeyLoader` | 从 PEM 字符串加载 ECDSA 公钥 |
|
||||
| 类 | 描述 |
|
||||
| --------------------- | ---------------------------- |
|
||||
| `RSAPrivateKeyLoader` | 从 PEM 字符串加载 RSA 私钥 |
|
||||
| `RSAPublicKeyLoader` | 从 PEM 字符串加载 RSA 公钥 |
|
||||
| `ECPrivateKeyLoader` | 从 PEM 字符串加载 ECDSA 私钥 |
|
||||
| `ECPublicKeyLoader` | 从 PEM 字符串加载 ECDSA 公钥 |
|
||||
|
||||
所有密钥加载器实现 `PrivateKeyLoader` 或 `PublicKeyLoader` 接口。
|
||||
|
||||
@@ -99,8 +99,8 @@ var ecPublicKey = new ECPublicKeyLoader().load(pemString);
|
||||
|
||||
### 异常
|
||||
|
||||
| 类 | 描述 |
|
||||
|---|---|
|
||||
| 类 | 描述 |
|
||||
| --------------------- | -------------------------- |
|
||||
| `KeyLoadingException` | PEM 格式密钥解析失败时抛出 |
|
||||
|
||||
---
|
||||
@@ -111,10 +111,10 @@ var ecPublicKey = new ECPublicKeyLoader().load(pemString);
|
||||
|
||||
### 核心类
|
||||
|
||||
| 类 | 描述 |
|
||||
|---|---|
|
||||
| `Calculator` | 统计计算器 — 均值、中位数、方差、标准差、总和、最小值、最大值 |
|
||||
| `PercentileCalculator` | 百分位数计算器,支持可配置的插值策略 |
|
||||
| 类 | 描述 |
|
||||
| ---------------------- | ------------------------------------------------------------- |
|
||||
| `Calculator` | 统计计算器 — 均值、中位数、方差、标准差、总和、最小值、最大值 |
|
||||
| `PercentileCalculator` | 百分位数计算器,支持可配置的插值策略 |
|
||||
|
||||
**Calculator 示例:**
|
||||
|
||||
@@ -135,8 +135,8 @@ double p99 = calculator.calculate(99);
|
||||
|
||||
### 数据模型
|
||||
|
||||
| 类 | 描述 |
|
||||
|---|---|
|
||||
| 类 | 描述 |
|
||||
| ---------------- | --------------------------------------------- |
|
||||
| `QuartileBounds` | 包含 Q1、中位数(Q2)、Q3 值及 IQR 和须线边界 |
|
||||
|
||||
---
|
||||
@@ -147,11 +147,11 @@ double p99 = calculator.calculate(99);
|
||||
|
||||
### 类
|
||||
|
||||
| 类 | 类型参数 | 可变性 |
|
||||
|---|---|---|
|
||||
| `Tuple<A, B>` | 两个元素 | 可变 |
|
||||
| `ImmutableTuple<A, B>` | 两个元素 | 不可变 |
|
||||
| `Triple<A, B, C>` | 三个元素 | 可变 |
|
||||
| 类 | 类型参数 | 可变性 |
|
||||
| -------------------------- | -------- | ------ |
|
||||
| `Tuple<A, B>` | 两个元素 | 可变 |
|
||||
| `ImmutableTuple<A, B>` | 两个元素 | 不可变 |
|
||||
| `Triple<A, B, C>` | 三个元素 | 可变 |
|
||||
| `ImmutableTriple<A, B, C>` | 三个元素 | 不可变 |
|
||||
|
||||
**示例:**
|
||||
@@ -183,10 +183,10 @@ Integer c = triple.getThird();
|
||||
|
||||
### 实现
|
||||
|
||||
| 类 | 描述 |
|
||||
|---|---|
|
||||
| `SequentialUuidGenerator` | 生成时间排序的顺序 UUID(优化数据库索引性能) |
|
||||
| `SnowflakeIdentityGenerator` | 雪花算法分布式唯一 ID 生成 |
|
||||
| 类 | 描述 |
|
||||
| ---------------------------- | --------------------------------------------- |
|
||||
| `SequentialUuidGenerator` | 生成时间排序的顺序 UUID(优化数据库索引性能) |
|
||||
| `SnowflakeIdentityGenerator` | 雪花算法分布式唯一 ID 生成 |
|
||||
|
||||
**顺序 UUID:**
|
||||
|
||||
@@ -204,6 +204,6 @@ long id = generator.nextId(); // 64 位雪花 ID
|
||||
|
||||
### 异常
|
||||
|
||||
| 类 | 描述 |
|
||||
|---|---|
|
||||
| 类 | 描述 |
|
||||
| ----------------- | ------------------------------------------ |
|
||||
| `TimingException` | 系统时钟回拨时抛出,保证 ID 唯一性不受影响 |
|
||||
@@ -12,14 +12,14 @@ OnixByte Toolbox 是一个面向 Java 应用的开发工具包,提供了一系
|
||||
|
||||
## 模块
|
||||
|
||||
| 模块 | Artifact ID | 描述 |
|
||||
|--------------------|----------------------|-----------------------------------|
|
||||
| 模块 | Artifact ID | 描述 |
|
||||
| ------------------ | -------------------- | ----------------------------------------------------- |
|
||||
| Common Toolbox | `common-toolbox` | 日常工具类 — AES 加密、哈希、Base64、集合、分支、范围 |
|
||||
| Crypto Toolbox | `crypto-toolbox` | RSA 和 ECDSA 算法的公私钥加载 |
|
||||
| Math Toolbox | `math-toolbox` | 统计计算、百分位数分析、四分位数计算 |
|
||||
| Tuple | `tuple` | 泛型二元组和三元组,含不可变版本 |
|
||||
| Identity Generator | `identity-generator` | 雪花算法和顺序 UUID 唯一标识符生成 |
|
||||
| Version Catalogue | `version-catalogue` | OnixByte 项目的集中式 Gradle 版本目录 |
|
||||
| Crypto Toolbox | `crypto-toolbox` | RSA 和 ECDSA 算法的公私钥加载 |
|
||||
| Math Toolbox | `math-toolbox` | 统计计算、百分位数分析、四分位数计算 |
|
||||
| Tuple | `tuple` | 泛型二元组和三元组,含不可变版本 |
|
||||
| Identity Generator | `identity-generator` | 雪花算法和顺序 UUID 唯一标识符生成 |
|
||||
| Version Catalogue | `version-catalogue` | OnixByte 项目的集中式 Gradle 版本目录 |
|
||||
|
||||
## 安装
|
||||
|
||||
|
||||
@@ -23,12 +23,12 @@ import { Tabs, Tab } from "@rspress/core/theme"
|
||||
|
||||
每个地区暴露四个属性:
|
||||
|
||||
| 属性 | 类型 | 描述 | 示例 |
|
||||
|---|---|---|---|
|
||||
| `abbreviation` | String | ISO 3166-1 alpha-2 缩写 | `HK`、`US`、`GB`、`RU` |
|
||||
| `code` | String | ISO 3166-1 alpha-3 代码 | `HKG`、`USA`、`GBR`、`RUS` |
|
||||
| `callingCode` | String | E.164 国际拨号前缀 | `852`、`1`、`44`、`7` |
|
||||
| `locale` | String | 默认语言和地区标签 | `zh_HK`、`en_US`、`en_GB`、`ru_RU` |
|
||||
| 属性 | 类型 | 描述 | 示例 |
|
||||
| -------------- | ------ | ----------------------- | ---------------------------------- |
|
||||
| `abbreviation` | String | ISO 3166-1 alpha-2 缩写 | `HK`、`US`、`GB`、`RU` |
|
||||
| `code` | String | ISO 3166-1 alpha-3 代码 | `HKG`、`USA`、`GBR`、`RUS` |
|
||||
| `callingCode` | String | E.164 国际拨号前缀 | `852`、`1`、`44`、`7` |
|
||||
| `locale` | String | 默认语言和地区标签 | `zh_HK`、`en_US`、`en_GB`、`ru_RU` |
|
||||
|
||||
## 安装
|
||||
|
||||
|
||||
@@ -20,16 +20,8 @@ import { Tabs, Tab } from "@rspress/core/theme"
|
||||
## 安装
|
||||
|
||||
<Tabs>
|
||||
<Tab label="npm">
|
||||
```bash
|
||||
npm install vite-plugin-port-checker -D
|
||||
```
|
||||
</Tab>
|
||||
<Tab label="pnpm">
|
||||
```bash
|
||||
pnpm add vite-plugin-port-checker -D
|
||||
```
|
||||
</Tab>
|
||||
<Tab label="npm">```bash npm install vite-plugin-port-checker -D ```</Tab>
|
||||
<Tab label="pnpm">```bash pnpm add vite-plugin-port-checker -D ```</Tab>
|
||||
</Tabs>
|
||||
|
||||
## 使用
|
||||
|
||||
+1
-1
@@ -2,4 +2,4 @@ export default {
|
||||
plugins: {
|
||||
"@tailwindcss/postcss": {},
|
||||
},
|
||||
};
|
||||
}
|
||||
Vendored
+3
-3
@@ -1,9 +1,9 @@
|
||||
declare module '*.css';
|
||||
declare module "*.css"
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly SSG_MD: boolean;
|
||||
readonly SSG_MD: boolean
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv;
|
||||
readonly env: ImportMetaEnv
|
||||
}
|
||||
Reference in new issue
Block a user