vault backup: 2026-05-25 13:24:32

This commit is contained in:
Willie
2026-05-25 13:24:32 +08:00
parent 7f726a3f5d
commit bd00a94612
12 changed files with 1721 additions and 34 deletions

3
.obsidian/community-plugins.json vendored Normal file
View File

@@ -0,0 +1,3 @@
[
"obsidian-git"
]

View File

@@ -0,0 +1,67 @@
{
"commitMessage": "vault backup: {{date}}",
"autoCommitMessage": "vault backup: {{date}}",
"commitMessageScript": "",
"commitDateFormat": "YYYY-MM-DD HH:mm:ss",
"autoSaveInterval": 5,
"autoPushInterval": 5,
"autoPullInterval": 10,
"autoPullOnBoot": false,
"autoCommitOnlyStaged": false,
"disablePush": false,
"pullBeforePush": true,
"disablePopups": false,
"showErrorNotices": true,
"disablePopupsForNoChanges": false,
"listChangedFilesInMessageBody": false,
"showStatusBar": true,
"updateSubmodules": false,
"syncMethod": "merge",
"mergeStrategy": "none",
"customMessageOnAutoBackup": false,
"autoBackupAfterFileChange": false,
"treeStructure": false,
"refreshSourceControl": true,
"basePath": "",
"differentIntervalCommitAndPush": true,
"changedFilesInStatusBar": false,
"showedMobileNotice": true,
"refreshSourceControlTimer": 7000,
"showBranchStatusBar": true,
"setLastSaveToLastCommit": false,
"submoduleRecurseCheckout": false,
"gitDir": "",
"showFileMenu": true,
"authorInHistoryView": "hide",
"dateInHistoryView": false,
"diffStyle": "split",
"hunks": {
"showSigns": false,
"hunkCommands": false,
"statusBar": "disabled"
},
"lineAuthor": {
"show": false,
"followMovement": "inactive",
"authorDisplay": "initials",
"showCommitHash": false,
"dateTimeFormatOptions": "date",
"dateTimeFormatCustomString": "YYYY-MM-DD HH:mm",
"dateTimeTimezone": "viewer-local",
"coloringMaxAge": "1y",
"colorNew": {
"r": 255,
"g": 150,
"b": 150
},
"colorOld": {
"r": 120,
"g": 160,
"b": 255
},
"textColorCss": "var(--text-muted)",
"ignoreWhitespace": false,
"lastShownAuthorDisplay": "initials",
"lastShownDateTimeFormatOptions": "date"
}
}

415
.obsidian/plugins/obsidian-git/main.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,10 @@
{
"author": "Vinzent",
"authorUrl": "https://github.com/Vinzent03",
"id": "obsidian-git",
"name": "Git",
"description": "Integrate Git version control with automatic backup and other advanced features.",
"isDesktopOnly": false,
"fundingUrl": "https://ko-fi.com/vinzent",
"version": "2.38.3"
}

View File

@@ -0,0 +1,705 @@
@keyframes loading {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.workspace-leaf-content[data-type="git-view"] .button-border {
border: 2px solid var(--interactive-accent);
border-radius: var(--radius-s);
}
.workspace-leaf-content[data-type="git-view"] .view-content {
padding-left: 0;
padding-top: 0;
padding-right: 0;
}
.workspace-leaf-content[data-type="git-history-view"] .view-content {
padding-left: 0;
padding-top: 0;
padding-right: 0;
}
.loading {
overflow: hidden;
}
.loading > svg {
animation: 2s linear infinite loading;
transform-origin: 50% 50%;
display: inline-block;
}
.obsidian-git-center {
margin: auto;
text-align: center;
width: 50%;
}
.obsidian-git-textarea {
display: block;
margin-left: auto;
margin-right: auto;
}
.obsidian-git-disabled {
opacity: 0.5;
}
.obsidian-git-center-button {
display: block;
margin: 20px auto;
}
.tooltip.mod-left {
overflow-wrap: break-word;
}
.tooltip.mod-right {
overflow-wrap: break-word;
}
/* Limits the scrollbar to the view body */
.git-view {
display: flex;
flex-direction: column;
position: relative;
height: 100%;
}
/* Re-enable wrapping of nav buttns to prevent overflow on smaller screens #*/
.workspace-drawer .git-view .nav-buttons-container {
flex-wrap: wrap;
}
.git-tools {
display: flex;
margin-left: auto;
}
.git-tools .type {
padding-left: var(--size-2-1);
display: flex;
align-items: center;
justify-content: center;
width: 11px;
}
.git-tools .type[data-type="M"] {
color: orange;
}
.git-tools .type[data-type="D"] {
color: red;
}
.git-tools .buttons {
display: flex;
}
.git-tools .buttons > * {
padding: 0;
height: auto;
}
.workspace-leaf-content[data-type="git-view"] .tree-item-self,
.workspace-leaf-content[data-type="git-history-view"] .tree-item-self {
align-items: center;
}
.workspace-leaf-content[data-type="git-view"]
.tree-item-self:hover
.clickable-icon,
.workspace-leaf-content[data-type="git-history-view"]
.tree-item-self:hover
.clickable-icon {
color: var(--icon-color-hover);
}
/* Highlight an item as active if it's diff is currently opened */
.is-active .git-tools .buttons > * {
color: var(--nav-item-color-active);
}
.git-author {
color: var(--text-accent);
}
.git-date {
color: var(--text-accent);
}
.git-ref {
color: var(--text-accent);
}
/* ====== diff2html ======
The following styles are adapted from the obsidian-version-history plugin by
@kometenstaub https://github.com/kometenstaub/obsidian-version-history-diff/blob/main/src/styles.scss
which itself is adapted from the diff2html library with the following original license:
https://github.com/rtfpessoa/diff2html/blob/master/LICENSE.md
Copyright 2014-2016 Rodrigo Fernandes https://rtfpessoa.github.io/
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
.theme-dark,
.theme-light {
--git-delete-bg: #ff475040;
--git-delete-hl: #96050a75;
--git-insert-bg: #68d36840;
--git-insert-hl: #23c02350;
--git-change-bg: #ffd55840;
--git-selected: #3572b0;
--git-delete: #cc3333;
--git-insert: #399839;
--git-change: #d0b44c;
--git-move: #3572b0;
}
.git-diff {
.d2h-d-none {
display: none;
}
.d2h-wrapper {
text-align: left;
border-radius: 0.25em;
overflow: auto;
}
.d2h-file-header.d2h-file-header {
background-color: var(--background-secondary);
border-bottom: 1px solid var(--background-modifier-border);
font-family:
Source Sans Pro,
Helvetica Neue,
Helvetica,
Arial,
sans-serif;
height: 35px;
padding: 5px 10px;
}
.d2h-file-header,
.d2h-file-stats {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
}
.d2h-file-header {
display: none;
}
.d2h-file-stats {
font-size: 14px;
margin-left: auto;
}
.d2h-lines-added {
border: 1px solid var(--color-green);
border-radius: 5px 0 0 5px;
color: var(--color-green);
padding: 2px;
text-align: right;
vertical-align: middle;
}
.d2h-lines-deleted {
border: 1px solid var(--color-red);
border-radius: 0 5px 5px 0;
color: var(--color-red);
margin-left: 1px;
padding: 2px;
text-align: left;
vertical-align: middle;
}
.d2h-file-name-wrapper {
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
font-size: 15px;
width: 100%;
}
.d2h-file-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: var(--text-normal);
font-size: var(--h5-size);
}
.d2h-file-wrapper {
border: 1px solid var(--background-secondary-alt);
border-radius: 3px;
margin-bottom: 1em;
max-height: 100%;
}
.d2h-file-collapse {
-webkit-box-pack: end;
-ms-flex-pack: end;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
border: 1px solid var(--background-secondary-alt);
border-radius: 3px;
cursor: pointer;
display: none;
font-size: 12px;
justify-content: flex-end;
padding: 4px 8px;
}
.d2h-file-collapse.d2h-selected {
background-color: var(--git-selected);
}
.d2h-file-collapse-input {
margin: 0 4px 0 0;
}
.d2h-diff-table {
border-collapse: collapse;
font-family: var(--font-monospace);
font-size: var(--code-size);
width: 100%;
}
.d2h-files-diff {
width: 100%;
}
.d2h-file-diff {
/*
overflow-y: scroll;
*/
border-radius: 5px;
font-size: var(--font-text-size);
line-height: var(--line-height-normal);
}
.d2h-file-side-diff {
display: inline-block;
margin-bottom: -8px;
margin-right: -4px;
overflow-x: scroll;
overflow-y: hidden;
width: 50%;
}
.d2h-code-line {
padding-left: 6em;
padding-right: 1.5em;
}
.d2h-code-line,
.d2h-code-side-line {
display: inline-block;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
white-space: nowrap;
width: 100%;
}
.d2h-code-side-line {
/* needed to be changed */
padding-left: 0.5em;
padding-right: 0.5em;
}
.d2h-code-line-ctn {
word-wrap: normal;
background: none;
display: inline-block;
padding: 0;
-webkit-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
user-select: text;
vertical-align: middle;
width: 100%;
/* only works for line-by-line */
white-space: pre-wrap;
}
.d2h-code-line del,
.d2h-code-side-line del {
background-color: var(--git-delete-hl);
color: var(--text-normal);
}
.d2h-code-line del,
.d2h-code-line ins,
.d2h-code-side-line del,
.d2h-code-side-line ins {
border-radius: 0.2em;
display: inline-block;
margin-top: -1px;
text-decoration: none;
vertical-align: middle;
}
.d2h-code-line ins,
.d2h-code-side-line ins {
background-color: var(--git-insert-hl);
text-align: left;
}
.d2h-code-line-prefix {
word-wrap: normal;
background: none;
display: inline;
padding: 0;
white-space: pre;
}
.line-num1 {
float: left;
}
.line-num1,
.line-num2 {
-webkit-box-sizing: border-box;
box-sizing: border-box;
overflow: hidden;
/*
padding: 0 0.5em;
*/
text-overflow: ellipsis;
width: 2.5em;
padding-left: 0;
}
.line-num2 {
float: right;
}
.d2h-code-linenumber {
background-color: var(--background-primary);
border: solid var(--background-modifier-border);
border-width: 0 1px;
-webkit-box-sizing: border-box;
box-sizing: border-box;
color: var(--text-faint);
cursor: pointer;
display: inline-block;
position: absolute;
text-align: right;
width: 5.5em;
}
.d2h-code-linenumber:after {
content: "\200b";
}
.d2h-code-side-linenumber {
background-color: var(--background-primary);
border: solid var(--background-modifier-border);
border-width: 0 1px;
-webkit-box-sizing: border-box;
box-sizing: border-box;
color: var(--text-faint);
cursor: pointer;
overflow: hidden;
padding: 0 0.5em;
text-align: right;
text-overflow: ellipsis;
width: 4em;
/* needed to be changed */
display: table-cell;
position: relative;
}
.d2h-code-side-linenumber:after {
content: "\200b";
}
.d2h-code-side-emptyplaceholder,
.d2h-emptyplaceholder {
background-color: var(--background-primary);
border-color: var(--background-modifier-border);
}
.d2h-code-line-prefix,
.d2h-code-linenumber,
.d2h-code-side-linenumber,
.d2h-emptyplaceholder {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.d2h-code-linenumber,
.d2h-code-side-linenumber {
direction: rtl;
}
.d2h-del {
background-color: var(--git-delete-bg);
border-color: var(--git-delete-hl);
}
.d2h-ins {
background-color: var(--git-insert-bg);
border-color: var(--git-insert-hl);
}
.d2h-info {
background-color: var(--background-primary);
border-color: var(--background-modifier-border);
color: var(--text-faint);
}
.d2h-del,
.d2h-ins,
.d2h-file-diff .d2h-change {
color: var(--text-normal);
}
.d2h-file-diff .d2h-del.d2h-change {
background-color: var(--git-change-bg);
}
.d2h-file-diff .d2h-ins.d2h-change {
background-color: var(--git-insert-bg);
}
.d2h-file-list-wrapper {
a {
text-decoration: none;
cursor: default;
-webkit-user-drag: none;
}
svg {
display: none;
}
}
.d2h-file-list-header {
text-align: left;
}
.d2h-file-list-title {
display: none;
}
.d2h-file-list-line {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
text-align: left;
}
.d2h-file-list {
}
.d2h-file-list > li {
border-bottom: 1px solid var(--background-modifier-border);
margin: 0;
padding: 5px 10px;
}
.d2h-file-list > li:last-child {
border-bottom: none;
}
.d2h-file-switch {
cursor: pointer;
display: none;
font-size: 10px;
}
.d2h-icon {
fill: currentColor;
margin-right: 10px;
vertical-align: middle;
}
.d2h-deleted {
color: var(--git-delete);
}
.d2h-added {
color: var(--git-insert);
}
.d2h-changed {
color: var(--git-change);
}
.d2h-moved {
color: var(--git-move);
}
.d2h-tag {
background-color: var(--background-secondary);
display: -webkit-box;
display: -ms-flexbox;
display: flex;
font-size: 10px;
margin-left: 5px;
padding: 0 2px;
}
.d2h-deleted-tag {
border: 1px solid var(--git-delete);
}
.d2h-added-tag {
border: 1px solid var(--git-insert);
}
.d2h-changed-tag {
border: 1px solid var(--git-change);
}
.d2h-moved-tag {
border: 1px solid var(--git-move);
}
/* needed for line-by-line*/
.d2h-diff-tbody {
position: relative;
}
/* My additions */
.cm-merge-revert {
width: 4em;
}
/* Ensure that merge revert markers are positioned correctly */
.cm-merge-revert > * {
position: absolute;
background-color: var(--background-secondary);
display: flex;
}
}
/* ====================== Line Authoring Information ====================== */
.cm-gutterElement.obs-git-blame-gutter {
/* Add background color to spacing inbetween and around the gutter for better aesthetics */
border-width: 0px 2px 0.2px;
border-style: solid;
border-color: var(--background-secondary);
background-color: var(--background-secondary);
}
.cm-gutterElement.obs-git-blame-gutter > div,
.line-author-settings-preview {
/* delegate text color to settings */
color: var(--obs-git-gutter-text);
font-family: monospace;
height: 100%; /* ensure, that age-based background color occupies entire parent */
text-align: right;
padding: 0px 6px;
white-space: pre; /* Keep spaces and do not collapse them. */
}
@media (max-width: 800px) {
/* hide git blame gutter not to superpose text */
.cm-gutterElement.obs-git-blame-gutter {
display: none;
}
}
.git-unified-diff-view,
.git-split-diff-view .cm-deletedLine .cm-changedText {
background-color: #ee443330;
}
.git-unified-diff-view,
.git-split-diff-view .cm-insertedLine .cm-changedText {
background-color: #22bb2230;
}
.git-obscure-prompt[git-is-obscured="true"] #git-show-password:after {
-webkit-mask-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-eye"><path d="M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0"></path><circle cx="12" cy="12" r="3"></circle></svg>');
}
.git-obscure-prompt[git-is-obscured="false"] #git-show-password:after {
-webkit-mask-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-eye-off"><path d="M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49"></path><path d="M14.084 14.158a3 3 0 0 1-4.242-4.242"></path><path d="M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143"></path><path d="m2 2 20 20"></path></svg>');
}
/* Override styling of Codemirror merge view "collapsed lines" indicator */
.git-split-diff-view .ͼ2 .cm-collapsedLines {
background: var(--interactive-normal);
border-radius: var(--radius-m);
color: var(--text-accent);
font-size: var(--font-small);
padding: var(--size-4-1) var(--size-4-1);
}
.git-split-diff-view .ͼ2 .cm-collapsedLines:hover {
background: var(--interactive-hover);
color: var(--text-accent-hover);
}
.git-signs-gutter {
.cm-gutterElement {
display: grid;
/* Needed to align the sign properly for different line heigts. Such as
* when having a heading or list item.
*/
padding-top: 0 !important;
}
}
.git-gutter-marker:hover {
border-radius: 2px;
}
.git-gutter-marker.git-add {
background-color: var(--color-green);
justify-self: center;
height: inherit;
width: 0.2rem;
}
.git-gutter-marker.git-change {
background-color: var(--color-yellow);
justify-self: center;
height: inherit;
width: 0.2rem;
}
.git-gutter-marker.git-changedelete {
color: var(--color-yellow);
font-weight: var(--font-bold);
font-size: 1rem;
justify-self: center;
height: inherit;
}
.git-gutter-marker.git-delete {
background-color: var(--color-red);
height: 0.2rem;
width: 0.8rem;
align-self: end;
}
.git-gutter-marker.git-topdelete {
background-color: var(--color-red);
height: 0.2rem;
width: 0.8rem;
align-self: start;
}
div:hover > .git-gutter-marker.git-change {
width: 0.6rem;
}
div:hover > .git-gutter-marker.git-add {
width: 0.6rem;
}
div:hover > .git-gutter-marker.git-delete {
height: 0.6rem;
}
div:hover > .git-gutter-marker.git-topdelete {
height: 0.6rem;
}
div:hover > .git-gutter-marker.git-changedelete {
font-weight: var(--font-bold);
}
.git-gutter-marker.staged {
opacity: 0.5;
}
/* Prevent shifting of the editor when git signs gutter is the only gutter present */
.cm-gutters.cm-gutters-before:has(> .git-signs-gutter:only-child) {
margin-inline-end: 0;
.git-signs-gutter {
margin-inline-start: -1rem;
}
}
.git-changes-status-bar-colored {
.git-add {
color: var(--color-green);
}
.git-change {
color: var(--color-yellow);
}
.git-delete {
color: var(--color-red);
}
}
.git-changes-status-bar .git-add {
margin-right: 0.3em;
}
.git-changes-status-bar .git-change {
margin-right: 0.3em;
}

View File

@@ -13,28 +13,12 @@
"state": {
"type": "markdown",
"state": {
"file": "Welcome.md",
"mode": "source",
"file": "prop-acc/一次性收费/概念-AdHocEvent状态机.md",
"mode": "preview",
"source": false
},
"icon": "lucide-file",
"title": "Welcome"
}
}
]
},
{
"id": "83848d40d51132e0",
"type": "tabs",
"children": [
{
"id": "765d8162454d6ac1",
"type": "leaf",
"state": {
"type": "graph",
"state": {},
"icon": "lucide-git-fork",
"title": "Graph view"
"title": "概念-AdHocEvent状态机"
}
}
]
@@ -110,7 +94,7 @@
"state": {
"type": "backlink",
"state": {
"file": "Welcome.md",
"file": "prop-acc/一次性收费/概念-AdHocEvent状态机.md",
"collapseAll": false,
"extraContext": false,
"sortOrder": "alphabetical",
@@ -120,7 +104,7 @@
"unlinkedCollapsed": true
},
"icon": "links-coming-in",
"title": "Backlinks for Welcome"
"title": "Backlinks for 概念-AdHocEvent状态机"
}
},
{
@@ -129,12 +113,12 @@
"state": {
"type": "outgoing-link",
"state": {
"file": "Welcome.md",
"file": "prop-acc/一次性收费/概念-AdHocEvent状态机.md",
"linksCollapsed": false,
"unlinkedCollapsed": true
},
"icon": "links-going-out",
"title": "Outgoing links from Welcome"
"title": "Outgoing links from 概念-AdHocEvent状态机"
}
},
{
@@ -172,13 +156,13 @@
"state": {
"type": "outline",
"state": {
"file": "Welcome.md",
"file": "prop-acc/一次性收费/概念-AdHocEvent状态机.md",
"followCursor": false,
"showSearch": false,
"searchQuery": ""
},
"icon": "lucide-list",
"title": "Outline of Welcome"
"title": "Outline of 概念-AdHocEvent状态机"
}
}
]
@@ -196,14 +180,21 @@
"daily-notes:Open today's daily note": false,
"templates:Insert template": false,
"command-palette:Open command palette": false,
"bases:Create new base": false
"bases:Create new base": false,
"obsidian-git:Open Git source control": false
}
},
"active": "849c5ff8936a2b67",
"active": "b06ed69835363258",
"lastOpenFiles": [
"prop-acc/一次性收费/场景-A流-前台购买IC卡.md",
"prop-acc/一次性收费/概念-CollectionOrder与Receipt.md",
"prop-acc/一次性收费/概念-A流与B流.md",
"prop-acc/一次性收费/概念-AdHocEvent状态机.md",
"prop-acc/index.md",
"prop-acc/一次性收费/index.md",
"prop-acc/一次性收费",
"Welcome.md",
"prop-acc",
"Untitled",
"Welcome.md"
"Untitled"
]
}

View File

@@ -1,5 +0,0 @@
This is your new *vault*.
Make a note of something, [[create a link]], or try [the Importer](https://help.obsidian.md/Plugins/Importer)!
When you're ready, delete this note and make the vault your own.

View File

@@ -0,0 +1,99 @@
---
title: 一次性收费(一次性收费)
tags:
- prop-acc
- 一次性收费
- 模块索引
audience:
- 业户
- 业务人员
status: stable
last_reviewed: 2026-05-25
code_version: 2026-05-22
---
# 一次性收费
## 这是什么?
业户在物业**单次购买**或缴费的事项 —— 不像物业费那样按月反复出账。典型有:
- **IC 卡**(门禁卡、停车卡、电梯卡)
- **装修出入证**(给装修工人用)
- **泳票**(夏季泳池单次票)
- **充电桩电费**(电动车一次性充值)
> [!info] 系统里叫什么
> 后台数据模型名是 `AdHocEvent`(临时事件)。每发生一笔购买就建一条记录,关联一张[[概念-CollectionOrder与Receipt|收款订单和收据]]。
## 两种支付方式
> [!example] 您是怎么买的?
> - **线下 A 流**:去物业前台,职员当场收钱给您出货
> - **线上 B 流**:打开小程序,选项目下单,微信/支付宝付款
详见 [[概念-A流与B流|A 流与 B 流对比]]。
## 核心概念(3 篇)
- [[概念-A流与B流]] — 线下/线上两种购买流程对比
- [[概念-CollectionOrder与Receipt]] — 您买完后系统生成了什么
- [[概念-AdHocEvent状态机]] — 一笔购买从下单到完成中间的几种状态
## 场景手册(26 篇,5 篇已写)
### 📦 A 流(线下前台,即收即付)
- ✅ [[场景-A流-前台购买IC卡]]
- ⏳ 场景-A流-前台购买装修出入证
- ⏳ 场景-A流-前台购买泳票
- ⏳ 场景-A流-前台办理充电桩电费充值
- ⏳ 场景-A流-装修公司批量采购出入证
### 📱 B 流(线上小程序)
- ✅ [[场景-B流-小程序下单+微信支付]]
- ⏳ 场景-B流-小程序下单+支付宝
- ⏳ 场景-B流-小程序下单 30 分钟内完成支付
- ✅ [[场景-跨渠道补缴]] — 儿女线上下,老人前台付
### ❌ 取消 / 退款
- ⏳ 场景-取消-业户改主意主动撤单
- ✅ [[场景-超时未付自动作废]]
- ✅ [[场景-已收款作废]]
- ⏳ 场景-取消-录错金额作废重做
### ⚠️ 异常 / 故障
- ⏳ 场景-异常-支付完成但实物发不出
- ⏳ 场景-异常-微信支付回调延迟
- ⏳ 场景-异常-业户重复下单
- ⏳ 场景-异常-支付分账失败
- ⏳ 场景-异常-同业户跨社区 Pending 单
### 🧾 收据 / 凭证
- ⏳ 场景-收据-现场打印纸质收据
- ⏳ 场景-收据-重打丢失收据
- ⏳ 场景-收据-小程序自助下载 PDF
### 📊 审计 / 对账
- ⏳ 场景-审计-月底现金对账
- ⏳ 场景-审计-IC 卡库存与售出数对账
- ⏳ 场景-审计-作废事由抽查
### 🔧 配置 / 准备
- ⏳ 场景-配置-新增收费项目
- ⏳ 场景-配置-下架收费项目并处理 Pending 单
## 相关代码
业务设计决策详见 `packages/prop-acc/issue.md` 的 Q1/Q2 段。
---
> [!todo] 还没写的场景
> ⏳ 标记的 21 个场景会在后续批次补完。如果您当前关心的场景未写,告诉我可以优先。

View File

@@ -0,0 +1,135 @@
---
title: 场景 - A 流 - 前台购买 IC 卡
tags:
- prop-acc
- 一次性收费
- 业务场景
- A流
audience:
- 业户
- 业务人员
status: stable
last_reviewed: 2026-05-25
code_version: 2026-05-22
---
# 场景:前台购买 IC 卡
最典型最高频的一次性收费场景。门禁卡、停车卡、电梯卡都走这条路径。
## 典型情境
> [!example] 真实情境
> 张阿姨,72 岁,上周搬家钥匙丢了想配新门禁卡。她不会用小程序,下午 3 点去了物业前台办公室。
## 业户视角(您要做什么)
### 第 1 步:到前台
带上**身份证 + 房产证(或租赁合同)** 到物业前台。
> [!tip] 装修业户特别注意
> 如果您是装修公司,需要带上**业主授权书**或合同复印件。
### 第 2 步:告诉职员要办什么
"我要补办一张门禁卡,房号是 12-3-501。"
### 第 3 步:付款
职员会告诉您**多少钱**(常见 ¥30-50 一张),您可选:
- **现金** —— 最常见
- **微信扫码** —— 职员出示物业收款码
- **POS 刷卡** —— 银行卡支付
### 第 4 步:拿卡 + 收据
- 实物 IC 卡:**当场拿到**
- 收据:可以选**打印纸质**或**微信收**
> [!success] 完成
> 整个过程 5 分钟内搞定。回家就能刷卡进门了。
## 业务人员视角(职员怎么操作)
### 第 1 步:打开后台
登录 Filament 后台 → 一次性收费 → 新建
### 第 2 步:填表单
| 字段 | 填什么 |
|---|---|
| 业户 | 通过手机号或房号查找业户 |
| 收费项目(RatePlan)| 选 "IC 卡 - 门禁" |
| 数量 | 1 |
| 金额 | 自动按 RatePlan 单价填(可手动改)|
| 支付方式 | 现金 / 微信 / POS |
| 收款银行账户 | 微信/POS 选对应银行账户;现金可空 |
| 备注 | 选填,如 "丢卡补办" |
### 第 3 步:提交
点击"创建"。系统瞬间完成:
-`AdHocEvent`(状态 Completed)
-`CollectionOrder`(状态 Completed)
-`Receipt`(状态 Issued)
- 触发收据 PDF 生成
### 第 4 步:出货 + 给收据
- 从抽屉拿一张空白 IC 卡,在制卡机里写入业户房号
- 把卡交给业户
- 把收据打印 / 发到业户微信
## 系统流程(技术视角)
```mermaid
sequenceDiagram
participant 业户
participant 前台
participant Filament
participant 数据库
participant 监听器
业户->>前台: 我要买门禁卡 + 付现金
前台->>Filament: 填表单 + 提交
Filament->>数据库: 开启事务
Filament->>数据库: 1. 建 CollectionOrder (Completed)
Filament->>数据库: 2. 建 AdHocEvent (Completed, 关联 CO)
Filament->>监听器: 3. 触发 CollectionOrderCompleted 事件
监听器->>数据库: 4. 建 Receipt + ReceiptItem
Filament->>数据库: 提交事务
Filament-->>前台: 成功通知
前台->>业户: 出卡 + 给收据
```
> [!info] 一气呵成
> 第 1-4 步在一个数据库事务里完成 —— 任何一步失败,整笔操作回滚,不会出现"扣了钱但没出卡"的半成品状态。
## 常见问题
> [!question] 业户没带身份证可以办吗?
> 各物业政策不同。系统不会强制 —— 是物业内部审核流程。
> [!question] 业户现金不够可以拆分支付吗?
> 当前一笔订单只支持一种支付方式。需要拆,可以分两单做(2 张 IC 卡分两笔录入)。
> [!question] 万一录错金额怎么办?
> 走 [[场景-已收款作废]] 流程,作废后重新录入。
> [!question] 卡的物理库存怎么管?
> 当前系统**不管物理库存**,只管财务记录。需要库存管理可以接外部系统,或挂 TODO 等业务方反馈。
## 相关概念
- [[概念-A流与B流]] — 为什么这叫 A 流
- [[概念-CollectionOrder与Receipt]] — 创建的三件套
- [[概念-AdHocEvent状态机]] — 直接进入 Completed 状态
## 异常分支
- 业户付完反悔 → [[场景-已收款作废]]
- 业户改主意不要了 → 录入前直接放弃,系统里没任何记录

View File

@@ -0,0 +1,79 @@
---
title: AdHocEvent 状态机
tags:
- prop-acc
- 一次性收费
- 核心概念
audience:
- 业务人员
status: stable
last_reviewed: 2026-05-25
code_version: 2026-05-22
---
# AdHocEvent 状态机
> [!info] 给谁看
> 这篇主要给**业务人员**和**财务**。业户只关心"我买完了没",不需要懂状态机。
每张一次性收费记录都有一个 `status` 字段,只有三种状态:
| 状态 | 中文 | 什么时候出现 |
|---|---|---|
| `Pending` | 待付款 | B 流下单后未付款 |
| `Completed` | 已收款 | A 流提交后 / B 流支付回调后 |
| `Voided` | 已作废 | 走 VoidAction 后 |
## 状态流转图
```mermaid
stateDiagram-v2
[*] --> Pending: B 流 小程序下单
[*] --> Completed: A 流 前台即收即付
Pending --> Completed: 业户付款<br/>(回调 或 前台补缴)
Pending --> Voided: 业户撤单<br/>或 超时未付
Completed --> Voided: 录错作废
```
## 每种状态下能做什么
| 当前状态 | 编辑(Edit) | 作废(Void) | 删除(Delete) |
|---|---|---|---|
| **Pending** | ✅ 可改业户、金额等 | ✅ 跟 Completed 一样级联废 CO | ❌ 已禁用 UI 入口 |
| **Completed** | ❌ 不可改(审计要求) | ✅ 级联废 CO + Receipt | ❌ 已禁用 UI 入口 |
| **Voided** | ❌ | ❌ 已经废了 | ❌ |
> [!warning] 为什么禁用 Delete?
> 删除是无审计的物理操作,B 流的 Pending 单删掉会让关联的 CollectionOrder 变孤儿。所有"取消"统一走 [[场景-已收款作废\|VoidAction]] —— 留 `voided_at` / `voided_reason` / `voided_by` 完整审计。
## 状态与 CollectionOrder / Receipt 的对应关系
```mermaid
stateDiagram-v2
state "Pending<br/>AdHocEvent + CO(Pending)<br/>没收据" as P
state "Completed<br/>AdHocEvent + CO(Completed)<br/>+ Receipt(Issued)" as C
state "Voided<br/>AdHocEvent + CO(Failed)<br/>+ Receipt(Voided)" as V
[*] --> P: B 流下单
[*] --> C: A 流即收即付
P --> C: 支付完成
P --> V: 业户撤 或 超时
C --> V: 已收款作废
```
详见 [[概念-CollectionOrder与Receipt]] 关于这三张表的关系。
## 状态相关的几个时间字段
- `created_at` —— 记录何时创建(下单时间)
- `occurred_at` —— 业务发生时间(可能与 created_at 不同,业务自定义)
- `meta.voided_at` —— 作废时间(如果走过 VoidAction)
- `meta.voided_reason` —— 作废原因(运营/财务追溯)
- `meta.voided_by` —— 谁作废的(责任追溯)
## 看完去哪?
- 想了解 CollectionOrder 和 Receipt 状态如何联动 → [[概念-CollectionOrder与Receipt]]
- 想了解 A/B 两种流程的区别 → [[概念-A流与B流]]
- 想看作废的真实例子 → [[场景-已收款作废]]

View File

@@ -0,0 +1,80 @@
---
title: A 流与 B 流
tags:
- prop-acc
- 一次性收费
- 核心概念
audience:
- 业户
- 业务人员
status: stable
last_reviewed: 2026-05-25
code_version: 2026-05-22
---
# A 流与 B 流
一次性收费支持两条独立购买路径,**底层用同一套数据表**,差别只在用户怎么发起。
## 一句话区别
> [!tip] 通俗版
> - **A 流**:您**到柜台**找物业阿姨,付了钱当场出货
> - **B 流**:您**自己在小程序**下单,微信付完,过来取(或者送到家)
## 对照表
| 维度 | A 流(线下即收即付) | B 流(线上订单—支付分离) |
|---|---|---|
| **典型用户** | 老人、不会用手机的业户 | 年轻业户、工作日没空到前台的人 |
| **触发动作** | 业户走到前台 | 小程序下单 |
| **付款时机** | 当场付(现金 / POS / 微信扫码) | 下单时锁定金额、付款分离 |
| **生效时机** | 即时 — 一笔操作完成所有 | 两步:下单 → 支付回调 |
| **失败模式** | 几乎不可能(人在柜台) | 可能超时未付、可能支付掉单 |
| **取消方式** | 走 [[场景-已收款作废\|作废]] 流程 | 改主意可主动撤;或超时自动作废 |
## 系统视角(流程图)
```mermaid
sequenceDiagram
participant 业户
participant 前台
participant 小程序
participant 系统
participant 支付网关
Note over 业户,系统: A 流(线下即收即付)
业户->>前台: 我要买 IC 卡
前台->>系统: 录入业户、项目、金额、付现金
系统->>系统: 一气呵成:建事件 + 建订单 + 建收据
系统-->>业户: 打印 / 微信收据立马拿到
Note over 业户,支付网关: B 流(订单—支付分离)
业户->>小程序: 下单 IC 卡
小程序->>系统: 建事件(Pending) + 建订单(Pending)
系统-->>业户: 锁价格、给您订单号
业户->>支付网关: 微信付款
支付网关->>系统: 支付回调
系统->>系统: 更新订单(Completed) + 自动出收据
系统-->>业户: 小程序通知:已付款,可来取货
```
## 为什么要两套?
> [!info] 业户视角
> - 老人不会用小程序 → A 流照顾他们
> - 年轻人不想跑前台 → B 流方便
> - **同一项业务两种付款方式都能用**
> [!info] 系统视角
> 两套**共用同一张 `AdHocEvent` 表 + 同一张 `CollectionOrder` 表**。差别只在:
> - A 流一气呵成创建后,状态直接 `Completed`
> - B 流先创建为 `Pending`,等支付回调才翻 `Completed`
>
> 这种设计的好处是**跨渠道补缴**天然支持 —— 详见 [[场景-跨渠道补缴]]。
## 看完去哪?
- 想了解购买后系统记了什么 → [[概念-CollectionOrder与Receipt]]
- 想了解订单的几种状态 → [[概念-AdHocEvent状态机]]
- 想看真实例子 → [[场景-A流-前台购买IC卡]] 或 [[场景-B流-小程序下单+微信支付]]

View File

@@ -0,0 +1,108 @@
---
title: CollectionOrder 与 Receipt
tags:
- prop-acc
- 一次性收费
- 核心概念
audience:
- 业户
- 业务人员
status: stable
last_reviewed: 2026-05-25
code_version: 2026-05-22
---
# CollectionOrder 与 Receipt
您在物业买东西,系统里实际生成的不是一条记录,而是**三件套**。理解了这三件套,就理解了财务系统大部分操作。
## 三件套是什么?
```mermaid
graph LR
A[AdHocEvent<br/>一次性收费事件] --> B[CollectionOrder<br/>收款订单]
B --> C[Receipt<br/>收据]
C --> D[ReceiptItem<br/>收据明细行]
classDef event fill:#fef3c7
classDef order fill:#dbeafe
classDef receipt fill:#dcfce7
class A event
class B order
class C,D receipt
```
| 表 | 是什么 | 业户能看到? |
|---|---|---|
| **AdHocEvent** | "您买了什么"的业务记录(IC 卡 / 装修证 / 泳票) | ❌ 内部 |
| **CollectionOrder** | "怎么付的"的收款记录(微信 / 现金 / POS / 支付宝 / 银行卡) | ✅ 订单号 CO-... |
| **Receipt** | "您付了钱"的收据 | ✅ 收据号 R-... 可打印/下载 |
| **ReceiptItem** | 收据上每行细目(例:IC 卡 × 1 = ¥30) | ✅ 在收据里看到 |
## 业户视角
> [!example] 业户视角:您拿到的东西
>
> 您去前台买了一张 IC 卡 ¥30,完成后:
>
> 1. **物品本身**:工本费一张实物 IC 卡
> 2. **收据**(PDF 或纸质):
> ```
> 收据 No: R-20260525-001
> ─────────────────────────
> IC 卡(门禁) ¥30.00
> ─────────────────────────
> 合计: ¥30.00
> 支付方式:现金
> 收款人:张阿姨(柜台)
> ```
>
> 这张收据就是您的凭证。月底想对账?有收据号一查就知道。
## 业务人员 / 财务视角
> [!example] 财务对账时
>
> 月底财务想知道这个月卖了多少 IC 卡,会查:
> - `AdHocEvent` 表 → 多少张 IC 卡售出,卖给谁
> - `CollectionOrder` 表 → 多少张订单是 "现金 / 微信 / 支付宝" 各多少
> - `Receipt` 表 → 已开收据多少张,作废多少张
>
> 三张表通过 ID 互相关联,任一张里查到一笔,都能反查到另外两张对应的行。
## 什么时候生成?
| 操作 | AdHocEvent | CollectionOrder | Receipt |
|---|---|---|---|
| **A 流前台买卡**(即收即付)| 立刻建 | 立刻建(Completed) | 立刻自动生成 |
| **B 流小程序下单**(尚未付款)| 立刻建(Pending) | 立刻建(Pending) | ❌ 还没生成 |
| **B 流支付回调到达**| 翻为 Completed | 翻为 Completed | 此刻自动生成 |
| **作废订单** | 翻为 Voided | 翻为 Failed | 翻为 Voided |
> [!info] 收据怎么"自动生成"
> 系统监听一个叫 `CollectionOrderCompleted` 的事件 —— 任何时候订单状态变为 Completed,系统自动建一张 Receipt 并附明细。业户不用做任何操作。
## 为什么要这么设计?
> [!tip] 为什么不只用一张表?
> 因为这三件事**关注点不同**:
>
> - **AdHocEvent** 关注"卖了什么、卖给谁"(业务面)
> - **CollectionOrder** 关注"钱怎么进来的、走哪个渠道"(财务/资金面)
> - **Receipt** 关注"给业户的凭证"(对外面)
>
> 同一笔交易,业务/财务/对外三个角度独立看,审计、对账、合规都清楚。
## 退款时的红字收据
> [!warning] 退款也走这套链路
> 退款时系统会生成一张**负数**的 CollectionOrder + Receipt(金额带负号),叫"红字凭证"。这是中国会计标准做法,详见保证金模块的退款场景文档(待补)。
>
> 一次性收费目前**主要走作废**而不是退款 —— 详见 [[场景-已收款作废]]。
## 看完去哪?
- 想了解订单状态怎么变 → [[概念-AdHocEvent状态机]]
- 想看真实例子 → [[场景-A流-前台购买IC卡]]
- 想看跨渠道场景 → [[场景-跨渠道补缴]]