Vier Jahre College und kurz vor dem Abschluss!
Dies ist mein letzter Blog während des Studiums. Ich fasse mein Abschlussprojekt zusammen und teile es. Der von mir gewählte Titel der Abschlussarbeit lautet „Backend Design of Gas Pipeline Network Equipment and Instrument Purchase, Sales and Inventory Management System“. Da ich in unserem Hauptfach der Einzige bin, der den technischen Weg geht, habe ich den Entwurf und die Entwicklung des gesamten Systems abgeschlossen selbst, was fast einen Monat später dauerte, erzielte ich endlich hervorragende Ergebnisse.
Wir werden hier weder Papiere diskutieren noch spezifische Implementierungsdetails aufschreiben. Wir werden hauptsächlich darüber sprechen, wie Sie Ihr eigenes System-Framework und die Systemimplementierung Schritt für Schritt erstellen und Ihre eigenen Erfahrungen teilen andere, und Experten können sie wie sich selbst behandeln. Es ist Zeit, ein paar Steine und Mörtel zu werfen! !
Am Ende des Blogs werden alle Dokumente zur Systementwicklung angehängt, mit Ausnahme der Abschlussarbeit. Dieser Artikel enthält im Allgemeinen den Inhalt der Abschlussarbeit!
2. Startseite nach der Admin-Anmeldung
6. Produkte hinzufügen
7
8. Lieferantenmanagement
9. Bestellverwaltung
10. Bestellexport
11. Bestandsansicht
12
2. Systemanforderungsanalyse
Bei der Entwicklung eines (einfachen) Systems möchte ich zunächst die Systemanforderungen analysieren und herausfinden, warum Sie dieses System bauen möchten, welche Funktionsmodule dieses System hat und welche Funktionen jede Funktion hat implementiert.
Mein Hauptzweck hier ist natürlich, mein Abschlussprojekt fertigzustellen und meine Abschlussarbeit einzureichen. Aber ich habe dieses System nicht einfach entwickelt, um eine Abschlussarbeit fertigzustellen. Die Grundidee besteht darin, am Beispiel der Entwicklung dieses Einkaufs-, Verkaufs- und Bestandsverwaltungssystems den gesamten Entwicklungsprozess eines Systems von der Anforderungsbestätigung über die Technologieauswahl, den Architekturentwurf, die Systemimplementierung bis hin zum Testeinsatz detailliert zu erläutern. Nutzen Sie das Wissen, die Technologie und die Erfahrungen aus Praktika umfassend, um ein System vollständig und besser zu implementieren. Der Aufbau eines grundlegenden System-Frameworks, die Bildung bestimmter Spezifikationen und die anschließende Entwicklung auf dieser Grundlage können viel Arbeit wie den Aufbau des Frameworks, das Hinzufügen von Abhängigkeiten, die Konfiguration usw. einsparen. Die Hauptarbeit und Bedeutung dieser Entwicklung sind wie folgt:① Lernen Sie, Benutzerbedürfnisse aus der Perspektive des Benutzers zu analysieren, vollständige Bedarfsanalyse und -gestaltung usw.
③ Beherrscht die Entwicklungstechnologie von Spring+SpringMVC+Hibernate+ExtJs.
④ Beherrschen Sie die Verwendung des Maven-Versionsverwaltungstools. ⑤ Versuchen Sie aus Sicht eines Unternehmens, Ihr eigenes zugrunde liegendes Grundgerüst aufzubauen. ⑥ Richten Sie ein vollständiges System für den Kauf, Verkauf und die Bestandsverwaltung von Gasleitungen ein, führen Sie Tests durch und analysieren Sie die Ergebnisse. ⑦ Stellen Sie das System im Internet bereit, um echte Webanwendungen zu implementieren.Um die Anforderungen des Systems zu analysieren, müssen Sie zunächst verstehen, was das Kauf-, Verkaufs- und Lagersystem ist wird auch als Supply-Chain-Management-System bezeichnet. Die grundlegendsten Inhalte sind Einkauf, Lagerbestand, Verkauf und Retourenmanagement. Das Einkaufs-, Verkaufs- und Lagersystem verfolgt und verwaltet den Einkauf, die Lagerung und den Verkauf in der Produktion und im Betrieb des Unternehmens. Von der Bestellung über die Einlagerung der Waren bis hin zum Verkauf der Waren aus dem Lager wird jeder Schritt verfolgt und aufgezeichnet. Lösen Sie effektiv Geschäftsprobleme wie das Vertriebsmanagement von Unternehmen. Was ist also der Unterschied zwischen dem Kauf-, Verkaufs- und Inventarsystem für Gasleitungen? Tatsächlich verfügt das Kauf-, Verkaufs- und Inventarsystem bereits über die Kauf-, Verkaufs- und Inventarfunktion allgemeiner Waren, und Gasleitungen sind eine Ware. Ausgehend davon begannen wir mit der Entwicklung von Systemfunktionsmodulen.
Das System ist in 6 Hauptmodule unterteilt, nämlich Grundeinrichtung, Beschaffungsmanagement, Verkaufsmanagement, Bestandsverwaltung, statistische Analyse und System Management. Zu den vom System festgelegten Rollen gehören Administrator, Käufer, Verkäufer, Bestandsadministrator usw.
Systemmodulstruktur (entworfen mit xmind):
Ein Gesamtprozess des Systems, von der Erstmalige Verwendung Zunächst richtet das System einen Superadministrator (Admin) ein, der über alle Berechtigungen des Systems verfügt. Melden Sie sich als Administrator am System an, legen Sie Rollen fest (Systemadministrator, Käufer, Verkäufer, Bestandsadministrator) und weisen Sie entsprechende Berechtigungen zu. Geben Sie dann die Benutzerverwaltung ein und geben Sie die Systembenutzer ein. Allgemeine Administratoren melden sich am System an und geben grundlegende Daten ein; Käufer müssen Lieferanten und Bestellungen eingeben, und Verkäufer müssen Verkaufsaufträge und Retourenaufträge eingeben. Bestandsverwalter müssen in der Bestandsverwaltung eine Kauf- und Verkaufsprüfung durchführen. Wenn die Kaufprüfung bestanden wird, werden die Waren in das Lager eingelagert und der Bestand erhöht, wenn die Verkaufsprüfung bestanden wird Das Lager und der Bestand werden reduziert. Administratoren können außerdem monatliche Einkaufsstatistiken und Verkaufsstatistiken einsehen.
Gesamtsystemprozess (entworfen mit Visio):
Anforderungen ermittelt , ist es notwendig, in die spezifische Entwicklungsphase einzutreten und zunächst zu bestimmen, welche Technologien, Entwicklungstools usw. zur Entwicklung dieses Systems verwendet werden.
Lassen Sie uns kurz über die Entwicklungsumgebung dieses Systems sprechen:
Entwicklungsplattform: Windows 8.1
Java-Version: jdk 1.8
Projektmanagement-Tool: Maven
Entwicklungstools: Intellij IDEA
Datenbank: MySql 5.1
Server: Tomcat 8.5
Entwicklungsframework: Spring4 + SpringMVC + Hibernate5
Front-End-Framework: ExtJs 4.2 + Jsp
Modellierungstools: PowerDesigner, Visio
Maven ist ein automatisiertes Build-Tool, das von der Apache Software Foundation verwaltet wird. Der Schwerpunkt liegt auf der Projektkonstruktion und dem Abhängigkeitsmanagement für die Java-Plattform. Es stellt ein zentrales Lager bereit, das uns dabei helfen kann, Komponenten und Open-Source-Bibliotheken von Drittanbietern automatisch herunterzuladen. Sie müssen sich nur koordiniert auf ein JAR-Paket in Ihrem Projekt verlassen, und Maven lädt es automatisch aus dem zentralen Lager herunter und lädt gleichzeitig andere JAR-Pakete herunter, von denen dieses JAR-Paket abhängt, und Sie können die Quelle herunterladen Code zum Lesen. Nach der Verwendung von Maven wird nur eine Kopie jedes JAR-Pakets selbst im lokalen Lager gespeichert, was erheblich Speicherplatz spart, das Projekt leichter macht und Verwirrung durch zu viele doppelte Dateien vermeidet. Gleichzeitig kann Maven für uns automatisch alle anderen JAR-Pakete importieren, von denen das aktuelle JAR-Paket abhängt, ohne manuelle Beteiligung, was viel Zeit und Energie spart. Mit Maven können Sie den gesamten Prozess der Bereinigung, Kompilierung, Prüfung, Verpackung und Bereitstellung mit nur einem einfachen Befehl automatisch abschließen. Unsere Projekte sind im Allgemeinen in Entwicklungsumgebungen und Produktionsumgebungen unterteilt. Mit Maven können Sie die laufende Umgebung angeben und die entsprechenden Konfigurationsdateien ersetzen Fehler.
IDEA ist eine integrierte Umgebung für die Java-Sprachentwicklung und Intellij gilt als eines der besten Java-Entwicklungstools. IDEA ist relativ stark in den Bereichen automatische Code-Eingabeaufforderung, Refactoring, Debugging und Integration verschiedener Versionstools (Maven, SVN usw.). Ich habe während meines Praktikums auf die Ideenentwicklung umgestellt. Im Vergleich zu Eclipse bietet die Idee mehr Vorteile beim Debuggen und bei der automatischen Codeaufforderung. Projekte haben grundsätzlich eine benutzerfreundlichere Verzeichnisstruktur, insbesondere Projekte mit mehreren Projekten. Natürlich ist Eclipse einfacher zu verwenden als Idea. Die Verwendung von Idea kann Ihre Entwicklungsgeschwindigkeit erhöhen, aber nur, wenn Sie sich viele Tastenkombinationen merken müssen. Verwenden Sie beispielsweise die Debugging-Funktion von Idea. Drücken Sie einfach die Tastenkombination Alt+F8 und geben Sie dann einen Ausdruck ein, um ihn während des Debuggens schnell auszuwerten Kennen Sie jeden Schritt des Debuggens, was sehr praktisch ist. Die gute Nutzung von IDEA kann unsere Entwicklungsgeschwindigkeit erheblich verbessern.
ExtJs können zum Entwickeln von Rich-Client-Ajax-Anwendungen verwendet werden. Es handelt sich um ein in Javascript geschriebenes Front-End-Ajax-Framework, das nichts mit Back-End-Technologie zu tun hat Erstellen Sie Front-End-Benutzeroberflächen und verfügen Sie über leistungsstarke Datenverarbeitungsfunktionen sowie Diagrammstatistiken usw. Gleichzeitig stehen Ihnen bei ExtJs viele personalisierte Themes zur Auswahl, was es zu einer guten Wahl für die Entwicklung von Backend-Managementsystemen macht.
Bevor Sie das Kauf-, Verkaufs- und Inventarsystem entwerfen und programmieren, entwerfen Sie zunächst Ihr eigenes zugrunde liegendes Framework Grundlage für die Entwicklung anderer spezifischer Projekte, sodass nicht jedes Mal, wenn ein Projekt entwickelt wird, viele sich wiederholende Arbeiten durchgeführt werden müssen. Dieses zugrunde liegende Framework umfasst hauptsächlich eine Entwicklungsspezifikation und einige gängige Toolklassen. Noch wichtiger ist, dass es verschiedene Frameworks nach Kategorien einführt, wie z. B. Spring, Hibernate und verschiedene Konfigurationsdateien. Wenn während der zukünftigen Entwicklung einige neue Funktionen hinzugefügt werden, können diese gleichzeitig dieser untersten Ebene hinzugefügt und kontinuierlich verbessert werden.
Um den Softwareentwicklungsprozess reibungslos zu gestalten und die Zuverlässigkeit, Lesbarkeit und Wartbarkeit des Codes zu verbessern, muss zunächst festgelegt werden sind die Entwicklungsspezifikationen. Nun, es gibt keine Regel ohne Regeln, und das gilt auch für die Softwareentwicklung. Hier sind einige einfache Regeln, die Sie befolgen sollten.
Als Erstes müssen Sie einige grundlegende Spezifikationen einhalten. Im Allgemeinen verwenden Unternehmen Domänennamen als Grundlage für alle Benennungen, z. B. Dateinamen, Paketnamen usw. Deshalb habe ich einen Domainnamen [www.lyyzoo.com] als meinen persönlichen Domainnamen beantragt. Verwenden Sie dann D:/lyyzoo-repo als Stammverzeichnis für die Entwicklung, also das persönliche Code-Warehouse. Alle zukünftigen Projekte werden in diesem Verzeichnis erstellt. Die gesamte Projektentwicklung verwendet Maven zum Verwalten von Projekten, sodass die Verzeichnisstruktur einem Standardverzeichnis der Maven-Spezifikation entspricht.
Mavens vereinbarte Verzeichnisstruktur:
① Benennung
Alle Benennungsanforderungen Siehe die Bedeutung von Versuchen Sie sicherzustellen, dass die Bedeutung der Variablen anhand des Variablennamens bekannt ist, und fügen Sie Kommentare hinzu, wenn Kommentare erforderlich sind.
> Paketnamen sind alle in Kleinbuchstaben geschrieben, in der Form von Domänenname + Modul, wie zum Beispiel: com.lyyzoo.service
> Buchstabe, jeder schreibt den ersten Buchstaben eines Wortes groß.
> Der Methodenname übernimmt die Camel-Nomenklatur und beginnt mit einem Kleinbuchstaben. Der erste Buchstabe jedes Wortes wird automatisch mit Lombok generiert. Fügen Sie einfach die Annotation @Data hinzu.
> Variablennamen übernehmen die Camel-Benennungsmethode, beginnend mit einem Kleinbuchstaben, und der erste Buchstabe jedes Wortes wird großgeschrieben. Variablennamen sollten nicht zu lang sein und können in Form von Abkürzungen vorliegen, die Bedeutung des Namens muss jedoch klar sein.
> Konstantennamen werden ausschließlich in Großbuchstaben geschrieben und jedes Wort wird durch „_“ getrennt.
②Layering
Das Projekt ist in Funktionsmodule unterteilt. Verschiedene Projekte erstellen unterschiedliche Projekte und verwenden Maven-Abhängigkeiten für die Verwaltung. Zu den grundlegenden Schichten des Pakets gehören Controller (Kontrollschicht), Service (Geschäftsschicht), Dao (Datenzugriffsschicht) und Entität (Modellschicht).
Die unterste Ebene des gesamten Projekts konzentriert sich auf einige gemeinsame und grundlegende Dinge, die zur zukünftigen Wiederverwendung integriert werden. Erstellen Sie zunächst ein Maven-Projekt mit dem Namen lyyzoo. lyyzoo dient als zugrunde liegendes Stammverzeichnis. Es gibt zwei Hauptuntermodule unter lyyzoo, nämlich lyyzoo-base und lyyzoo-starter. Es ist ein Basismodul und wird für einige einfache Java- und JavaEE-Programme verwendet. Es ist mit JavaEE verwandt und hängt von lyyzoo ab. Basis und führt auch Frameworks von Drittanbietern wie Spring und Hibernate ein. Fügen Sie dann jedem Modul spezifische Untermodule hinzu. Welches Modul in der zukünftigen Entwicklung verwendet werden muss, fügen Sie einfach dieses Modul in den Abhängigkeiten hinzu.
Das zugrunde liegende Modulstrukturdiagramm:
Das Folgende ist die Beziehung zwischen jedem POM:
① lyyzoo > pom.xml
② lyyzoo-base > pom.xml
③ lyyzoo-starter > pom.xml
Nachdem die Struktur erstellt wurde, müssen Sie einige detaillierte Abhängigkeitskonfigurationsarbeiten durchführen. Lyyzoo ist die übergeordnete Klasse aller Module Fügen Sie es zu lyyzoo hinzu. Gemeinsame Eigenschaften, Abhängigkeitsverwaltung, Maven-Plug-Ins usw.
Geben Sie zunächst alle Versionsnummern aus und geben Sie sie in
1 <properties> 2 <!-- Global --> 3 <lyyzoo.version>1.0-SNAPSHOT</lyyzoo.version> 4 <java.version>1.8</java.version> 5 <maven.compiler.source>${java.version}</maven.compiler.source> 6 <maven.compiler.target>${java.version}</maven.compiler.target> 7 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 8 <!-- lombok --> 9 <lombok.version>1.16.14</lombok.version>10 <!-- log -->11 <slf4j.version>1.7.7</slf4j.version>12 <logback.version>1.1.3</logback.version>13 <!-- jdbc -->14 <mysql.version>5.1.38</mysql.version>15 <!-- -->16 <c3p0.version>0.9.5.2</c3p0.version>17 <!-- test -->18 <junit.version>4.12</junit.version>19 <!-- servlet -->20 <servlet.version>3.1.0</servlet.version>21 <!-- spring -->22 <spring.version>4.2.6.RELEASE</spring.version>23 <aspectjrt.version>1.7.3</aspectjrt.version>24 <aspectjweaver.version>1.7.3</aspectjweaver.version>25 <!-- hibernate -->26 <hibernate.version>5.0.1.Final</hibernate.version>27 <hibernate.jpa.version>1.0.0.Final</hibernate.jpa.version>28 </properties>
Führen Sie als Nächstes das Abhängigkeitsmanagement ein und führen Sie andere Projekte in lyyzoo ein. Die Abhängigkeiten von Alle JAR-Pakete von Drittanbietern werden zu
① Zuerst müssen Sie andere Module unter lyyzoo einführen, z. B. lyyzoo-base-core, lyyzoo-starter-base und andere Module.
1 <!-- lyyzoo --> 2 <dependency> 3 <groupId>com.lyyzoo</groupId> 4 <artifactId>lyyzoo-base-core</artifactId> 5 <version>${lyyzoo.version}</version> 6 </dependency> 7 <dependency> 8 <groupId>com.lyyzoo</groupId> 9 <artifactId>lyyzoo-base-data</artifactId>10 <version>${lyyzoo.version}</version>11 </dependency>12 <dependency>13 <groupId>com.lyyzoo</groupId>14 <artifactId>lyyzoo-starter-base</artifactId>15 <version>${lyyzoo.version}</version>16 </dependency>17 <dependency>18 <groupId>com.lyyzoo</groupId>19 <artifactId>lyyzoo-starter-jpa</artifactId>20 <version>${lyyzoo.version}</version>21 </dependency>
② JDBC-bezogen, das zugehörige JAR-Paket ist c3p0 und wird für die Datenbankverbindung verwendet Pool; MySQL-Treiberpaket; dbutils, das einfach JDBC kapselt und einfach und bequem zu verwenden ist.
1 <!-- jdbc --> 2 <dependency> 3 <groupId>com.mchange</groupId> 4 <artifactId>c3p0</artifactId> 5 <version>${c3p0.version}</version> 6 </dependency> 7 <dependency> 8 <groupId>mysql</groupId> 9 <artifactId>mysql-connector-java</artifactId>10 <version>${mysql.version}</version>11 </dependency>12 <dependency>13 <groupId>commons-dbutils</groupId>14 <artifactId>commons-dbutils</artifactId>15 <version>1.5</version>16 </dependency>
③ Protokollbezogen:
1 <!-- log --> 2 <dependency> 3 <groupId>org.slf4j</groupId> 4 <artifactId>slf4j-api</artifactId> 5 <version>${slf4j.version}</version> 6 </dependency> 7 <dependency> 8 <groupId>ch.qos.logback</groupId> 9 <artifactId>logback-classic</artifactId>10 <version>${logback.version}</version>11 </dependency>12 <!-- slf4j -->13 <dependency>14 <groupId>org.slf4j</groupId>15 <artifactId>log4j-over-slf4j</artifactId>16 <version>${slf4j.version}</version>17 </dependency>
④ Frühlingsbezogen, einschließlich Spring AOP, Spring MVC usw.
1 <!-- springframework --> 2 <dependency> 3 <groupId>org.springframework</groupId> 4 <artifactId>spring-aop</artifactId> 5 <version>${spring.version}</version> 6 </dependency> 7 <dependency> 8 <groupId>org.springframework</groupId> 9 <artifactId>spring-aspects</artifactId>10 <version>${spring.version}</version>11 </dependency>12 <dependency>13 <groupId>org.springframework</groupId>14 <artifactId>spring-beans</artifactId>15 <version>${spring.version}</version>16 </dependency>17 <dependency>18 <groupId>org.springframework</groupId>19 <artifactId>spring-context</artifactId>20 <version>${spring.version}</version>21 </dependency>22 <dependency>23 <groupId>org.springframework</groupId>24 <artifactId>spring-context-support</artifactId>25 <version>${spring.version}</version>26 </dependency>27 <dependency>28 <groupId>org.springframework</groupId>29 <artifactId>spring-core</artifactId>30 <version>${spring.version}</version>31 </dependency>32 <dependency>33 <groupId>org.springframework</groupId>34 <artifactId>spring-jdbc</artifactId>35 <version>${spring.version}</version>36 </dependency>37 <dependency>38 <groupId>org.springframework</groupId>39 <artifactId>spring-jms</artifactId>40 <version>${spring.version}</version>41 </dependency>42 <dependency>43 <groupId>org.springframework</groupId>44 <artifactId>spring-orm</artifactId>45 <version>${spring.version}</version>46 </dependency>47 <dependency>48 <groupId>org.springframework</groupId>49 <artifactId>spring-tx</artifactId>50 <version>${spring.version}</version>51 </dependency>52 <dependency>53 <groupId>org.springframework</groupId>54 <artifactId>spring-web</artifactId>55 <version>${spring.version}</version>56 </dependency>57 <dependency>58 <groupId>org.springframework</groupId>59 <artifactId>spring-webmvc</artifactId>60 <version>${spring.version}</version>61 </dependency>
⑤ Winterschlaf bezogen:
1 <dependency> 2 <groupId>org.hibernate</groupId> 3 <artifactId>hibernate-c3p0</artifactId> 4 <version>${hibernate.version}</version> 5 </dependency> 6 <dependency> 7 <groupId>org.hibernate.common</groupId> 8 <artifactId>hibernate-commons-annotations</artifactId> 9 <version>${hibernate.version}</version>10 </dependency>11 <dependency>12 <groupId>org.hibernate</groupId>13 <artifactId>hibernate-core</artifactId>14 <version>${hibernate.version}</version>15 </dependency>16 <dependency>17 <groupId>org.hibernate</groupId>18 <artifactId>hibernate-entitymanager</artifactId>19 <version>${hibernate.version}</version>20 </dependency>
所有的依赖添加好后,就需要为各个子模块添加具体的依赖了,根据每个子模块的功能,添加相关的依赖,而不是将所有的依赖一次性加入。这样我们就可以根据自己开发的项目的需要,添加模块依赖,而不是一次性加入所有jar包,避免冗余,增大项目的体积。下面以lyyzoo-base-data和lyyzoo-starter-jpa为例说明。
lyyzoo-base-data模块是基础数据相关,主要与数据库打交道,那么就需要引入mysql驱动、数据库连接池c3p0等,pom.xml如下:
1 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 2 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 "> 3 <modelVersion>4.0.0</modelVersion> 4 <parent> 5 <groupId>com.lyyzoo</groupId> 6 <artifactId>lyyzoo-base</artifactId> 7 <version>1.0-SNAPSHOT</version> 8 </parent> 9 <artifactId>lyyzoo-base-data</artifactId>10 <name>${project.artifactId}</name>11 <packaging>jar</packaging>12 13 <dependencies>14 <!-- dbutils -->15 <dependency>16 <groupId>commons-dbutils</groupId>17 <artifactId>commons-dbutils</artifactId>18 </dependency>19 <!-- jdbc -->20 <dependency>21 <groupId>mysql</groupId>22 <artifactId>mysql-connector-java</artifactId>23 </dependency>24 <dependency>25 <groupId>com.mchange</groupId>26 <artifactId>c3p0</artifactId>27 </dependency>28 <!-- test -->29 <dependency>30 <groupId>com.lyyzoo</groupId>31 <artifactId>lyyzoo-base-test</artifactId>32 <scope>test</scope>33 </dependency>34 </dependencies>35 </project>
lyyzoo-starter-jpa是Java持久化相关,所以主要引入hibernate相关的依赖,同时,starter-jpa会依赖base-data及starter-base,pom.xml如下:
1 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 2 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 "> 3 <modelVersion>4.0.0</modelVersion> 4 <parent> 5 <groupId>com.lyyzoo</groupId> 6 <artifactId>lyyzoo-starter</artifactId> 7 <version>1.0-SNAPSHOT</version> 8 </parent> 9 <artifactId>lyyzoo-starter-jpa</artifactId>10 <name>${project.artifactId}</name>11 <packaging>jar</packaging>12 13 <dependencies>14 <dependency>15 <groupId>com.lyyzoo</groupId>16 <artifactId>lyyzoo-base-data</artifactId>17 </dependency>18 <dependency>19 <groupId>com.lyyzoo</groupId>20 <artifactId>lyyzoo-starter-base</artifactId>21 </dependency>22 <dependency>23 <groupId>org.hibernate</groupId>24 <artifactId>hibernate-c3p0</artifactId>25 </dependency>26 <dependency>27 <groupId>org.hibernate.common</groupId>28 <artifactId>hibernate-commons-annotations</artifactId>29 </dependency>30 <dependency>31 <groupId>org.hibernate</groupId>32 <artifactId>hibernate-core</artifactId>33 </dependency>34 <dependency>35 <groupId>org.hibernate</groupId>36 <artifactId>hibernate-ehcache</artifactId>37 </dependency>38 <dependency>39 <groupId>org.hibernate.javax.persistence</groupId>40 <artifactId>hibernate-jpa-2.1-api</artifactId>41 </dependency>42 <dependency>43 <groupId>org.hibernate</groupId>44 <artifactId>hibernate-validator</artifactId>45 </dependency>46 <dependency>47 <groupId>com.lyyzoo</groupId>48 <artifactId>lyyzoo-starter-test</artifactId>49 <scope>test</scope>50 </dependency>51 </dependencies>52 </project>
有了前面的基础之后,接下来进行重点的类结构设计。底层需要做的最重要的工作就是将一些通用的类抽象出来,以便于以后重用,从而提高开发效率。例如返回结果和分页类的封装、通用的工具类、JDBC相关的操作等。
说明一下,整个底层很大一部分是从之前实习的公司(这个就不说了)直接拿过来的,有些则更改了。相信我拿取的这部分代码并不会涉及机密问题,不用于商业用途,仅仅只是学习,应该没多大问题。
lyyzoo-base-core 目录结构:
lyyzoo-base-core是基础核心,封装了返回结果和加入了一些常用的工具类。
com.lyyzoo.bean包下封装了BaseBean和Result,BaseBean是Bean类的一个基础类,实现了序列化,重写了toString()方法,如下:
1 package com.lyyzoo.bean; 2 3 import org.apache.commons.lang3.builder.ToStringBuilder; 4 import org.apache.commons.lang3.builder.ToStringStyle; 5 6 import java.io.Serializable; 7 8 /** 9 * BaseBean 实现序列化 <p>10 *11 * @author bojiangzhou12 * @date 2017-03-2713 */14 public abstract class BaseBean implements Serializable {15 16 /**17 * ToStringBuilder – 用于辅助实现Object.toString()方法<p>18 */19 public String toString() {20 return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);21 }22 23 }
Result则封装了返回的结果,这里主要与ExtJs相对应,ExtJs进行ajax请求时,返回success=true,会进入success()方法;返回success=false,会进入failure()方法。同时,封装了返回消息、标识、错误信息等,便于前端拿到相应数据。
1 package com.lyyzoo.bean; 2 3 import lombok.Data; 4 5 import java.util.Map; 6 7 /** 8 * 封装返回结果<p> 9 *10 * @author bojiangzhou11 * @date 2017-03-2712 */13 @Data14 public class Result extends BaseBean {15 private static final long serialVersionUID = 1L;16 17 /**18 * 成功标志19 */20 public boolean success;21 /**22 * 返回标示23 */24 public Integer code;25 /**26 * 相关消息27 */28 public String msg;29 /**30 * 相关数据31 */32 public Object data;33 /**34 * 错误详细35 */36 public Map<String, Object> errors;37 38 public Result() {39 40 }41 42 public Result(boolean success) {43 this.success = success;44 }45 46 public Result(boolean success, Integer code, Object data, String msg) {47 this(success);48 this.code = code;49 this.data = data;50 this.msg = msg;51 }52 53 public Result(Integer code, Map<String, Object> errors, String msg) {54 this.success = false;55 this.code = code;56 this.errors = errors;57 this.msg = msg;58 }59 60 public boolean isSuccess() {61 return this.success;62 }63 64 public boolean hasErrors() {65 if (this.errors != null && this.errors.size() > 0) {66 return true;67 }68 return false;69 }70 }
其它的主要是一些工具类,例如Arrays继承了org.apache.commons.lang3.ArrayUtils,使得Arrays具有了丰富的操作集合的工具。再如Dates,Dates里封装了大量的对日期操作的方法,比如格式化日期、获取某个日期当天的开始时间和结束时间等。
lyyzoo-starter-base 目录结构:
lyyzoo-starter-base是web应用程序的一个基础,主要封装了基础实体类以及spring-base和日志的配置。实体的顶层是AbstractEntity
1 package com.lyyzoo.data.entity; 2 3 import com.lyyzoo.bean.BaseBean; 4 5 import java.io.Serializable; 6 7 /** 8 * 抽象实体类 重写equals和hashCode方法 9 *10 * @author bojiangzhou11 * @date 2017-03-2812 */13 @SuppressWarnings("serial")14 public abstract class AbstractEntity<ID extends Serializable> extends BaseBean implements Serializable {15 16 public abstract ID getId();17 18 @Override19 public boolean equals(Object obj) {20 21 if (null == obj) {22 return false;23 }24 if (this == obj) {25 return true;26 }27 if (!getClass().equals(obj.getClass())) {28 return false;29 }30 31 AbstractEntity<?> that = (AbstractEntity<?>) obj;32 33 return null == this.getId() ? false : this.getId().equals(that.getId());34 }35 36 @Override37 public int hashCode() {38 int hashCode = 17;39 40 hashCode += null == getId() ? 0 : getId().hashCode() * 31;41 42 return hashCode;43 }44 45 }
BaseEntity则继承AbstractEntity,BaseEntity作为其它实体的父类存在,BaseEntity定义了主键ID的生成策略。代码如下:
1 package com.lyyzoo.data.entity; 2 3 import lombok.Data; 4 5 import javax.persistence.GeneratedValue; 6 import javax.persistence.GenerationType; 7 import javax.persistence.Id; 8 import javax.persistence.MappedSuperclass; 9 10 /**11 * 统一定义id的entity基类.12 * <p/>13 * 抽象实体基类,提供统一的ID,和相关的基本功能14 * <p/>15 */16 @Data17 @MappedSuperclass18 @SuppressWarnings("serial")19 public abstract class BaseEntity extends AbstractEntity<Long> {20 21 /**22 * id 字段23 */24 @Id25 protected Long id;26 27 @Id28 @GeneratedValue(strategy = GenerationType.IDENTITY)29 public Long getId() {30 return this.id;31 }32 33 public void setId(Long id) {34 this.id = id;35 }36 }
spring-base.xml是基础配置,主要配置了扫描系统的配置文件、注解等。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns="http://www.springframework.org/schema/beans" 4 xmlns:beans="http://www.springframework.org/schema/beans" 5 xmlns:context="http://www.springframework.org/schema/context" 6 xmlns:aop="http://www.springframework.org/schema/aop" 7 xmlns:tx="http://www.springframework.org/schema/tx" 8 xmlns:util="http://www.springframework.org/schema/util" 9 xmlns:mvc="http://www.springframework.org/schema/mvc"10 xmlns:jee="http://www.springframework.org/schema/jee"11 xmlns:jdbc="http://www.springframework.org/schema/jdbc"12 xmlns:tool="http://www.springframework.org/schema/tool"13 xmlns:dwr="http://www.directwebremoting.org/schema/spring-dwr"14 xsi:schemaLocation="15 http://www.springframework.org/schema/beans/spring-beans.xsd16 http://www.springframework.org/schema/context/spring-context.xsd17 http://www.springframework.org/schema/aop/spring-aop.xsd18 http://www.springframework.org/schema/tx/spring-tx.xsd19 http://www.springframework.org/schema/util/spring-util.xsd20 http://www.springframework.org/schema/mvc/spring-mvc.xsd21 http://www.springframework.org/schema/jee/spring-jee.xsd22 http://www.springframework.org/schema/jdbc/spring-jdbc.xsd23 http://www.springframework.org/schema/tool/spring-tool.xsd24 http://www.directwebremoting.org/schema/spring-dwr-3.0.xsd"25 >26 27 <description>Spring Base Config</description>28 29 <!-- 加载config.properties -->30 <context:property-placeholder location="classpath*:/config/*.properties" file-encoding="UTF-8" />31 32 <!-- 使用annotation 自动注册bean, 并保证@Required、@Autowired的属性被注入 -->33 <context:component-scan base-package="com.lyyzoo">34 <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>35 <context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>36 </context:component-scan>37 38 <!-- 支持上传文件 -->39 <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>40 41 </beans>
lyyzoo-starter-jpa 目录结构:
lyyzoo-starter-jpa集成了持久化相关的操作,配置等。
首先需要做持久化相关的配置(spring-base-jpa.xml),如数据源、SessionFactory、事务管理等。数据源使用c3p0,数据源相关配置如数据库驱动、地址等写到到配置文件中。配置Hibernate SessionFactory的同时,增加了JdbcTemplate。事务管理器使用Hibernate的事务管理。总的配置如下:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns="http://www.springframework.org/schema/beans" 4 xmlns:beans="http://www.springframework.org/schema/beans" 5 xmlns:tx="http://www.springframework.org/schema/tx" 6 xmlns:aop="http://www.springframework.org/schema/aop" 7 xmlns:context="http://www.springframework.org/schema/context" 8 xmlns:mvc="http://www.springframework.org/schema/mvc" 9 xmlns:util="http://www.springframework.org/schema/util" 10 xmlns:jee="http://www.springframework.org/schema/jee" 11 xmlns:jdbc="http://www.springframework.org/schema/jdbc" 12 xmlns:tool="http://www.springframework.org/schema/tool" 13 xmlns:dwr="http://www.directwebremoting.org/schema/spring-dwr" 14 xsi:schemaLocation=" 15 http://www.springframework.org/schema/beans/spring-beans.xsd 16 http://www.springframework.org/schema/context/spring-context.xsd 17 http://www.springframework.org/schema/aop/spring-aop.xsd 18 http://www.springframework.org/schema/tx/spring-tx.xsd 19 http://www.springframework.org/schema/util/spring-util.xsd 20 http://www.springframework.org/schema/mvc/spring-mvc.xsd 21 http://www.springframework.org/schema/jee/spring-jee.xsd 22 http://www.springframework.org/schema/jdbc/spring-jdbc.xsd 23 http://www.springframework.org/schema/tool/spring-tool.xsd 24 http://www.directwebremoting.org/schema/spring-dwr-3.0.xsd" 25 > 26 27 <description>Spring Jpa</description> 28 29 <!-- 使用C3P0 --> 30 <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> 31 <property name="driverClass" value="${jdbc.driver}"/> 32 <property name="jdbcUrl" value="${jdbc.url}"/> 33 <property name="user" value="${jdbc.username}"/> 34 <property name="password" value="${jdbc.password}"/> 35 36 <!-- default: 3 --> 37 <!-- 初始化时获取三个连接,取值应在minPoolSize与maxPoolSize之间。--> 38 <property name="initialPoolSize" value="${c3p0.initialPoolSize:5}"/> 39 40 <!-- default: 3 --> 41 <!-- 连接池保留最小连接数--> 42 <property name="minPoolSize" value="${c3p0.minPoolSize:5}"/> 43 44 <!-- default: 15 --> 45 <!-- 连接池保留最大连接数。--> 46 <property name="maxPoolSize" value="${c3p0.maxPoolSize:15}"/> 47 48 <!-- default: 0(秒) = 永不丢弃--> 49 <!-- 最大空闲时间,600 秒内未使用则连接被丢弃。--> 50 <property name="maxIdleTime" value="${c3p0.maxIdleTime:600}"/> 51 </bean> 52 53 <!-- sessionFactory --> 54 <bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"> 55 <property name="dataSource" ref="dataSource" /> 56 <property name="hibernateProperties" > 57 <props> 58 <prop key="dialect">${hibernate.dialect}</prop> 59 <prop key="show_sql">${hibernate.show.sql}</prop> 60 <prop key="hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop> 61 </props> 62 </property> 63 <!-- 扫描hibernate注解实体 --> 64 <property name="packagesToScan"> 65 <list> 66 <value>com.lyyzoo.*.entity</value> 67 </list> 68 </property> 69 </bean> 70 71 <!-- jdbcTemplate --> 72 <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> 73 <property name="dataSource" ref="dataSource"/> 74 </bean> 75 <bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate"> 76 <constructor-arg ref="dataSource"/> 77 </bean> 78 <bean id="jdbcTemplateSupport" class="com.lyyzoo.data.jdbc.JdbcTemplateSupport"> 79 <property name="dataSource" ref="dataSource"/> 80 <property name="namedParameterJdbcTemplate" ref="namedParameterJdbcTemplate"/> 81 </bean> 82 83 <!-- 事务管理器 --> 84 <bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager"> 85 <property name="sessionFactory" ref="sessionFactory" /> 86 </bean> 87 88 <!-- 事务通知属性 --> 89 <tx:advice id="transactionManagerAdvice" transaction-manager="transactionManager"> 90 <tx:attributes> 91 <tx:method name="get*" read-only="true" /> 92 <tx:method name="load*" read-only="true" /> 93 <tx:method name="find*" read-only="true" /> 94 <tx:method name="list*" read-only="true" /> 95 <tx:method name="page*" read-only="true" /> 96 <tx:method name="query*" read-only="true" /> 97 <tx:method name="*" propagation="REQUIRED" /> 98 </tx:attributes> 99 </tx:advice>100 101 <!-- AOP 事务 -->102 <aop:config proxy-target-class="true">103 <aop:pointcut id="serviceMethod" expression="execution(* com.lyyzoo..*Service.*(..))"/>104 <aop:advisor pointcut-ref="serviceMethod" advice-ref="transactionManagerAdvice"/>105 </aop:config>106 107 <!-- 扫描注解 -->108 <context:component-scan base-package="com.lyyzoo" />109 110 </beans>
然后在com.lyyzoo.data.domain包下,Page封装了分页相关的属性,如当前页数据、页码、每页大小、总页数等。com.lyyzoo.data.util包下,Sqls封装了操作sql语句的工具,比如根据传入的查询语句,返回查询总数的sql语句。在com.lyyzoo.data.jdbc包下,JdbcTemplateSupport封装了比较复杂的查询,比如根据传入的参数、分页条件等,自动查询数据、总数并封装到Page里返回。com.lyyzoo.data.dao包下,SimpleDao封装了基于Hibernate SessionFactory的简单的增删改查操作,如save(),get()等方法。dao包下,BaseDao继承SimpleDao,并代理了JdbcTemplateSupport,使得BaseDao具有SimpleDao和JdbcTemplateSupport的功能,BaseDao则作为其它dao的一个父类存在。最后,在com.lyyzoo.service包下,BaseService代理了BaseDao,进行了进一步的封装,BaseService是其它service类的父类,其它service就可以直接调用save、get、page等内置方法。
lyyzoo-starter-web自然跟web相关,在com.lyyzoo.web包下,BaseController作为controller类的父类,主要封装了返回结果集等信息。还有web相关配置,如视图解析器等内容:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns="http://www.springframework.org/schema/beans" 4 xmlns:beans="http://www.springframework.org/schema/beans" 5 xmlns:tx="http://www.springframework.org/schema/tx" 6 xmlns:aop="http://www.springframework.org/schema/aop" 7 xmlns:context="http://www.springframework.org/schema/context" 8 xmlns:mvc="http://www.springframework.org/schema/mvc" 9 xmlns:task="http://www.springframework.org/schema/task"10 xmlns:util="http://www.springframework.org/schema/util"11 xmlns:jee="http://www.springframework.org/schema/jee"12 xmlns:jdbc="http://www.springframework.org/schema/jdbc"13 xmlns:tool="http://www.springframework.org/schema/tool"14 xmlns:dwr="http://www.directwebremoting.org/schema/spring-dwr"15 xsi:schemaLocation="16 http://www.springframework.org/schema/beans/spring-beans.xsd17 http://www.springframework.org/schema/context/spring-context.xsd18 http://www.springframework.org/schema/aop/spring-aop.xsd19 http://www.springframework.org/schema/tx/spring-tx.xsd20 http://www.springframework.org/schema/util/spring-util.xsd21 http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd22 http://www.springframework.org/schema/task/spring-task-3.2.xsd23 http://www.springframework.org/schema/jee/spring-jee.xsd24 http://www.springframework.org/schema/jdbc/spring-jdbc.xsd25 http://www.springframework.org/schema/tool/spring-tool.xsd26 http://www.directwebremoting.org/schema/spring-dwr-3.0.xsd"27 >28 29 <description>Spring Web</description>30 31 <!-- 加载config.properties -->32 <context:property-placeholder location="classpath*:/config/*.properties" file-encoding="UTF-8" />33 34 <!-- 扫描Controller以及ControllerAdvice -->35 <context:component-scan base-package="com.lyyzoo" use-default-filters="false">36 <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>37 <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>38 </context:component-scan>39 40 <!-- 静态资源映射 -->41 <mvc:resources location="/static/" mapping="/static/**"/>42 43 <!-- 容器默认的DefaultServletHandler处理 所有静态内容与无RequestMapping处理的URL--> 44 <mvc:default-servlet-handler />45 46 <!-- 支持@Controller注解 -->47 <mvc:annotation-driven />48 49 <!-- 视图解析器 -->50 <bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">51 <property name="prefix" value="/WEB-INF/view"/>52 <property name="suffix" value=".jsp"/>53 </bean>54 55 </beans>
类结构图参考《底层类结构.vsd》
在开始开发一个系统之前,首先需要做的就是根据需求分析设计系统的实体对象以及对应的数据库表结构,这是开发的基础。
根据前面的需求分析设计的功能模块,实体对象可以分为5个模块,分别是系统模块(system)、基础模块(base)、采购模块(purchase)、销售模块(sale)、库存模块(stock)。下面是实体清单:
有了清单之后,利用PowerDesigner进行数据库物理模型设计。由于拥有对数据库的完全控制权,所以不对表设置约束,所有的约束在程序代码中进行控制。下面列出各个实体的属性即对应的表,具体可参考《数据库物理模型.pdm》。物理模型设计完成后,创建名为gpss的数据库,然后创建各个表。
数据库模型:
需求分析做完了,技术没问题,底层架构也设计好了,数据库设计好了,前面的所有准备工作做完了,下面就要进行燃气管进销存系统的编码实现了。首先要做的工作就是创建工程,项目名拟为gpss,即燃气管进销存(Gas Purchase Sale Stock)的缩写,工程名则为lyyzoo-gpss。
在IDEA中创建lyyzoo-gpss的maven工程,继承lyyzoo。同时,在lyyzoo-gpss下创建两个子模块,分别为lyyzoo-gpss-base和lyyzoo-gpss-web,base模块包含了系统的dao、entity、service等层次,主要为java文件;web则包含了controller层、jsp、js等web相关资源文件。
lyyzoo-gpss目录结构:
lyyzoo-gpss > pom.xml:
1 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 2 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 "> 3 <modelVersion>4.0.0</modelVersion> 4 <parent> 5 <groupId>com.lyyzoo</groupId> 6 <artifactId>lyyzoo</artifactId> 7 <version>1.0-SNAPSHOT</version> 8 </parent> 9 <groupId>com.lyyzoo.gpss</groupId>10 <artifactId>lyyzoo-gpss</artifactId>11 <name>lyyzoo :: gpss</name>12 <packaging>pom</packaging>13 14 <modules>15 <module>lyyzoo-gpss-base</module>16 <module>lyyzoo-gpss-web</module>17 </modules>18 19 <profiles>20 <!-- 开发环境 -->21 <profile>22 <id>dev</id>23 <activation>24 <property>25 <name>env</name>26 <value>dev</value>27 </property>28 </activation>29 </profile>30 <!-- 线上环境 -->31 <profile>32 <id>online</id>33 <activation>34 <property>35 <name>env</name>36 <value>online</value>37 </property>38 </activation>39 </profile>40 </profiles>41 <build>42 <plugins>43 <plugin>44 <groupId>com.juvenxu.portable-config-maven-plugin</groupId>45 <artifactId>portable-config-maven-plugin</artifactId>46 <version>1.1.5</version>47 <executions>48 <execution>49 <goals>50 <goal>replace-package</goal>51 </goals>52 </execution>53 </executions>54 <configuration>55 <portableConfig>src/main/resources/portable/config-${env}.xml</portableConfig>56 </configuration>57 </plugin>58 </plugins>59 </build>60 61 </project>
lyyzoo-gpss-base主要是dao层、service层、model层的集成,所以需要依赖于底层lyyzoo-starter-jpa、lyyzoo-starter-web。
lyyzoo-gpss-base > pom.xml:
1 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 2 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 "> 3 <modelVersion>4.0.0</modelVersion> 4 <parent> 5 <groupId>com.lyyzoo.gpss</groupId> 6 <artifactId>lyyzoo-gpss</artifactId> 7 <version>1.0-SNAPSHOT</version> 8 </parent> 9 <artifactId>lyyzoo-gpss-base</artifactId>10 <name>lyyzoo :: gpss :: base</name>11 <packaging>jar</packaging>12 13 <dependencies>14 <dependency>15 <groupId>com.lyyzoo</groupId>16 <artifactId>lyyzoo-starter-jpa</artifactId>17 </dependency>18 <dependency>19 <groupId>com.lyyzoo</groupId>20 <artifactId>lyyzoo-starter-web</artifactId>21 </dependency>22 </dependencies>23 24 </project>
lyyzoo-gpss-web是web相关,是controller层所在。依赖于lyyzoo-gpss-base、lyyzoo-starter-base等。
lyyzoo-gpss-web > pom.xml:
1 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 2 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 "> 3 <modelVersion>4.0.0</modelVersion> 4 <parent> 5 <groupId>com.lyyzoo.gpss</groupId> 6 <artifactId>lyyzoo-gpss</artifactId> 7 <version>1.0-SNAPSHOT</version> 8 </parent> 9 <artifactId>lyyzoo-gpss-web</artifactId>10 <name>lyyzoo :: gpss :: web</name>11 <packaging>war</packaging>12 13 <dependencies>14 <dependency>15 <groupId>com.lyyzoo.gpss</groupId>16 <artifactId>lyyzoo-gpss-base</artifactId>17 <version>${project.version}</version>18 </dependency>19 <dependency>20 <groupId>com.lyyzoo</groupId>21 <artifactId>lyyzoo-starter-base</artifactId>22 </dependency>23 <dependency>24 <groupId>com.lyyzoo</groupId>25 <artifactId>lyyzoo-starter-jpa</artifactId>26 </dependency>27 <dependency>28 <groupId>com.lyyzoo</groupId>29 <artifactId>lyyzoo-starter-web</artifactId>30 </dependency>31 <dependency>32 <groupId>com.lyyzoo</groupId>33 <artifactId>lyyzoo-starter-test</artifactId>34 </dependency>35 36 37 </dependencies>38 39 40 <build>41 <finalName>gpss</finalName>42 <outputDirectory>src/main/webapp/WEB-INF/classes</outputDirectory>43 </build>44 45 </project>
工程建好后,首要要做的就是系统的配置工作了,如web.xml,这应该算是web项目的起点了。进入lyyzoo-gpss-web/src/main/webapp/WEB-INF/web.xml,进行web的配置,主要的一些配置有加载系统的配置文件、Spring、字符过滤器、SpringMVC等配置。
一些基础的配置如下:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app xmlns="http://java.sun.com/xml/ns/javaee" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 5 " 6 version="3.0"> 7 8 <display-name>lyyzoo :: gpss</display-name> 9 10 <!-- 加载配置文件 -->11 <context-param>12 <param-name>contextConfigLocation</param-name>13 <param-value>classpath*:/spring/spring-base*.xml</param-value>14 </context-param>15 16 <!-- Spring -->17 <listener>18 <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>19 </listener>20 21 <!-- 编码过滤器 -->22 <filter>23 <filter-name>encodingFilter</filter-name>24 <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>25 <init-param>26 <param-name>encoding</param-name>27 <param-value>UTF-8</param-value>28 </init-param>29 <init-param>30 <param-name>forceEncoding</param-name>31 <param-value>true</param-value>32 </init-param>33 </filter>34 <filter-mapping>35 <filter-name>encodingFilter</filter-name>36 <url-pattern>/*</url-pattern>37 </filter-mapping>38 39 <!-- 访问控制 -->40 <filter>41 <filter-name>VisitFilter</filter-name>42 <filter-class>com.lyyzoo.gpss.filter.VisitFilter</filter-class>43 </filter>44 <filter-mapping>45 <filter-name>VisitFilter</filter-name>46 <url-pattern>/admin/*</url-pattern>47 </filter-mapping>48 49 <!-- Spring MVC -->50 <servlet>51 <servlet-name>SpringMVC</servlet-name>52 <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>53 <init-param>54 <param-name>contextConfigLocation</param-name>55 <param-value>classpath*:/spring/spring-web*.xml</param-value>56 </init-param>57 <load-on-startup>1</load-on-startup>58 </servlet>59 <servlet-mapping>60 <servlet-name>SpringMVC</servlet-name>61 <url-pattern>/</url-pattern>62 </servlet-mapping>63 64 <!-- 错误页面 -->65 <error-page>66 <error-code>404</error-code>67 <location>/error/404</location>68 </error-page>69 <error-page>70 <error-code>500</error-code>71 <location>/error/500</location>72 </error-page>73 <error-page>74 <exception-type>java.lang.Throwable</exception-type>75 <location>/error/500</location>76 </error-page>77 78 <!-- 首页 -->79 <welcome-file-list>80 <welcome-file>/</welcome-file>81 </welcome-file-list>82 83 </web-app>
接着,配置系统将会用到的一些属性,如JDBC驱动、数据库用户名和密码等。在lyyzoo-gpss-web/src/main/resources/config下,创建config.properties配置文件,这个配置文件中的属性将会被底层spring配置的文件所引用。
比如JDBC的属性:
1 #################################### 2 # DATABASE 3 #################################### 4 # local_db 5 jdbc.driver=net.sf.log4jdbc.DriverSpy 6 jdbc.url=jdbc:log4jdbc:mysql://localhost:3306/gpss?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&failOverReadOnly=false 7 jdbc.username=root 8 jdbc.password=root 9 10 # 初始化连接11 c3p0.initialPoolSize=512 # 连接池保留最小连接数13 c3p0.minPoolSize=514 # 连接池保留最大连接数15 c3p0.maxPoolSize=1516 # 最大空闲时间17 c3p0.maxIdleTime=60018 19 #hibernate20 hibernate.dialect=org.hibernate.dialect.MySQL5Dialect21 hibernate.show.sql=false22 hibernate.hbm2ddl.auto=update
但是上面的配置只是本机的一个开发环境,如果我将项目发布到生产环境,那就至少需要重新修改数据库地址、用户名和密码。这样是比较麻烦的,所以,在lyyzoo-gpss-web/src/main/portable下创建两个xml文件,分别为开发环境和生产环境的:config-dev.xml和config-online.xml。
config-online.xml:
1 <?xml version="1.0" encoding="utf-8" ?> 2 3 <portable-config> 4 <config-file path="WEB-INF/classes/config/config.properties"> 5 6 <!-- JDBC --> 7 <replace key="jdbc.driver"><![CDATA[com.mysql.jdbc.Driver]]></replace> 8 <replace key="jdbc.url"><![CDATA[jdbc:mysql://192.168.1.91:30112/fff2f025c2b04?useUnicode=true&characterEncoding=utf-8]]></replace> 9 <replace key="jdbc.username">a6564a1169d94</replace>10 <replace key="jdbc.password">d3e6d1aea5e04</replace>11 12 <!-- hibernate -->13 <replace key="hibernate.show.sql">false</replace>14 <replace key="hibernate.hbm2ddl.auto">none</replace>15 16 <!-- debug -->17 <replace key="app.debug">false</replace>18 <replace key="app.env">online</replace>19 <replace key="logback.level">INFO</replace>20 <replace key="jdbc.resultsettable">ERROR</replace>21 22 23 </config-file>24 </portable-config>
然后配置一个“不同环境打包”的maven插件——“portable-config-maven-plugin”,通过该插件,就可以在打包的时候加上需要打包的环境,例如指定online环境,在打包时就会将config-online.xml中的属性替换到config.properties里,这样一来就不必我们手动去替换了。配置好之后,我们在打包时可以使用命令[mvn clean package –Denv=online]打包线上环境的war包。
maven环境配置,在lyyzoo-gpss > pom.xml中配置两个环境:
1 <profiles> 2 <!-- 开发环境 --> 3 <profile> 4 <id>dev</id> 5 <activation> 6 <property> 7 <name>env</name> 8 <value>dev</value> 9 </property>10 </activation>11 </profile>12 <!-- 线上环境 -->13 <profile>14 <id>online</id>15 <activation>16 <property>17 <name>env</name>18 <value>online</value>19 </property>20 </activation>21 </profile>22 </profiles>
不同环境打包插件(portable-config-maven-plugin)的配置:
1 <build> 2 <plugins> 3 <plugin> 4 <groupId>com.juvenxu.portable-config-maven-plugin</groupId> 5 <artifactId>portable-config-maven-plugin</artifactId> 6 <version>1.1.5</version> 7 <executions> 8 <execution> 9 <goals>10 <goal>replace-package</goal>11 </goals>12 </execution>13 </executions>14 <configuration>15 <portableConfig>src/main/resources/portable/config-${env}.xml</portableConfig>16 </configuration>17 </plugin>18 </plugins>19 </build>
工程建好后,创建包的结构。按照一般的分层方式,分为dao层、entity层、service层,同时,每层下按模块划分为system、base、purchase、sale、stock。然后在entity下根据表设计创建实体类。
lyyzoo-gpss-base 目录结构:
然后是lyyzoo-gpss-web模块,该模块主要是controller层,以及静态资源文件、jsp文件等。com.lyyzoo.gpss.web作为controller层的包,同样,在web下按系统模块划分。
lyyzoo-gpss-web 目录结构:
lyyzoo-gpss-web/src/main/webapp/static作为静态文件的根目录,static/lib目录作为三方类库的根目录,如ExtJs、jQuery、其它的插件等。static/css是系统css文件的根目录;static/img是图片的根目录;static/js是系统js文件根目录,/js下同样按模块划分。
静态资源文件目录结构:
jsp文件不能直接让用户访问,需要放到/WEB-INF下,与配置的spring视图解析器相对应,所有的jsp文件放到/WEB-INF/view目录下,view目录下按模块划分,index.jsp是系统的登录页面。/WEB-INF/layout/目录下,将jsp中需要引入的一些资源等做了整合,如ExtJs的文件、meta描述信息、taglib等,整合后,jsp中如果需要引入整合jsp即可,可减少很多重复的工作。
taglib.jsp中引入了标签和设置了资源的路径:
1 <%@ page contentType="text/html;charset=UTF-8" language="java" %> 2 3 <%-- 引入标签 --%> 4 <%@taglib prefix="c" uri="" %> 5 <%@taglib prefix="fn" uri="" %> 6 <%@taglib prefix="fmt" uri="" %> 7 <%@taglib prefix="sf" uri="" %> 8 9 <%-- 资源路径 --%>10 <c:set var="CTX" value="${pageContext.request.contextPath}" />11 <c:set var="STATIC_CTX_URL" value="${CTX}/static" />12 <c:set var="LIB" value="${STATIC_CTX_URL}/lib" />13 <c:set var="JS" value="${STATIC_CTX_URL}/js"/>14 <c:set var="CSS" value="${STATIC_CTX_URL}/css"/>15 <c:set var="IMG" value="${STATIC_CTX_URL}/img"/>
meta.jsp中主要设置了一些meta描述信息和ICO图标:
1 <%@ page contentType="text/html;charset=UTF-8" language="java" %> 2 <%@ include file="/WEB-INF/layout/taglib.jsp" %> 3 4 <meta charset="utf-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=9,chrome=1"> 6 7 <meta name="description" content=""> 8 <meta name="author" content=""> 9 <meta name="renderer" content="webkit">10 <%-- 图标 --%>11 <link type="image/x-icon" href="${IMG}/favicon.ico" rel="icon">
extjs-neptune.jsp则引入了ExtJs相关的css和js文件,以及jQuery等:
1 <%@ page contentType="text/html;charset=UTF-8" language="java" %> 2 <%@ include file="/WEB-INF/layout/taglib.jsp" %> 3 4 <link rel="stylesheet" type="text/css" href="${LIB}/ext/4.2.2/theme/ext-theme-neptune/ext-theme-neptune-all.css?1.1.11"/> 5 <link rel="stylesheet" type="text/css" href="${LIB}/ext/4.2.2/icons/icon.css?1.1.11"/> 6 7 <script type="text/javascript" src="${LIB}/ext/4.2.2/ext-all.js?1.1.11"></script> 8 <script type="text/javascript" src="${LIB}/ext/4.2.2/locale/ext-lang-zh_CN.js?1.1.11"></script> 9 <script type="text/javascript" src="${LIB}/jquery/2.1.1/jquery.js?1.1.11"></script>10 <script type="text/javascript" src="${JS}/Global.js?1.1.11"></script>11 12 <script type="text/javascript">13 window.CTX = "${CTX}";14 window.STATIC_CTX_URL = "${STATIC_CTX_URL}";15 window.LIB = "${LIB}";16 window.JS = "${JS}";17 window.CSS = "${CSS}";18 window.IMG = "${IMG}";19 </script>
WEB-INF目录结构:
首先创建一个BaseController,BaseController继承底层的BaseController,增加了HttpSession,以及获取当前登录用户的方法,这样其它的controller继承该BaseController后,就可以非常方便的获得当前session和登录用户了。
BaseController代码如下:
1 package com.lyyzoo.gpss.web; 2 3 import com.lyyzoo.gpss.entity.system.User; 4 import org.springframework.beans.factory.annotation.Autowired; 5 6 import javax.servlet.http.HttpSession; 7 8 /** 9 *10 * <p>11 *12 * @author bojiangzhou13 * @date 2017-04-0214 */15 public class BaseController extends com.lyyzoo.web.BaseController {16 17 @Autowired18 protected HttpSession session;19 20 public User getCurrentUser(){21 return (User) session.getAttribute("currentUser");22 }23 24 public Long getCurrentUserId(){25 return getCurrentUser().getId();26 }27 28 public String getCurrentUserAccount(){29 return getCurrentUser().getAccount();30 }31 32 }
创建HomeController作为登录和主页的处理器。主要包含的方法有访问登录页面,访问登录后的主页,以及登录处理等。Controller需要加上@Controller标注该类为controller,使用@RequestMapping()支持访问rest形式的地址访问。HomeController中注入UserService,用于处理用户登录相关的业务。
用户进入登录界面,jsp页面以的形式请求验证码,验证码使用工具类生成,以流的形式输出,生成的验证码保存到session中。用户输入登录账号、密码和验证码登录系统。首先前台会检测输入是否为空等,传到后台,从session中取出验证码判断验证码是否正确,不正确则刷新验证码并且需要用户重新输入验证码。验证码通过后,使用登录账号和密码查找数据库,如果有,则将该用户保存到session中,跳转到管理页面,登录成功。否则提示用户名或密码错误。
HomeController代码如下:
1 package com.lyyzoo.gpss.web; 2 3 import com.lyyzoo.gpss.entity.system.User; 4 import com.lyyzoo.gpss.service.system.UserService; 5 import com.lyyzoo.gpss.util.VCodeGenerator; 6 import com.lyyzoo.util.Cryptos; 7 import com.lyyzoo.util.Strings; 8 import org.springframework.beans.BeanUtils; 9 import org.springframework.beans.factory.annotation.Autowired; 10 import org.springframework.stereotype.Controller; 11 import org.springframework.web.bind.annotation.PathVariable; 12 import org.springframework.web.bind.annotation.RequestMapping; 13 import org.springframework.web.bind.annotation.RequestMethod; 14 15 import javax.imageio.ImageIO; 16 import javax.servlet.http.HttpServletResponse; 17 import javax.servlet.http.HttpSession; 18 import java.awt.image.BufferedImage; 19 import java.io.IOException; 20 21 /** 22 * <p> 23 * 24 * @author bojiangzhou 25 * @date 2017-03-29 26 */ 27 @Controller 28 @RequestMapping("") 29 public class HomeController extends BaseController { 30 31 @Autowired 32 private UserService userService; 33 34 /** 35 * 到首页/登录页面 36 */ 37 @RequestMapping(value = {"", "/", "/index", "/login"}) 38 public String index(){ 39 return "/index"; 40 } 41 42 /** 43 * 管理员主页 44 */ 45 @RequestMapping("/admin/home") 46 public String toHome(){ 47 return "/home/home"; 48 } 49 50 /** 51 * 登录 52 */ 53 @RequestMapping(value = "/login", method = RequestMethod.POST) 54 public String login(String account, String password, String vcode, HttpSession session){ 55 String redirect = REDIRECT("/login"); 56 //基本验证 57 if(Strings.isNullOrEmpty(account)){ 58 session.setAttribute("errorMsg", "账号必须填写!"); 59 return redirect; 60 } 61 if(Strings.isNullOrEmpty(password)){ 62 session.setAttribute("errorMsg", "密码必须填写!"); 63 return redirect; 64 } 65 if(Strings.isNullOrEmpty(vcode)){ 66 session.setAttribute("errorMsg", "验证码必须填写!"); 67 return redirect; 68 } 69 //验证码 70 String sessionVcode = (String) session.getAttribute("vcode"); 71 if(!vcode.equalsIgnoreCase(sessionVcode)){ 72 session.setAttribute("errorMsg", "验证码错误!"); 73 return redirect; 74 } 75 //验证用户名和密码 76 password = Cryptos.encryptMd5(password); 77 User loginUser = userService.login(account, password); 78 if(loginUser == null){ 79 session.setAttribute("errorMsg", "账号或密码错误!"); 80 return redirect; 81 } 82 if(loginUser.getIsLocked() == User.IsLocked.YES){ 83 session.setAttribute("errorMsg", "账号已锁定,不能登录!"); 84 return redirect; 85 } 86 87 //保存到session的时候清除密码 88 User currentUser = new User(); 89 BeanUtils.copyProperties(loginUser, currentUser); 90 currentUser.setPassword(null); 91 92 //登录成功 93 session.setAttribute("currentUser", currentUser); 94 95 return REDIRECT("/admin/home"); 96 } 97 98 /** 99 * 获取验证码100 */101 @RequestMapping("/vcode")102 public void getVCode(HttpSession session, HttpServletResponse response) throws IOException {103 //创建验证码生成器对象104 VCodeGenerator vcGenerator = new VCodeGenerator();105 //生成验证码106 String vcode = vcGenerator.generatorVCode();107 //将验证码保存在session域中,以便判断验证码是否正确108 session.setAttribute("vcode", vcode);109 //生成验证码图片110 BufferedImage vImg = vcGenerator.generatorRotateVCodeImage(vcode, true);111 //输出图像112 ImageIO.write(vImg, "gif", response.getOutputStream());113 }114 115 /**116 * 退出系统117 */118 @RequestMapping("/logoff")119 public String logoff(HttpSession session){120 session.invalidate();121 return REDIRECT("/");122 }123 124 @RequestMapping("/function")125 public String function(){126 return "/home/function";127 }128 129 @RequestMapping("/welcome")130 public String welcome(){131 return "/home/welcome";132 }133 134 /**135 * 错误页面136 * @param code137 * @return138 */139 @RequestMapping("/error/{code}")140 public String error(@PathVariable String code) {141 return "/error/" + code;142 }143 144 145 146 }
UserService中根据用户名和密码获取用户:
1 package com.lyyzoo.gpss.service.system; 2 3 @Service 4 public class UserService extends BaseService<User> { 5 @Autowired 6 private UserDao userDao; 7 /** 8 * 获取登录用户 9 */10 public User login(String account, String password){11 Map<String, Object> filter = new HashMap<>();12 filter.put("account", account);13 filter.put("password", password);14 filter.put("state", Applications.Flag.YES);15 16 User loginUser = get(filter);17 return loginUser;18 }19 }
Nachdem die Systementwicklung abgeschlossen ist, muss sie zunächst lokal getestet werden Als Ganzes wird ab der Anmeldung jedes Modul, jede Funktion und jeder Prozess im Detail getestet.
Testen Sie zunächst, ob der Benutzer nicht angemeldet ist. Der Benutzer kann nicht auf die Verwaltungsseite zugreifen. Geben Sie die Zugangsadresse direkt in die Adressleiste ein, um zu sehen, ob er zur Anmeldeseite springt. Testen Sie dann zumindest, ob das zu jeder Rolle gehörende Konto normal angemeldet ist. Überprüfen Sie nach der Anmeldung, ob das zu jeder Rolle gehörende Menü normal angezeigt wird.
Zum Testen anderer Module verwenden Sie das der jeweiligen Rolle entsprechende Konto, um sich beim System anzumelden und die entsprechenden Funktionen zu testen. Beispielsweise betritt der Administrator das System, um Grunddaten, Produktinformationen, Lagerinformationen usw. einzugeben. Der Einkaufsadministrator gibt Lieferanteninformationen ein, gibt Bestellungen ein und reicht sie zur Überprüfung ein. Der Vertriebsadministrator gibt Kundeninformationen ein, gibt Verkaufsaufträge ein und reicht sie zur Überprüfung ein. Der Lagerverwalter prüft die Bestellung, und wenn die Prüfung bestanden wird, erhöht sich der Lagerbestand; wenn der Kundenauftrag die Prüfung besteht, verringert sich der Lagerbestand. Überprüfen Sie die Bestandsinformationen und führen Sie die entsprechenden Vorgänge durch, um festzustellen, ob der Bestandsbetrag korrekt ist. Die Testergebnisse können in den Systemtest-Screenshots eingesehen werden.
Beim Testen der Systemfehlertoleranz wird hauptsächlich das Verhalten des Systems unter abnormalen Bedingungen getestet, indem einige falsche Datentypen und Werte außerhalb des Bereichs eingegeben werden. Wenn der Benutzer illegale Daten eingibt, wird er sofort daran erinnert, die richtigen Daten einzugeben. Zunächst wird im Frontend beurteilt, ob die vom Benutzer eingegebenen Daten rechtmäßig sind und ob sie eingegeben werden müssen. Nach der Übermittlung der Daten an das Backend wird auch im Code beurteilt, ob die Daten korrekt sind, bevor sie gespeichert werden zur Datenbank. Der vom System verwendete JDBC kann bis zu einem gewissen Grad auch SQL-Injection und andere Probleme verhindern. Wenn im System unvorhersehbare Ausnahmen auftreten, wird der Benutzer über eine benutzerfreundliche Oberfläche darauf hingewiesen, sodass Techniker das System zeitnah warten können.
Generell verlief der gesamte Testprozess reibungslos. Es gab einige kleinere Probleme, die sofort behoben wurden. Alle Funktionen wurden implementiert und das System kann einen grundlegenden Kauf-, Verkaufs- und Lagerprozess abdecken und in Zukunft erweitert werden.
Ich verwende Mopo Cloud, um das Projekt bereitzustellen. Es ist einfach zu verwenden und innerhalb einer bestimmten Grenze kostenlos. Erstellen Sie zunächst eine Java-Anwendung namens GPS in Mobo Cloud, erstellen Sie dann einen MySQL-Dienst, binden Sie ihn an die Java-Anwendung und kopieren Sie die Datenbankverbindung in die Konfigurationsdatei. Exportieren Sie die lokale GPS-Datenbank und importieren Sie sie in die erstellte MySQL-Anwendung. Verwenden Sie dann den Befehl mvn clean package –Denv=online in IDEA, um das Kriegspaket für die Online-Umgebung zu packen und das Kriegspaket in Mobo Cloud zu veröffentlichen. Projekt starten, dann zugreifen und testen, alles ist normal. Sie können auf den Domainnamen zugreifen, um ihn anzuzeigen. Da das Kompatibilitätsproblem von ext nicht vollständig gelöst wurde, wird empfohlen, Google Chrome zum Anzeigen zu verwenden. Später erfahren wir, wie man es in Clouds wie Alibaba Cloud bereitstellt.
Durch die schrittweise Gestaltung und Vervollständigung dieses Gasrohr-Einkaufs-, Verkaufs- und Inventarsystems habe ich ein besseres Verständnis für die Softwarearchitektur, das Design, Framework, Codierung, und ich bin besser im Umgang mit verschiedenen Tools und mit dem Softwareentwicklungsprozess vertrauter. Dieses System ist ein Produkt des Wissens, das ich in vier Jahren Studium gelernt habe, angefangen beim Einholen des Thesenvorschlags, dem Verstehen des Vorschlags, der Konsultation relevanter Informationen, der Durchführung von Bedarfsanalysen, dem Entwerfen von Funktionsmodulen und dem Entwerfen von Datenbanken bis hin zur Kombination meiner Berufserfahrung während des Studiums Praktikum und das Unternehmen Der architektonische Entwurf bildet seine eigene unterste Ebene. Auf dieser Basis erfolgt die Systementwicklung.
Nach fast einem Monat Abschlussprojekt habe ich mir relevante Kenntnisse in allen Aspekten der Softwareentwicklung angeeignet, mich mit dem gesamten Prozess vertraut gemacht und meinen Wissensstand in vier Jahren an der Universität getestet. Offensichtlich gibt es immer noch viele Mängel. Obwohl in diesem System beispielsweise das SSH-Framework verwendet wird, sind noch viele Dinge über die prinzipielle Implementierung des Frameworks unklar. Ich persönlich glaube, dass man beim Lernen wissen muss, was passiert und warum. Insbesondere bei der Technologie muss man die zugrunde liegenden Prinzipien verstehen, um seine Fähigkeiten zu verbessern. Diese werden in zukünftigen Arbeiten sukzessive verbessert.
Dies ist im Grunde das Ende des Schreibens seit vier Jahren an der Uni. Von nun an werde ich bei der Arbeit lernen.
Ich bin sehr glücklich, weil ich die Schule verlasse und zur Arbeit gehen kann, um Geld zu finden. Ich bin sehr traurig, weil ich die Schule verlasse und die angenehmste Zeit in meinem Leben möglicherweise meine Studienzeit ist.
Ich mag meine Schule sehr – die Southwest University, gelegen am Fuße des Jinyun-Berges, mit wunderschöner Umgebung und wunderschönen Frauen, haha!
Abschließend werden die Dokumente zur Systementwicklung geteilt: Kauf-, Verkaufs- und Inventarsystem für Gaspipeline-Netzwerkausrüstung
Das Verzeichnis von Das komprimierte Paket lautet wie folgt:
Das obige ist der detaillierte Inhalt vonEinkaufs-, Verkaufs- und Bestandsverwaltungssystem für Abschlussprojekte – beginnend mit der Grundkonstruktion. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!